写在前面

三个月以前,在一篇讲卡通风格的Shader的最后,我们说到在Surface Shader中实现描边效果的弊端,也就是只对表面平缓的模型有效。这是因为我们是依赖法线和视角的点乘结果来进行描边判断的,因此,对于那些平整的表面,它们的法线通常是一个常量或者会发生突变(例如立方体的每个面),这样就会导致最后的效果并非如我们所愿。如下图所示:

aaarticlea/png;base64,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" alt="" />

因此,我们有一个更好的方法来实现描边效果,也就是通过两个pass进行渲染——首先渲染对象的背面,用黑色略微向外扩展一点,就是我们的描边效果;然后正常渲染正面即可。而我们应该知道,surface shader是不可以使用pass的。

如果我们想要使用上述方法实现描边,我们就需要写另一种shader——fragment shader。和surface shader相比,这种shader需要我们编写更多的代码,处理更多的事情,但也可以让我们更加了解shader是如何工作的。而之前的一篇文章也分析过,其实surface shader的背后也是生成了对应的vertex&fragment shader。

这篇文章主要参考了Unity Gems里的一篇文章,但正如文章评论里所说,有些技术比如求attenuation稳重方法已经“过时”,因此本文会对这类问题以及一些作者没有说清的问题给予说明。在查资料的时候,发现由于Unity背后做了太多事,定义了很多变量、函数和宏,而又没有给出详尽的使用说明,写起来实在太头大了。。。同样,本篇内容仅供参考。

Vertex & Fragment Shaders

Vertex & Fragment Shaders的工作流程如下图所示(简略版,来自Unity Gems):

所以,看起来也没那么难啦~我们只需要编写两个函数就可以喽~

我们来分析下它的流程。首先,vertex program收到系统传递给它的模型数据,然后把这些处理成我们后续需要的数据(但至少要包含这些顶点的位置信息)进行输出。其他的输出数据比如有,纹理的UV坐标以及其他需要传递给fragment program的数据。然后,系统对vertex program输出的顶点数据进行插值,并将插值结果传递给fragment program。最后,fragment program根据这些插值结果计算最后屏幕上的像素颜色。

在本篇文章,我们首先会学习编写一个简单的diffuse & diffuse bumped shader。然后再来具体看如何编写一个具有多个passes的shader。

Diffuse, Vertex Lit Fragment Shader

开始的开始,我们首先需要在SubShader中使用Pass {}关键字定义一个pass。一个Pass可以为该阶段定义一系列的tags。例如,我们可以剔除(Cull)背面或者正面,控制是否写入Z buffer等。我们的diffuser shader将会剔除背面。具体可见官网

下面是我们的Pass定义:

		Pass {
Tags { "LightMode" = "Vertex" }
Cull Back
Lighting OnCGPROGRAM #pragma vertex vert
#pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc" // More code here ENDCG
}

在上面的代码里,我们定义了一个pass,设定LightMode为Vertex,告诉它打开光源并且剔除背面。然后,我们定义了CG程序的开头部分,指定了vertex和fragment programs的名字。最后,我们包含了Unity定义的一个文件,以便在后面的CG程序中可以使用某些函数和变量。

LightMode是个非常重要的选项,因为它将决定该pass中光源的各变量的值。如果一个pass没有指定任何LightMode tag,那么我们就会得到上一个对象残留下来的光照值,这并不是我们想要的。其他各个LightMode的具体含义可以参见官网(很重要,一定要去看,特别是对于每个Pass的细节解释,一定要点进去看!!!),这里做一个简单的解释。

  • LightMode=Vertex:会设置4个光源,并按亮度从明到暗进行排序,它们的值会存储在unity_LightColor[n], unity_LightPosition[n], unity_LightAtten[n]这些数组中。因此,[0]总会得到最亮的光源。
  • LightMode=ForwardBase: _LightColor0将会是主要的directional light的颜色。
  • LightMode=ForwardAdd:和上面一样, _LightColor0将是该逐像素光源的颜色。

Vertex Lit是什么

在我们写shader的时候有很多选择——我们可以定义多个passes,其中每一个pass处理一个光源,这样来处理所有的光源;或者我们选择逐顶点处理所有的光源(在一个pass里处理掉),然后再对它们进行插值。很明显,后面这种方式会快很多,因为它仅仅需要一个pass就可以了,而前一个方式需要更多的passes。

