这是关于渲染基础的系列教程的第二部分。这个渲染基础的系列教程的第一部分是有关矩阵的内容。在这篇文章中我们将编写我们的第一个着色器代码并导入纹理。
这个系列教程是使用Unity 5.4.0开发的,这个版本目前还是开放测试版本。我使用的是build 5.4.0b10版本。
![](https://img-blog.csdnimg.cn/img_convert/70099206d9b4699dd1ef6f3413fb8233.png)
对球使用纹理。
1. 默认的场景
当你在Unity中创建新的场景的时候,你将使用默认的相机和定向的光源。 通过GameObject / 3D Object / Sphere这个菜单项来创建一个简单的球体,然后把这个简单的球体放在原点,并把相机放在它的前面。
![](https://img-blog.csdnimg.cn/img_convert/5e6957015ac7c9c91a729b1c4d13ab95.png)
默认场景中的默认球体。
这是一个非常简单的场景,但是已经有很多复杂的渲染内容了。为了更好地抓住渲染过程,摆脱所有奇怪的东西是非常有帮助的,首先让我们只关心渲染的基础部分。
1.1 剥离那些和渲染无关的内容
通过菜单项Window / Lighting来查看场景的光照设置。 这将打开一个具有三个选项卡的光照窗口。我们只对默认情况下处于激活状态的“场景”选项卡感兴趣。
![](https://img-blog.csdnimg.cn/img_convert/e8265045b694a42006b11c80563b9799.png)
默认的光照设置。
这是一个关于环境光照的部分,你可以在其中选择天空盒。这个天空盒目前用于场景背景、环境光照和反射。让我们将其设置为none进行关闭。
在你进行设置的时候,你还可以关闭预计算和实时全局光照的面板。我们不会很快使用到这些东西。
![](https://img-blog.csdnimg.cn/img_convert/08e94b8395038e0da1b91fb09f7a2552.png)
不再使用天空盒了。
在没有天空盒的情况下,环境光源会自动切换为纯色。环境光源的默认颜色为深灰色,具有非常浅的蓝色色调。而反射变为纯黑色,如警告框所示。
正如你可能期望的那样,球体会变得更暗,背景会是纯色。但是,得到的结果却是背景是深蓝色。那么这个颜色来自哪里?
![](https://img-blog.csdnimg.cn/img_convert/063797431c9598a03ea70bde0f39123b.png)
简化后的光照
背景颜色是根据摄像机来定义的。它在默认情况下会渲染天空盒,但是它也会回落到纯色状态。
![](https://img-blog.csdnimg.cn/img_convert/41ec3c0322b59171bc90801cccf4e246.png)
默认的相机设置。
为什么背景颜色的透明通道值为5而不是255?
要进一步简化渲染的话,请取消方向光源对象的激活或将其删除。这将摆脱场景中的直接光照,以及由直接光照所投射的阴影。剩下的就是背景,会用环境颜色显示球体的轮廓。
![](https://img-blog.csdnimg.cn/img_convert/3ded689afb066d89b75baaaff12d5793.png)
球体处于黑暗之中。
2. 从物体到二维图像
我们这个非常简单的场景是用两个步骤绘制出来的。 首先,图像用相机的背景颜色进行填充。然后将我们的球体的轮廓绘制在填充颜色的上面。
Unity怎么知道它必须画一个球体? 我们有一个球体对象,这个对象有一个网格渲染器组件。如果此对象位于相机的视图内,那么就应该出现在最终的图像中。 Unity通过检查对象的包围盒否与相机的视锥体相交来验证这一点。
![](https://img-blog.csdnimg.cn/img_convert/19c9b9d9f544f9df6ebcc1919e15c266.png)
默认的球体。
变换组件用于改变网格和包围盒的位置、方向和大小。实际上,整个变换层次都会被用到,正如第1部分“矩阵”中所描述的那样。如果对象会出现在相机的视图中,则这个物体会被安排进行渲染。
最后,图形处理器负责渲染对象的网格。 特定的渲染指令由对象的材质定义。 材质引用了着色器 - 这是一个图形处理器程序,加上它可能有的任何设置。
![](https://img-blog.csdnimg.cn/img_convert/e62750727b4c127956fd63606efc1bc8.png)
每个组件控制着渲染哪些内容。
我们的对象目前有默认材质,它使用Unity的标准着色器。我们要用我们自己的着色器来代替它,我们将从头开始构建它。
2.1 你的第一个着色器程序
通过Assets / Create / Shader / Unlit Shader创建一个新的着色器,并且将它命名为类似My First Shader这样的名字。
![](https://img-blog.csdnimg.cn/img_convert/176ce2040cc9c3764c2bd2c3c4cf0ee7.png)
你的第一个着色器程序。
打开着色器文件并删除其内容,所以我们可以从头开始。
着色器代码用Shader关键字定义。它后面是一个字符串,描述可用于选择此着色器的着色器菜单项。它不需要匹配文件名。然后跟着的是填充了着色器内容的块。
Shader"Custom/My First Shader"{
}
保存文件。 你将收到不支持这个着色器的警告,因为它没有子着色器或是备选着色器。这是因为它是空的缘故。
虽然这个着色器没有什么功能,但是我们可以将它分配给一个材质。因此,通过Assets / Create / Material来创建一个新材质,并从着色器菜单中选择我们的着色器。
![](https://img-blog.csdnimg.cn/img_convert/be5464281e85365288d5a92963ab88e0.png)
![](https://img-blog.csdnimg.cn/img_convert/66ad76bb650164acbef31b305c78320c.png)
使用了你的着色器的材质。
更改我们的球体对象,使我们的球体对象使用我们自己的材质,而不是默认材质。 球体将变为洋红色。发生这种情况是因为Unity会切换到一个错误的着色器,它使用这种颜色来引起你对问题的注意。
![](https://img-blog.csdnimg.cn/img_convert/71d81ca03f993cc88cee045a55be97eb.png)
![](https://img-blog.csdnimg.cn/img_convert/a78eb2e48fd7fb4b26ac40f3b9bcff83.png)
使用了你的着色器的材质。
着色器的错误提示信息中提到子着色器。你可以使用这些子着色器将多个着色器变量组合在一起。这允许你为不同的构建平台或者LOD值不同的情况下提供不同的子着色器。让我们举个简单的例子来说,你可以为桌面电脑上运行的那个应用使用一个子着色器,而为移动设备上运行的应用使用另一个子着色器。
Shader"Custom/My First Shader"{
SubShader
{
}
}
子着色器里面必须包含至少一个通道。着色器通道是对象实际被渲染的地方。 我们将使用一个通道,但着色器里面可以有多个。具有多个通道意味着对象被多次渲染,这是很多效果所需要的。
Shader"Custom/My First Shader"{
SubShader
{
Pass
{
}
}
}
我们的球体现在可能变成白色,因为我们使用的是一个空通道的默认行为。如果发生这种情况,这意味着我们不再收到任何着色器错误的提示信息。但是,你可能仍然在控制台中看到旧的错误提示信息。编辑器倾向于坚持提示错误信息,因为当着色器重新编译而没有错误的时候,这些错误提示信息是不会被清除的。
![](https://img-blog.csdnimg.cn/img_convert/cadd71a0cf6d7f78d1a40794c6c3bd8b.png)
一个白色的球体。
2.2 着色器程序
现在是时候来编写我们自己的着色器程序了。我们用Unity的着色语言来做这个功能,这是HLSL和CG着色语言的变体。我们必须用CGPROGRAM关键字指示我们的代码开始。我们必须以ENDCG关键字来指示我们的代码终止。
Pass
{
CGPROGRAM
ENDCG
}
着色器编译器现在会发出警告,警告我们的着色器里面没有顶点程序和片段程序。着色器由两个程序组成,也就是顶点程序和片段程序。顶点程序负责处理网格的顶点数据。 这包括从对象空间到显示空间的转换,就像我们在第1部分“矩阵”中所做的那样。片段程序负责对位于网格三角形内的单个像素进行渲染。
![](https://img-blog.csdnimg.cn/img_convert/8923c59685251d37468a89a189903b82.png)
顶点程序和片段程序。
我们必须通过pragma指令告诉编译器使用哪些程序。
CGPROGRAM
#pragma vertex MyVertexProgram
#pragma fragment MyFragmentProgram
ENDCG
编译器会再次发出警告,这次因为它找不到我们指定的程序。这是因为我们还没有定义这些程序的缘故。
定义顶点程序和片段程序就像定义方法一样,非常像C#里面的做法,虽然它们通常被称为函数。让我们简单地创建两个空的返回void的方法,并给它们适当的名称。
CGPROGRAM
#pragma vertex MyVertexProgram
#pragma fragment MyFragmentProgram
voidMyVertexProgram () {
}
voidMyFragmentProgram () {
}
ENDCG
此时,着色器将编译,球体将消失。或者你仍然会得到错误信息提示。这取决于你的编辑器使用的是哪个渲染平台。如果你使用的是Direct3D 9渲染平台,你可能会得到错误信息提示。
2.3 着色器的编译
Unity的着色器编译器接受我们的代码,并将其转换为不同的程序,具体如何转换取决于目标平台。不同的平台需要不同的解决方案。 例如,如果是Windows 平台的话,需要的是Direct3D,如果是Mac平台的话,需要的是OpenGL ,如果是移动平台的话,需要的是OpenGL ES,等等。 我们不是在这里处理单个编译器,而是处理多个编译器。
你最终使用哪个编译器取决于你的定位。由于这些编译器并不相同,因此每个平台可能会产生不同的结果。举个简单的例子来说,我们的空程序使用OpenGL和Direct3D 11的话,就能正常工作,但在如果使用的是Direct3D 9,就会失败。
在编辑器中选择着色器,并查看检查器窗口。它会显示有关着色器的一些信息,包括当前的编译器错误。还有一个带有“编译和显示代码”按钮和下拉菜单的“编译代码”项。 如果单击“编译和显示代码”按钮,Unity将编译着色器代码并在编辑器中打开着色器代码的输出,因此你可以检查生成的代码具体是什么。
![](https://img-blog.csdnimg.cn/img_convert/3b8b37b4d77965f788595c61b4446e9b.png)