Unity 中用 Vertex & Fragment Shader 实现 surface shader 中的 Diffuse 和 Decal

自己体验了以后发现surface shader确实是隐藏了好多好多的内部实现呀,像我没有考虑的多光源,阴影,衰减等问题,在surface shader中都是被隐藏实现好了的,而且还是多平台适配的,Vertex & Fragment Shader则还要自己写,还好unity也给我们提供了一些现成方法可以调。 _(:зゝ∠)_

所以总结一下,在写需要接收复杂光源信息的材质时,写surface shader应该会简单不少。
如果是不接收光或很少光的材质时,Vertex & Fragment Shader应该代码更清晰易懂点,速度更快一点。
unity的surface shader这项技术还是可以省我们不少事的,可以提高开发速度。

注意:以下2个shader,只考虑了只有一个平行光的情况,没有考虑多光源,阴影,衰减等问题。

surface shader的实现可以去看官方的shader资源资源去看,下面的代码是可以继续优化的,这里为了步骤清晰,就多分了一些变量

// 漫反射shader,对应 Legacy Shaders/Diffuse
Shader "Z_TestShader/TestDiffuse"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags{ "RenderType" = "Opaque" }

        Pass
        {
            Tags { "LightMode" = "ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // 前向渲染编译指令
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"  
            #include "Lighting.cginc"  

            uniform float4 _Color;

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 normal : NORMAL;
                float3 lightDir : TEXCOORD0;
            };

            v2f vert (appdata_full v)
            {
                v2f o;

                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

                // 把模型空间的模型法线转到世界空间,以下是等效的2种方法
                //o.normal = normalize(mul(v.normal, _World2Object));
                o.normal = UnityObjectToWorldNormal(v.normal);

                // 取光线的方向,以下是等效的2种方法
                //o.lightDir = normalize(_WorldSpaceLightPos0.xyz);
                o.lightDir = ObjSpaceLightDir(v.vertex);
                o.lightDir = normalize(mul(_Object2World, o.lightDir));

                return o;
            }

            fixed4 frag (v2f i) : COLOR
            {
                // 环境光
                float3 ambientLight = UNITY_LIGHTMODEL_AMBIENT.rgb;

                // 漫反射
                fixed diff = max (0, dot(i.normal, i.lightDir));
                float3 diffCol = _LightColor0.rgb * diff;

                // 最终颜色合成
                float4 col;
                col = float4(ambientLight + diffCol, 1) * _Color;

                return col;
            }
            ENDCG
        }
    }
}
// 贴花(就是把一张纹理贴在另一张的上面)shader,对应Legacy Shaders/Decal
Shader "Z_TestShader/TestDecal"
{
    Properties
    {
        _Color ("Main Color", Color) = (1,1,1,1)
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _DecalTex ("Decal (RGBA)", 2D) = "black" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 250

        Pass
        {
            Tags { "LightMode" = "ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"  
            #include "Lighting.cginc"

            uniform sampler2D _MainTex;
            uniform sampler2D _DecalTex;
            uniform fixed4 _Color;

            float4 _MainTex_ST;
            float4 _DecalTex_ST;

            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv_MainTex : TEXCOORD0;
                float2 uv_DecalTex : TEXCOORD1;
                float3 normal : NORMAL;
                float3 lightDir : TEXCOORD3;
            };

            v2f vert (appdata_full v)
            {
                v2f o;

                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

                // 用材质的 Tiling, Offset 2个参数计算纹理uv
                o.uv_MainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.uv_DecalTex = TRANSFORM_TEX(v.texcoord, _DecalTex);

                //o.normal = normalize(mul(v.normal, _World2Object));
                o.normal = UnityObjectToWorldNormal(v.normal);

                //o.lightDir = normalize(_WorldSpaceLightPos0.xyz);
                o.lightDir = ObjSpaceLightDir(v.vertex);
                o.lightDir = normalize(mul(_Object2World, o.lightDir));

                return o;
            }

            fixed4 frag (v2f i) : COLOR
            {
                // 贴花算法
                float4 tex = tex2D(_MainTex, i.uv_MainTex);
                float4 decal = tex2D(_DecalTex, i.uv_DecalTex);
                tex.rgb = lerp (tex.rgb, decal.rgb, decal.a);
                tex *= _Color;

                float3 ambientLight = UNITY_LIGHTMODEL_AMBIENT.rgb;

                fixed diff = max (0, dot(i.normal, i.lightDir));
                float3 diffCol = _LightColor0.rgb * diff;

                // 最终颜色合成
                float4 col;
                col = float4(ambientLight + diffCol, 1) * tex;

                return col;
            }
            ENDCG
        }
    }
}

注意:还有一点,如果你要在一个场景里跟 surface shader 放在一起做效果对比的话,记得在 Window->Lighting 界面里,把环境光的 Ambient Source 选项,从SkyBox 换成 Color,因为我发现在 SkyBox 模式下(也就是环境光来自于天空盒),UNITY_LIGHTMODEL_AMBIENT 这个宏得到的环境光是不准确的,至少是跟 surface shader 获得的环境光是有差别的。可能如果想获得一样的环境光还需要经过一些处理才行吧。所以你要想让2种 shader 效果一样,记得要改一下这里。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值