本文由@唐三十胖子出品,转载请注明出处。
文章链接:https://blog.csdn.net/iceSony/article/details/84634170
这篇文章将总结和提炼《Unity Shader入门精要》的第七章“基础纹理”的内容。
通过这篇文章,你可以知道
1)渐变纹理的应用
2)遮罩纹理的应用
一.渐变纹理的应用
纹理的最初使用,是为了给一个模型表面上色。实际上,纹理可以用来存储表面属性,如之前的法线纹理将法线信息存储在一张纹理中。通过纹理也可以控制漫反射光照结果。而渐变纹理,实现的是在材质纹理上再对光照信息进行处理。
分别使用
具体代码如下
Shader "sony/Shader174" { Properties { _Diffuse("漫反射系数",Color) = (1.0,1.0,1.0,1.0) _MainTex("主纹理",2D) = "white"{} } SubShader { Pass { Tags{ "LightMode" = "ForwardBase" } CGPROGRAM #include "lighting.cginc" #pragma vertex vert #pragma fragment frag float4 _Diffuse; sampler2D _MainTex; float4 _MainTex_ST; struct a2v { float4 pos : POSITION; float3 normal : NORMAL; float4 texcoord:TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; }; v2f vert(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.pos); o.worldNormal = UnityObjectToWorldNormal(v.normal); return o; } fixed4 frag(v2f i) : SV_Target { float3 worldNormal = i.worldNormal; float3 worldLightDir = UnityWorldSpaceLightDir(worldNormal); float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed halfLambert = 0.5*dot(worldNormal, worldLightDir) + 0.5; float3 diffuse = _LightColor0.rgb * _Diffuse.rgb * tex2D(_MainTex,float2(halfLambert, halfLambert)); return fixed4(ambient + diffuse,1.0); } ENDCG } } }
代码很短看看就好,其中让人不解的是tex2D内容的uv坐标
为什么是halfLambert呢?
原因是纹理本身就是一个一维纹理,因此纹理的uv我们都可以通过半兰伯特来代替
- 这里进行纹理采样的uv坐标为半兰伯特值,将法线与光照方向的点积映射到[0,1]也就是说原本光照不到的地方会取到渐变纹理中靠左下部分的颜色。
- 由于采样时uv坐标都是相等的,因此取到的颜色应该是对应纹理坐标[0,1]内的对角线上的颜色。
- 还有一点值得注意的是,当采用突变性的渐变纹理时(如第一张渐变纹理),漫反射的结果是阴影之间更加分明,类似于卡通效果。
二.遮罩纹理的应用
遮罩纹理应用于很多商业游戏中,用来保护某些区域,免于某些修改。两个常见的应用:
- 使模型某些区域的高光强烈,某些区域较弱。而不是将高光反射应用到模型的所有地方,使用遮罩纹理可以更加细腻的控制高光的光照效果。
- 制作地形材质时需要混合多张图片,例如表现草地,石子,裸露土地的纹理。使用遮罩纹理可以控制如何混合这些纹理。
使用者遮罩纹理的流程:通过采样得到遮罩纹理的纹素值,然后使用其中某个通道的值与某种表面属性进行相乘,当该通道值为0时,可以保护表面不受属性影响。
其实就用了一步
fixed mask = tex2D(_MaskTex, i.uv).r * _MaskScale;
在最后的高光反射的时候进行混合即可
fixed3 specular = _LightColor0.rgb * _Specular * pow(max(0, dot(tangentNormal, halfDir)), _Gloss) * mask;
实现代码
Shader "sony/Shader178" { Properties { _MainTex("Texture", 2D) = "white" {} _BumpTex("BumpTex", 2D) = "white" {} _MaskTex("MaskTex", 2D) = "white" {} _BumpScale("BumpScale", Float) = 1.0 _MaskScale("MaskScale", Float) = 1.0 _Color("Color Tint", Color) = (1, 1, 1, 1) _Specular("Specular", Color) = (1, 1, 1, 1) _Gloss("Gloss", Range(8.0, 256)) = 20 } SubShader { Pass { Tags{ "LightMode" = "ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" sampler2D _MainTex; float4 _MainTex_ST; sampler2D _BumpTex; float4 _BumpTex_ST; sampler2D _MaskTex; float4 _MaskTex_ST; float _BumpScale; float _MaskScale; fixed4 _Color; fixed4 _Specular; float _Gloss; struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal : NORMAL; float4 tangent : TANGENT; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float3 lightDir : TEXCOORD1; float3 viewDir : TEXCOORD2; }; v2f vert(appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); 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 lightDir = normalize(i.lightDir); fixed3 viewDir = normalize(i.viewDir); fixed4 bumpColor = tex2D(_BumpTex, i.uv); fixed3 tangentNormal = UnpackNormal(bumpColor); tangentNormal.xy *= _BumpScale; tangentNormal.z = sqrt(1 - 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, lightDir)); fixed mask = tex2D(_MaskTex, i.uv).r * _MaskScale; fixed3 halfDir = normalize(viewDir + lightDir); fixed3 specular = _LightColor0.rgb * _Specular * pow(max(0, dot(tangentNormal, halfDir)), _Gloss) * mask; return fixed4(ambient + diffuse + specular, 1.0); } ENDCG } } FallBack "Specular" }
这里有两点需要注意的地方:
- 主纹理,法线纹理和遮罩纹理的纹理坐标都来自主纹理的uv坐标,也就是说当修改材质面板的主纹理的缩放和偏移值时,法线纹理和遮罩纹理都会相应变化,而修改法线纹理和遮罩纹理的缩放偏移值是不会对计算结果产生任何影响的,事实上测试的结果也是这样。
- 遮罩纹理的计算过程中只用到了纹理的r通道,其他3个通道其实可以用来存储更多的设置值。
通过遮罩处理后,高光的效果不是全部反映到整个区域,而是由r通道进行选择,遮罩纹理中全黑色的地方,即r=0处是不会受到高光影响的,这也是为什么高光部分的裂缝处的凹槽更加清晰。
下一章我们将介绍Shader开发中神奇的实现:透明效果
下一章见:)