作者学习shader已经有很长一段时间了,这篇文章是对shader的整理和总结。shader的世界缤纷多彩,浩瀚无穷,作者的认识只是弥勒山下的一粒沙子,如沧海一粟,希望次文章可以对您有所帮助,那便是作者最大的欣慰!
学习资料和工具推荐:
古人有云:工欲善其事必先利其器!
●书籍:
《Unity Shaders and Effects Cookbooks》作者读的第一本书
《GPU 编程与CG 语言之阳春白雪下里巴人》可以帮助你了解硬件,此书作者水平很高
《Real-Time Rendering.3rd》经典之作,不过不适合入门
《Unity shader入门精要》一个女孩写的书,适合大部分想要学unity shader的初学者
《untiy3d ShaderLab实战详解》适合入门unity shader
《3D数学基础:图形与游戏开发》如果真的想学好shader,学数学是必须的,这是我觉得比较系统全面,并且适合入门的数学书籍
《GPU编程精粹》系列 这个大神可以试试,初学者不太适合学习
《Unity5.X从入门到精通》shader章节 相信官方的水平
书籍暂且推荐这些,其实还有很多,如果需要可以互相交流。
●博客和教学资料:
学习资料暂时这些,如果有更好的欢迎补充
●逆天工具:
Shader Forge
编写shader的可视化编辑器,使编写shader难度大大减少,也方便开发者理解shader,大大增加开发的效率,适合初学者(一般大神都鄙视这个工具,大神都是用手写)
vs2013自动补全代码,语法高亮显示插件
相信大部分开发者已经用惯了VS,但是VS居然不支持shader代码高亮和自动补全,这个是无法容忍的事,好在作者混迹github多年,上面奇人异事很多,故而找到这个插件。
shader基础
●概念:Shader(着色器)实际上就是一小段程序,它负责将输入的Mesh(网格)以指定的方式和输入的贴图或者颜色等组合作用,然后输出。绘图单元可以依据这个输出来将图像绘制到屏幕上。输入的贴图或者颜色等,加上对应的Shader,以及对Shader的特定的参数设置,将这些内容(Shader及输入参数)打包存储在一起,得到的就是一个Material(材质)。之后,我们便可以将材质赋予合适的renderer(渲染器)来进行渲染(输出)了。
Shader只是一段规定好输入(颜色,贴图等)和输出(渲染器能够读懂的点和颜色的对应关系)的程序。而Shader开发者要做的就是根据输入,进行计算变换,产生输出而已。
如图:
可以总结成一句话:
1.GameObject里有MeshRenderer,
2.MeshRenderer里有Material列表,
3.每个Material里有且只有一个Shader;
4.Material在编辑器暴露该Shader的可调属性。
所以,关键是如何编写shader。
●unity自建shader说明:
Standard surface shader会产生一个包含了标准关照模型(使用了unity5中新添加的基于物理的渲染方法)的表面着色器模板(为我们提供了典型的表面着色器的实现方法)
Unity shader则会产生一个不包含关照(但包含雾效)的基本的顶点/片元着色器
Image effect shader则为我们实现各种屏幕后处理效果提供了一个基本的模板
Compute shader会产生一种特殊的shader文件,这类shader目的是利用GPU的并行性来进行一些与常规渲染流水线无关的计算。
●Unity Shader文件操作说明:
show generated code 对于表面着色器,我们通过单击show generated code按钮来打开一个新的文件,在该文件里将显示unity在背后为该表面着色器生成的顶点/片元着色器。
Compile and show code 下拉列表可以让开发者检查该unity shader针对的不同图像编程接口(如opebgl ,d3d9,d3d11等)最终编译成的shader代码,我们可以利用这些代码来分析和优化着色器。
●Unity内置shader文件分析:
CGIncludes文件夹中包含了所有的内置包含文件
DefaultResources文件夹中包含了一些内置组件或功能所需要的unity shader,例如一些GUI元素使用的shader
DefaultResourcesExtra则包含了所有unity中内置的shader
Editor文件夹目前只包含一个脚本文件,它用于定义unity5引入的standard shader所用的材质面板
(可以到官网下载内置文件:https://unity3d.com/cn/get-unity/download/archive)
Unity Shader的结构
在unity里面,我们写shader,用到的是ShaderLab。ShaderLab是unity提供的编写unity shader的一种说明性语言。
基础结构如下:
简单点,可以总结成一下图片:
●shader的名字:
每个Unity Shader文件的第一行都需要通过Shader语义来定义该Shader的名字。
●shader的属性:
在Properties语义块中包含了一系列的属性,这些属性将会显示在材质面板中。
Properties的语义块的形式通常如下:
其中的_Color就是表示属性的名字,而"Main Color"则表示在材质面板上出现的名字,Color则是为属性定义的类型,Default Value表示我们为属性设置的默认值,在我们第一次把Unity Shader赋给一个材质时,材质面板上显示的就是他的默认值。
以上的类型可以规为以下几类:
数字类型:他们的默认值即为数据,如Int.Float.Range
四维向量:他们的默认值是用圆括号包围的一个四维向量,如Color.Vector
纹理类型:他们的默认值是一个字符串后面跟一个花括号来指定的,其中的字符串要么是空的要么是内置的纹理的名称,如"black", "white"等,花括号的用处原本是用于指定一些纹理属性的,但是在Unity5.0后的版本中这些选项被移除了。
具体代码如下:
Properties语义块的作用仅是为了让这些属性可以出现在材质面板中:
●SubShader
1.设置渲染状态:
ShaderLab提供了一系列渲染状态的设置指令,这些指令可以设置显卡的各种状态,如开启混合/深度测试,具体选项如下。
2.标签:
SubShader的标签是一个键值对,它的键和值都是字符串类型,这些键值对是SubShader与渲染引擎之间的沟通桥梁。标签的结构如下:
Tags { " TagName1 " = " Value1 " " TagName2 " = " Value2 "}
标签声明仅可以在SubShader中场景,而不可以在Pass块中声明。Pass中虽然也可以定义标签,但这些标签是不同于SubShader中的标签的。具体内容可以参考官方文档:https://docs.unity3d.com/Manual/SL-SubShaderTags.html
3.Pass语义块:
[Name]:首先我们可以在Pass中定义Pass的名称。
[Tags]:Pass语义块的标签不同于SubShader的标签,下面是Pass语义块中的标签类型:
4.Fallback:
这一指令用于在其它的SubShader不能运作的时候,使用Fallback值中的Shader。
Unity Shader的形态:
●固定管线
固定管线是为了兼容老式显卡。都是顶点光照。之后固定管线可能是被Unity抛弃的功能,所以最好不学它、当它不存在。
●顶点/片元着色器
以上图是可编程shader的编程格式,它具有复杂,灵活性高的特点,一直都是大神最喜爱的编写shader的方式。具体用法和参数可以参考官方文档:https://docs.unity3d.com/Manual/SL-SubShaderTags.html
●表面着色器
表面着色器是在顶点/片元着色器的上面做了一层抽象的封装,目的是为了模拟人类的思维方式,使开发人员开发的难度大大降低,并且写出来的程序易于理解和交流,也大大减少了代码量。但是弊端就是程序的灵活性低,扩展性小,并且消耗的性能比较高。
具体内容可以参考官方文档:https://docs.unity3d.com/Manual/SL-SubShaderTags.html
●选择Unity Shader的建议
1.除非设备是不支持可编程管线的着色器的,才考虑使用固定函数着色器,否则都使用可编程管线的着色器。
2.想和各种光源打资产,则可以使用表面着色器,但要注意他在移动端的表现。
3.如果需要使用的光照数目特别少,那么顶点/片元着色器是一个更好的选择。
4.如果有很多自定义的渲染效果,那么选择顶点/片元着色器。
总结
shader是一门博大精深的学科,因为里面涉及到大量的和计算机图形学有关的内容,不是单单靠一两篇博客就可以说清楚,弄明白的。如果读者们有意想精深学习shader编程,必须要花大量的时间打好基础功,学习并非一朝一夕。而且,在学的过程中不仅要知道“怎么用”,更重要的是要知道“为什么要这样用”,多多透过问题的表象思考其本质问题,这样才能朝大牛的方向不断的前进!
本人才疏学浅,如果有错误的地方欢迎指正!