视觉效果

《Shader 入门精要》之环境光+漫反射+高光反射

2020-10-22  本文已影响0人  烂醉花间dlitf

UnityCG.cginc 部分结构体

struct appdata_base {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct appdata_tan {
    float4 vertex : POSITION;
    float4 tangent : TANGENT;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct appdata_full {
    float4 vertex : POSITION;
    float4 tangent : TANGENT;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
    float4 texcoord1 : TEXCOORD1;
    float4 texcoord2 : TEXCOORD2;
    float4 texcoord3 : TEXCOORD3;
    fixed4 color : COLOR;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

可视化

测试


Shader "Unlit/01TestShader"
{
    Properties
    {

    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f
            {
                float4 position:SV_POSITION;
                fixed3 color:COLOR0;
            };

            v2f vert(appdata_base i) 
            {
                v2f o;
                o.position  = UnityObjectToClipPos(i.vertex); // 模型坐标转换到裁剪坐标
                o.color = fixed3(0.5,1,1); // 每个顶点都是蓝色的
                return o;
            }

            fixed4 frag(v2f i):SV_Target
            {   
                return fixed4(i.color,1);              
            }

            ENDCG
        }
    }
}

运行结果

UV 可视化


Shader "Unlit/01TestShader"
{
    Properties
    {

    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f
            {
                float4 position:SV_POSITION;
                fixed3 color:COLOR0;
            };

            v2f vert(appdata_base i) 
            {
                v2f o;
                o.position  = UnityObjectToClipPos(i.vertex);
                o.color = fixed3(i.texcoord.xy,0);
                return o;
            }

            fixed4 frag(v2f i):SV_Target
            {   // r,b 取 uv 坐标,如果超过了 [0,1],那么加蓝调。
                fixed3 c = frac(i.color); // 取小数部分
                if(any(saturate(i.color.xy)-i.color.xy)) // saturate,限制在 [0,1] 之间,any,全部为 0 ,返回 false
                {
                    c.b = 0.5;
                    // return fixed4(0,0,1,1); // 超过了 [0,1],直接变成蓝色。
                }
                return fixed4(c,1);              
            }

            ENDCG
        }
    }
}
运行结果

法线可视化


Shader "Unlit/01TestShader"
{
    Properties
    {

    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f
            {
                float4 position:SV_POSITION;
                fixed3 color:COLOR0;
            };

            v2f vert(appdata_full  i) 
            {
                v2f o;
                o.position  = UnityObjectToClipPos(i.vertex);
                o.color = i.normal * 0.5 + 0.5; // 把法线映射到 [0,1] 的范围
                return o;
            }

            fixed4 frag(v2f i):SV_Target
            {   
                return fixed4(i.color,1);
            }

            ENDCG
        }
    }
}
运行效果

切线可视化


Shader "Unlit/01TestShader"
{
    Properties
    {

    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f
            {
                float4 position:SV_POSITION;
                fixed3 color:COLOR0;
            };

            v2f vert(appdata_full  i) 
            {
                v2f o;
                o.position  = UnityObjectToClipPos(i.vertex);
                o.color = i.tangent * 0.5 + 0.5; // 把切线映射到 [0,1] 的范围
                return o;
            }

            fixed4 frag(v2f i):SV_Target
            {   
                return fixed4(i.color,1);
            }

            ENDCG
        }
    }
}

运行结果

副切线可视化


Shader "Unlit/01TestShader"
{
    Properties
    {

    }
    SubShader
    {
        Pass
        {
            Fog { Mode Off }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

           struct appdata {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
            float4 tangent : TANGENT;
            };

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

            v2f vert(appdata_full  i) 
            {
                v2f o;
                o.pos  = UnityObjectToClipPos(i.vertex);
                float3 bitangent = cross( i.normal, i.tangent.xyz ) * i.tangent.w; // w 为 1 或者 -1,用来确定副切线的方向
                o.color.xyz = bitangent * 0.5 + 0.5; // 把副切线映射到 [0,1] 的范围
                // o.color.xyz = fixed3(-1,-1,-1); 

                return o;
            }

            fixed4 frag(v2f i):SV_Target
            {   
                return fixed4(i.color,1);
            }

            ENDCG
        }
    }
}

运行结果

漫反射模型

逐顶点

Shader "MyShader/MyDiffuseShader"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityShaderVariables.cginc"
            #include "Lighting.cginc"
            fixed4 _Diffuse;

            struct appdate
            {
                float4 position:POSITION;
                float3 normal:NORMAL;
            };

            struct v2f
            {
                float4 position:SV_POSITION;
                fixed3 color:COLOR0; 
            };

            v2f vert (appdate i)
            {
                v2f o;
                o.position = UnityObjectToClipPos(i.position);

                // 环境光
                fixed4 ambient = UNITY_LIGHTMODEL_AMBIENT;

                // 漫反射光源
                float3 normal = normalize(mul(i.normal,(float3x3)unity_WorldToObject)); // 计算法线,这种方式避免法线直接通过矩阵变换之后与表面不垂直的情况。
                float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz); // 光照方向
                o.color = ambient + _LightColor0 * _Diffuse * saturate(dot(normal,lightDirection)); // 颜色 * 就是对应位置相乘
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4(i.color,1);
            }
            ENDCG
        }
    }
}
运行结果

最后有个颜色相乘,其实就是对应位置相乘,比如说 Diffuse 为 (1,0,0),然后直射光颜色为 (0,1,0)。那么结果就是 (0,0,0),显示出来也就是黑色的。而且不能随着光源的远近有不同亮度的变化,并且应该是被完全遮挡的地方也可能会很亮,比如嘴巴那边,光是从背照过来的。

逐像素

Shader "MyShader/MyDiffuseShader"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityShaderVariables.cginc"
            #include "Lighting.cginc"
            fixed4 _Diffuse;

            struct appdate
            {
                float4 position:POSITION;
                float3 normal:NORMAL;
            };

            struct v2f
            {
                float4 position:SV_POSITION;
                fixed3 normal:COLOR0; 
            };

            v2f vert (appdate i)
            {
                v2f o;
                o.position = UnityObjectToClipPos(i.position);
                o.normal = normalize(mul(i.normal,(float3x3)unity_WorldToObject)); // 计算法线
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz); // 光照的方向
                fixed3 lightColor = _LightColor0.rgb; // 光源的颜色
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 计算环境光
                fixed3 color = ambient + _Diffuse.rgb * lightColor * saturate(dot(i.normal,lightDirection)); // 计算最终的颜色
                return fixed4(color,1);
            }
            ENDCG
        }
    }
}
运行结果

半兰伯特模型

Shader "MyShader/MyDiffuseShaderHalfLambert"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1)
        _HalfLambert("HalfLambert",float) = 0
    }
    SubShader
    {
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityShaderVariables.cginc"
            #include "Lighting.cginc"
            fixed4 _Diffuse;
            float _HalfLambert;

            struct appdate
            {
                float4 position:POSITION;
                float3 normal:NORMAL;
            };

            struct v2f
            {
                float4 position:SV_POSITION;
                fixed3 normal:COLOR0; 
            };

            v2f vert (appdate i)
            {
                v2f o;
                o.position = UnityObjectToClipPos(i.position);
                o.normal = normalize(mul(i.normal,(float3x3)unity_WorldToObject)); // 计算法线
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz); // 光照的方向
                fixed3 lightColor = _LightColor0.rgb; // 光源的颜色
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 计算环境光
                fixed3 color = ambient + _Diffuse.rgb * lightColor *(_HalfLambert * dot(i.normal,lightDirection) + _HalfLambert); // 使用半兰伯特模型模型
                return fixed4(color,1);
            }
            ENDCG
        }
    }
}

半波兰特模型没有物理依据,只是单纯的视觉增强效果,将原来光线方向的单位向量在法线上的投影小于零就使用 0,改成将 [-1,1] 投影到 [0,1] 的范围。

三种漫反射模型对比

高光模型

高光逐顶点

