美文网首页
Unity Shader应用

Unity Shader应用

作者: 爱吃鱼的鸡米 | 来源:发表于2022-01-04 22:36 被阅读0次

    实验性工具 Shader Forge

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

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

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

    Lambert光照模型
    在兰伯特光照中,很明显,朝向光的点最亮(灰度为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
    image.png

    代码

    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图(需要烘焙),来指明被遮挡的区域。

    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"
    }
    
    

    相关文章

      网友评论

          本文标题:Unity Shader应用

          本文链接:https://www.haomeiwen.com/subject/cboxcrtx.html