欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

UnityShader学习笔记(凹凸映射)

程序员文章站 2022-07-04 16:18:51
...

法线纹理

法线纹理中存储的就是表面的法线方向。由于法线方向的分量范围在[-1, 1],而像素的分量范围为[0, 1],因此我们需要做一个映射, 通常使用的映射就是:UnityShader学习笔记(凹凸映射)

这就要求,我们在Shader中对法线纹理进行纹理采样后,还需要对结果进行一次反映射的过程,以得到原先的法线方向。反映射的过程实际就是使用上面映射函数的逆函数:UnityShader学习笔记(凹凸映射)

在切线空间下进行光照计算

		//纹理贴图
        _MainTex("MainTex",2D)="white"{}
        //法线贴图
        _BumpMap("Normal Map",2D) = "bump"{}
        //定义
        	sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _BumpMap;
            float4 _BumpMap_ST;
v2f vert (appdata_tan v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
                o.normalUv = TRANSFORM_TEX(v.texcoord,_BumpMap);

                //求副切线向量
                //float3 binormal = cross(normalize(v.normal),normalize(v.tangent.xyz)) * v.tangent.w;
                //float3x3 rotation = float3x3(v.tangent.xyz,binormal,v.normal);
                TANGENT_SPACE_ROTATION;

                //求切线空间光源方向及视角方向
                //我们使用Unity的内置函数ObjSpaceLightDir和ObjSpaceViewDir来得到模型空间下的光照和视角方向
                //再利用变换矩阵rotation把它们从模型空间变换到切线空间中。
                o.lightDir = mul(rotation,ObjSpaceLightDir(v.vertex)).xyz;
                o.viewDir = mul(rotation,ObjSpaceViewDir(v.vertex)).xyz;
                
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //在切线空间进行光照计算
                //切线空间的光照方向  视角方向
                fixed3 tangentLightDir = normalize(i.lightDir);
                fixed3 tangentviewDir = normalize(i.viewDir);
                //法线贴图采样
                fixed4 packedNormal = tex2D(_BumpMap,i.normalUv);
                //UnpackNormal函数得到法线方向
                fixed3 tangentNormal = UnpackNormal(packedNormal);
                tangentNormal.xy *= _BumpScale;
                tangentNormal.z = sqrt(1 - saturate(dot(tangentNormal.xy,tangentNormal.xy)));

                //环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 albedo = tex2D(_MainTex,i.uv).rgb;
                //漫反射
                fixed3 diffuse = _LightColor0.rgb * albedo * _Diffuse.rgb * (max(0,dot(tangentLightDir,tangentNormal))*0.5 + 0.5);
                //高光反射
                fixed3 halfDir = normalize(tangentLightDir + tangentviewDir);
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(tangentNormal,halfDir)),_Gloss);

                fixed3 color = ambient + diffuse + specular;
                return fixed4(color,1);
            }

UnityShader学习笔记(凹凸映射)UnityShader学习笔记(凹凸映射)
在世界空间下进行光照计算

struct v2f
            {
                float4 vertex:SV_POSITION;
                float4 uv:TEXCOORD0;
                float4 TtiW0 : TEXCOORD1;
                float4 TtiW1 : TEXCOORD2;
                float4 TtiW2 : TEXCOORD3;
            };

            v2f vert (appdata_tan v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                //TRANSFORM_TEX将模型顶点的uv和Tiling、Offset两个变量进行运算,计算出实际显示用的定点uv。
                o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);
                o.uv.zw = TRANSFORM_TEX(v.texcoord,_BumpMap);
                //计算世界坐标下的顶点位置,法线,切线,副法线
                float3 worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
                fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
                fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
                fixed3 worldBinormal = cross(worldNormal,worldTangent) * v.tangent.w;
                //按列摆放得到从切线空间到世界空间的变化矩阵
                o.TtiW0 = float4(worldTangent.x,worldBinormal.x,worldNormal.x,worldPos.x);
                o.TtiW1 = float4(worldTangent.y,worldBinormal.y,worldNormal.y,worldPos.y);
                o.TtiW2 = float4(worldTangent.z,worldBinormal.z,worldNormal.z,worldPos.z);
                
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //求世界坐标
                float3 worldPos =float3(i.TtiW0.w,i.TtiW1.w,i.TtiW2.w);
                //计算世界空间下的光照和视角
                fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                //获得法线纹理
                fixed4 packedNormal = tex2D(_BumpMap,i.uv.zw);
                fixed3 tangentNormal = UnpackNormal(packedNormal);
                tangentNormal.xy *= _BumpScale;

                //切线空间法线转换到世界坐标
                fixed3 worldNormal = normalize(float3(dot(i.TtiW0.xyz,tangentNormal),dot(i.TtiW1.xyz,tangentNormal),dot(i.TtiW2.xyz,tangentNormal)));

                //环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 albedo = tex2D(_MainTex,i.uv.xy).rgb;
                //漫反射
                fixed3 diffuse = _LightColor0.rgb * albedo * _Diffuse.rgb * (max(0,dot(lightDir,tangentNormal))*0.5 + 0.5);
                //高光反射
                fixed3 halfDir = normalize(lightDir + viewDir);
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(tangentNormal,halfDir)),_Gloss);

                fixed3 color = ambient + diffuse + specular;
                return fixed4(color,1);
            }

UnityShader学习笔记(凹凸映射)
从视觉表现上, 在切线空间下和在世界空间下计算光照几乎没有任何差别。

渐变纹理

//漫反射
                fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos);
                fixed halfLambert = max(0,dot(worldLightDir,i.worldNormal))*0.5 + 0.5;
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * tex2D(_RampTex,fixed2(halfLambert,halfLambert));      

使用的纹理依次是:
UnityShader学习笔记(凹凸映射)
UnityShader学习笔记(凹凸映射)UnityShader学习笔记(凹凸映射)

UnityShader学习笔记(凹凸映射)
遮罩纹理(mask texture)

它非常有用,在很多商业游戏中都可以见到它的身影。那么什么是遮罩呢?简单来讲,遮罩允许我们可以保护某些区域,使它们免于某些修改。

例如,在之前的实现中,我们都是把高光反射应用到模型表面的所有地方,即所有的像素都使用同样大小的高光强度和高光指数。但有时,我们希望模型表面某些区域的反光强烈一些,而某些区域弱一些。为了得到更加细腻的效果,我们就可以使用一张遮 罩纹理来控制光照。另一种常见的应用是在制作地形材质时需要混合多张图片,例如表现草地的纹理、表现石子的纹理、表现裸露土地的纹理等,使用遮罩纹理可以控制如何混合这些纹理。