[UnityShader学习日志]5.第九章实验记录

第九章-更复杂的光照

提要

  • 本章节对渲染路径,多光源及阴影的实现进行了讲解

渲染路径

  • 渲染路径——Rendering Path决定了光照是如何应用到UnityShader的,其实就是告诉Shader如何计算光照,实现光照效果

前向渲染路径

  • 前向渲染路径是最传统最常用的一种渲染路径,每进行一次完整的前向渲染,首先需要渲染对象的渲染图元,并计算颜色缓冲区深度缓冲区的信息,其中深度缓冲决定了一个片元是否可见(比较深度值),以此来进行光照等计算,更新颜色缓冲区中的颜色值
  • 多光源的情况:对于每个逐像素的光源,都需要一个Pass,也就是说如果一个物体在多个逐像素光源的影响范围内,这个物体就要执行多个Pass。渲染引擎通常会限制每个物体的逐像素光照数目

Render Mode

  • 在光源的Light组件中,有一个Render Mode属性,设置为Not Important的光源会按逐顶点/SH处理,Important则会按逐像素处理,其余的则会根据是否超过Quality Setting中逐像素光源数量来进行处理,默认是逐像素

Base Pass和Additional Pass

  • Base Pass:主要计算环境光,自发光,以及一个逐像素的平行光,默认支持阴影,只计算一次,第六章中都是在Bass Pass中进行的
  • Additional Pass:对于其他影响物体的逐像素光源,每个光源执行一次Pass,在Additional Pass中会开启混合模式——与上一次的光照结果在帧缓存中进行叠加,从而实现多个光照的渲染效果
  • 对于前向渲染,一个Unity Shader会定义一个Base Pass(也可以定义多次,如双面渲染),以及一个Additional Pass

顶点照明渲染路径

  • 是前向渲染路径的一个子集,在一个Pass中就可以完成对物体的渲染,所有光源都是逐顶点处理的,是最快速的渲染路径,也具有最广泛的硬件支持,不常用

延迟渲染路径

  • 延迟渲染利用了额外的缓冲区——G缓冲,它存储了表面的法线,位置,以及用于光照计算的材质属性,并且利用了一个额外的Pass,我们只计算看得见的片元

Unity的光源类型

  • 平行光:Directional Light:也就是之前一直用的,平行光可以照亮的范围是无限制的,类似于太阳,它没有一个位移的位置,几何属性只有光源的方向,光照强度也不会随着距离而发生改变
  • 点光源,由一个点发出,向所有方向衍生(球体)
  • 聚光灯:由一个特定位置向特定方向(锥形区域)延申的光

实验1-1:前向渲染体验:多光源

  • Base Pass,其实跟之前差不多(因为之前就是默认场景里只有一个平行光源),不过这次要加上编译指令#pragma multi_compile_fwdbase,它保证在Shader中使用光照衰减等光照变量可以被正确赋值
  • 关于衰减:平行光可以认为没有衰减,所以衰减值为1.0
      Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            #pragma multi_compile_fwdbase

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            struct a2v
            {   
                float4 vertex : POSITION;
                float3 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);

                return o;
            }

            fixed4 frag(v2f i) : SV_TARGET0
            {
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                fixed3 LightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                fixed3 halfDir = normalize(LightDir + viewDir);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,LightDir));
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal,halfDir)),_Gloss);

                fixed atten = 1.0;//衰减

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

            ENDCG

          }
  • Additional Pass,加入编译指令#pragma multi_compile_fwdbase,并且开启混合模式,不再计算环境光。
    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;
                    float3 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);

                    return o;
                }

                fixed4 frag(v2f i) : SV_TARGET0
                {
                    fixed3 worldNormal = normalize(i.worldNormal);
                    fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
 

                    #ifdef USING_DIRECTIONAL_LIGHT
                           fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                    #else
                           fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
                    #endif
                    

                    fixed3 halfDir = normalize(lightDir + viewDir);

                    #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

                    fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,lightDir));
                    fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal,halfDir)),_Gloss);

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

                }

            

            ENDCG
        }
  • 光照衰减纹理:Unity在内部使用一张名为_LightTexture()的纹理来计算光照衰减

