第一章 创建你的第一个着色器

在这一章我们包含了一些在当今游戏开发着色器管线中更通用的漫反射技术基础。在这一章我们将会学习下面的知识点:

  • 创建一个基础的标准着色器
  • 从Unity4迁移旧着色器至Unity5
  • 为着色器添加属性
  • 在表面着色器中使用属性

介绍

让我在脑海中想象一个完全由白色绘制立方体。尽管立方体的每一个面的颜色都是相同的,但是由于不同方向的光线照射和我们看这个立方体的角度的不同,我们总能发现立方体不同的白色阴影。这种层级的逼真场景就是通过3D图形学中的着色器实现的,它是一种模拟光的作用原理的特别的程序。一个木质的立方体和一个金属的立方体也许可以是同一种3d模型,之所以让他们看起来一个是木质的,一个是金属的,就是因为它们使用了不同的着色器的缘故。我们循序渐进,第一章将会向你介绍如何在Unity中进行着色器编码。如果你从来没有编写着色器的经验,那么在这一章,你将会了解着色器是什么,他们如何工作和如何自定义着色器。 接着在这一章的结尾,你将会学习如何构建拥有基础操作的基础着色器。有了这些知识后,那么你将可以创建任何的表面着色器。

创建一个基本的标准着色器

每一个Unity游戏开发者应该都对组件(components)这个概念非常熟悉。游戏中的对象都有很多的组件,这些组件决定了游戏中的对象看起来是什么样子和会有什么样的行为。然而游戏脚本(scripts ) 定义的是游戏对象会有怎样的行为,渲染器(renderers )决定游戏对象如何出现在屏幕中。 对于我们想要看到游戏对象类型,Unity本身提供了一些渲染器。每一个3D模型通常都有一个网格渲染器。一个游戏对象应该只能有一个渲染器,但是一个渲染器它可以包含多个材质(materials)。 每个材质封装了一个着色器–3D图形的最后一环。这些组件的关系可以用如下的示意图表示:

  • 始前准备 开始学习这个知识点之前,你需要打开你的Unity5并且创建一个新的项目。本书的内容讲解都会在这个项目中开展,随着学习的深入你之后自己创建的着色器都可以放在这。这一步完成之后—-欢迎来到着色器实时编程的精彩世界。

  • 操作步骤

    在创建我们的第一个着色器前,让我们为实验着色器创建一个简单游戏场景。首先我们导航到Unity的菜单栏,然后选择游戏对象|创建空对象。然后在Unity编辑器的**层级面板(Hierarchy)**选中刚刚创建的空对象,在上面创建一个平面作为地面,再创建几个球体用来应用我们的着色器,然后在场景里面创建平行光源。当场景弄好后,我们接下来就按步骤开始着色器的编写:

    1. 在编辑器的项目(Project)窗口中,直接右键选择创建(Create)|文件夹(Folder)。【这里的文件夹名字我就直接用英文了,大家在自己开发的过程中也尽量用有意义的英文文件夹吧】 注意 如果你导入了本书提供的项目文件(就是你从网站上下载的代码,他是一个unitypackage包,导入之后这个文件自动就有了),你可以直接跳至步骤4。

    2. 选择该文件夹,右键然后选择重命名(Rename),把这个文件夹命名成Shaders。或者你也可以选中该文件夹,然后按F2,重命名为Shaders

    3. 用上面同样的方法创建一个Materials的文件夹,用来放材质文件的。

    4. 右键Shaders文件夹,然后在出的窗口中选择创建(Create)|着色器(Shader)|标准表面着色器(Standard Surface Shader)(这里注意跟原文不一样,创建一个着色器要三步,书中只有两部)。接着我们创建一个材质,右键Materials文件夹,然后在弹窗中选择创建(Create)|材质(Material)

    5. 把刚刚创建的着色器和材质都命名成StandardDiffuse。【各位,文件名也用英文呀,因为怕Unity对中文的支持不好】

    6. 然后用Visual Studio 2015或者Visual Studio Code打开StandardDiffuse这个着色器【这里我不建议用MonoDevelop这个编辑器,原文是用这个,不好用,强烈建议用各位用Visual Studio Code打开,这个编辑器很好用,一定要去试试】
      注意
      打开着色器你会发现Unity其实已经为我们的着色器生成了一些基本的代码。这些基础代码给了你一个基础的漫反射着色器,而且可以传入一张纹理。我们会在后面的步骤修改这些着色器代码,创建自己的着色器。

    7. 首先我们给自己的着色器一个自定义的文件夹【这不是传统的文件夹,我更倾向理解为材质选择路径】,这样方便使用时可以按照这个文件夹找到它。着色器的第一行代码是一段描述,这段描述的作用是当我们为材质选择着色器时,这段描述会会转换成选择路径,给材质添加我们自己的着色器。我们把这个路径重写为 Shader “CookbookShaders/StandardDiffuse”。当然你也能在任何时间把它命名为任何路径。不用特别在意这个路径名。然后记得保存我们的代码,然后切换回Unity编辑器。当Unity编辑器检测到着色器代码有更新,它会自动重新编译着色器。修改后的着色器代码如下所示:

    Shader "CookbookShaders/StandardDiffuse" {
    	Properties {
    		_Color ("Color", Color) = (1,1,1,1)
    		_MainTex ("Albedo (RGB)", 2D) = "white" {}
    		_Glossiness ("Smoothness", Range(0,1)) = 0.5
    		_Metallic ("Metallic", Range(0,1)) = 0.0
    		}
    		SubShader {
    			Tags { "RenderType"="Opaque" }
    			LOD 200
    			CGPROGRAM
    			// Physically based Standard lighting model, and enable shadows on all light types
    			#pragma surface surf Standard fullforwardshadows
    			// Use shader model 3.0 target, to get nicer looking lighting
    			#pragma target 3.0
    			sampler2D _MainTex;
    			struct Input {
    				float2 uv_MainTex;
    				};
    			half _Glossiness;
    			half _Metallic;
    			fixed4 _Color;
    			void surf (Input IN, inout SurfaceOutputStandard o) {
    				// Albedo comes from a texture tinted by color
    				fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
    				o.Albedo = c.rgb;
    				// Metallic and smoothness come from slider variables
    				o.Metallic = _Metallic;
    				o.Smoothness = _Glossiness;
    				o.Alpha = c.a;
    			}
    			ENDCG
    		}FallBack "Diffuse"
    }
    
    1. 专业一点说,这是一个**基于物理原理渲染(physically-basedrendering)**的表面着色器,只是Unity5把它作为了一个内建的标准着色器。根据这种着色器的字面意思,这是一种根据现实中光的物理原理,来模拟游戏中光照射到物体时所表现的物理特性,通过这种模拟,来虚拟现实。 如果你用的时早期版本的着色器(比如Unity4),那么你的着色器代码跟现在的会有比较大的差别。相比于现在的基于物理原理的着色器技术而言,Unity4使用的技术还是相对比较简单的。所有的这些着色器类型我们会在本书后面的章节介绍。

    2. 当你的着色器创建好后,我们需要将它与材质关联起来。选中我们之前在步骤4创建的StandardDiffuse材质,然后看检查器面板(Inspector tab)。然后再着色器(Shader)下拉列表中选择CookbookShaders | StandardDiffuse。(如果你在第7步修改的路径跟书上不一样,那么这里的选项也不一样)通过上述步骤,你的着色器就会跟这个材质关联起来,你现在可以把这个材质添加到一个游戏对象中去了。 注意

      你可以在**项目窗口(Project tab)选中这个材质,然后直接拖拽到你在游戏场景中的游戏对象身上。当然你也可以直接选中这个材质,然后拖拽到Unity编辑器的检查器面板(Inspector tab)上,把这个材质应用到这个游戏对象上,前提是你要先选中这个游戏对象,然后再拖拽,这样检查器面板(Inspector tab)**显示的才是游戏对象的属性。

      下面时这个示例的屏幕截图:【各位按照自己的情况来就行了,不一定非要找一个跟书本一样的模型】

  • 原理介绍

    Unity帮助你简化了着色器运行的环境配置,你很多时候只需要点击鼠标就可以完成。但事实上这些简单的操作背后有着大量的各种各样的工作,只是Unity引擎替你做了。Unity 使用CG着色器语言,并且它在背后做了大量的工作【unity会自动生成相应的CG代码】,让你在写着色器的时候非常高效。用表面着色器格式的语言来写着色器更加方便。比如处理你自己的纹理的坐标或者线性变换矩阵,这些功能都已经准备好,不用再从头开始。以前写着色器,你必须重新创建一个着色器然后一遍又一遍的重新很多代码。随着你对表面着色器的深入理解,你可能会越想了解CG语言更底层的功能以及Unity是如何处理那些更底层的**图形处理单元(graphics processing unit (GPU))**任务的。

    注意 Unity项目中的所有文件都有自己的引用,跟它在你电脑上具体的某个文件夹上没有关系。我们在编辑器上,你可以可以随便移动着色器文件和材质文件,它们之间不会有关联信息丢失的风险。但你千万不要在编辑器外面移动这些文件,【比如直接打开项目文件夹,在电脑上直接移动这些文件】这样的话Unity编辑器不能够更新这些文件之间的关联,可能会发生丢失的情况。

    我们通过简单的修改着色器的路径属性可以给着色器一个我们想要的名字,我们在Unity环境中进行了基础的漫反射着色器的研究,包括光呀,阴影呀之类的。而这些,仅仅是通过改变一行代码。


  • 相关补充
    在Unity5中内建的着色器的源码通常被隐藏起来了,你不能像打开自己的着色器代码那样打开它。在你的Unity安装目录Unity45\Editor\Data\CGIncludes,你能找到大部分的Unity内建的CG功能代码。在这个目录下面,你能找到被Unity隐藏起来的一些着色器。 经过多年的迭代,它们已经发生了很多的改变;如果你想查阅Unity不同版本之间的色器源码发生了那些变化,下面这个网站也许是个好去处:https://unity3d.com/get-unity/download/archive。选择你的Unity版本,然后在下拉列表中选择内建着色器(Built in shaders) ,如下图所示。 此时我们需要留意其中的三个文件—UnityCG.cgincLighting.cgincUnityShaderVariables.cginc。我们现在学习的着色器都只要用到这三个相关的文件: