复杂的光照

参考文献:《Unity shader入门精要》

Unity的渲染路径

渲染路径主要用于和光源打交道,shader编写中需要为pass指定渲染路径,这样unity才会明白程序员想要使用的渲染路径,然后将光源和处理后的光照信息放到这些数据里,程序员才能在着色器中进行访问。

大多数情况下,一个项目只使用一种渲染路径,因此我们可以为整个项目设置渲染时的渲染路径。在Unity的Edit->Project Settings->Player->Other Settings->Rendering Path中选择项目所需的渲染路径。默认情况下,该选择的是前向渲染路径。

但一个摄像机下可指定不同的渲染路径,同样在Rendering Path下指定。

利用pass标签下的LightMode来指定渲染路径,eg:

Pass{

Tag{"LightMode"="ForwardBase"}

}

上面的代码将告诉Unity,该Pass将使用前向渲染路径中的ForwardBase路径。

LightMode标签支持的渲染路径设置选项

标签名

描述

Always

不管使用哪种渲染路径,该Pass总是会被渲染,但不会计算任何光照

ForwardBase

用于前向渲染,该Pass会计算环境光,最重要的平行光,逐顶点/SH光源和Lightmaps

ForwardAdd

用于前向渲染。该Pass会计算额外的逐像素光源,每个Pass对应一个光源

Deferred

用于延迟渲染。该Pass会渲染G缓冲(G-Buffer)

ShadowCaster

把物体的深度信息渲染到阴影映射纹理(shadowmap)或一张深度纹理中

PrepassBase

用于遗留的延迟渲染。该Pass会渲染法线和高光反射的指数部分

PrepassFinal

用于遗留的延迟渲染。该Pass通过合并纹理、光照和自发光来渲染得到最后的颜色

Vertex、VertexMRGBM和VertexLM

用于遗留的顶点照明渲染

使用渲染路径的主要作用是为Unity内置的光照变量进行正确的赋值。

前向渲染路径

原理:每进行一次完整的前向渲染路径,我们需要渲染该对象的渲染图元,并计算两个缓冲区信息:一个是颜色缓冲区,一个是深度缓冲区,深度缓冲区决定一个片元是否可见,如果可见再更新颜色缓冲区中的颜色值。

前向渲染三种处理光照的方式:

1. 逐顶点处理

2. 逐像素处理

3. 球鞋函数处理(SH处理)

决定一个光源使用哪种处理模式取决于它的类型和渲染模式。光源类型是指该光源是平行光还是点光源还是其他类型的光源。而光源的渲染模式指的是该光源是否是重要的。

Unity实际处理光源使用的判断规则如下:

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

前向渲染两种Pass浅析:(BasePass和Additional Pass)

  • 在使用basePass的时候,通常需要加上#pragma multi_compile_fwdbase来保证我们在使用光照衰减等光照变量可以正确的被赋值,同理使用additional Pass的时候需要加上#pragma multi_compile_fwdadd
  • BasePass计算的光照为一个逐像素的平行光以及所有逐顶点和SH光源,而Additional Pass计算的光照为其他影响该物体的逐像素光源每个光源执行一次pass
  • BassPass默认支持阴影,而Additional Pass默认没有阴影效果,不过可以用#pragma multi_compile_fwdadd_fullshadows来代替#pragma multi_compile_fwdadd,为点光源和聚光灯开启阴影效果
  • 在Additional Pass中一般会开启混合模式,因为每一个Additional Pass会和上一次光照效果进行叠加,从而得到最终的有多个光照的渲染效果。通常采用的混合模式为Blend One One
  • 对于前向渲染来说,通常会定义一个BasePass和一个AdditionalPass,一个BasePass只会执行一次,而一个AdditionalPass没一个逐像素光源都会执行一次

延迟渲染

在前向渲染下的场景中光源数目较多造成性能瓶颈的情况下使用

原理:延迟渲染主要包含个Pass。在第一个Pass中,我们不进行任何光照计算,而是仅仅计算哪些片元是可见的,这主要是通过深度缓冲技术来实现,当发现一个片元是可见的,我们就把它的相关信息存储到G缓冲区中。然后,在第二个Pass中,我们利用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缓冲中的信息
                        readGBuffer(pixel,materialInfo,pos,normal,light,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数目通常就是两个,这跟场景中包含的光源数目是没有关系的。所以延迟渲染的效率不依赖于场景的复杂度,而是和我们使用的屏幕空间的大小有关。这是因为,我们需要的信息都存储在缓冲区中,而这些缓冲区可以理解成一张张2D图像,我们的计算实际上就是在这些图像空间中进行的。

使用延迟渲染的注意事项:

  • 对于延迟渲染路径来说,它最适合在场景中光源数目很多、如果使用前向渲染会造成性能瓶颈的情况下使用。
  • 延迟渲染有一些缺陷:1. 不支持真正的抗锯齿功能。 2. 不能处理半透明物体。 3. 对显卡有一定要求,如果要使用延迟渲染的话,显卡必须支持MRT,Shader Mode 3.0以上,深度渲染纹理以及双面的模板缓冲。

光源的类型:

  • 平行光
  • 点光源
  • 聚光灯

前向渲染的实例解析:

shader代码:

Shader "Custom/Chapter9-ForwardRendering"
{
    Properties
    {
    //材质属性 BlinnPhong模型中需要的三个常量参数
    _Diffuse("Diffuse",Color)=(1,1,1,1)    //漫反射中所需的diffuse值(漫反射系数)
    _Specular("Specular",Color)=(1,1,1,1)  //高光反射中所需的specular值(高光反射系数)
    _Gloss("Gloss",Range(8.0,256))=20  //高光反射中所需的材质光泽度
        
    }
    SubShader
    {
        
        //Base Pass
        Pass{
            Tags{"LightMode"="ForwardBase"}

            CGPROGRAM
            //使用该指令能确保使用光照衰减等光照变量能被正确赋值
            #pragma multi_compile_fwdbase  
            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            struct a2v{
                float4 vertex:POSITION;
                float4 normal:NORMAL;
            };

            struct v2f{
                float4 pos:SV_POSITION;
                float3 worldNormal:TEXCOORD0;
                float3 worldPos:TEXCOORD1;
            };

            v2f vert(a2v v){
                v2f o;
                //得到裁剪空间下的顶点坐标
                o.pos=UnityObjectToClipPos(v.vertex);
                //得到世界坐标系下的模型法线和顶点坐标
                o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject);
                o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;

                return o;
            }

            fixed4 frag(v2f i):SV_Target{
                //计算环境光
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;

                //利用兰伯特公式计算漫反射
                fixed3 worldNormal=normalize(i.worldNormal);
                fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);
                fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir));

                //计算高光反射
                fixed3 viewDir=normalize(_WorldSpaceCameraPos-i.worldPos.xyz);
                fixed3 halfDir=normalize(viewDir+worldLightDir);
                fixed3 specular=_LightColor0.rgb*_Specular*pow(saturate(dot(worldNormal,halfDir)),_Gloss);
                
                //计算光照衰减
                fixed atten=1.0;

                return fixed4(ambient+(diffuse+specular)*atten,1.0);

            }
                
            ENDCG
        }

        //Additional Pass
        Pass{
            Tags{"LightMode"="ForwardAdd"}

            //设置混合模式
            Blend One One

            CGPROGRAM
            //使用该指令能确保使用光照衰减等光照变量能被正确赋值
            #pragma multi_compile_fwdadd
            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            
            struct a2v{
                float4 vertex:POSITION;
                float4 normal:NORMAL;
            };

            struct v2f{
                float4 pos:SV_POSITION;
                float3 worldNormal:TEXCOORD0;
                float3 worldPos:TEXCOORD1;
            };

            v2f vert(a2v v){
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);
                o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject);
                o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
                return o;
            }

            fixed4 frag(v2f i):SV_Target{
                
                //fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;无需计算环境光

                fixed3 worldNormal=normalize(i.worldNormal);

                //根据不同光源类型来计算光照方向
                #ifdef USING_DIRECTIONAL_LIGHT
                    fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);
                #else
                     fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz-i.worldPos.xyz);
                #endif

                fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir));

                fixed3 viewDir=normalize(_WorldSpaceCameraPos-i.worldPos.xyz);
                fixed3 halfDir=normalize(viewDir+worldLightDir);
                fixed3 specular=_LightColor0.rgb*_Specular*pow(saturate(dot(worldNormal,halfDir)),_Gloss);

               //根据不同光源类型来计算光照的衰减
                #ifdef USING_DIRECTIONAL_LIGHT
                    fixed atten=1.0;    
                #else
                    float3 lightCoord=mul(unity_WorldToLight,float4(i.worldPos,1)).xyz;
                    fixed atten=tex2D(_LightTexture0,dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL;
                #endif

                return fixed4((diffuse+specular)*atten,1.0);

            }

            ENDCG
        }
    }
    FallBack "Diffuse"
}

猜你喜欢

转载自blog.csdn.net/a1601611709/article/details/114270580