美文网首页
【Unity Shader入门精要学习】复杂的光照(一)

【Unity Shader入门精要学习】复杂的光照(一)

作者: 小王子称号发放NPC | 来源:发表于2019-09-15 14:41 被阅读0次

    Unity的渲染路径

    在Unity里,渲染路径(Rendering Path)决定了光照是如何应用到Unity Shader中的,只有正确的设置每个Pass的渲染路径,Unity才会给Shader提供正确的光源信息(位置,方向,颜色,强度,衰减),我们在计算光照时才会有正确的结果。
    在Unity中提供了两种渲染路径

    一、前向渲染(Forward Rendering Path)

    前向渲染也有两种路径:

    Base Pass
    Tags{"LightModel"="ForwardBase"}
    Addtional Pass
    Tags{"LightModel"="ForwardAdd"}
    

    前向渲染路径是传统的渲染方式,也是常用的一种渲染路径

    伪代码
    Pass
    {
        for(each primitive in 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);
                }  
            }
        }
    }
    

    1、原理

    每进行一次完整的前向渲染,都需要渲染该对象的渲染图元,并计算两个缓冲区的信息:颜色缓冲区,深度缓冲区。利用深度缓冲区来决定一个片元是否可见,如果可见就更新颜色缓冲区的颜色值。
    每个逐像素光源,都需要进行一次完整的渲染流程。如果一个物体在多个逐像素光源的影响区域内,则这个物体需要执行多个Pass,每个Pass计算一个逐像素的的光照结果,然后在帧缓冲中把这些光照结果混合起来得到最终的颜色值。也就是如果有N个物体受M个光源影响,则需要执行N*M次的Pass。所以逐像素的光源如果多了就会有更多的Pass。因此引擎一般都会限制逐像素光照的数目。

    2、Unity中的前向渲染

    而实际上一个Pass不仅仅是可以执行逐像素的光照,还可以进行逐顶点等光照,这取决于计算光照的流水线以及使用的数学模型。当渲染一个物体时,Unity会计算哪些光源会照亮这个物体,以及这个光源照亮物体的方式(就是用哪种方式来计算)。
    在Unity中,前向渲染路径有3种处理光照(即照亮物体的方式)的方式:逐顶点处理,逐像素处理,球谐函数(Spherical Harmonics Function)处理。而决定一个光源使用哪种处理模式取决于光源的类型和渲染的模式光源类型指的是该光源是平行光还是其他类型光,而光源的渲染模式指的是该光源是否是重要的(Important)。如果设置成Important,就意味着使用逐像素的方式。
    在前向渲染中,Unity会根据场景中各个光源的设置以及这些光源对物体的影响程度(距离物体的远近,光强度等)对场景中的光源进行一个重要度的排序。其中一定数目的光源按照逐像素的方式处理,最多有4个光源会逐顶点的方式处理,剩下的就会按照SH处理,具体规则如下:
    (1)场景中最亮的平行光总是按照逐像素处理。
    (2)渲染模式被设置成Not Important的会按照逐顶点或SH处理

    image.png
    (3)渲染模式被设置成Important的会按照逐像素处理
    image.png
    (4)如果场景中逐像素的光源数目小于Quality Setting中的逐像素光源数量,那么剩余的逐像素光源也会按照逐像素处理
    image.png
    两种Pass的对光照的计算
    (1)Base Pass
    支持访问光照纹理(lightmap)、平行光默认支持阴影(平行光源开启阴影功能)、可以在Base Pass中计算环境光,自发光(因为这两种光只需要计算一次就可以)。既可以逐像素光照,也可以逐顶点。一般只执行一次。
    (2)Addtional Pass
    Addtional Pass中渲染的光源默认情况下没有阴影,即便光源本身设置了Shadow Type,但是可以使用编译指令:
    #pragma multi_compile_fwadd_fullshadows
    为点光源,聚光灯开启阴影效果。还需要开启混合模式,因为我们需要把Addtional Pass中的得到光照颜色和上一次的光照叠加起来,从而得到多个光照渲染的效果,如果没有混合,之前的颜色会覆盖掉,看起来像只受最后一个光源的影响,一般都是Blend One One,也是可以是其他。
    Shadow Type

    通常对于前向渲染UnityShader只需要定义一个Base Pass(也可以定义多个,如双面渲染,需要先画后面,再画前面),一个Addtional Pass。一般Base Pass只会执行一次(定义多个就执行多次),而Addtional Pass会根据影响该物体的其他逐像素光源的数目被多次调用(逐顶点也可以)。
    渲染路径的设置用于告诉Unity该Pass在前向渲染路径中的位置,引擎就会在相关计算中填充一些内置变量。至于如何使用这些变量,完全取决于开发者,如在BasePass和Addtional Pass中逐顶点光照而不是逐像素光照。


    两种Pass的特性

    二、延迟渲染(Deferred Rendering Path)

    前向渲染的问题是:当场景中存在大量实时光源时,渲染性能会急剧下降。当有很多光源重叠在一个区域内,为了得到最终的光照颜色,需要为每个光源执行一次Pass,如果这个区域内很多物体那么执行Pass的次数会成倍增加,而每执行一次Pass就会重新渲染一次物体,这样性能会下降。
    延迟渲染(Deferred Rendering)目的是为了解决前向渲染产生的瓶颈问题(延迟渲染已经是一种古老的渲染方法,只是近几年又流行起来)。除了前向渲染使用的颜色缓冲区和深度缓冲区延迟渲染还会使用额外的缓冲区,被统称为G缓冲区(Geometry,G-Buffer)G缓冲区存储了我们所关心的表面信息(通常都是离摄像机最近的表面),如表面的法线,位置,材质属性。

    1、原理

    伪代码
    Pass1
    {
        //第一个Pass不进行光照,只根据深度把能写入G-Buffer的片元挑选出来
        for(each primitive in model)
        {
            //所有被这个三角形包围的片元
            for(each fragment covered by this primitive)
            {
                if(failed in depth test)
                {
                    //如果深度测试失败,说明该片元不可见,不应该被写入G-Buffer
                    discard;
                }
                else{
                     //如果可见就就要把相关信息存储到G-Buffer中
                    WriteGBuffer(materialInfo,pos,normal,lightDir,viewDir);
                }  
            }
        }
    }
    
    Pass2
    {
        //第二个Pass会根据G-Buffer中的片元信息进行光照
        for(each pixel in 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,第一个Pass中不进行任何光照,只根据深度信息把符合条件的片元信息存储到G-Buffer中。第二个Pass,会根据G-Buffer中的各个片元的信息,如法线,视角方向,漫反射系数等进行光照计算。
    可以看出延迟渲染和场景中的光源数量是没有关系的,因为经过第一个Pass之后,G-Buffer里面存储的就已经是可以直接进行光照计算的片元信息了,只需要画一次就可以了(对与前向渲染可能得需要画N*M次)。所以对于延迟渲染G-Buffer的大小才是性能的瓶颈所在,而G-Buffer又和屏幕空间大小有关,所以延迟渲染的效率在于我们使用的屏幕空间大小。所以说延迟渲染所有光源都可以使用逐像素处理。

    2、延迟渲染的缺点

    (1)不支持真正的抗锯齿(anti-aliasing)
    (2)无法处理半透明物体(半透明关闭了深度写入)
    (3)需要显卡支持

    3、Unity中的延迟渲染

    在Unity中使用延迟渲染。需要提供两个Pass。
    第一个Pass用于渲染G缓冲,在这个Pass中,需要把物体的漫反射颜色,高光反射颜色,平滑度,法线,和深度信息渲染到屏幕空间的G-Buffer中,对于每个物体,这个Pass只会执行一次。
    第二个Pass,用于真正的关照计算,会使用上一个Pass中的渲染数据来计算最终的颜色,在存储到帧缓冲区中。第二个Pass可以默认使用Unity内置的Standard光照模型。

    相关文章

      网友评论

          本文标题:【Unity Shader入门精要学习】复杂的光照(一)

          本文链接:https://www.haomeiwen.com/subject/olcpyctx.html