当前位置: 首页 > article >正文

《UnityShader 入门精要》更复杂的光照

代码&示例图见:zaizai77/Shader-Learn: 实现一些书里讲到的shader

到了这里就开启了书里的中级篇,之后会讲解 Unity 中的渲染路径,如何计算光照衰减和阴影,如何使用高级纹理和动画等一系列进阶内容

Unity 中的渲染路径

在Unity里,渲染路径(Rendering Path)决定了光照是如何应用到Unity Shader中的。我们需要为每个Pass指定它使用的渲染路径

在Unity 5.0版本之前,主要有3种:前向渲染路径(Forward Rendering Path)​、延迟渲染路径(Deferred Rendering Path)和顶点照明渲染路径(Vertex Lit Rendering Path)​。

设置Rendering path

摄像机组件的Rendering Path中的设置可以覆盖Project Settings中的设置

如果当前的显卡并不支持所选择的渲染路径,Unity会自动使用更低一级的渲染路径。例如,如果一个GPU不支持延迟渲染,那么Unity就会使用前向渲染。

        Pass  {
            Tags  {  "LightMode"  =  "ForwardBase"  }

前向渲染路径

前向渲染路径的原理

每进行一次完整的前向渲染,我们需要渲染该对象的渲染图元,并计算两个缓冲区的信息:一个是颜色缓冲区,一个是深度缓冲区。我们利用深度缓冲来决定一个片元是否可见,如果可见就更新颜色缓冲区中的颜色值。伪代码描述:

        Pass  {
            for  (each  primitive  in  this  model)  {
              for  (each  fragment  covered  by  this  primitive)  {
                  if  (failed  in  depth  test)  {
                      // 如果没有通过深度测试,说明该片元是不可见的
                      discard;
                  }  else  {
                      // 如果该片元可见
                      // 就迚行光照计算
                      float4  color  =  Shading(materialInfo,  pos,  normal,  lightDir,  viewDir);
                      // 更新帧缓冲
                      writeFrameBuffer(fragment,  color);
                  }
              }
            }
        }

对于每个逐像素光源,我们都需要进行上面一次完整的渲染流程。如果一个物体在多个逐像素光源的影响区域内,那么该物体就需要执行多个Pass,每个Pass计算一个逐像素光源的光照结果,然后在帧缓冲中把这些光照结果混合起来得到最终的颜色值。假设,场景中有N个物体,每个物体受M个光源的影响,那么要渲染整个场景一共需要N*M个Pass。可以看出,如果有大量逐像素光照,那么需要执行的Pass数目也会很大。因此,渲染引擎通常会限制每个物体的逐像素光照的数目。

Unity中的前向渲染

一个Pass不仅仅可以用来计算逐像素光照,它也可以用来计算逐顶点等其他光照。这取决于光照计算所处流水线阶段以及计算时使用的数学模型。当我们渲染一个物体时,Unity会计算哪些光源照亮了它,以及这些光源照亮该物体的方式。

在Unity中,前向渲染路径有3种处理光照(即照亮物体)的方式:逐顶点处理、逐像素处理,球谐函数(Spherical Harmonics, SH)处理。而决定一个光源使用哪种处理模式取决于它的类型和渲染模式。光源类型指的是该光源是平行光还是其他类型的光源,而光源的渲染模式指的是该光源是否是重要的(Important)​。

在前向渲染中,当我们渲染一个物体时,Unity会根据场景中各个光源的设置以及这些光源对物体的影响程度(例如,距离该物体的远近、光源强度等)对这些光源进行一个重要度排序

  • 场景中最亮的平行光总是按逐像素处理的。
  • 渲染模式被设置成Not Important的光源,会按逐顶点或者SH处理。
  • 渲染模式被设置成Important的光源,会按逐像素处理。
  • 如果根据以上规则得到的逐像素光源数量小于Quality Setting中的逐像素光源数量(Pixel Light Count),会有更多的光源以逐像素的方式进行渲染。

光照会在Unity Shader的Pass中进行计算。前向渲染有两种Pass:

  • Base Pass
  • Additional Pass

  • #pragma multi_compile_fwdbase和#pragma multi_compile_fwdadd,只有分别为Bass Pass和Additional Pass使用这两个编译指令,得到一些正确的光照变量,例如光照衰减值等
  • Base Pass旁边的注释给出了Base Pass中支持的一些光照特性。例如在Base Pass中,我们可以访问光照纹理(lightmap)​。
  • Base Pass中渲染的平行光默认是支持阴影的(如果开启了光源的阴影功能)​,而Additional Pass中渲染的光源在默认情况下是没有阴影效果的,即便我们在它的Light组件中设置了有阴影的Shadow Type。但我们可以在Additional Pass中使用 #pragma multi_compile_fwdadd_fullshadows代替#pragma multi_compile_fwdadd编译指令,为点光源和聚光灯开启阴影效果,但这需要Unity在内部使用更多的Shader变种。
  • 环境光和自发光也是在Base Pass中计算的。这是因为,对于一个物体来说,环境光和自发光我们只希望计算一次即可,而如果我们在Additional Pass中计算这两种光照,就会造成叠加多次环境光和自发光,这不是我们想要的。
  • 在Additional Pass的渲染设置中,我们还开启和设置了混合模式。这是因为,我们希望每个Additional Pass可以与上一次的光照结果在帧缓存中进行叠加,从而得到最终的有多个光照的渲染效果。如果我们没有开启和设置混合模式,那么Additional Pass的渲染结果会覆盖掉之前的渲染结果,看起来就好像该物体只受该光源的影响。通常情况下,我们选择的混合模式是Blend One One
  • 对于前向渲染来说,一个Unity Shader通常会定义一个Base Pass(Base Pass也可以定义多次,例如需要双面渲染等情况)以及一个Additional Pass。一个Base Pass仅会执行一次(定义了多个Base Pass的情况除外)​,而一个Additional Pass会根据影响该物体的其他逐像素光源的数目被多次调用,即每个逐像素光源会执行一次Additional Pass。

(要记得东西也太多了吧。。。。)

内置的光照变量和函数

顶点照明渲染路径

顶点照明渲染路径是对硬件配置要求最少、运算性能最高,但同时得到效果最差的一种类型。它仅支持逐顶点的光源计算,不支持逐像素,所以无法得到:阴影、法线映射、高精度的高光反射等效果,它是前向渲染的一个子集。

Unity 中的顶点照明渲染

顶点照明渲染路径通常在一个Pass中就可以完成对物体的渲染,在这个Pass中,我们会计算关心的光源对物体的照明,且是按照逐顶点处理的。

它是Unity中最快速的渲染路径,且具有最广泛的硬件支持(游戏机上不支持这种路径)。Unity 5以后被作为一个遗留的渲染路径,未来的版本中可能会被移除。

可访问的内置变量和函数

在Unity中一个顶点照明的Pass中最多可以访问8个逐顶点光源。

顶点照明渲染路径中可以使用的内置变量:

如果影响该物体的光源数目小于8,则数组中剩余的光源颜色会被设置成黑色。有些变量我们同样可以在前向渲染路径中使用,如:unity_LightColor,但这些变量的数组的维度和数值在不同渲染路径下的值是不同的。

顶点照明渲染路径中可以使用的内置函数:

延迟渲染路径

当场景中包含大量实时光源,使用前向渲染会造成性能急剧下降。每个光源都会执行一个Pass,重复地渲染一个物体会存在很多相同的重复计算。

在这样的环境下,本是一种更古老的渲染方法的延迟渲染,又流行了起来。除了前向渲染中使用的颜色缓冲深度缓冲外,延迟渲染还使用了额外的缓冲区——G缓冲(Geometry Buffer,G-buffer)。G缓冲区存储了我们关心的表面(通常指离相机最近的表面)的其他信息,如:表面的法线、位置、用于光照计算的材质属性等。

延迟渲染的过程大致可以用下面的伪代码来描述:

        Pass  1  {
            // 第一个Pass不迚行真正的光照计算
            // 仅仅把光照计算需要的信息存储到G缓冲中

            for  (each  primitive  in  this  model)  {

              for  (each  fragment  covered  by  this  primitive)  {
                  if  (failed  in  depth  test)  {
                    // 如果没有通过深度测试,说明该片元是不可见的
                    discard;
                  }  else  {
                    // 如果该片元可见
                    // 就把需要的信息存储到G缓冲中
                    writeGBuffer(materialInfo,  pos,  normal,  lightDir,  viewDir);
                  }
              }
          }
        }

        Pass  2  {
          // 利用G缓冲中的信息迚行真正的光照计算

          for  (each  pixel  in  the  screen)  {
              if  (the  pixel  is  valid)  {
                  // 如果该像素是有效的
                  // 读取它对应的G缓冲中的信息
                  readGBuffer(pixel,  materialInfo,  pos,  normal,  lightDir,  viewDir);

                  // 根据读取到的信息迚行光照计算
                  float4  color  =  Shading(materialInfo,  pos,  normal,  lightDir,  viewDir);
                  // 更新帧缓冲
                  writeFrameBuffer(pixel,  color);
              }
          }
        }

延迟渲染使用的Pass数目通常是2个,与场景中包含的光源数目没有关系,和屏幕的大小有关。

延迟渲染适合在光源数目众多、使用前向渲染造成性能瓶颈的情况下使用,每个光源都可以按逐像素处理。但它也有缺点:

  • 不支持真正的抗锯齿功能;
  • 不能处理半透明物体;
  • 对显卡有要求,必须支持MRT(Multiple Render Targets)、Shader Mode 3.0及以上、深度渲染纹理以及双面的模板缓冲。

延迟渲染需要两个Pass:

  • 第一个Pass用于渲染G缓冲

把物体的漫反射颜色、高光反射颜色、平滑度、法线、自发光和深度等信息渲染到屏幕空间的G缓冲区中对每个物体来说,这个Pass仅执行一次。

  • 第二个Pass用于计算真正的光照模型

使用上一个Pass中渲染的数据来计算最终的颜色光照颜色,再存储到帧缓冲中。

这些变量都可以在UnityDeferred Library.cginc文件中找到它们的声明。

更加深入的东西就直接看官方文档吧,太深入了,写了也是白写,记不住

Unity - Manual: Introduction to rendering paths in the Built-In Render Pipeline

4种渲染路径(前向渲染路径、延迟渲染路径、遗留的延迟渲染路径和顶点照明渲染路径)的详细比较,包括它们的特性比较(是否支持逐像素光照、半透明物体、实时阴影等)​、性能比较以及平台支持

Unity 的光源类型

之前的例子中用且仅用了一个平行光光源,之后在本节中,我们将会学习如何在Unity中处理点光源(point light)和聚光灯(spot light)​。

Unity一共支持4种光源类型:平行光、点光源、聚光灯和面光源(area light)面光源仅在烘焙时才可发挥作用,所以这里不讨论

光源类型有什么影响

最常使用的光源属性有:光源的位置方向(到某点的方向)、颜色强度以及衰减(到某点的衰减,与点到光源的距离有关)。

平行光

它的几何定义是最简单的。平行光可以照亮的范围是没有限制的,它通常是作为太阳这样的角色在场景中出现的,它没有一个唯一的位置,也就是说,它可以放在场景中的任意位置,它的几何属性只有方向,光照强度不会随着距离而发生改变。

点光源

点光源的照亮空间则是有限的,它是由空间中的一个球体定义的。点光源可以表示由一个点发出的、向所有方向延伸的光


http://www.kler.cn/a/410860.html

相关文章:

  • Redis-09 SpringBoot集成Redis
  • .net core MVC入门(一)
  • python语言基础-5 进阶语法-5.4 正则表达式
  • java charAt()返回数值型 详解
  • Element UI 打包探索【2】
  • Linux---ps命令
  • 力扣——寻找峰值
  • 智能合约运行原理
  • 实现可视化大屏的适配,并且解决缩放导致的事件偏移问题
  • 【源码】Sharding-JDBC源码分析之SQL中分片键路由ShardingSQLRouter的原理
  • pytorch torch.Tensor.item() 方法介绍
  • 【VRChat 改模】开发环境搭建:VCC、VRChat SDK、Unity 等环境配置
  • Pytorch使用手册-Datasets DataLoaders(专题三)
  • 李春葆《数据结构》-课后习题代码题
  • Ubuntu20.04+ROS 进行机械臂抓取仿真:环境搭建(一)
  • Amazon商品详情API接口:电商创新与用户体验的驱动力
  • 电子消费品生产线:科技的时尚,玛哈特矫平机为生产线打造平整面板
  • 【SpringBoot】MapStruct生成映射代码
  • 【论文笔记】Number it: Temporal Grounding Videos like Flipping Manga
  • Qt之QMainWidget相关
  • nohup java -jar supporterSys.jar --spring.profiles.active=prod
  • 5.算法移植第六篇YOLOV5 /onnx模型转换成rknn
  • Oracle 深入学习 Part 8: Managing Tablespaces and Data Files(管理表空间和数据文件)
  • Linux中的权限管理
  • 数据结构 ——— 快速排序算法的实现(hoare版本)
  • 贵州茅台[600519]行情数据接口