如果我们写了一个Vertex Lit shader,那么我们就会按照第二种方式那样,一次考虑所有的光源对顶点的影响。如果我们写了一个多passes的shader,那么它就会被多次调用,每次针对一个光源,考虑该光源对模型的影响。

对于Vertex Lit,Unity已经为我们编写了一些辅助函数,我们会在后面看到。

The Vertex Program

下面,我们正式开始编写代码。首先,我们需要定义vertex program。而它需要得到模型的相关信息作为输入,因此,我们定义下面的结构:

			struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};

这个结构定义依赖某些语法,即那些“:XXX”样子的值。我们的变量叫什么并不重要,但这些“:XXX”语法则说明系统将使用哪些值去填充它们。这里,我们通过上述代码可以得到了model space中的顶点位置、法线方向以及纹理坐标。

在fragment shaders里,空间(spaces)的概念是非常重要的。空间重要是指坐标的相对位置。
  • 在model space中,坐标是相对于网格的原点(0,0,0)定义的。我们的vertex function需要把这些坐标转换到clip space中,为投影做准备。
  • 在tangent space中,坐标是相对于模型的正面定义的——在处理法线纹理时我们使用这个space,这在后面会具体讲到。
  • 在world space中,坐标是相对于世界的原点(0,0,0)定义的。
  • 在view space中,坐标是相对于摄像机定义的,因此在这个space中,摄像机的位置就是(0,0,0)。
  • 在clip space中,通常图元会被裁剪,然后再通过屏幕映射投影到屏幕空间中。
如果你读过一些关于shaders的文章,那么你大概会见过关于选择哪个space来照亮模型的理论。初学者往往会有点困惑,这实际上就是选择你要把光源方向、位置等数据转换到哪个坐标系中来进行相关运算,得到最终的像素值。希望在本篇的最后,你可以明白这些问题!

那么,在定义了vertex program的输入后,我们还需要定义它的输出。之前我们说过,vertex program的输出将会被插值用于生成像素,而这些插值后的值就是fragment program的输入。

			struct v2f {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float3 color : TEXCOORD1;
};

上面就是我们的输出。在这里,之前所说的语义就没有那么重要了——只有一个是必须的,即用POSITION标识的变量,这是把顶点坐标转换到clip space后的位置。我们输出的所有值(并且没有uniform限定词)都将在fragment program之前被插值。

注意:但对于DX11和Xbox360来说,必须要有语义说明,否则会报错。即需要为变量指定TEXCOORD1等位置。

出于性能的考虑,很显然我们应该尽可能在vertex function里进行更多的运算,这是因为vertex function是逐顶点调用的,而fragment function则是逐像素调用的。

下面是真正的vertex function,它把输入a2v转换成输出v2f(也是fragment function的输入)。

			v2f vert(a2v v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.color = ShadeVertexLights(v.vertex, v.normal);
return o;
}

第一行,我们定义了输出v2f的一个实例。然后把顶点的位置和Unity提前定义的一个矩阵UNITY_MATRIX_MVP(在UnityShaderVariables.cginc里定义)相乘,从而把顶点位置从model space转换到clip space。我们使用了矩阵乘法操作mul来执行这个步骤。

第二行,我们为给定的纹理计算其uv坐标,即根据mesh上的uv坐标来计算真正的纹理上对应的位置。我们使用了Unity.CG.cginc中的宏TRANSFORM_TEX来实现。

注意,要使用宏TRANSFORM_TEX,我们需要在shader中定义一些额外的变量,即必须定义一个名为_YourTextureName_ST (也就是你的纹理的名字加一个 _ST后缀)。这是因为宏TRANSFORM_TEX的定义为:#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)。这是因为我们的纹理有Tiling和Offset参数,如下图中面板所示,因此需要对原mesh上的uv进行相应调整才能得到真正的纹理坐标。

最后,我们计算得到顶点的初始颜色——即光源对该顶点的影响。在我们的第一个shader中,我们使用一个名为ShadeVertexLights的函数,它的输入为模型的顶点和法线。这是一个内置的函数,它将考虑4个距离最近(若距离相等则按光源类型排序)的光源以及一个环境光(在Edit->Render Settings->Ambient Light里设置)。它的实现可以在UnityCG.cginc里找到。其他辅助函数可以详见官网

The Fragment Shader

根据上述过程,系统会在每个顶点上调用vertex program,并将其输出在同一个几何图元上进行插值。下面,我们根据这些插值后的值来得到对应的像素值。下面是真正的fragment program:

			float4 frag(v2f i) : COLOR {
float4 c = tex2D(_MainTex, i.uv);
c.rgb = c.rgb * i.color * 2;
return c;
}

上述代码使用了surface shader中也很常见的纹理采样操作,来得到对应的纹理像素值。然后,将该纹理颜色和插值后的vertex function输出的顶点光颜色进行相乘,并把结果乘以2(否则颜色会太暗。)。最后,返回得到的像素值。

完整代码

最后,完整的Vertex Lit Diffuse代码如下:

Shader "Custom/VertexLit" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 300 Pass {
Tags { "LightMode" = "Vertex" } Cull Back
Lighting On CGPROGRAM #pragma vertex vert
#pragma fragment frag #include "UnityCG.cginc" sampler _MainTex;
float4 _MainTex_ST; struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
}; struct v2f {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float3 color : TEXCOORD1;
}; v2f vert(a2v v) {
v2f o;o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.color = ShadeVertexLights(v.vertex, v.normal);
return o;
} float4 frag(v2f i) : COLOR {
float4 c = tex2D(_MainTex, i.uv);
c.rgb = c.rgb * i.color * 2;
return c;
} ENDCG
}
}
FallBack "Diffuse"
}

这样,我们就完成了第一个vertex & fragment shader。上述效果如果用surface shader可能只要几句话,但你渐渐会发现,虽然使用vertex & fragment shader会增加更多的代码量,但它能做的真是太多了!

上述shader的效果如下(啦啦啦,又是小苹果+呆萌小怪兽的组合~~~):

Diffuse Normal Map Shader

下面我们要向shader添加一个非常常见的法线纹理(Normal Texture)。

Normal Maps

如果你在Unity里使用过法线纹理的话,你应该知道在使用之前,你需要先把该纹理的类型设置成Normal,对吧?那么,到底为什么要这样呢?法线纹理跟其他纹理有什么不一样呢?

法线纹理具有以下性质

  • 它存储了模型表面的法线方向。有基于model space(肉眼看起来颜色比较丰富,有红色蓝色等)和基于tangent space(通常都是蓝色的)的两种法线纹理,而Unity常见的是后面一种法线纹理。
  • 由于法线向量中每一维的范围在(-1,1),因此我们需要把它重新映射到(0,255)。具体做法是把原值除以2再偏移0.5,最后乘以255。
  • 在存储的时候是压缩存储。因为法线纹理都是被正则化的,即是单位向量,模为1,所以实际上只需要存储该向量的两个维度就可以了,第三维可以用前两个推导出来。
  • 由于上一点,每一个维度占用16 bits,即每个rgba包含了两个维度的值。
当使用法线纹理的时候,我们需要在tangent space中处理光照对模型的影响。也就是说,我们需要把和计算光照对像素的影响的数据都转换到tangent space中,然后在这个坐标系中计算得到最终的颜色。而且,在这里我们实际上是计算了逐像素的光照,而不是像前一个shader那样是逐顶点的。
我们选择在tangent space计算光照是因为这种做法的计算量更少。我们只需要基于每个顶点,把光照信息(有时还需要观察点信息等)转换到tangent space,再对其进行插值即可。而另一种方式是在world space中处理光照,这意味着我们需要把法线纹理中的每一个法线转换到world space中,因此我们需要基于每个像素进行处理。和逐顶点的处理方式相比,这种方法显然需要更多的计算。

在Unity里转换到tangent space是比较容易的。下面,我们不会使用逐顶点的光照处理函数ShadeVertexLights,而是逐像素的处理光照。

照亮我们的模型

下面,我们将使用Lambert光照模型,也就是法线*光照方向*衰减*2。
在我们把需要的数据都转换到tangent space后,处理光照就变得非常简单了。可以用下图(来源:Unity Gems)来演示这样一个过程:

但是,光源在哪里呢?

Unity为我们提供了那些对模型有影响的光源(按重要度排序,例如距离远近、光照类型等)的位置、颜色和衰减等信息。

Unity使用了三个数据来定义顶点光源:unity_LightPosition,unity_LightAtten和unity_LightColor。例如[0]表示最重要的光源。

当我们编写一个multi-pass的光照模型(正如我们下面写的那样)时,我们只需要一次处理一个单独的光源,这种情况下,Unity同样定义了一个名为_WorldSpaceLightPos0的值,来帮助我们得到它的位置,并且还提供了一个非常有用的函数ObjSpaceLightDir,它可以计算得到该光源的方向。而为了得到该光源的颜色,我们可以在程序中包含“Lighting.cginc”文件,然后使用_LightColor0进行访问。

Forward Lighting(而非Vertex Lit)

在第一个shader里我们使用了vertex lights,而现在,我们来看下怎么为光源定义多个passes。那么,开始吧!

首先,我们需要更改Tags中的LightMode,让其值为ForwardBase,来让Unity我们设置光源数据。

		Pass {
Tags { "LightMode" = "ForwardBase" }

然后,我们还需要添加#pragma指令:

#pragma multi_compile_fwdbase

这都是为了能让Unity各种内置数据、宏定义等可以正常工作。真的是很头大啊,至今官方也没有给出详细的参考资料。。。(Rant!!!)

然后,为了使用法线纹理我们需要定义两个变量,一个是名为_XXX的sampler2D变量,一个是名为_XXX_ST的float4变量(当然你还需要在Properties中定义一个名为_XXX的新属性)。

现在我们需要为vertex program定义新的输入:

			struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
};

这里我们添加了一个新的变量,其语义是:TANGENT。我们会在把光源方向转换到tangent space中时需要这个变量。

Tangent Space转换

为了把向量从object space转换到tangent space,我们需要为顶点定义另外两个向量。通常对一个顶点来说,我们知道它的法线normal,而其中一个向量tangent是和normal正交的,另一个向量binormal则是normal和tangent的叉乘结果。有了这三个向量,我们就可以定义一个矩阵来执行到tangent space的转换。

幸运地是,UnityCG.cginc里定义了一个名为TANGENT_SPACE_ROTATION的宏,它提供了一个名为rotation的矩阵来把object space下的坐标转换到tangent space中。

Vertex到Fragment Programs的输出

在知道转换的方法后,我们需要在vertex function里计算tangent space下的光源方向,然后对其进行插值后传递给fragment function。因此,我们需要在vertex function的输出里添加新的变量——光源方向。

			struct v2f {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float2 uv2 : TEXCOORD1;
float3 lightDirection : TEXCOORD2;
LIGHTING_COORDS(3,4)
};

lightDirection将会存储插值后的光源方向向量。uv2将会存储法线纹理的纹理坐标。最后的LIGHTING_COORDS(3,4)是在AutoLight.cginc里定义的宏,它负责创建光源坐标,用于某些内置的光照计算。在下面计算光源的attenuation时,我们会需要这些值。

该shader只对directional lights和point lights有效。本例中我们没有考虑spotlight的角度。

The Vertex Program

			v2f vert(a2v v) {
v2f o; TANGENT_SPACE_ROTATION;
o.lightDirection = mul(rotation, ObjSpaceLightDir(v.vertex));
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv2 = TRANSFORM_TEX(v.texcoord, _BumpTex); TRANSFER_VERTEX_TO_FRAGMENT(o);
return o;
}

在vertex program里,我们使用了宏TANGENT_SPACE_ROTATION(在UnityCG.cginc里定义)来创建一个名为rotation的矩阵,并使用它把object space转换到tangent space中。

为了让这个宏能够正确处理我们的输入,vertex program的输入必须是一个名为v的结构体,并且它包含了一个名为normal的法线以及一个名为tangent的切线。这都是因为它的宏定义里指明了变量的名字的缘故。

然后,我们使用内置函数ObjSpaceLightDir(v.vertex)计算了在object space中光源(这时指的就是最重要的那个光源)的方向。随后,我们再把结果和新的rotation矩阵相乘,从而把方向从object space又转换到了tangent space。

下面几行,我们计算得到顶点在clip space中的位置以及纹理的uv坐标。

最后,我们使用了名为的TRANSFER_VERTEX_TO_FRAGMENT宏,它同样在AutoLight.cginc里定义,和上面v2f中的宏LIGHTING_COORDS协同工作,它会根据该pass处理的光源类型(spot?point?or directional?)来计算光源坐标的具体值,以及进行和shadow相关的计算等。

Directional和Point Lights

Unity把光源的位置存储在float4类型的_WorldSpaceLightPos0里,即_WorldSpaceLightPos0包含了4个元素。如果这个光源是directional,那么xyz就是这个光源的方向,而w(即最后一个元素)则是0;如果这时一个point light,那么xyz将表示光源的位置,而w则是1。那么,这些有什么影响呢?

这其实方便了ObjSpaceLightDir函数的计算过程。它首先将顶点的位置乘以光源位置的w元素,然后再用光源位置减去顶点的位置,来得到光源方向。因此,如果是一个directional light,我们相乘后就会得到0,即返回光源的xyz值(实际上就是光源的方向);如果是一个point light,我们就会得到顶点到光源的一个方向向量。

The Fragment Function

			float4 frag(v2f i) : COLOR {
float4 c = tex2D(_MainTex, i.uv);
float3 n = UnpackNormal(tex2D(_BumpTex, i.uv2)); float3 lightColor = UNITY_LIGHTMODEL_AMBIENT.xyz; float atten = LIGHT_ATTENUATION(i); // Angle to the light
float diff = saturate(dot(n, normalize(i.lightDirection)));
lightColor += _LightColor0.rgb * (diff * atten); c.rgb = lightColor * c.rgb * 2; return c;
}

在fragment function里,我们首先从法线纹理里解压出法线。然后,我们使用Unity设置的环境光作为初始颜色值。随后,我们计算了衰减值,即光源距离的远近。这里,我们同样使用了AutoLight.cginc里的宏,即LIGHT_ATTENUATION,它同样会判断该pass处理的光源类型,然后得到光源的衰减率。

然后,我们把法线和光源方向进行点乘得到漫反射值,再和光源颜色以及衰减值结合起来,叠加到像素值上。为了得到光源的颜色,我们使用了_LightColor0——这需要我们在shader中包含“Lighting.cginc”文件。或者,我们也可以在shader中定义一个名为_LightColor0的变量,Unity会自行填充它的值。

uniform float4 _LightColor0;

完整代码

最后完整的代码如下:

Shader "Custom/DiffuseNormal" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_BumpTex ("Bump Texture", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 300 Pass {
Tags { "LightMode" = "ForwardBase" } Cull Back
Lighting On CGPROGRAM #pragma vertex vert
#pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc" sampler _MainTex;
sampler _BumpTex; float4 _MainTex_ST;
float4 _BumpTex_ST; struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
}; struct v2f {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float2 uv2 : TEXCOORD1;
float3 lightDirection : TEXCOORD2;
LIGHTING_COORDS(3,4)
}; v2f vert(a2v v) {
v2f o; TANGENT_SPACE_ROTATION;
o.lightDirection = mul(rotation, ObjSpaceLightDir(v.vertex));
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv2 = TRANSFORM_TEX(v.texcoord, _BumpTex); TRANSFER_VERTEX_TO_FRAGMENT(o);
return o;
} float4 frag(v2f i) : COLOR {
float4 c = tex2D(_MainTex, i.uv);
float3 n = UnpackNormal(tex2D(_BumpTex, i.uv2)); float3 lightColor = UNITY_LIGHTMODEL_AMBIENT.xyz; float atten = LIGHT_ATTENUATION(i); // Angle to the light
float diff = saturate(dot(n, normalize(i.lightDirection)));
lightColor += _LightColor0.rgb * (diff * atten); c.rgb = lightColor * c.rgb * 2; return c;
} ENDCG
}
}
FallBack "Diffuse"
}

Shader效果如下:

在Forward Mode中处理Multiple Lights

通过上面的学习,我们已经学会了如何处理一个光源,但仅仅是一个。要处理多光源,我们就需要编写另一个pass,并且使用新的tags来告诉Unity我们想要逐个处理光源。

这基本上只需要两步:

  • 一个pass处理第一个光源,就像我们上面做的那样
  • 然后定义更多的pass,来处理后续的光源,并把结果添加(add on)到前面的结果上
因此,我们把之前pass的代码再粘贴一遍,来创建一个新的pass,但要把tag改成:
Tags { "LightMode" = "ForwardAdd" }
并且更改#pragma指令:
#pragma multi_compile_fwdadd

然后添加一个新的命令来告诉Unity怎样混合前后两个pass的值:

Blend One One

然后,我们移除掉第二个pass对UNITY_LIGHTMODEL_AMBIENT的处理,因为我们已经在第一个pass中处理过这个值了。我们最后的代码如下:

Shader "Custom/DiffuseNormal" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_BumpTex ("Bump Texture", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 300 Pass {
Tags { "LightMode" = "ForwardBase" } Cull Back
Lighting On CGPROGRAM #pragma vertex vert
#pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc" sampler _MainTex;
sampler _BumpTex; float4 _MainTex_ST;
float4 _BumpTex_ST; struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
}; struct v2f {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float2 uv2 : TEXCOORD1;
float3 lightDirection : TEXCOORD2;
LIGHTING_COORDS(3,4)
}; v2f vert(a2v v) {
v2f o; TANGENT_SPACE_ROTATION;
o.lightDirection = mul(rotation, ObjSpaceLightDir(v.vertex));
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv2 = TRANSFORM_TEX(v.texcoord, _BumpTex); TRANSFER_VERTEX_TO_FRAGMENT(o);
return o;
} float4 frag(v2f i) : COLOR {
float4 c = tex2D(_MainTex, i.uv);
float3 n = UnpackNormal(tex2D(_BumpTex, i.uv2)); float3 lightColor = UNITY_LIGHTMODEL_AMBIENT.xyz; float atten = LIGHT_ATTENUATION(i); // Angle to the light
float diff = saturate(dot(n, normalize(i.lightDirection)));
lightColor += _LightColor0.rgb * (diff * atten); c.rgb = lightColor * c.rgb * 2; return c;
} ENDCG
} Pass {
Tags { "LightMode" = "ForwardAdd" } Cull Back
Lighting On
Blend One One CGPROGRAM #pragma vertex vert
#pragma fragment frag #pragma multi_compile_fwdadd #include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc" sampler _MainTex;
sampler _BumpTex; float4 _MainTex_ST;
float4 _BumpTex_ST; struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
}; struct v2f {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float2 uv2 : TEXCOORD1;
float3 lightDirection : TEXCOORD2;
LIGHTING_COORDS(3,4)
}; v2f vert(a2v v) {
v2f o; TANGENT_SPACE_ROTATION;
o.lightDirection = mul(rotation, ObjSpaceLightDir(v.vertex));
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv2 = TRANSFORM_TEX(v.texcoord, _BumpTex); TRANSFER_VERTEX_TO_FRAGMENT(o);
return o;
} float4 frag(v2f i) : COLOR {
float4 c = tex2D(_MainTex, i.uv);
float3 n = UnpackNormal(tex2D(_BumpTex, i.uv2)); float3 lightColor = float3(0); float lengthSq = dot(i.lightDirection, i.lightDirection);
float atten = LIGHT_ATTENUATION(i); // Angle to the light
float diff = saturate(dot(n, normalize(i.lightDirection)));
lightColor += _LightColor0.rgb * (diff * atten); c.rgb = lightColor * c.rgb * 2; return c;
} ENDCG
}
}
FallBack "Diffuse"
}
我们在场景里放置两个光源——一个平行光,用于ForwardBase Pass的计算,一个Point Light,用于ForwardAdd Pass的计算。效果如下:

写在最后

本文里对处理光源attenuation的方法和Unity Gems里的方法不同,按原文里的做法在Unity 4.5(更早的版本不清楚)是无法得到正确的attenuation的,即把点光源拉进拉远不会对模型有任何影响,除非拉出了光源范围,这时会有一个不正常的明暗突变。为了找正确的方法真是麻烦啊。。。Unity关于shader的文档的确需要加强,而且在Unity里写Vertex & Fragment Shader绝对比想象中的难,有一条准则就是,如果它提供给里某些功能的函数(比如这里计算attenuation的方法,要4个步骤,#pragma multi_compile_fwdadd + LIGHTING_COORDS + TRANSFER_VERTEX_TO_FRAGMENT+ LIGHT_ATTENUATION),那么千万不要自己尝试去写一个函数出来。。。某些内置的变量实在是不知道它们什么时候工作、怎么工作。。。

【Unity Shaders】Vertex & Fragment Shader入门的更多相关文章

  1. Unity Shaders Vertex & Fragment Shader入门

    http://blog.csdn.net/candycat1992/article/details/40212735 三个月以前,在一篇讲卡通风格的Shader的最后,我们说到在Surface Sha ...

  2. 【Unity Shaders】Mobile Shader Adjustment —— 为手机定制Shader

    本系列主要參考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同一时候会加上一点个人理解或拓展. 这里是本书全部的插图.这里是本书所需的代码和 ...

  3. 【Unity Shaders】Mobile Shader Adjustment—— 什么是高效的Shader

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  4. 【Unity Shaders】Shader中的光照

    写在前面 自己写过Vertex & Fragment Shader的童鞋,大概都会对Unity的光照痛恨不已.当然,我相信这是因为我们写得少...不过这也是由于官方文档对这方面介绍很少的缘故, ...

  5. unity shader入门(一):基本结构话痨版

    unity shader 有三种形式:表面着色器(Surface Shader),顶点/片元着色器(Vertex/Fragment Shader),固定函数着色器(Fixed Function Sha ...

  6. 【Unity Shaders】学习笔记——SurfaceShader(一)认识结构

    [Unity Shaders]学习笔记——SurfaceShader(一)认识结构 转载请注明出处:http://www.cnblogs.com/-867259206/p/5595747.html 写 ...

  7. 【Unity Shaders】法线纹理(Normal Mapping)的实现细节

    写在前面 写这篇的目的是为了总结我长期以来的混乱.虽然题目是"法线纹理的实现细节",但其实我想讲的是如何在shader中编程正确使用法线进行光照计算.这里面最让人头大的就是各种矩阵 ...

  8. 【Unity Shaders】Alpha Test和Alpha Blending

    写在前面 关于alpha的问题一直是个比较容易摸不清头脑的事情,尤其是涉及到半透明问题的时候,总是不知道为什么A就遮挡了B,而B明明在A前面.这篇文章就总结一下我现在的认识~ Alpha Test和A ...

  9. 【Unity Shaders】Unity里的雾效模拟

    写在前面 熟悉Unity的都知道,Unity可以进行基本的雾效模拟.所谓雾效,就是在远离我们视角的方向上,物体看起来像被蒙上了某种颜色(通常是灰色).这种技术的实现实际上非常简单,就是根据物体距离摄像 ...

随机推荐

  1. ANI功能分析

    1 ANI ANI(Adapt Noise Immunity)就是基于CCK错包率,和/或CCK错包率,自动调整抗扰等级,从而提高或降低灵敏度,达到提高整体性能的目标. 2 关键常量 firstep_ ...

  2. c++中双冒号的作用

    双冒号(::)用法 参考链接:https://segmentfault.com/a/1190000000345680 1.表示“域操作符”例:声明了一个类A,类A里声明了一个成员函数void f(), ...

  3. BI之SSIS入门最新版Visual Studio调试技巧

    简介 最近公司业务需要用到BI SSIS,SSIS是什么?"SSIS是Microsoft SQL Server Integration Services的简称,是生成高性能数据集成解决方案( ...

  4. 阿里云部署Node.js项目(CentOS)

    Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,用来方便地搭建快速的易于扩展的网络应用.Node.js 使用了一个事件驱动.非阻塞式 I/O 的模型,使其轻量又 ...

  5. Node.js系列文章:利用console输出日志文件

    通常我们在写Node.js程序时,都习惯使用console.log打印日志信息,但这也仅限于控制台输出,有时候我们需要将信息输出到日志文件中,实际上利用console也可以达到这个目的的,今天就来简单 ...

  6. Python自动化测试入门

    在当前自动化测试中,最火的语言就是Python,很容易上手.然后就是Java+testng+appium做的UI自动化测试.下面我们就用Python脚本,做自动化集成测试. (1)获取APK文件中ID ...

  7. 继承自 DevExpress 17.2 的自定义控件如何在工具箱显示

    最近把DevExpress版本从13.1升级到了17.2,结果发现继承自DevExpress的自定义控件居然在工具箱中消失了,弄了两天还是没有任何头绪,部分自定义Dev控件可以正常出现,但大部分自定义 ...

  8. Linux c readdir是非线程安全,需用readdir_r,要注意用静态变量当做返回值的函数的非线程安全性

    readdir函数: struct dirent *readdir(DIR *dirp); The  data  returned by readdir() may be overwritten by ...

  9. Java中的Lock锁

    Lock锁介绍: 在java中可以使用 synchronized 来实现多线程下对象的同步访问,为了获得更加灵活使用场景.高效的性能,java还提供了Lock接口及其实现类ReentrantLock和 ...

  10. SpringMVC+Spring+Mybatis整合,使用druid连接池,声明式事务,maven配置

    一直对springmvc和mybatis挺怀念的,最近想自己再搭建下框架,然后写点什么. 暂时没有整合缓存,druid也没有做ip地址的过滤.Spring的AOP简单配置了下,也还没具体弄,不知道能不 ...