【UnityShader入门精要】凹凸贴图、渐变纹理、遮罩纹理

温(mian)馨(ze)提(sheng)示(ming):

  1. 此文内容质量差,仅作为个人学习过程记录
  2. 所有内容在UnityShader入门精要上都有
  3. 从typora复制粘贴过来格式问题还挺多的,懒得慢慢改了hh ^^

纹理贴图

同之前一样分别创建场景并去掉天空盒、创建物体、材质、shader

简单的纹理贴图如下实现:

 // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
 ​
 Shader "Custom/SingleTexture_youyider"
 {
     Properties
     {
         _Color ("Color Tint", Color) = (1,1,1,1)
         _MainTex ("Main Tex", 2D) = "white" {}
         _Gloss ("Gloss", Range(8.0,256)) = 20
         _Specular("Specular",Color)=(1,1,1,1)
     }
     SubShader
     {
         Pass
         {
             Tags{"LightMode"="ForwardBase"}    
             CGPROGRAM
 ​
             #pragma vertex vert
             #pragma fragment frag
 ​
             #include"Lighting.cginc"
 ​
             fixed4 _Color;
             float _Gloss;
             fixed4 _Specular;
             sampler2D _MainTex;
             float4 _MainTex_ST;
 ​
             struct a2v
             {
                 float4 vertex:POSITION;
                 float3 normal:NORMAL;
                 float4 texcoord:TEXCOORD0;
             };
 ​
             struct v2f
             {
                 float4 pos:SV_POSITION;
                 float3 worldNormal:TEXCOORD0;
                 float3 worldPos:TEXCOORD1;
                 float2 uv:TEXCOORD2;
             };
 ​
             v2f vert(a2v v)
             {
                 v2f o;
                 o.pos=UnityObjectToClipPos(v.vertex);
 ​
                 o.worldNormal=UnityObjectToWorldNormal(v.normal);
 ​
                 o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
 ​
                 o.uv=v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw;
 ​
                 return o;
             }
 ​
             fixed4 frag(v2f i):SV_Target
             {                
                 fixed3 worldNormal=normalize(i.worldNormal);
                 fixed3 worldLightDir=normalize(UnityWorldSpaceLightDir(i.worldPos));
 ​
                 fixed3 albedo=tex2D(_MainTex,i.uv).rgb*_Color.rgb;
                 fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;
 ​
                 fixed3 diffuse=_LightColor0.rgb*albedo*max(0,dot(worldNormal,worldLightDir));
 ​
                 fixed3 viewDir=normalize(UnityWorldSpaceViewDir(i.worldPos));
                 fixed3 halfDir=normalize(worldLightDir+viewDir);
 ​
                 fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(max(0,dot(worldNormal,halfDir)),_Gloss);
 ​
                 return fixed4(ambient + diffuse +specular,1.0);
             }
 ​
             ENDCG
         }
     }
     FallBack "Specular"
 }
 ​

得到的效果如下:

凹凸贴图

凹凸映射有两种,一种是高度纹理,一种是法线纹理。前者存入的是强度值,可以由像素计算灰度值得到然后计算法线朝向;后者存储的可以是在模型空间下的法线,也可以是每个顶点在切线空间下的法线扰动TBN矩阵。由于模型空间的法线是绝对的,而切线空间的法线是相对的,所以很多时候用后者可以做到很多前者做不到的东西。

即使是在切线空间中存储了法线,但是在计算光照的时候还是有两种方法,分别是在切线空间下计算或在世界空间下计算,前者效率更高而后者通用性更强

在切线空间下计算光照

值得注意的是切线的第四个分量是切线空间的方向性。它的值通常是+1或-1,用于确定副切线是沿着正方向还是负方向。这是因为副切线可以通过切线和法线的叉乘来计算,但是叉乘的结果可能指向两个可能的方向之一。切线的w分量可以确保TBN矩阵的正确性。

代码如下:

 Shader "Custom/NormalMapTangentSpace_youyider"
 {
     Properties
     {
         _Color ("Color Tint", Color) = (1,1,1,1)
         _MainTex ("Main Tex", 2D) = "white" {}
         _Gloss ("Gloss", Range(8.0,256)) = 20
         _Specular("Specular",Color)=(1,1,1,1)
         _BumpMap("Normal Map",2D)="bump"{}
         _BumpScale("Bump Scale",Float)=1.0
     }
     SubShader
     {
         Pass
         {
             Tags{"LightMode"="ForwardBase"}    
             CGPROGRAM
 ​
             #pragma vertex vert
             #pragma fragment frag
 ​
             #include"Lighting.cginc"
 ​
             fixed4 _Color;
             float _Gloss;
             fixed4 _Specular;
             sampler2D _MainTex;
             float4 _MainTex_ST;
             float _BumpScale;
             sampler2D _BumpMap;
             float4 _BumpMap_ST;
 ​
             struct a2v
             {
                 float4 vertex:POSITION;
                 float3 normal:NORMAL;
                 float4 texcoord:TEXCOORD0;
                 float4 tangent:TANGENT;
             };
 ​
             struct v2f
             {
                 float4 pos:SV_POSITION;
                 float4 uv:TEXCOORD0;
                 float3 lightDir:TEXCOORD1;
                 float3 viewDir:TEXCOORD2;
             };
 ​
             v2f vert(a2v v)
             {
                 v2f o;
                 o.pos=UnityObjectToClipPos(v.vertex);
 ​
                 o.uv.xy=v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw;
                 o.uv.zw=v.texcoord.xy*_BumpMap_ST.xy+_BumpMap_ST.zw;
 ​
                 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;
 ​
                 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.uv.zw);
                 fixed3 tangentNormal;
 ​
                 //tangentNormal.xy=(packedNormal.xy*2-1)*_BumpScale;
                 //tangentNormal.z=sqrt(1.0-saturate(dot(tangentNormal.xy,tangentNormal.xy)));
 ​
                 tangentNormal=UnpackNormal(packedNormal);
                 tangentNormal.xy*=_BumpScale;
                 tangentNormal.z=sqrt(1.0-saturate(dot(tangentNormal.xy,tangentNormal.xy)));
                 
                 fixed3 albedo=tex2D(_MainTex,i.uv).rgb*_Color.rgb;
                 fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;
 ​
                 fixed3 diffuse=_LightColor0.rgb*albedo*max(0,dot(tangentNormal,tangentLightDir));
 ​
                 fixed3 halfDir=normalize(tangentLightDir+tangentViewDir);
 ​
                 fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(max(0,dot(tangentNormal,halfDir)),_Gloss);
 ​
                 return fixed4(ambient + diffuse +specular,1.0);
             }
 ​
             ENDCG
         }
     }
     FallBack "Specular"
 }

在材质面板的右上角选择好贴图就可以看到:

修改Bump Scale让他不那么皱巴巴的:

在世界空间下计算

在世界空间下计算的话,要从vs计算变换矩阵然后传给fs

代码如下:

 // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
 ​
 Shader "Custom/NormalMapWorldSpace_youyider"
 {
     Properties
     {
         _Color ("Color Tint", Color) = (1,1,1,1)
         _MainTex ("Main Tex", 2D) = "white" {}
         _Gloss ("Gloss", Range(8.0,256)) = 20
         _Specular("Specular",Color)=(1,1,1,1)
         _BumpMap("Normal Map",2D)="bump"{}
         _BumpScale("Bump Scale",Float)=1.0
     }
     SubShader
     {
         Pass
         {
             Tags{"LightMode"="ForwardBase"}    
             CGPROGRAM
 ​
             #pragma vertex vert
             #pragma fragment frag
 ​
             #include"Lighting.cginc"
 ​
             fixed4 _Color;
             float _Gloss;
             fixed4 _Specular;
             sampler2D _MainTex;
             float4 _MainTex_ST;
             float _BumpScale;
             sampler2D _BumpMap;
             float4 _BumpMap_ST;
 ​
             struct a2v
             {
                 float4 vertex:POSITION;
                 float3 normal:NORMAL;
                 float4 texcoord:TEXCOORD0;
                 float4 tangent:TANGENT;
             };
 ​
             struct v2f
             {
                 float4 pos:SV_POSITION;
                 float4 uv:TEXCOORD0;
                 float4 TtoW0:TEXCOORD1;
                 float4 TtoW1:TEXCOORD2;
                 float4 TtoW2:TEXCOORD3;
             };
 ​
             v2f vert(a2v v)
             {
                 v2f o;
                 o.pos=UnityObjectToClipPos(v.vertex);
 ​
                 o.uv.xy=v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw;
                 o.uv.zw=v.texcoord.xy*_BumpMap_ST.xy+_BumpMap_ST.zw;
 ​
                 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.TtoW0=float4(worldTangent.x,worldBinormal.x,worldNormal.x,worldPos.x);
                 o.TtoW1=float4(worldTangent.y,worldBinormal.y,worldNormal.y,worldPos.y);
                 o.TtoW2=float4(worldTangent.z,worldBinormal.z,worldNormal.z,worldPos.z);
 ​
                 return o;
             }
 ​
             fixed4 frag(v2f i):SV_Target
             {           
                 float3 worldPos=float3(i.TtoW0.w,i.TtoW1.w,i.TtoW2.w);
 ​
                 fixed3 lightDir=normalize(UnityWorldSpaceLightDir(worldPos));
                 fixed3 viewDir=normalize(UnityWorldSpaceViewDir(worldPos));
 ​
                 fixed3 bump=UnpackNormal(tex2D(_BumpMap,i.uv.zw));
                 bump*=_BumpScale;
                 bump.z=sqrt(1.0-saturate(dot(bump.xy,bump.xy)));
 ​
                 bump=normalize(half3(dot(i.TtoW0.xyz,bump),dot(i.TtoW1.xyz,bump),dot(i.TtoW2,bump)));
             
                 fixed3 albedo=tex2D(_MainTex,i.uv).rgb*_Color.rgb;
                 fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;
 ​
                 fixed3 diffuse=_LightColor0.rgb*albedo*max(0,dot(bump,lightDir));
 ​
                 fixed3 halfDir=normalize(lightDir+viewDir);
 ​
                 fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(max(0,dot(bump,halfDir)),_Gloss);
 ​
                 return fixed4(ambient + diffuse +specular,1.0);
             }
 ​
             ENDCG
         }
     }
     FallBack "Specular"
 }
 ​

可以看到代码v2f的结构体中的TtoW用了float4,多出来的三个w分量可以用来存储世界空间的顶点位置

vs函数中v.tangent.w部分是一个技巧,用于调整副切线的方向;并且三个TtoW的xyz分量共同构成了从切线空间到世界空间的矩阵

渐变纹理

默认的场景样子

代码如下:

 Shader "Custom/RampTexture_youyider"
 {
     Properties
     {
         _Color ("Color Tint", Color) = (1,1,1,1)
         _RampTex ("Ramp Tex", 2D) = "white" {}
         _Gloss ("Gloss", Range(8.0,256)) = 20
         _Specular("Specular",Color)=(1,1,1,1)
     }
     SubShader
     {
         Pass
         {
             Tags{"LightMode"="ForwardBase"}    
             CGPROGRAM
 ​
             #pragma vertex vert
             #pragma fragment frag
 ​
             #include"Lighting.cginc"
 ​
             fixed4 _Color;
             float _Gloss;
             fixed4 _Specular;
             sampler2D _RampTex;
             float4 _RampTex_ST;
 ​
             struct a2v
             {
                 float4 vertex:POSITION;
                 float3 normal:NORMAL;
                 float4 texcoord:TEXCOORD0;
             };
 ​
             struct v2f
             {
                 float4 pos:SV_POSITION;
                 float3 worldNormal:TEXCOORD0;
                 float3 worldPos:TEXCOORD1;
                 float2 uv:TEXCOORD2;
             };
 ​
             v2f vert(a2v v)
             {
                 v2f o;
                 o.pos=UnityObjectToClipPos(v.vertex);
 ​
                 o.worldNormal=UnityObjectToWorldNormal(v.normal);
 ​
                 o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
 ​
                 o.uv=TRANSFORM_TEX(v.texcoord,_RampTex);
 ​
                 return o;
             }
 ​
             fixed4 frag(v2f i):SV_Target
             {                
                 fixed3 worldNormal=normalize(i.worldNormal);
                 fixed3 worldLightDir=normalize(UnityWorldSpaceLightDir(i.worldPos));
 ​
                 fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
 ​
                 fixed halfLambert=0.5*dot(worldNormal,worldLightDir)+0.5;
                 fixed3 diffuseColor=tex2D(_RampTex,fixed2(halfLambert,halfLambert)).rgb*_Color.rgb;
 ​
                 fixed3 diffuse=_LightColor0.rgb*diffuseColor;
 ​
                 fixed3 viewDir=normalize(UnityWorldSpaceViewDir(i.worldPos));
                 fixed3 halfDir=normalize(worldLightDir+viewDir);
 ​
                 fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(max(0,dot(worldNormal,halfDir)),_Gloss);
 ​
                 return fixed4(ambient + diffuse +specular,1.0);
             }
 ​
             ENDCG
         }
     }
     FallBack "Specular"
 }
 ​

效果如下:很有二次元的味道

遮罩纹理

