本节书摘来自华章计算机《Unity着色器和屏幕特效开发秘笈(原书第2版)》一书中的第1章,第1.5节,作者 [英]艾伦朱科尼(Alan Zucconi)[美]肯尼斯拉默斯(Kenneth Lammers),译 占红来,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1.5 在表面着色器中使用属性
我们已经创建了一些属性,现在在着色器中开始试着把这些属性用起来,通过这些属性让材质的微调过程更加高效。
可以通过材质的Inspector标签页得到这些属性的值,因为我们给属性绑定了一个变量名。但是在通过变量名访问属性值之前,要先准备一些东西。
1.5.1 操作步骤
下面是在表面着色器中使用属性的步骤:
- 因为我们在1.2节中已经删除了_MainTex,所以开始之前,先移除下面几行代码:
- 接下来,在着色器中的CGPROGRAM行之下添加如下代码:
- 第2步完成之后,可以在着色器中使用属性的值了。我们先将_Color属性和_AmbientColor属性的值相加之后,赋给o.Albedo,所以在surf()函数中添加下面的代码:
- 最终的着色器代码应该看起来像下面这样。如果你在MonoDevelop中保存代码然后在Unity中打开,着色器会被自动编译。如果没有错误的话,可以修改材质的环绕色和发光色了,也可以通过滑块来增加最终颜色的饱和度等值。就是这么智能!
下面这张截图是使用我们新添加的属性,在Inspector标签页微调了颜色和饱和度之后得到的结果。
1.5.2 工作原理
当你在Properties代码块中声明一个新的属性时,实际上是在给着色器提供一种获取材质的Inspector标签页中值的方式,这个值存储在变量名对应的存储单元中。在这个例子中,_AmbientColor、_Color和 _MySliderValue就是所有我们用来存储微调值的变量。为了能在SubShader{}代码块中使用这些值,需要创建三个与属性变量同名的变量。这样就能给二者自动建立一种联系,以便它们知道是在围绕同一个数据做修改。此外,它还声明了我们想存储在子着色器变量中的数据类型 ,这也给我们日后的优化带来了不少方便。
如果你创建了子着色器变量,就可以在surf()函数中使用它们了。在这里,我们想将_Color和 _AmbientColor变量加到一起作为幂函数的底,而将_MySliderValue变量作为幂函数的幂次数。
绝大部分着色器都是从标准着色器开始的,然后逐步调整到想要的样子。我们现在已经创建好了一个基础的表面着色器,通过这个表面着色器你可以创建任何你想要的漫反射组件。
材质是资源的一种,也就意味着游戏运行时任何一点针对材质的修改都是永久性的。如果你误操作了某个属性的值,可以通过Ctrl + Z来撤销。
1.5.3 更多内容
和其他的编程语言差不多,Cg不允许代码错误。此时,如果你的代码中有笔误,着色器是不会正常显示的。如果发生了这种情况,你的材质会呈现无任何涂层的洋红色:
如果脚本不能编译,Unity会阻止游戏输出甚至阻止游戏运行。相反,着色器中的错误并不会阻止游戏运行。
如果你的某个着色器呈现洋红色,就要花点时间研究问题在哪里了。如果选中出错的着色器,会在Inspector标签页中看到一个错误列表:
除了显示出错行之外,错误消息也同时在告诉你应该去这里修复问题。上面截图里的错误信息是在删除SubShader{}代码块中的sampler2D _MainTex变量的时候产生的。但是错误出现在首次想要访问这个变量的地方。
找到并且修复问题的过程称为调试,大家常犯的错误如下:
- 少个括号。如果你在某个部分忘了一个反括号,编译器会在这一部分文本的最后、最前或者某个新的部分处报错。
- 少了分号。这是最为常见的一种错误,也是最容易发现和修复的。报错信息通常出现在下一行。
- 在Properties代码块中定义了一个属性,但是没有在SubShader{}代码块中标明为一个变量。
- 与C#脚本混淆了,Cg中的浮点数并不需要在后面加一个f,比如1.0而不是1.0f。
着色器提供的报错信息可能非常难懂,特别是在某些有特别严格的语法约束的时候。如果你不懂报错信息的意思,最好在网上搜一搜。Unity论坛里面有很多开发者,他们很有可能碰到或者修复过你现在碰到的问题。
1.5.4 参考
表面着色器及其属性的更多内容放在第2章中。如果你想知道着色器火力全开的时候能做些什么,可以看看第10章,这一章有本书中所覆盖的大部分高级着色技术。