Unity Shader:(九)基础纹理
一、UV坐标
Q:什么是UV坐标?
A:顶点在纹理中对应的2D坐标,通常用(u,v)表示。
Q:UV坐标取值范围?
A:纹理有很多种像素大小,但是UV坐标通常被归一化在[0,1]范围内。
Q:原点(0,0)在纹理哪个位置?
A:左下角。
二、单张纹理
1 // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' 2 // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' 3 4 Shader "Unlit/SingleTexture" 5 { 6 Properties 7 { 8 _MainTex("Texture", 2D) = "white" {} 9 _Color("Color",Color) = (1,1,1,1) 10 _Specular("Specular", Color) = (1, 1, 1, 1) 11 _Gloss("Gloss",Range(8.0,256)) = 20 12 } 13 SubShader 14 { 15 Pass 16 { 17 Tags { "LightMode" = "ForwardBase" } 18 19 CGPROGRAM 20 #pragma vertex vert 21 #pragma fragment frag 22 23 #include "Lighting.cginc" 24 25 sampler2D _MainTex; 26 fixed4 _Color; 27 fixed4 _Specular; 28 float _Gloss; 29 float4 _MainTex_ST; 30 31 struct a2v //顶点着色器的输入结构体 32 { 33 float4 vertex : POSITION; 34 float3 normal:NORMAL; 35 float4 texcoord : TEXCOORD0; 36 }; 37 38 struct v2f //顶点着色器的输出结构体 39 { 40 float4 pos : SV_POSITION; 41 float3 worldNormal:TEXCOORD0; 42 float3 worldPos:TEXCOORD1; 43 float2 uv:TEXCOORD2; 44 }; 45 46 //顶点着色器 47 v2f vert(a2v v) 48 { 49 v2f o; 50 o.pos = UnityObjectToClipPos(v.vertex); 51 o.worldNormal = UnityObjectToWorldNormal(v.normal); 52 o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz; 53 o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; 54 return o; 55 } 56 57 fixed4 frag(v2f i) : SV_Target 58 { 59 fixed3 worldNormal = normalize(i.worldNormal);//计算世界空间下的法线方向 60 fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));//计算世界空间下的光照方向 61 //材质的反射率 62 fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb; //tex2D纹理采样:第一个参数:需要被采样的纹理,第二个参数:纹理坐标 63 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; //环境光照*材质反射率=环境光 64 fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));//漫反射 65 fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); 66 fixed3 halfDir = normalize(worldLightDir + viewDir); 67 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss); //高光反射 68 return fixed4(ambient + diffuse + specular, 1.0); 69 } 70 ENDCG 71 } 72 73 } 74 Fallback "Specular" 75 }
三、凹凸映射
使用一张纹理来修改模型的表面法线。
方法一:高度纹理:模拟表面位移然后得到一个修改后的发现值。
高度图的颜色越浅表示越凸。
优点:直观。
缺点:计算复杂。由像素灰度值计算而得。
方法二:法线纹理:直接存储表面法线。
法线方向分量范围 [-1,1]
像素分量范围[0,1]
相互转换:pixel = ( normal + 1 ) / 2
normal = pixel * 2 -1
切线空间的法线纹理:
优点:实现简单;边界更平滑;自由度高(见与模型空间法线区别);可进行UV动画;可重用;可压缩。
与模型空间法线区别:模型空间法线纹理记录的是绝对法线信息,仅可用于创建它时的那个模型,不能应用到其他模型。
而切线空间下法线纹理是相对法线信息,就算应用到不同网格上,也可以得到一个合理的结果。
实现:
1. 切线空间下计算:
思路:在片元着色器中通过纹理采样 => 切线空间下的法线
顶点着色器:从模型空间变换到切线空间 =>
计算视角方向、光照方向 => 光照结果
1 // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' 2 3 Shader "Unity Shaders Book/Chapter 7/Normal Map In Tangent Space" { 4 Properties { 5 _Color ("Color Tint", Color) = (1, 1, 1, 1) 6 _MainTex ("Main Tex", 2D) = "white" {} 7 _BumpMap ("Normal Map", 2D) = "bump" {} //法线纹理,bump是内置纹理 8 _BumpScale ("Bump Scale", Float) = 1.0 //控制凹凸层度 9 _Specular ("Specular", Color) = (1, 1, 1, 1) 10 _Gloss ("Gloss", Range(8.0, 256)) = 20 11 } 12 SubShader { 13 Pass { 14 Tags { "LightMode"="ForwardBase" } 15 16 CGPROGRAM 17 18 #pragma vertex vert 19 #pragma fragment frag 20 21 #include "Lighting.cginc" 22 23 fixed4 _Color; 24 sampler2D _MainTex; 25 float4 _MainTex_ST; 26 sampler2D _BumpMap; 27 float4 _BumpMap_ST; 28 float _BumpScale; 29 fixed4 _Specular; 30 float _Gloss; 31 32 struct a2v { 33 float4 vertex : POSITION; 34 float3 normal : NORMAL; 35 float4 tangent : TANGENT; //切线方向 第4个坐标用来表示副切线的方向 36 float4 texcoord : TEXCOORD0; 37 }; 38 39 struct v2f { 40 float4 pos : SV_POSITION; 41 float4 uv : TEXCOORD0; 42 float3 lightDir: TEXCOORD1; 43 float3 viewDir : TEXCOORD2; 44 }; 45 46 v2f vert(a2v v) { 47 v2f o; 48 o.pos = UnityObjectToClipPos(v.vertex); 49 50 o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; 51 o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw; 52 53 //UnityCG.cginc中定义,直接得到rotation变换矩阵 54 TANGENT_SPACE_ROTATION; 55 56 o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;//把模型空间下的光照变换到切线空间 57 o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz; //把模型空间下的视角方向变换到切线空间 58 59 return o; 60 } 61 62 fixed4 frag(v2f i) : SV_Target { 63 fixed3 tangentLightDir = normalize(i.lightDir); 64 fixed3 tangentViewDir = normalize(i.viewDir); 65 66 fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw); //对法线纹理采样 67 fixed3 tangentNormal; 68 69 tangentNormal = UnpackNormal(packedNormal); 70 tangentNormal.xy *= _BumpScale; 71 tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy))); 72 73 fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb; 74 75 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; 76 77 fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir)); 78 79 fixed3 halfDir = normalize(tangentLightDir + tangentViewDir); 80 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss); 81 82 return fixed4(ambient + diffuse + specular, 1.0); 83 } 84 85 ENDCG 86 } 87 } 88 FallBack "Specular" 89 }
效果图:
凹凸层度调成-1,就是凸起效果:
2. 世界空间下计算
在片元着色器中把法线方向从切线空间变换到世界空间下。
顶点着色器:顶点切线、副切线、法线 => 从切线空间到世界空间的变换矩阵,传递给片元着色器。
片元着色器:法线方向从切空间转换到世界空间。
1 // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' 2 // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' 3 4 Shader "Unity Shaders Book/Chapter 7/Normal Map In World Space" { 5 Properties { 6 _Color ("Color Tint", Color) = (1, 1, 1, 1) 7 _MainTex ("Main Tex", 2D) = "white" {} 8 _BumpMap ("Normal Map", 2D) = "bump" {} 9 _BumpScale ("Bump Scale", Float) = 1.0 10 _Specular ("Specular", Color) = (1, 1, 1, 1) 11 _Gloss ("Gloss", Range(8.0, 256)) = 20 12 } 13 SubShader { 14 Pass { 15 Tags { "LightMode"="ForwardBase" } 16 17 CGPROGRAM 18 19 #pragma vertex vert 20 #pragma fragment frag 21 22 #include "Lighting.cginc" 23 24 fixed4 _Color; 25 sampler2D _MainTex; 26 float4 _MainTex_ST; 27 sampler2D _BumpMap; 28 float4 _BumpMap_ST; 29 float _BumpScale; 30 fixed4 _Specular; 31 float _Gloss; 32 33 struct a2v { 34 float4 vertex : POSITION; 35 float3 normal : NORMAL; 36 float4 tangent : TANGENT; 37 float4 texcoord : TEXCOORD0; 38 }; 39 40 struct v2f { 41 float4 pos : SV_POSITION; 42 float4 uv : TEXCOORD0; 43 float4 TtoW0 : TEXCOORD1; 44 float4 TtoW1 : TEXCOORD2; 45 float4 TtoW2 : TEXCOORD3; 46 }; 47 48 v2f vert(a2v v) { 49 v2f o; 50 o.pos = UnityObjectToClipPos(v.vertex); 51 52 o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; 53 o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw; 54 55 float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; 56 fixed3 worldNormal = UnityObjectToWorldNormal(v.normal); 57 fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz); 58 fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; 59 60 // Compute the matrix that transform directions from tangent space to world space 61 // Put the world position in w component for optimization 62 o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x); 63 o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y); 64 o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z); 65 66 return o; 67 } 68 69 fixed4 frag(v2f i) : SV_Target { 70 // Get the position in world space 71 float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w); 72 // Compute the light and view dir in world space 73 fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos)); 74 fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos)); 75 76 // Get the normal in tangent space 77 fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw)); 78 bump.xy *= _BumpScale; 79 bump.z = sqrt(1.0 - saturate(dot(bump.xy, bump.xy))); 80 // Transform the narmal from tangent space to world space 81 bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump))); 82 83 fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb; 84 85 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; 86 87 fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(bump, lightDir)); 88 89 fixed3 halfDir = normalize(lightDir + viewDir); 90 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(bump, halfDir)), _Gloss); 91 92 return fixed4(ambient + diffuse + specular, 1.0); 93 } 94 95 ENDCG 96 } 97 } 98 FallBack "Specular" 99 }
四、渐变纹理
1 // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' 2 // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' 3 4 Shader "Unity Shaders Book/Chapter 7/Ramp Texture" { 5 Properties { 6 _Color ("Color Tint", Color) = (1, 1, 1, 1) 7 _RampTex ("Ramp Tex", 2D) = "white" {} 8 _Specular ("Specular", Color) = (1, 1, 1, 1) 9 _Gloss ("Gloss", Range(8.0, 256)) = 20 10 } 11 SubShader { 12 Pass { 13 Tags { "LightMode"="ForwardBase" } 14 15 CGPROGRAM 16 17 #pragma vertex vert 18 #pragma fragment frag 19 20 #include "Lighting.cginc" 21 22 fixed4 _Color; 23 sampler2D _RampTex; 24 float4 _RampTex_ST; 25 fixed4 _Specular; 26 float _Gloss; 27 28 struct a2v { 29 float4 vertex : POSITION; 30 float3 normal : NORMAL; 31 float4 texcoord : TEXCOORD0; 32 }; 33 34 struct v2f { 35 float4 pos : SV_POSITION; 36 float3 worldNormal : TEXCOORD0; 37 float3 worldPos : TEXCOORD1; 38 float2 uv : TEXCOORD2; 39 }; 40 41 v2f vert(a2v v) { 42 v2f o; 43 o.pos = UnityObjectToClipPos(v.vertex); 44 45 o.worldNormal = UnityObjectToWorldNormal(v.normal); 46 47 o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; 48 49 o.uv = TRANSFORM_TEX(v.texcoord, _RampTex); 50 51 return o; 52 } 53 54 fixed4 frag(v2f i) : SV_Target { 55 fixed3 worldNormal = normalize(i.worldNormal); 56 fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); 57 58 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; 59 60 // Use the texture to sample the diffuse color 61 fixed halfLambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5;//半兰伯特模型 62 fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb * _Color.rgb; 63 64 fixed3 diffuse = _LightColor0.rgb * diffuseColor; 65 66 fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); 67 fixed3 halfDir = normalize(worldLightDir + viewDir); 68 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss); 69 70 return fixed4(ambient + diffuse + specular, 1.0); 71 } 72 73 ENDCG 74 } 75 } 76 FallBack "Specular" 77 }
需要注意,要把渐变纹理的Wrap Mode改为Clamp模式,因为精度问题,Repeat模式可能会在高光区域反黑点。
五、遮罩纹理
遮罩有什么作用?遮罩允许我们可以保护某些区域,使他们免于某些修改。
1 // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' 2 3 Shader "Unity Shaders Book/Chapter 7/Mask Texture" { 4 Properties { 5 _Color ("Color Tint", Color) = (1, 1, 1, 1) 6 _MainTex ("Main Tex", 2D) = "white" {} 7 _BumpMap ("Normal Map", 2D) = "bump" {} 8 _BumpScale("Bump Scale", Float) = 1.0 9 _SpecularMask ("Specular Mask", 2D) = "white" {} //遮罩纹理 10 _SpecularScale ("Specular Scale", Float) = 1.0 //遮罩影响度 11 _Specular ("Specular", Color) = (1, 1, 1, 1) 12 _Gloss ("Gloss", Range(8.0, 256)) = 20 13 } 14 SubShader { 15 Pass { 16 Tags { "LightMode"="ForwardBase" } 17 18 CGPROGRAM 19 20 #pragma vertex vert 21 #pragma fragment frag 22 23 #include "Lighting.cginc" 24 25 fixed4 _Color; 26 sampler2D _MainTex; 27 float4 _MainTex_ST; 28 sampler2D _BumpMap; 29 float _BumpScale; 30 sampler2D _SpecularMask; 31 float _SpecularScale; 32 fixed4 _Specular; 33 float _Gloss; 34 35 struct a2v { 36 float4 vertex : POSITION; 37 float3 normal : NORMAL; 38 float4 tangent : TANGENT; 39 float4 texcoord : TEXCOORD0; 40 }; 41 42 struct v2f { 43 float4 pos : SV_POSITION; 44 float2 uv : TEXCOORD0; 45 float3 lightDir: TEXCOORD1; 46 float3 viewDir : TEXCOORD2; 47 }; 48 49 v2f vert(a2v v) { 50 v2f o; 51 o.pos = UnityObjectToClipPos(v.vertex); 52 53 o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; 54 55 TANGENT_SPACE_ROTATION; 56 o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz; 57 o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz; 58 59 return o; 60 } 61 62 fixed4 frag(v2f i) : SV_Target { 63 fixed3 tangentLightDir = normalize(i.lightDir); 64 fixed3 tangentViewDir = normalize(i.viewDir); 65 66 fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uv)); 67 tangentNormal.xy *= _BumpScale; 68 tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy))); 69 70 fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb; 71 72 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; 73 74 fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir)); 75 76 fixed3 halfDir = normalize(tangentLightDir + tangentViewDir); 77 // Get the mask value 78 fixed specularMask = tex2D(_SpecularMask, i.uv).r * _SpecularScale; 79 // Compute specular term with the specular mask 80 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss) * specularMask; 81 82 return fixed4(ambient + diffuse + specular, 1.0); 83 } 84 85 ENDCG 86 } 87 } 88 FallBack "Specular" 89 }