代码如下:

 Shader "Custom/RampTexture_youyider"
 {
     Properties
     {
         _Color ("Color Tint", Color) = (1,1,1,1)
         _MainTex ("Main Tex", 2D) = "white" {}
         _Gloss ("Gloss", Range(8.0,256)) = 20
         _Specular("Specular",Color)=(1,1,1,1)
         _SpecularMask("Specular Mask",2D)="white"{}
         _SpecularScale("Specular Scale",Float)=1.0
         _BumpMap("Normal Map",2D)="bump"{}
         _BumpScale("Bump Scale",Float)=1.0
     }
     SubShader
     {
         Pass
         {
             Tags{"LightMode"="ForwardBase"}    
             CGPROGRAM
 ​
             #pragma vertex vert
             #pragma fragment frag
 ​
             #include"Lighting.cginc"
 ​
             fixed4 _Color;
             float _Gloss;
             fixed4 _Specular;
             sampler2D _MainTex;
             float4 _MainTex_ST;
             float _BumpScale;
             sampler2D _BumpMap;
             float _SpecularScale;
             sampler2D _SpecularMask;
 ​
             struct a2v
             {
                 float4 vertex:POSITION;
                 float3 normal:NORMAL;
                 float4 texcoord:TEXCOORD0;
                 float4 tangent:TANGENT;
             };
 ​
             struct v2f
             {
                 float4 pos:SV_POSITION;
                 float2 uv:TEXCOORD0;
                 float3 lightDir:TEXCOORD1;
                 float3 viewDir:TEXCOORD2;
             };
 ​
             v2f vert(a2v v)
             {
                 v2f o;
                 o.pos=UnityObjectToClipPos(v.vertex);
 ​
                 o.uv.xy=v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw;
 ​
                 TANGENT_SPACE_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.uv);
 ​
                 fixed3 tangentNormal=UnpackNormal(packedNormal);
                 tangentNormal.xy*=_BumpScale;
                 tangentNormal.z=sqrt(1.0-saturate(dot(tangentNormal.xy,tangentNormal.xy)));
                 
                 fixed3 albedo=tex2D(_MainTex,i.uv).rgb*_Color.rgb;
                 fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;
 ​
                 fixed3 diffuse=_LightColor0.rgb*albedo*max(0,dot(tangentNormal,tangentLightDir));
 ​
                 fixed3 halfDir=normalize(tangentLightDir+tangentViewDir);
 ​
                 fixed specularMask=tex2D(_SpecularMask,i.uv).r*_SpecularScale;
                 fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(max(0,dot(tangentNormal,halfDir)),_Gloss)*specularMask;
 ​
                 return fixed4(ambient + diffuse +specular,1.0);
             }
 ​
             ENDCG
         }
     }
     FallBack "Specular"
 }
 ​

绝大部分和以前的都差不多,只有在fs中对specular项多乘了个系数。

  • 19
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity Shader 中的 Texture Mask 意思是使用一张灰度图作为遮罩来控制另一张纹理的显示程度。这种技术通常用于实现一些特效效果,例如角色的阴影、光照效果等等。 下面是一个简单的 Texture Mask Shader 的实现: ```c# Shader "Custom/TextureMask" { Properties { _MainTex ("Texture", 2D) = "white" {} _MaskTex ("Mask Texture", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 100 CGPROGRAM #pragma surface surf Lambert sampler2D _MainTex; sampler2D _MaskTex; fixed4 _Color; struct Input { float2 uv_MainTex; float2 uv_MaskTex; }; void surf (Input IN, inout SurfaceOutput o) { // 取样原始纹理和 Mask 纹理 fixed4 c = tex2D(_MainTex, IN.uv_MainTex); float mask = tex2D(_MaskTex, IN.uv_MaskTex).r; // 根据 Mask 灰度值来控制显示程度 o.Albedo = c.rgb * mask; o.Alpha = c.a * mask; o.Emission = 0; } ENDCG } FallBack "Diffuse" } ``` 在这个 Shader 中,我们定义了两个属性 `_MainTex` 和 `_MaskTex` 分别表示原始纹理和 Mask 纹理。然后在 `surf` 函数中,我们通过 `tex2D` 函数取样两个纹理,并根据 Mask 灰度值来控制原始纹理的显示程度。 在使用这个 Shader 的时候,只需要将原始纹理和 Mask 纹理分别赋值给 `_MainTex` 和 `_MaskTex` 属性即可: ```c# Material mat = new Material(Shader.Find("Custom/TextureMask")); mat.SetTexture("_MainTex", originalTexture); mat.SetTexture("_MaskTex", maskTexture); ``` 这样就可以使用 Mask 纹理来控制原始纹理的显示程度了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值