起因:
因为初学Shader时遇到了很多问题,为此看了不少视频,也看过不少博客文章。写这系列文章一来思考总结,二来希望能给后来的学习者一点帮助。文末会列出参考的网站和博客,如果文中有错误,还希望有高手指点一二。
正文
- shader的分类:
1.表面着色器
2.顶点/片元着色器
3.固定渲染管线着色器
第三个主要用于老式的GPU,所以现在主要学习前两个。
2.shader的基本结构
Shader基本结构.png
3.在看一个简单的shader脚本
在Unity里新建一个Shader,默认的第一个就可以了;双击打开。
默认创建的是表面着色器,大家对着代码图片修改一下,下面我会一一解释。
第1行:指定Shader的路径和名字
第3-6行:定义着色器的属性,作为输入提供给所有子着色器并在材质球上作为可调参数显示出来。每一条属性的定义的语法如下:
_Name("Display Name", type) = defaultValue[{options}]
属性名 面板显示的名字 属性类型 属性的默认值 可选选项
关于属性的解释,猫大已经写的很好了,请看这里。
第8行:这里的标签表示 在系统渲染非透明物体时调用此子着色器。子着色器和Pass可以被若干的标签(tags)所修饰,而硬件将通过判定这些标签来决定什么时候调用该子着色器和Pass。对于SubShader里的标签和Pass里的标签有不同的语法。请参照Unity官方文档:SubShaderTags和PassTags。
第9行:LOD其实就是根据设备性能的不同编译不同版本的Shader(网上找到的解释是如此,但是感觉不是那么回事)
第10行和第22行:开始标记和结束标记,表明开始和结束标记之间是一段CG程序。
第11行:编译指令,这个指令声明了我们要写一个表面Shader,并且指定了了光照模型。指令的写法如下:
#pragma surface surf lightModel [optionalparams]
surface-声明这是一个表面着色器
surf-此着色器的函数名
lightModel-使用的光照模型
optionalparams-可选参数
第12行:两个知识点,首先讲第一个;fixed4和编程语言里的int float差不多,都是向系统请求从内存中开辟出一个位置用来存储数据。第二个,为什么在properties{}里面已经声明初始化了但还要在CG程序中再声明一遍,因为properties和SubShader是两个不同的模块,如果想在CG程序中访问在Properties中所定义的变量的话,必须使用和之前变量相同的名字进行声明,才能与Properties的变量进行连接。
第13行到第16行:定义了一个结构体,把一会需要传给surf函数进行计算的数据放到里面,这个脚本中的这个结构体没有任何作用,但是系统又强制要求我们必须要在表面着色器的编写中有一个Input结构体,所以必须要写一个,否则会报错。
第17行到第21行:这就是我们之前声明过的函数了,surf;他有两个参数即输入的数据IN和输出的数据o;这个函数的作用就是,对输入进来的数据进行一定的处理,然后在进行输出。在本代码中,我们并没有使用输入参数,而是直接把之前在properties声明的_Color属性赋值给输出参数。输入参数是结构体,输出参数当然也是结构体,文末会贴出一张图片,大家可以对着图片上的输入输出结构体来推测函数里的这两个语句所做的操作。
第22行:CG程序结束标记
第24行:这行语句的作用是告诉系统,如果上面的子着色器都没法调用的话,则调用unity内置的着色器Diffuse进行渲染。
注:如果透明度没效果,请在编译指令下边加上alpha 。例如:
#pragma surface surf lightModel alpha
在实际运行中,由运行的平台决定的使用哪个子着色器进行渲染。子着色器是代码的主体,每个子着色器中包含一个或者多个的Pass。在计算着色时,平台优先选择可以使用的子着色器,然后依次运行其中的Pass,得到输出结果。在实际开发时,我们将直接在SubShader这个层次上写代码,系统会把我们的代码编译成若干个合适的Pass。
4.下面是关于Input和SurfaceOutput的结构一些注释
输入输出的结构体样式.png三:链接
Unity官方的手册文档:材质,着色器和纹理;着色器编写参考;
参考的网站博客:猫都能学会的Unity3D Shader入门指南;Unity Shader 入门(三)颜色和贴图Shader
网友评论