温(mian)馨(ze)提(sheng)示(ming):
- 此文内容质量差,仅作为个人学习过程记录
- 所有内容在UnityShader入门精要上都有
- 从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项多乘了个系数。