Shader "Unlit/SpecularVertexLevelMat"
{
    Properties
    {   
        _DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
        _Spacular("Spacular",Color) = (1,1,1,1)
        _Gloss("Gloss",float) = 0
    }
    SubShader
    {
        Tags { "LightMode"="ForwardBase" }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "UnityShaderVariables.cginc"
            #include "Lighting.cginc"

            fixed4 _DiffuseColor;
            fixed4 _Spacular;
            float _Gloss;

            struct appdata
            {
                float4 vertex : POSITION;
                fixed3 normal : NORMAL;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                fixed3 color:COLOR0;
            };


            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);

                // 环境光
                fixed3 ambientColor = UNITY_LIGHTMODEL_AMBIENT.xyz;

                // 漫反射光
                float3 normal = normalize(mul(v.normal,(float3x3)unity_WorldToObject)); // 计算法线
                fixed3 lightColor = _LightColor0.rgb; // 光源的颜色
                float3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); // 光照的方向,是从模型出发的,也就是跟场景中看到的光照方向相反
                fixed3 diffuseColor = _DiffuseColor * lightColor * saturate(dot(normal,worldLightDir));

                // 计算高光
                float3 reflectDir = normalize(reflect(-worldLightDir,normal)); // 计算光的反射方向
                float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz); // 计算观察的方向
                fixed3 spacularColor = _Spacular * lightColor * pow(saturate(dot(reflectDir,viewDir)),_Gloss);
                o.color = ambientColor + diffuseColor + spacularColor;
                // o.color = reflectDir;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4(i.color,1);
            }
            ENDCG
        }
    }
}
运行结果

高光部分会跟随视角的变换改变强度和位置。_Gloss 越大,高光越小。观察点的位置不是摄像机的位置,而是我们在 Scene 人眼的位置。

高光逐像素

Shader "Unlit/SpecularVertexLevelMat"
{
    Properties
    {   
        _DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
        _Spacular("Spacular",Color) = (1,1,1,1)
        _Gloss("Gloss",float) = 0
    }
    SubShader
    {
        Tags { "LightMode"="ForwardBase" }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "UnityShaderVariables.cginc"
            #include "Lighting.cginc"

            fixed4 _DiffuseColor;
            fixed4 _Spacular;
            float _Gloss;

            struct appdata
            {
                float4 vertex : POSITION;
                fixed3 normal : NORMAL;
            };

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


            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject)); // 计算法线
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                return o;
            }

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

                // 漫反射光
                float3 normal = i.worldNormal; // 计算法线
                fixed3 lightColor = _LightColor0.rgb; // 光源的颜色
                float3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); // 光照的方向,是从模型出发的,也就是跟场景中看到的光照方向相反
                fixed3 diffuseColor = _DiffuseColor * lightColor * saturate(dot(normal,worldLightDir));

                // 计算高光
                float3 reflectDir = normalize(reflect(-worldLightDir,normal)); // 计算光的反射方向
                float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos); // 计算观察的方向
                fixed3 spacularColor = _Spacular * lightColor * pow(saturate(dot(reflectDir,viewDir)),_Gloss);
                fixed3 color = ambientColor + diffuseColor + spacularColor;
                return fixed4(color,1);
            }
            ENDCG
        }
    }
}

运行结果
运行结果

逐像素 BlinnPhong 模型

Shader "MyShader/BlinnPhongMat"
{
    Properties
    {   
        _DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
        _Spacular("Spacular",Color) = (1,1,1,1)
        _Gloss("Gloss",float) = 0
    }
    SubShader
    {
        Tags { "LightMode"="ForwardBase" }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "UnityShaderVariables.cginc"
            #include "Lighting.cginc"

            fixed4 _DiffuseColor;
            fixed4 _Spacular;
            float _Gloss;

            struct appdata
            {
                float4 vertex : POSITION;
                fixed3 normal : NORMAL;
            };

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


            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject)); // 计算法线
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                return o;
            }

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

                // 漫反射光
                float3 normal = i.worldNormal; // 计算法线
                fixed3 lightColor = _LightColor0.rgb; // 光源的颜色
                float3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); // 光照的方向,是从模型出发的,也就是跟场景中看到的光照方向相反
                fixed3 diffuseColor = _DiffuseColor * lightColor * saturate(dot(normal,worldLightDir));

                // 使用 BlinnPhone 模型,因为计算简单很多
                float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); // 计算观察的方向
                float3 h_normal = normalize(viewDir.xyz + worldLightDir.xyz); // 计算视角方向和光源方向的中间方向,用它和法线的夹角来确定光强
                fixed3 spacularColor = _Spacular * lightColor * pow(max(0,dot(normal,h_normal)),_Gloss);
                fixed3 color = ambientColor + diffuseColor + spacularColor;
                return fixed4(color,1);
            }
            ENDCG
        }
    }
}
BlinnPhong 模型

从下面的对比图可以看到相同的 _Gloss ,BlinnPhong 的光圈要更大一点,BlinnPhong 也是一种经验模型,因为可以简化求反射角的计算,而且可能在某些场景更加的真实。

左:BlinnPhong 右:基本光照模型

使用 Unity 里面自带的函数

// File: UnityCG.cginc
// Transforms direction from object to world space
inline float3 UnityObjectToWorldDir( in float3 dir )
{
    return normalize(mul((float3x3)unity_ObjectToWorld, dir));
}

// Transforms direction from world to object space
inline float3 UnityWorldToObjectDir( in float3 dir )
{
    return normalize(mul((float3x3)unity_WorldToObject, dir));
}

// Transforms normal from object to world space
inline float3 UnityObjectToWorldNormal( in float3 norm )
{
#ifdef UNITY_ASSUME_UNIFORM_SCALING
    return UnityObjectToWorldDir(norm);
#else
    // mul(IT_M, norm) => mul(norm, I_M) => {dot(norm, I_M.col0), dot(norm, I_M.col1), dot(norm, I_M.col2)}
    return normalize(mul(norm, (float3x3)unity_WorldToObject));
#endif
}

// Computes world space view direction, from object space position
inline float3 UnityWorldSpaceViewDir( in float3 worldPos )
{
    return _WorldSpaceCameraPos.xyz - worldPos;
}

// Computes world space view direction, from object space position
// *Legacy* Please use UnityWorldSpaceViewDir instead
inline float3 WorldSpaceViewDir( in float4 localPos ) // 如果这里的 w 为 0,那么跟直接调用 UnityWorldSpaceViewDir 是一样的
{
    float3 worldPos = mul(unity_ObjectToWorld, localPos).xyz;
    return UnityWorldSpaceViewDir(worldPos);
}

修改之后的 BlinnPhone 模型:

Shader "MyShader/BlinnPhongMat"
{
    Properties
    {   
        _DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
        _Spacular("Spacular",Color) = (1,1,1,1)
        _Gloss("Gloss",float) = 0
    }
    SubShader
    {
        Tags { "LightMode"="ForwardBase" }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "UnityShaderVariables.cginc"
            #include "Lighting.cginc"

            fixed4 _DiffuseColor;
            fixed4 _Spacular;
            float _Gloss;

            struct appdata
            {
                float4 vertex : POSITION;
                fixed3 normal : NORMAL;
            };

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


            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal); // 使用 UnityObjectToWorldNormal 计算法线
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                return o;
            }

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

                // 漫反射光
                float3 normal = i.worldNormal; 
                fixed3 lightColor = _LightColor0.rgb; 
                float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 使用 UnityWorldSpaceLightDir获取光照的方向
                fixed3 diffuseColor = _DiffuseColor * lightColor * saturate(dot(normal,worldLightDir));

                // 使用 BlinnPhone 模型,因为计算简单很多
                float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); // 使用 UnityWorldSpaceViewDir获取观察的方向
                float3 h_normal = normalize(viewDir.xyz + worldLightDir.xyz); 
                fixed3 spacularColor = _Spacular * lightColor * pow(max(0,dot(normal,h_normal)),_Gloss);
                fixed3 color = ambientColor + diffuseColor + spacularColor;
                return fixed4(color,1);
            }
            ENDCG
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读