目录学习
@code
尽管在一开始,咱们在渲染中使用纹理是为了定义一个物体的颜色,但后来人们发现,纹理其实能够用来存储任何表面属性。一种常见的用法就是使用渐变纹理来控制漫反射光照的结果。在以前计算漫反射光照结果的时候,咱们都是使用表面法线和光照方向的点积结果与材质的反射率相乘来获得表面的漫反射光照。但有时,咱们须要更加灵活的控制光照。使用这种技术,能够保证物体的轮廓线相比于以前使用的传统漫反射光照更加明显,并且可以提供多种色调变化。
在本节中,咱们将学习如何用一张渐变纹理来控制漫反射光照。而后获得相似下图的效果。
能够看出,使用这种方式能够自由的控制物体的漫反射光照。不一样的渐变纹理有不一样的特性。例如在左边的图中,咱们使用一张从紫色调到浅黄的渐变纹理;而中间的渐变纹理是从黑色向浅灰色靠拢,而中间的分界线略微微发红,这是由于画家在插画中每每会在阴影中使用这种色调;右边的渐变纹理则一般被用于卡通风格的渲染,这种渐变纹理中的渲染一般是突变的,既没有平滑过渡,以此来模拟卡通中的阴影色块。
为了达到上述效果,咱们须要进行如下工做:
(1)咱们在Properties语义块中声明一个纹理属性来存储渐变纹理:orm
Properties{ _Color("Color Tint",Color)={1,1,1,1} _RampTex("Ramp Tex",2D)="white"{} _Specualr("Specular",Color)={1,1,1,1} _Gloss("Gloss",Range(8.0,256))=20 }
(2)而后,咱们在Subshader语义块中定义了一个Pass语义块,而且在Pass的第一行指明了该Pass的光照模式:blog
Subshader{ Pass{ Tags{"LightMode"="ForwardBase"} } }
LightMode是Pass标签中的一种,它用于定义该Pass在Unity的光照流水线中的角色
(3)而后,咱们使用CGPROGRAM和ENDCG开包围住Cg代码片,以定义最重要的顶点着色器和片元着色器代码。咱们使用#pragma指令来告诉Unity,咱们定义的顶点着色器和片元着色器叫什么名字。在本例中它们名字分别是vert和frag图片
CGPROGRAM #pragma vertex vert #pragma fragment frag
(4)为了使用Unity内置的一些变量,如_LightColor0,还须要包含进Unity的内置文件Lighting.cginc:get
#include"Lighting.cginc"
(5)随后,咱们须要定义和Properties中各个属性相匹配的变量:it
fixed4 _Color; sampler2D _RampTex; float4 _RampTex_ST; fixed4 _Specular; float _Gloss;
咱们为渐变纹理_RampTex定义了它的纹理属性变量_RampTex_ST
(6)定义顶点着色器输入和输出结构体class
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; }
(7)定义顶点着色器:变量
v2f vert(a2v v){ v2f o; o.pos=mul(UNITY_MATRIX_MVP,v.vertex); o.worldNormal=UnityObjectWorldNormal(v.normal); o.worldPos=mul(_Object2World,v.vertex).xyz; o.uv=TRANSFORM_TEX(v.texcoord,_RampTex); }
咱们使用了内置的TRANSFORM_TEX宏来计算通过平铺和偏移后的纹理坐标。
(8)接下来是关键的片元着色器:渲染
fixed4 frag(v2f i):SV_Target{ fixed3 worldNormal=normalize(i.worldNormal); fixed3 worldLightDir=normalize(UnityWorldLightDir(i.worldPos)); fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz; //Use the texture to sample the diffuse color 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); }
在上面的代码中,咱们使用了之前所讲的半兰伯特模型,经过对该法线方向和光照方向的点积作一次0.5倍的缩放以及0.5倍的平移来计算半兰伯特部分的halfLambert。这样咱们获得的halfLambert的范围被映射到了[0,1]之间。以后咱们使用halfLambert来构建一个纹理坐标,并用这个纹理坐标对渐变纹理_RampTex进行采样,因为_RampTex实际上就是一个一维纹理(它在纵轴方向上颜色不变),所以纹理坐标的u和v方向咱们都使用了halfLambert。而后把从渐变纹理采样获得的颜色和材质颜色_Color相乘,获得最终的漫反射颜色。剩下的代码就是计算高光反射和环境光,并把它们的结果进行相加。
(9)最后,咱们为该UnityShader设置合适的Fallback:
Fallback"Specular"
须要注意的是。咱们须要把渐变纹理的Wrap Mode设置为Clamp模式,以防止因为浮点数精度而形成的问题。下图给出了WrapMode分别为Repeat和Clamp模式的效果对比。
能够看出,左图(使用Repeat模式)在高光区域有一些黑点。这是因为浮点数精度形成的,当咱们使用fixed2(halfLambert,halfLambert)对渐变纹理进行采样时,虽然理论上halfLambert的值在[0,1]之间,但可能会有1.00001这样的值出现。若是咱们使用的是Repeat模式,此时就会舍弃整数部分,只保留小数部分,获得的值就是0.00001,对应了渐变图中最左边的值,即黑色。所以,就会出现图中这样在高光区域反而有黑点的状况。咱们只需把纹理的WrapMode设为Clamp模式就能够解决这种问题。