【unity shader 入门精要 读书笔记】高光反射模型

高光公式:

 

1、逐顶点的实现模式:

Shader "Custom/SpecularVertex" 
{
    Properties
    {
        _Diffuse("Diffuse", Color) = (1, 1, 1, 1)
        _Specular("Specular", Color) = (1, 1, 1, 1)
        _Gloss("Gloss", range(8, 256)) = 20
    }

    SubShader
    {
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "Lighting.cginc"
                fixed4 _Diffuse;
                fixed4 _Specular;
                float _Gloss;

                struct a2v
                {
                    float4 vertex : POSITION;
                    float3 normal : NORMAL;
                };
                struct v2f
                {
                    float4 pos : SV_POSITION;
                    float3 color : COLOR;
                };
                v2f vert(a2v v)
                {
                    v2f o;
                    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

                    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                    fixed3 worldNormal = normalize(mul(v.normal, (float3x3)_World2Object));

                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                    fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                    //入射光线方向关于表面法线的反射方向。
                    //reflect函数的入射方向要求是由光源指向交点处,
                    //因此需要对 worldLightDir 取反,然后再传给 reflect 函数。
                    // reflect(入射方向,法线)
                    fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
                    //观察方向:
                    //通过_WorldSpaceCameraPos得到世界空间中的摄像机位置,
                    //再把顶点位置从模型空间变换到世界空间下,
                    //再通过和_WorldSpaceCarmeraPos,相减即可得到世界空间下的视角方向。
                    fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(_Object2World, v.vertex).xyz);
                    fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
                    o.color = ambient + diffuse + specular;
                    return o;
                }

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

 

fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));

这里是计算反射方向的方法,实际开发的时候,不确定的时候,可以逐个测试一下。

总得来说,就是使用世界光源,和模型的法线,进行 reflect操作。

 

 

2、逐片元的高光反射实践:

Shader "Custom/SpecularFragment" 
{
    Properties
    {
        _Diffuse("Diffuse", color) = (1, 1, 1, 1)
        _Speculart("Specular", color) = (1, 1, 1, 1)
        _Gloss("Gloss", range(8, 256)) = 20
    }

    SubShader
    {
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            fixed3 _Diffuse;
            fixed3 _Specular;
            float _Gloss;

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

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

            v2f vert(a2v v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.worldNormal = mul(v.normal, (float3x3)_World2Object);
                o.worldPos = mul(_Object2World, v.vertex);
                return o;
            }

            fixed4 frag(v2f i)  : SV_Target
            {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 worldNormal = normalize(i.worldNormal);

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                //这里是计算反射方向的,即是入射方向和法线方向
                fixed3 reflectDir =normalize(reflect(-worldLightDir, worldNormal));
                //这里是计算观察方向的,即是观察方向和当前的坐标的方向。
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
                //计算高光的,光源颜色 x 高光材质 x 反射的 r 
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
                return fixed4(ambient + diffuse + specular, 1.0);
            }
            ENDCG
        }
    }
}

 

 

3、Blinn Phong 计算模型

 

Shader "Custom/SpecularBlinnPhong" 
{
    Properties
    {
        _Diffuse("Diffuse", color) = (1, 1, 1, 1)
        _Specular("Specular", color) = (1, 1, 1, 1)
        _Gloss("Gloss", range(8.0, 256.0)) = 20
    }
    SubShader
    {
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"

            fixed3 _Diffuse;
            fixed3 _Specular;
            float _Gloss;

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

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

            v2f vert(a2v v)
            {
                v2f o;
                //顶点转换
                o.pos= mul(UNITY_MATRIX_MVP, v.vertex);
                //模型空间下的顶点法线转换为世界空间下的顶点法线
                o.worldNormal = mul(v.normal, (float3x3)_World2Object);
                //模型空间下的顶点转换为世界空间下的顶点。
                o.worldPos = mul(_Object2World, v.vertex);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target 
            {
                //获取环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                //世界空间下的法线
                fixed3 worldNormal = normalize(i.worldNormal);
                //世界空间下的光源
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                //漫反射计算
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                //反射计算
                fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
                //观察角度
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
                //Blinn Phong 计算。
                fixed3 halfDir = normalize(worldLightDir + viewDir);
                //specular计算
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss) ;
                return fixed4(ambient + diffuse + specular, 1);
            }
            ENDCG
        }
    }
}

 

 

改进:

我们可以通过使用 unity 内置的帮助函数对上面的代码进行优化:

 

例如 WorldSpaceViewDir 函数,我们上面的代码已经进行了实现:

 

 

 

而计算光源方向的 3 个函数:

WorldSpaceLightDir

UnityWorldSpaceLightDir

ObjSpaceLightDir

就稍微复杂一点,因为 unity 处理了不同光源类型的情况,

需要注意的是,这 3 个函数仅可用于向前渲染

 

改进后的代码:

 

 

 

Shader "Custom/SpecularBlinnPhongUnityFunction" 
{
    Properties
    {
        _Diffuse("Diffuse", color) = (1, 1, 1, 1)
        _Specular("Specular", color) = (1, 1, 1, 1)
        _Gloss("Gloss", range(8.0, 256.0)) = 20
    }
    SubShader
    {
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"

            fixed3 _Diffuse;
            fixed3 _Specular;
            float _Gloss;

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

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

            v2f vert(a2v v)
            {
                v2f o;
                //顶点转换
                o.pos= mul(UNITY_MATRIX_MVP, v.vertex);
                //模型空间下的顶点法线转换为世界空间下的顶点法线
                //★
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                //模型空间下的顶点转换为世界空间下的顶点。
                o.worldPos = mul(_Object2World, v.vertex);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target 
            {
                //获取环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                //世界空间下的法线
                fixed3 worldNormal = normalize(i.worldNormal);
                //世界空间下的光源
                //★
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                //漫反射计算
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                //反射计算
                fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
                //观察角度
                //★
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                //Blinn Phong 计算。
                fixed3 halfDir = normalize(worldLightDir + viewDir);
                //specular计算
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss) ;
                return fixed4(ambient + diffuse + specular, 1);
            }
            ENDCG
        }
}

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值