第五章 Unity中的基础光照(2)
目录
1. Unity中的环境光和自发光在标准光照模型中,环境光和自发光的计算是最简单的。 2. 在UnityShader中实现漫反射光照模型在了解了上述理论后,我们现在来看一下如何在Unity中实现这些基本的光照模型。首先,我们来实现标准光照模型中的漫反射光照部分。 从公式可以看出,要计算漫反射需要知道4个参数:入射光线的颜色和强度Clight,材质的漫反射系数mdiffuse,表面法线n以及光源方向I。 为防止点积的结果出现负值,我们需要使用max操作,而Cg提供了这样的函数。在本例中使用Cg的另一个函数可以达到同样的目的,即saturate函数。 函数:saturate(x) 参数:x:为用于操作的标量或矢量,可以是float、float2、float3等类型。 描述:把x截取在[0,1]的范围内,如果x是一个矢量,那么会对它的每一个分量进行这样的操作。 2.1 实践:逐顶点光照我们首先来看如何实现一个逐顶点的漫反射光照效果。在学习完本节后,我们会得到类似于下图的效果。 (1)首先,为了得到并且控制材质的漫反射颜色,我们首先在Shader语的Properties语义块中声明了一个Color类型的属性,并把它的初始值设置为白色: Properties{ _Diffuse("Diffuse",Color)=(1,1,1) } (2)然后,我们在SubShader语义块中定义了一个Pass语义块。这是因为顶点/片元着色器的代码需要写在Pass语义块,而非SubShader语义块中。而且我们在Pass的第一行指明了该Pass的光照模式: SubShader{ Pass{ Tags{"LightMode"="ForwardBase"} } } LightMode标签是Pass标签中的一种,它用于定义该Pass在Unity的光照流水线中的角色,在后面我们会更加详细的解释它。在这里我们只需要知道,只有定义了正确的LightMode,我们才能得到一些Unity内置光照变量,例如下面讲到的_LightColor0。 CGPROGRAM #pragma vertex vert #pragma fragment frag (4)为了使用Unity内置的一些变量,如后面讲到的_LightColor0,还需要包含进Unity的内置文件Lighting.cginc: #include "Lighting.cginc" (5)为了在Shader中使用Properties语义块中声明的属性,我们需要定义一个和该属性类型相匹配的变量: fixed _Diffuse; 通过这样的方式,我们就可以得到漫反射公式中需要的参数之一——材质的漫反射属性。由于颜色的属性范围在0到1之间,我们可以使用fixed精度的变量来存储它。 struct a2v{ float4 vertex:POSITION; float3 normal:NORMAL; }; struct v2f{ float4 pos:SV_POSITION; fixed3 color:COLOR; }; 为了访问顶点的法线,我们需要在a2v中定义一个normal变量,并通过NORMAL语义来告诉Unity要把模型顶点的法线信息存储到normal变量中。为了把在顶点着色器计算得到的光照颜色传递给片元着色器,我们需要在v2f中定义一个color变量,且并不是必须使用COLOR语义,一些资料中会使用TEXCOORD0语义。 v2f vert(a2v v){ v2f o; //Transform the vertex from object space to projection space o.pos = mul(UNITY_MATRIX_MVP,v.vertex); //get ambient term fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; //Transform the normal fram object space to worldspace fixed3 worldNormal = normalize(mul(v.normal,(float3×3)_World2Object); //Get the light direction in world space fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz); //compute diffuse term fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLight)); o.color = ambient + diffuse; return o; } 在第一行我们首先定义了返回值o。我们已经重复过很多次,顶点着色器最基本的任务就是把顶点位置从模型空间转换到裁剪空间。因此我们需要使用Unity内置的模型×世界×投影矩阵UNITY_MATRIX_MVP来完成这的坐标变换。接下来我们通过Unity的内置变量UNITY_LIGHTMODEL_AMBIENT得到了环境光部分。 fixed frag(v2f i):SV_Target{ return fixed(i.color,1.0); } (9)最后,我们需要把这个Unity Shader的回调Shader设置为内置的Diffuse: 2.2 实践:逐像素光照我们只需要对Shader进行一些更改就可以实现逐像素的漫反射效果,如下图所示: 对以前的代码修改如下: (1)修改顶点着色器的输出结构体v2f: struct v2f{ float4 pos:SV_POSITION; float3 worldNormal:TEXCOORD0; } (2)顶点着色器不需要计算光照模型,只需要把世界空间下的法线传递给片元着色器即可。 v2f vert(a2v v){ v2f o; //Transform the vertex from object space to projection space o.pos = mul(UNITY_MATRIX_MVP,v.vertex) //transform the normal fram object space to world space o.worldNormal=mul(v.normal,(float3×3)_World2Object); return o; } (3)片元着色器需要计算漫反射光照模型: fixed4 frag(v2f i):SV_Target{ //Get ambient term fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; //Get the normal in world space fixed3 worldNormal = normalize(i.worldNormal); //Get the light direction in world space fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); //Compute diffuse term fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir)); fixed3 color = ambient + diffuse; return fixed4(color,1.0); } 逐像素光照可以得到更加平滑的光照效果。但是即便使用了逐像素,漫反射光照,有一个问题仍然存在。在光照无法到达的区域,模型的外观通常是全黑的,没有任何明暗变化,这会使模型背光区域看起来就像一个平面一样,失去了模型细节表现。实际上我们可以通过添加环境光来得到非全黑的效果,但即便这样让然无法解决背光面明暗一样的缺点。为此,有一种改善技术被提了出来,这就是半兰伯特(Half Lambert)光照模型 2.3 半兰伯特模型在2.1小结中,我们使用的漫反射光照模型也被称为兰伯特光照模型,因为它符合兰伯特定律——在平面某点漫反射光的光强与该反射点的法向量和入射光角度的余弦值成正比。为了改变上小结中提出的问题,Valve公司在开发游戏《半条命》时提出了一种技术,由于该技术是在原兰伯特光照模型的基础上进行了一个简单的修改,因此被称为半兰伯特光照模型。 可以看出,与原兰伯特模型相比,半兰伯特光照模型没有使用max操作来防止n和l的点积为负值,而是对其结果进行了一个α倍的缩放再加上一个β大小的偏移。绝大多数情况下,α和β的值均为0.5,即公式为 通过这样的方式,我们可以把n·l的结果范围从[-1,1]映射到[0,1]的范围内。也就是说,对于模型的背光面,在原版兰伯特光照模型中点积结果将映射到同一个值,即0值处;而在半兰伯特模型中,背光面也可以有明暗变化,不同的点积结果会映射到不同的值上。 需要注意的是,半兰伯特是没有任何物理依据的,它仅仅是一个视觉加强技术。 我们只需把代码进行一点修改就可以得到半兰伯特模型: fixed frag(v2f i):SV_Target{ ...... //Compute diffuse term fixed halfLambert = dot(worldNormal,worldLightDir)*0.5+0.5; fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*halfLambert; fixed3 color = ambient+diffuse; return fixed(color,1.0); } 在上面代码中,我们使用了半兰伯特模型替代了原有的兰伯特模型。下图给出了逐顶点漫反射光照、逐像素漫反射光照和半兰伯特光照的对比效果。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |