《Unity着色器和屏幕特效》——第2章 创建自定义着色器 2.1 打开程序项目-阿里云开发者社区

开发者社区> 华章出版社> 正文
登录阅读全文

《Unity着色器和屏幕特效》——第2章 创建自定义着色器 2.1 打开程序项目

简介:

本节书摘来自华章计算机《Unity着色器和屏幕特效》一书中的第2章,第2.1节,作者[美]杰米·迪恩(Jamie Dean),译 周翀,张薇,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

第2章

创建自定义着色器

这一章的内容将继续围绕飞船维修场景展开。通过添加自定义着色器,场景的真实感将获得进一步提升。我们将探索物体表面与场景光照之间的互动关系,展示Unity的基于物理的着色系统为游戏画面的质量带来的提升。
本章涉及的内容如下:

  • 从无到有创建基本的自定义着色器。
  • 为自定义着色器添加属性。
  • 在场景中测试自定义着色器。
  • 使用Cg语言编写着色器代码。
  • 用多遍渲染实现更好的透明效果。
  • 创建一个自定义的大气着色器。

2.1 打开程序项目

如果读者还没有打开过本书附带的示例程序项目,请从前言中提到的网址处下载示例程序项目ZIP包。该压缩包里包含了本书所需的所有场景和资源。

第1章曾使用标准着色器为太空场景中的大部分物体创建表面材质。如果读者跳过了第1章,并且还没有建立程序项目,请先将程序项目准备好,以便能够更好地学习本章内容。

到目前为止,场景中星球的卫星还没有被设置材质。在本章中我们将为卫星从无到有创建自定义着色器。这将是一个展示如何创建自定义着色器的好机会。

2.1.1 打开场景

本章的场景文件包含了将要操作的全部模型和材质。

在Unity的主界面菜单栏上单击文件 | 打开场景(File | Open Scene)菜单,在弹出的文件对话框内双击打开PACKT_Scenes文件夹,再双击打开Chapter2_Start场景。

screenshot

本章从第1章结束的地方开始,场景中宇航员停下Sotello号飞船进行维修,为登陆星球“Ridley VI”做好准备。

2.1.2 创建第一个自定义着色器

Unity采用ShaderLab语言作为着色器的主体框架,并使用Cg语言实现更复杂的功能。我们先从着色器的主体开始创建,并且解释着色器代码的结构和语法规则。

1.在项目(Project)面板上单击PACKT_Shaders文件夹,使它的内容显示在资源(Assets)面板上。

Final_Shaders文件夹内有所有已完成的自定义着色器可供读者作为参考和对比。

2.右键单击资源(Assets)面板上的任一空白位置,在弹出的菜单上单击创建(Create) | 着色器(Shader)| 标准表面着色器(Standard Surface Shader)命令。

3.将新产生的着色器重命名为“Moon”。

请注意,在资源(Assets)面板上为着色器输入的名称仅用于在该面板上显示。将来显示在材质属性的着色器选单里的名称则在着色器代码中定义。

4.在资源(Assets)面板上双击“Moon”,该着色器将在MonoDevelop软件中打开。

Unity为新创建的着色器自动生成了默认代码。但是为了更好地掌握着色器语法和规则,我们要先将默认代码删除,然后重写着色器程序。

5.MonoDevelop启动完成后,着色器“Moon”的代码会显示在标题为“Moon.shader”的编辑窗口中。全选并删除该窗口中的所有代码。

接下来我们将从定义着色器名称开始。注意这里指的是将显示在材质属性的着色器选单上的名称,而不是显示在资源(Assets)面板上的名称。

6.添加下面代码。

screenshot

以上代码定义了一个着色器代码块,并且指定该着色器显示在着色器选单中的名称。

为着色器命名时可以在名称前面加上一个文件夹名称,以便更好地按照某种层次关系来组织着色器。如果当前文件夹名尚未被现有着色器使用,则Unity会自动创建该文件夹。

在这里我们指定文件夹名“PACKT”,以便将自己创建的着色器与标准资源库(Standard Assets)提供的区分开。

