Unity系列文章目录
文章目录
- Unity系列文章目录
- 前言
- 一、渐变纹理是什么
- 参考
前言
尽管在一开始,我们在渲染中使用纹理是为了定义一个物体的颜色,但后来人们发现,纹理
 其实可以用于存储任何表面属性。一种常见的用法就是使用渐变纹理来控制漫反射光照的结果。
 在之前计算漫反射光照时,我们都是使用表面法线和光照方向的点积结果与材质的反射率相乘来
 得到表面的漫反射光照。但有时,我们需要更加灵活地控制光照结果。这种技术在游戏《军团要
 塞2》(英文名:《Team Fortress 2》)中流行起来,它也是由Valve 公司(提出半兰伯特光照技术的
 公司)提出来的,他们使用这种技术来渲染游戏中具有插画风格的角色。Valve 发表了一篇著名
 的论文来专门讲述在制作《军团要塞2》时使用的技术。
 这种技术最初由Gooch 等人在1998 年他们发表的一篇著名的论文《A Non-Photorealistic
 Lighting Model For Automatic Technical Illustration》中被提出,在这篇论文中,作者提出了一种基
 于冷到暖色调(cool-to-warm tones)的着色技术,用来得到一种插画风格的渲染效果。使用这种
 技术,可以保证物体的轮廓线相比于之前使用的传统漫反射光照更加明显,而且能够提供多种色
 调变化。而现在,很多卡通风格的渲染中都使用了这种技术。我们在14.1 节中会专门学习如何编
 写一个卡通风格的Unity Shader。
