【Unity】Shader应用

实验性工具 Shader Forge

  • 这里的连连看插件用的是Shader Forge,但是现在已经不更新了,最后更新是在2018的版本中,但是也可以用。
  • Shader Forge最后生成的Shader代码没有Shader Graph那么多,稍微方便一点,所以先用这个。

漫反射模型(兰伯特光照)

兰伯特光照是最基础的漫反射模型,其效果如下


在兰伯特光照中,很明显,朝向光的点最亮(灰度为1),背向光的点最暗(灰度为0),中间则介于0~1之间。
通过光的反方向lDir某个点的朝向(该点法向量)nDir的接近程度,就可以得知该点是否面朝光
刚好,数学上的点乘Dot刚好可以用于表示两个向量方向的接近程度,数值越大,两者方向越接近。(两个向量前提都经过了归一化处理)
点乘的定义是:a·b = |a||b|cosθ
当某点面朝光时,θ = 0,那么cosθ = 1。最后点乘结果也是等于1。
模型上的每个点都进行一次 灰度值 = a·b 的运算,就可以得到整个模型经过漫反射之后的外观。

  • 用到的函数:
    UnityObjectToClipPos(点的局部坐标)
    UnityObjectToWorldNormal( 点的局部法向量 )
    dot(向量,向量)

代码

Shader "Shader Forge/02" {
    Properties {
    }
    SubShader {
        Tags {
            "RenderType"="Opaque"
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct VertexInput {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float3 normalWS : TEXCOORD;
            };

            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                o.pos = UnityObjectToClipPos( v.vertex );
                o.normalWS = UnityObjectToWorldNormal( v.normal );
                return o;
            }
            float4 frag(VertexOutput i) : COLOR {
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                float c = dot(i.normalWS,lightDir);
                c = max(0,c);
                return float4(c,c,c,1);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

镜面反射模型(高光反射)

Phong 模型

思想:亮度 = Dot(光折射方向, 视方向)

  • 用到的函数:reflect(入射光,法线) 、mul( 矩阵,矩阵(或向量) )
  • 用到的矩阵:unity_ObjectToWorld
  • 用到的全局变量:_WorldSpaceCameraPos、_WorldSpaceLightPos0

代码

Shader "Shader Forge/02" {
    Properties {
        _Color("颜色",Color) = (1.0,1.0,0.0,1.0)
        _SpecPow("高光次幂",Range(1,50)) = 30
    }
    SubShader {
        Tags {
            "RenderType"="Opaque"
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            
            uniform float4 _Color;
            uniform float _SpecPow;

            struct VertexInput {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            struct VertexOutput {
                float4 posCS : SV_POSITION;
                float4 posWS : TEXCOORD0;
                float3 normalWS : TEXCOORD1;
            };

            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                o.posCS = UnityObjectToClipPos( v.vertex );
                o.posWS = mul(unity_ObjectToWorld, v.vertex);
                o.normalWS = UnityObjectToWorldNormal( v.normal );
                return o;
            }
            float4 frag(VertexOutput i) : COLOR {
                //准备向量
                float3 nDir = i.normalWS;
                float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS);
                float3 lDir = normalize(_WorldSpaceLightPos0.xyz);
                float3 rDir = reflect(-lDir,nDir);
                //准备点积结果
                float nDotl = dot(nDir,lDir);
                float vDotr = dot(vDir,rDir);
                //光照模型
                float lambert = max(0,nDotl);
                //float phong = pow(max(0,vDotr),_SpecPow);  直接截断负值的方式
                float phong = pow((vDotr+1)/2,_SpecPow);  // 将[-1,1]的区间映射到[0,1]的方式
                //输出结果
                float3 finalRGB = _Color * lambert + phong;
                return float4(finalRGB,1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

Blinn-Phong 模型

思想:亮度 = Dot(半角方向, 法线方向)

代码(除了frag其他和Phone一样)

            float4 frag(VertexOutput i) : COLOR {
                float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS);
                float3 lDir = normalize(_WorldSpaceLightPos0.xyz);
                float3 hDir = normalize(vDir+lDir);
                float3 nDir = i.normalWS;

                float nDotl = dot(nDir,lDir);
                float nDoth = dot(nDir,hDir);

                float lambert = max(0,nDotl);
                float B_Phong = pow(max(0,nDoth),_SpecPow);

                float3 finalRGB = _Color * lambert + B_Phong;
                return float4(finalRGB,1.0);
            }

将法线映射为颜色

float4 frag(VertexOutput i) : COLOR {
     return float4(i.normalWS,1.0);
}

三色环境光

思路:

  • 不同侧面的环境光:利用法线方向来确定朝向。
  • 模型对环境光的遮挡导致的亮暗区别(例如耳朵朝内凹,因此内部容易被遮挡,会较暗):利用一张AO图(需要烘焙),来指明被遮挡的区域。

代码 (最后随意地结合了一下Lambert和Phong)

Shader "Shader Forge/03" {
    Properties {
        _Color("颜色",Color) = (1.0,1.0,0.0,1.0)
        _SpecPow("高光次幂",Range(1,90)) = 30
        _EnvUpCol("上部环境光色",Color) = (1,1,1,1)
        _EnvDownCol("下部环境光色",Color) = (0.2,0.2,0.2,1)
        _EnvSideCol("侧边环境光色",Color) = (0.1,0.8,0.1,1)
        _Occlusion("环境遮罩图AO",2d) = "white"{}
        _EnvIntensity("环境光强度",Range(0,1)) = 0.3
    }
    SubShader {
        Tags {
            "RenderType"="Opaque"
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            
            uniform float4 _Color;
            uniform float _SpecPow;

            uniform float4 _EnvUpCol;
            uniform float4 _EnvDownCol;
            uniform float4 _EnvSideCol;
            uniform float _EnvIntensity;
            uniform sampler2D _Occlusion;
            struct VertexInput {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };
            struct VertexOutput {
                float4 posCS : SV_POSITION;
                float4 posWS : TEXCOORD0;
                float3 normalWS : TEXCOORD1;
                float2 uv : TEXCOORD2;
            };

            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                o.posCS = UnityObjectToClipPos( v.vertex );
                o.posWS = mul(unity_ObjectToWorld, v.vertex);
                o.normalWS = UnityObjectToWorldNormal( v.normal );
                o.uv = v.uv;
                return o;
            }
            float4 frag(VertexOutput i) : COLOR {
                //准备向量
                float3 nDir = i.normalWS;
                float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS);
                float3 lDir = normalize(_WorldSpaceLightPos0.xyz);
                float3 rDir = reflect(-lDir,nDir);
                //准备点积结果
                float nDotl = dot(nDir,lDir);
                float vDotr = dot(vDir,rDir);
                //光照模型
                    //漫反射 + 镜面反射
                float lambert = max(0,nDotl);
                float phong = pow((vDotr+1)/2,_SpecPow);

                    //环境光
                float upMask = max(0,i.normalWS.y);
                float downMask = max(0,-i.normalWS.y);
                float sideMask = 1 - upMask - downMask;
                float occlusion = tex2D(_Occlusion,i.uv);
                float3 envCol = _EnvUpCol*upMask + _EnvSideCol*sideMask + _EnvDownCol*downMask;

                //输出结果
                float3 finalRGB = (_Color * lambert *(1-_EnvIntensity) + envCol * _EnvIntensity * occlusion + phong );  //这个暂时先用加法简单相加了,实际不知道是什么样的
                
                return float4(finalRGB,1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

posted @ 2022-01-04 22:37  JimmyZou  阅读(0)  评论(0编辑  收藏  举报  来源