Unity的阴影

实现阴影的原理

  • 现实生活中,当一个光源发射的一条光线遇到一个不透明的物体时,若不考虑反射,这条光线就会被该物体遮挡,无法继续照亮其他物体,那么这个物体就会向周围投射阴影,也就是说阴影就是光线无法到达的区域。
  • 在实时渲染中,一般使用Shadow Map来实现阴影,这种技术就是首先把摄像机的位置放在和光源重合的位置上,那么场景中该光源的阴影区域就是摄像机看不到的地方,这就是阴影映射纹理,本质上它也是一张深度图,因为它记录了从该光源的位置出发,能看到的场景中距离它最近的表面位置(也就是深度信息)。

ShadowCaster

  • 为了得到阴影映射纹理,可以按照正常的渲染流程,调用Base Pass和Additional Pass来更新深度信息,但这种方式会对性能浪费(因为又会进行光照的计算),实际上需要的只是深度信息,因此Unity选择使用一个额外的Pass来专门更新光源的阴影映射纹理,这个Pass就是LightMode标签被设置为ShadowCaster的Pass,它仅仅负责计算阴影映射纹理(输出深度信息)

投射与接收阴影

  • 投射阴影:若我们想要一个物体向其他物体投射阴影,就必须把该物体加入到指定光源的阴影映射纹理的计算中。
  • 接收阴影:若我们想要一个物体接收其他物体的阴影,就必须在Shader中对阴影映射纹理进行采样,把采样结果和光照结果相乘来产生阴影的效果

实验2-1:不透明物体接收阴影

  • 不透明物体接收阴影,只需要几个步骤:
  • 加入头文件#include "AutoLight.cginc"
  • 在输出结构体v2f中,添加一个内置宏SHADOW_COORDS ,注意参数为插值寄存器的索引值
  • 在顶点着色器中,使用TRANSFER_SHADOW(),计算阴影纹理坐标
  • 在片元着色器中,计算阴影值,使用内置宏SHADOW_ATTENUATION
  • 在输出光照计算结果时,让阴影值乘上漫反射与高光反射值
Shader "Unlit/Shadow"
{
     Properties
    {
        _Diffuse("Diffuse",color) = (1,1,1,1)
        _Specular("Specular",color) = (1,1,1,1)
        _Gloss("Gloss",Range(8.0,256)) = 20

    }
    SubShader
    {
        
        
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            #pragma multi_compile_fwdbase

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

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

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                SHADOW_COORDS(2)
            };

            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);

                TRANSFER_SHADOW(o);

                return o;
            }

            fixed4 frag(v2f i) : SV_TARGET0
            {
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                fixed3 LightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                fixed3 halfDir = normalize(LightDir + viewDir);
                fixed shadow = SHADOW_ATTENUATION(i);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,LightDir));
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal,halfDir)),_Gloss);

                fixed atten = 1.0;//衰减

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

            ENDCG

          }

            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;
                    float3 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);

                    return o;
                }

                fixed4 frag(v2f i) : SV_TARGET0
                {
                    fixed3 worldNormal = normalize(i.worldNormal);
                    fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
 

                    #ifdef USING_DIRECTIONAL_LIGHT
                           fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                    #else
                           fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
                    #endif
                    

                    fixed3 halfDir = normalize(lightDir + viewDir);

                    #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

                    fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,lightDir));
                    fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal,halfDir)),_Gloss);

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

                }

            

            ENDCG

        }
    }
    FallBack "Specular"
}

posted @ 2023-05-26 11:21  月牙同学  阅读(51)  评论(0)    收藏  举报