虽然前文已反复强调,在代码中指定的着色器名称将显示在材质的属性中,并且与在资源(Assets)面板上指定的名称彼此独立,但如果在这两处指定完全不同的名称却往往会使人迷惑。因为假如开发者在材质属性中看到一个着色器名称,并且希望修改其代码时,他有可能难以在资源(Assets)面板上找到这个着色器。

接下来定义着色器的属性(properties)区。在左右大括号之间,添加下面代码。

screenshot

属性区为着色器定义了原始材质属性,例如,定义一个颜色值,一个漫反射纹理,或者一个可以由使用者调节的滑动条控件等。
属性类似于C#或者UnityScript中的公有变量。

上面代码定义了一个颜色属性_Color。位于每一行开始处的属性名称一般由一个下划线开始。在后面的着色器主体程序中,将使用这个名称来引用这个属性。

双引号内的名称将显示在材质属性的检阅(Inspector)面板上。颜色属性被定义为一组由四个实数组成的值,分别代表R、G、B和A。

等号右侧为颜色属性的默认值,这里被定义为不透明的纯白色。后面可以在着色器的程序代码里或者材质属性里修改这个值。

在属性区后面继续添加下面代码。

screenshot

SubShader,即子着色器,包含着色器的主体部分,负责处理输入的颜色、纹理以及其他数据。

所有着色器都至少包含一个子着色器。这种结构的初衷是帮助开发者对着色器的不同组成部分进行划分,尤其适用于创建能够在不同硬件平台上工作的着色器。

当这种着色器在一个平台上工作时,系统将自动选择使用与当前GPU最兼容的一个子着色器。

第9章将深入探索着色器的硬件兼容性问题。

本例中的子着色器包含一遍渲染,即一个Pass。这是对子着色器的最低要求。到目前为止,完整的着色器代码如下。

下面的着色器代码使用ShaderLab语言编写,这是Unity的标准着色器语言。后面我们还将使用另一种着色器语言,Cg语言,来编写代码。这是C语言的一个变种,专门用于开发计算机图形程序中的着色器。名称中的“C”指C语言,“g”指图形(Graphics)。

screenshot

请将上面完整的着色器代码输入到MonoDevelop的编辑窗口中,然后保存当前编辑。这是一个能够在Unity 5中运行的最简单着色器。

在输入着色器代码时要确保左、右大括号对称,否则着色器将无法运行。
完成上述步骤后,请将MonoDevelop的主窗口最小化,回到Unity的界面里。

下面会将上面简单的着色器应用于一个材质,看一看它在场景中的表现如何。

实际观察着色器的表现

现在,第一个简单的自定义着色器已经创建完成,并且需要将它应用到场景中的物体表面,以便观察效果。

1.启动Unity。在层次(Hierarchy)视图里双击smallMoon游戏对象。Unity会自动向它拉近镜头。

screenshot

目前小卫星使用Unity默认的材质。接下来为它创建新材质。

2.在项目(Project)面板上找到并单击PACKT_Materials文件夹,使文件夹内容显示在资源(Assets)面板上。

3.右键单击资源(Assets)面板上的任一空白位置,在弹出的菜单上单击创建(Create) | 材质(Material)命令。

4.将新产生的材质重命名为“Moon”。新材质的属性将在检阅(Inspector)面板可见。

5.在检阅(Inspector)面板上,单击着色器(Shader)下拉选单。

6.在选单中选择PACKT/Moon。

检阅(Inspector)面板上的材质属性设置项将随之改变。

screenshot

现在该材质只有一个颜色(Color)属性。

7.在资源(Assets)面板上将“Moon”材质拖放到场景中的卫星模型上,为卫星模型设置该材质。

8.在检阅(Inspector)面板上,单击颜色(Color)属性右侧的矩形方框。在弹出的颜色选择对话框中选择一个暗红色颜色。

9.在场景视图中,卫星将改变颜色。

screenshot

目前新着色器在物体表面的显示效果为单色,没有阴影和高亮。这是一个典型的无光照着色器,能够以极高速度运行。接下来我们将为这个着色器添加纹理支持。

为着色器添加纹理支持

可以通过添加纹理来改善卫星表面的显示效果。这需要修改新建的自定义着色器。

1.回到MonoDevelop的界面里。

2.继续向Properties代码块内添加下面代码。

screenshot

这样便定义了一个名称为“_MainTex”的新属性。这是在Unity里通常被用于定义基础纹理贴图的属性名称。基础纹理贴图一般指着色器的反光(Albedo)属性。属性类型2D指定该属性是一个二维贴图。

3.在Pass代码块内的Color一行后面添加如下代码。

screenshot

上述代码通过属性名称为着色过程设置纹理贴图,纹理颜色将与当前颜色相乘。

4.保存当前编辑并返回到Unity主界面。

如果前面的代码输入正确,着色器编译成功,则在检阅(Inspector)面板上将在颜色(Color)属性后面出现一个新的纹理贴图槽,其名称“Albedo (RGB)”即为代码里面_MainTex属性双引号内的名称。

screenshot

现在我们可以为卫星表面设置纹理贴图了。

5.在项目(Project)面板上找到并单击PACKT_Textures文件夹,使文件夹内容显示在资源(Assets)面板上。

6.找到“moon_albedo”纹理并将它拖放到检阅(Inspector)面板上的纹理贴图槽上。

7.在场景视图中,小卫星物体的外观将有纹理显示。

screenshot

在卫星表面上,纹理将与前面设置的暗红色混合。虽然卫星现在看起来有所改善,但它仍然无法响应场景的光照。为了使它的表面产生明面和暗面,我们需要向着色器中添加更多代码。

为着色器添加光照支持

为了使卫星表面支持场景光照,需要对着色器做如下修改。

1.返回到MonoDevelop的主界面。

大部分Unity的着色器使用Cg语言来实现具体的着色工作。现在用Cg语言来取代目前的Pass中的代码。

2.在代码编辑窗口中将Pass代码块整体选中并删除。

3.在原Pass代码块的位置添加下面代码。

screenshot

上面关键字标识了一段Cg代码的开始。

4.按几次回车,空出若干行后,添加下面代码。

screenshot

上面关键字标识了一段Cg代码的结束。每一段Cg代码都必须包围在Cg开始标识和Cg结束标识之间。接下来在它们之间插入具体的代码。

5.紧接在CGPROGRAM行的后面添加下面代码。

screenshot

这一行代码叫做着色器编译指令。这类编译指令代码必须紧跟在Cg开始标识行后面。该行定义了该段代码的作用,以及所使用的光照模型。

在这里我们使用支持明暗效果的兰伯特(Lambert)光照模型。

Cg代码段的开始和结束标识,以及编译指令在MonoDevelop的编辑窗口中被显示为品红色,这将它们与真正的Cg着色代码区分开。

6.在编译指令和Cg代码段结束标识之间,添加如下代码。

screenshot

Input结构体定义了Cg代码块的输入数据有哪些内容。在本例中输入数据包含了纹理坐标(UV)数据,这是在物体表面进行贴图的基础。在这里,纹理坐标数据是一对包含有两个浮点数的值。

7.接下来添加如下代码。

screenshot

上面两行代码将前面定义的属性处理为Cg代码中的变量或对象,以便能够在后续计算中引用它们。纹理属性处理为二维采样器对象,颜色属性处理为包含四个浮点数的值,分别代表红(R)、绿(G)、蓝(B)和透明度(A)四个通道。

最后我们将添加着色器代码的主体。

8.继续添加如下代码。

screenshot

surf函数定义了整个着色器的输入数据IN和输出数据o。着色器需要将纹理贴图的颜色值与单色颜色值相乘,并作为反光值输出。由于前面已经指定了使用兰伯特光照模型,所以不需要写出具体的光照计算代码。

9.保存对着色器代码的修改,并返回到Unity主界面。

screenshot

着色器成功编译后,场景中的卫星面向光源的一侧被照亮,而另一侧则变暗。下一步我们将通过为宇航员的透明头盔创建自定义着色器来学习更复杂的着色器。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享: