美文网首页shader
Unity Shader 正确阴影的溶解效果

Unity Shader 正确阴影的溶解效果

作者: 洒一地阳光_217d | 来源:发表于2021-03-30 21:57 被阅读0次

    Unity Shader系列文章:Unity Shader目录-初级篇

    Unity Shader系列文章:Unity Shader目录-中级篇

    效果:
    溶解效果

    所需贴图:


    箱子贴图 法线贴图 噪声贴图

    shader代码:

    // 溶解效果 (附带正确阴影投射)
    Shader "Custom/Dissolve"
    {
        Properties
        {
            _BurnAmount ("Burn Amount", Range(0, 1)) = 0 // 控制消融程度
            _LineWidth ("Line Width", Range(0, 0.2)) = 0.1 // 消融时边缘的线宽
            _MainTex ("Texture", 2D) = "white" { }
            _BumpMap ("Normal Map", 2D) = "bump" { }// 法线纹理
            _BurnFirstColor ("Burn First Color", Color) = (1, 0, 0, 1) // 消融边缘第一种颜色
            _BurnSecondColor ("Burn Second Color", Color) = (1, 0, 0, 1) // 消融边缘第二种颜色
            _BurnMap ("Burn Map", 2D) = "white" { }// 噪声纹理
        }
        SubShader
        {
            Tags { "RenderType" = "Opaque" "Queue" = "Geometry" }
    
            // Base Pass 计算平行光、环境光
            Pass
            {
                Tags { "LightMode" = "ForwardBase" }
    
                // 关闭剔除,正面和背面都会渲染 (因为消融会导致裸露模型内部的构造)
                Cull Off
    
                CGPROGRAM
    
                #include "Lighting.cginc"
                #include "AutoLight.cginc"
    
                // 编译指令,保证在pass中得到Pass中得到正确的光照变量
                #pragma multi_compile_fwdbase
    
                #pragma vertex vert
                #pragma fragment frag
    
                fixed _BurnAmount;
                fixed _LineWidth;
                sampler2D _MainTex;
                sampler2D _BumpMap;
                fixed4 _BurnFirstColor;
                fixed4 _BurnSecondColor;
                sampler2D _BurnMap;
    
                // 纹理的缩放和偏移系数
                float4 _MainTex_ST;
                float4 _BumpMap_ST;
                float4 _BurnMap_ST;
    
                // 应用传递给定点着色器的数据
                struct a2v
                {
                    float4 vertex: POSITION; // 语义: 顶点坐标
                    float3 normal: NORMAL; // 语义: 法线
                    float4 tangent: TANGENT; // 语义: 切线
                    float4 texcoord: TEXCOORD0; // 语义: 纹理坐标
                };
                
                // 顶点着色器传递给片元着色器的数据
                struct v2f
                {
                    float4 pos: SV_POSITION; // 语义: 裁剪空间的顶点坐标
                    float2 uvMainTex: TEXCOORD0;
                    float2 uvBumpMap: TEXCOORD1;
                    float2 uvBurnMap: TEXCOORD2;
                    float3 lightDir: TEXCOORD3;
                    float3 worldPos: TEXCOORD4;
                    SHADOW_COORDS(5) // 内置宏:声明一个用于对阴影纹理采样的坐标 (这个宏参数需要是下一个可用的插值寄存器的索引值,这里是5)
                };
    
                // 顶点着色器
                v2f vert(a2v v)
                {
                    v2f o;
    
                    // 将顶点坐标从模型空间变换到裁剪空间
                    // 等价于o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                    o.pos = UnityObjectToClipPos(v.vertex);
    
                    // 计算纹理坐标 (缩放和平移)
                    // 等价于o.uv = v.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
                    o.uvMainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
                    o.uvBumpMap = TRANSFORM_TEX(v.texcoord, _BumpMap);
                    o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);
    
                    // 内置宏:TANGENT_SPACE_ROTATION(切线空间到模型空间的变换矩阵)等价于:
                    // 使用模型空间下的法线方向和切线方向叉积得到副切线方向
                    // float3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;
                    //定义 3x3 变换矩阵 rotation,分别将切线方向、副切线方向和法线方向按行摆放组成了这个矩阵。
                    // float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);
                    TANGENT_SPACE_ROTATION;
                    // 将光向量从模型空间变换到切线空间
                    o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
    
                    // 将观察向量从模型空间变换到切线空间
                    o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    
                    // 内置宏:用于计算声明的阴影纹理坐标
                    TRANSFER_SHADOW(o);
    
                    return o;
                }
    
                // 片元着色器
                fixed4 frag(v2f i): SV_TARGET
                {
                    fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
    
                    // burn.r - _BurnAmount < 0 则放弃此片元不绘制
                    clip(burn.r - _BurnAmount);
    
                    fixed3 tangentLightDir = normalize(i.lightDir);
                    // 若法线纹理Texture Type未设置成Normal map,
                    // 要从像素映射回法线,即[0, 1]转化到[-1, 1]
                    // tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;
                    
                    // 如果设置了Normal map类型,Unity会根据平台使用不同的压缩方法,
                    // _BumpMap.rbg值不是对应的切线空间的xyz值了,要用Unity内置函数
                    fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uvBumpMap));
    
                    fixed3 albedo = tex2D(_MainTex, i.uvMainTex);
    
                    // 获得环境光
                    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
    
                    // 计算漫反射
                    // 兰伯特公式:Id = Ip * Kd * N * L
                    // IP:入射光的光颜色;
                    // Kd:漫反射颜色;
                    // N:单位法向量;
                    // L:单位光向量;
                    fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentLightDir, tangentNormal));
    
                    // 使用了 smoothstep 函数来计算混合系数。当t值为1时,表明该像素位于消融的边界处;
                    // 当t值为0时,表明该像素为正常的模型颜色,而中间的插值则表示需要模拟一个烧焦效果。
                    fixed t = 1 - smoothstep(0, _LineWidth, burn.r - _BurnAmount);
                    fixed3 burnColor = lerp(_BurnFirstColor, _BurnSecondColor, t);
                    // 为了让效果更接近烧焦的痕迹,还使用 pow 函数对结果进行处理
                    burnColor = pow(burnColor, 5);
    
                    // 计算阴影值和光照衰减
                    UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
    
                    fixed3 finalColor = lerp(ambient + diffuse * atten, burnColor, t * step(0.0001, _BurnAmount));
    
                    return fixed4(finalColor, 1);
                }
    
                ENDCG
    
            }
    
            // 自定义用于投射阴影的 Pass (如果仍然使用普通的阴影 Pass,那么被剔除的区域仍然会向其他物体投射阴影,造成“穿帮”。)
            Pass
            {
                Tags { "LightMode" = "ShadowCaster" }
    
                CGPROGRAM
    
                #pragma vertex vert
                #pragma fragment frag
    
                #pragma multi_compile_shadowcaster
    
                #include "UnityCG.cginc"
    
                fixed _BurnAmount;
                sampler2D _BurnMap;
                float4 _BurnMap_ST;
    
                struct v2f
                {
                    V2F_SHADOW_CASTER;
                    float2 uvBurnMap: TEXCOORD1;
                };
    
                v2f vert(appdata_base v)
                {
                    v2f o;
    
                    TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
    
                    o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);
    
                    return o;
                }
    
                fixed4 frag(v2f i): SV_TARGET
                {
                    // 已经消融的面片,不再投影
                    fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
                    clip(burn.r - _BurnAmount);
    
                    // Unity内置宏,把结果输出到深度图和阴影映射纹理中
                    SHADOW_CASTER_FRAGMENT(i)
                }
    
                ENDCG
    
            }
        }
    
        Fallback "Diffuse"
    }
    
    

    相关文章

      网友评论

        本文标题:Unity Shader 正确阴影的溶解效果

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