一、渐变纹理是什么
在本节中,我们将学习如何使用一张渐变纹理来控制漫反射光照。在学习完本节后,我们可
 以得到类似图7.18 中的效果。

 可以看出,使用这种方式可以自由地控制物体的漫反射光照。不同的渐变纹理有不同的特性。
 例如,在左边的图中,我们使用一张从紫色调到浅黄色调的渐变纹理;而中间的图使用的渐变纹
 理则和《军团要塞2》中渲染人物使用的渐变纹理是类似的,它们都是从黑色逐渐向浅灰色靠拢,
 而且中间的分界线部分微微发红,这是因为画家在插画中往往会在阴影处使用这样的色调;右侧
 的渐变纹理则通常被用于卡通风格的渲染,这种渐变纹理中的色调通常是突变的,即没有平滑过
 渡,以此来模拟卡通中的阴影色块。
 为了实现上述效果,我们需要进行如下准备工作。
 (1)在Unity 中新建一个场景。在本书资源中,该场景名为Scene_7_3。在Unity 5.2 中,默
 认情况下场景将包含一个摄像机和一个平行光,并且使用了内置的天空盒子。在Window ->
 Lighting -> Skybox 中去掉场景中的天空盒子。
 (2)新建一个材质。在本书资源中,该材质名为RampTextureMat。
 (3)新建一个Unity Shader。在本书资源中,该Unity Shader 名为Chapter7-RampTexture。把
 新的Unity Shader 赋给第2 步中创建的材质。
 (4)向场景中拖曳一个Suzanne 模型,并把第2 步中的材质赋给该模型。
 (5)保存场景。
 打开新建的Chapter7-RampTexture,删除所有已有代码,并进行如下修改。
 (1)首先,我们需要为这个Shader 起一个名字:
 Sh ader “Unity Shaders Book/Chapter 7/Ramp Texture” {
 (2)我们在Properties 语义块中声明一个纹理属性来存储渐变纹理:
 Properties {
 _Color (“Color Tint”, Color) = (1,1,1,1)
 _RampTex (“Ramp Tex”, 2D) = “white” {}
 _Specular (“Specular”, Color) = (1, 1, 1, 1)
 _Gloss (“Gloss”, Range(8.0, 256)) = 20
 }
 (3)然后,我们在SubShader 语义块中定义了一个Pass 语义块,并在Pass 的第一行指明了该
 Pass 的光照模式:
 SubShader {
 Pass {
 Tags { “LightMode”=“ForwardBase” }
 LightMode 标签是Pass 标签中的一种,它用于定义该Pass 在Unity 的光照流水线中的角色。
 (4)然后,我们使用CGPROGRAM 和ENDCG 来包围住Cg 代码片,以定义最重要的顶点着
 色器和片元着色器代码。我们使用#pragma 指令来告诉Unity,我们定义的顶点着色器和片元着色
 器叫什么名字。在本例中,它们的名字分别是vert 和frag:
 CGPROGRAM
 #pragma vertex vert
 #p ragma fragment frag
 (5)为了使用Unity 内置的一些变量,如_LightColor0,还需要包含进Unity 的内置文件
 Lighting.cginc:
 #i nclude “Lighting.cginc”
 (6)随后,我们需要定义和Properties 中各个属性类型相匹配的变量:
 fixed4 _Color;
 sampler2D _RampTex;
 float4 _RampTex_ST;
 fixed4 _Specular;
 fl oat _Gloss;
 我们为渐变纹理_RampTex 定义了它的纹理属性变量_RampTex_ST。
 (7)定义顶点着色器的输入和输出结构体:
 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;
 };
 (8)定义顶点着色器:
 v2f vert(a2v v) {
 v2f o;
 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
 o.worldNormal = UnityObjectToWorldNormal(v.normal);
 o.worldPos = mul(_Object2World, v.vertex).xyz;
 o.uv = TRANSFORM_TEX(v.texcoord, _RampTex);
 return o;
 }
 我们使用了内置的TRANSFORM_TEX 宏来计算经过平铺和偏移后的纹理坐标。
 (9)接下来是关键的片元着色器:
 fixed4 frag(v2f i) : SV_Target {
 fixed3 worldNormal = normalize(i.worldNormal);
 fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(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);
 }
 在上面的代码中,我们使用6.4.3 节中提到的半兰伯特模型,通过对法线方向和光照方向的点
 积做一次0.5 倍的缩放以及一个0.5 大小的偏移来计算半兰伯特部分halfLambert。这样,我们得
 到的halfLambert 的范围被映射到了[0,1]之间。之后,我们使用halfLambert 来构建一个纹理坐
 标,并用这个纹理坐标对渐变纹理_RampTex 进行采样。由于_RampTex 实际就是一个一维纹理(它
 在纵轴方向上颜色不变),因此纹理坐标的u 和v 方向我们都使用了halfLambert。然后,把从渐
 变纹理采样得到的颜色和材质颜色_Color 相乘,得到最终的漫反射颜色。剩下的代码就是计算高
 光反射和环境光,并把它们的结果进行相加。相信读者已经对这些步骤非常熟悉了。
 (10)最后,我们为该Unity Shader 设置合适的Fallback:
 Fa llback “Specular”
 保存后返回场景。我们在本书资源中提供了多种渐变纹理,如Ramp_Texture0.psd 和
 Ramp_Texture1.psd 等。读者可以尝试把不同的渐变纹理拖曳到材质面板查看效果。
 需要注意的是,我们需要把渐变纹理的Wrap Mode 设为Clamp 模式,以防止对纹理进行采样
 时由于浮点数精度而造成的问题。图7.19
 给出了Wrap Mode 分别为Repeat 和Clamp
 模式的效果对比。
 可以看出,左图(使用Repeat 模式)
 中在高光区域有一些黑点。这是由浮点精
 度造成的,当我们使用fixed2(halfLambert,
 halfLambert)对渐变纹理进行采样时,虽
 然理论上halfLambert 的值在[0, 1]之间,
 但可能会有1.000 01 这样的值出现。如果
 我们使用的是Repeat 模式,此时就会舍
 弃整数部分,只保留小数部分,得到的值
 就是0.000 01,对应了渐变图中最左边的值,即黑色。因此,就会出现图中这样在高光区域反而
 有黑点的情况。我们只需要把渐变纹理的Wrap Mode 设为Clamp 模式就可以解决这种问题。


参考
Unity Shader入门精要、
 冯乐乐
                










