其他分享
首页 > 其他分享> > 读UnityShader入门精要第六章-Unity中的基础光照

读UnityShader入门精要第六章-Unity中的基础光照

作者:互联网

1.我们如何看到这个世界

1.1  光源

  光是由光源发出的,在实时渲染中,光源被当成一个没有体积的点.

  在光学中,使用辐照度(irradiance)来量化光.当光打在一个平面上时,平面接收到的光的辐照度由光本身的辐照度乘上光纤方向和平面的夹角的余弦值得到,因此最终使用点积来求辐照度.

1.2  吸收和散射

  光线到达物体表面后,会产生两种结果:散射(scattering)和吸收(absorption).

  散射改变光的方向.如果散射后光进入了物体内部,这种散射称为折射(refraction)或透射(transmission);如果散射后光射向物体外部,这种散射称为反射(reflection).对于不透明物体,经过折射后的光线还会继续与物体内部的颗粒相交,另一些则被物体吸收.同时,光线经过物体的反射后的光线将具有和入射光线不同的分布和颜色.

  在光照模型中,高光反射(specular)部分表示物体表面是如何反射光线的,漫反射(diffuse)部分则表示有多少光线会被折射\吸收和散射出表面.出射度(exitance)描述了出射光线和入射光线的方向和数量的比值.

1.3  着色

  着色(shadering):根据材质属性(如漫反射属性等)\光源信息(如光源方向\辐照度等),使用一个等式去计算沿某个观察方向的出射度的过程.这个等式也称为光照模型(Lighting Model).

1.4  BRDF光照模型

  BRDF(Bidirectional Reflectance Distribution Function):用于描述一个表面是如何和光照进行交互的方程.

2.标准光照模型

  标准光照模型只关心直接光照,即那些直接从光源发射出来照射到物体表面后,经过物体表面的一次反射直接进入摄像机的光线.

  标准光照模型将进入摄像机的光线分为4部分:

  1)自发光(emissive)

  2)高光反射(specular)

  3)漫反射(diffuse)

  4)环境光(ambient)

2.1  环境光

  在真实的世界中,物体除了被直接光照照亮外,还会被间接光照(在多个物体之间进行反射后进入摄像机的光线)照亮.由于是多次反射的光线,这类光照很难计算,因此使用环境光模拟间接光照.

  环境光就是一个全局变量,即场景中的所有物体都使用这个环境光.

2.2  自发光

  光线可以不经过反射直接由光源进入摄像机,这种类型的光称为自发光.自发光的计算不难,但是需要注意的是,自发光和光源不同,自发光并不会照亮周围的表面.

2.3  漫反射

  漫反射用于对那些被物体表面随机散射到各个方向的辐照度进行建模.漫反射的观察角度不重要,因为漫反射完全随机.

  漫反射光照符合兰伯特定理(Lambert`s law):

  这个公式代表漫反射结果的颜色等于光照颜色乘以漫反射表面材质颜色乘以漫反射表面辐照度.注意,漫反射表面辐照度不能为负值,这样可以避免表面被来自于后方的光照亮.

2.4  高光反射

  这里的高光反射是一种经验模型,并不能完全符合真实世界中的高光反射现象.

2.5  逐像素还是逐顶点

  光照模型如果在片元着色器中计算,我们称为逐像素光照(per-pixel lighting);光照模型如果在顶点着色器中计算,我们就称为逐顶点光照(per-vertex lighting).

  在逐像素光照中,每个像素都会先得到像素的法线,然后进行光照模型的计算,这种计算称为Phong着色.

  在逐顶点光照中,每个顶点上会计算光照,然后像素的光照会通过顶点进行插值得到,这称为高洛德着色.由于片元内部像素的光照是由顶点插值得到的,因此光照模型中有非线性的计算时会出现问题,而且片元内部像素的颜色总是暗于顶点处的最高颜色值.

 3.在UnityShader中实现漫反射光照模型

3.1  逐顶点光照

Shader "Custom/DiffuseVertexLevel"
{
    Properties
    {
        //用于漫反射光照模型的颜色
        _Diffuse("Diffuse", Color) = (1,1,1,1)
    }
        SubShader
    {
        Pass{
            //定义光照模式
            Tags{ "LightMode" = "ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "Lighting.cginc"//使用Unity的内置变量

            fixed4 _Diffuse;//光照模型颜色
            
            //顶点着色器输入
            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            //顶点着色器输出
            struct v2f {
                float4 pos : SV_POSITION;
                fixed3 color : COLOR;
            };

            //顶点着色器
            v2f vert(a2v v) {
                v2f o;
                //从物体局部空间转化到裁剪空间中
                o.pos = UnityObjectToClipPos(v.vertex);
                //获取环境光部分
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                //世界空间下的法向量
                fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                //世界空间下的光源方向
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                //计算漫反射部分,saturate函数将值限定在0-1之间
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));

                //最终颜色为环境光加上漫反射光颜色
                o.color = ambient + diffuse;

                return o;
            }

            fixed4 frag(v2f i) : SV_Target{
                return fixed4(i.color, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

3.2  逐像素光照

Shader "Custom/DiffusePixelLevel"
{
    Properties
    {
        //用于漫反射光照模型的颜色
        _Diffuse("Diffuse", Color) = (1,1,1,1)
    }
        SubShader
    {
        Pass{
            //定义光照模式
            Tags{ "LightMode" = "ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "Lighting.cginc"//使用Unity的内置变量

            fixed4 _Diffuse;//光照模型颜色
            
            //顶点着色器输入
            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            //顶点着色器输出
            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
            };

            v2f vert(a2v v) {
                v2f o;
                //从物体局部空间转化到裁剪空间中
                o.pos = UnityObjectToClipPos(v.vertex);
                //法向量也从局部空间转化到裁剪空间中
                o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);

                return o;
            }

            //片元着色器
            fixed4 frag(v2f i) : SV_Target{

                //获取环境光部分
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                //世界空间下的法向量
                fixed3 worldNormal = normalize(i.worldNormal);
                //世界空间下的光源方向
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                //计算漫反射部分,saturate函数将值限定在0-1之间
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));

                //计算最终颜色
                fixed3 color = ambient + diffuse;

                return fixed4(color, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

3.3  两种光照模型的效果对照

  左侧为逐顶点光照的效果,右侧为逐像素光照的效果.

4.在UnityShader中实现高光反射光照模型

4.1  逐顶点光照

Shader "Custom/SpecularVertexLevel"
{
    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"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

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

            struct v2f {
                float4 pos : SV_POSITION;
                float3 color : COLOR;
            };

            v2f vert(a2v v) {
                v2f o;
                
                o.pos = UnityObjectToClipPos(v.vertex);
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

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

                //计算世界坐标下反射方向
                fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
                //计算世界坐标下视角方向
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);

                //计算反射部分颜色
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

                //计算得到最终的颜色
                o.color = ambient + diffuse + specular;
                
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                return fixed4(i.color, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

4.2  逐像素光照

Shader "Custom/SpecularPixelLevel"
{
    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"

            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).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 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
                //计算世界坐标下视角方向
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

                //计算反射部分颜色
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

                return fixed4(ambient + diffuse + specular, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

4.3  Blimm-Phong光照模型

  对逐像素光照模型中计算高光反射的代码稍作修改就是Blimm-Phong光照模型:

Shader "Custom/BlinnPhong"
{
    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"

            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).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.xyz - i.worldPos.xyz);
                fixed3 halfDir = normalize(worldLightDir + viewDir);

                //计算反射部分颜色
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Gloss);

                return fixed4(ambient + diffuse + specular, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

4.4  3种高光反射光照对比

  左中右分别为逐顶点光照模型\逐片元光照模型\Blinn-Phong光照模型.

标签:vertex,fixed3,Unity,UnityShader,v2f,worldNormal,精要,Diffuse,光照
来源: https://www.cnblogs.com/movin2333/p/16648044.html