技术美术成长之路——UnityShader篇(二)、Unity Shader
本文参考:相关文章汇总:
开始正题:主要是学习记录,方便以后查阅,建议直接看书!
第三章、Unity Shader 基础
写过OpenGL和DirectX应该都知道,初始化跑起来程序就得几百行。而UnityShader就帮我们省去了这几百行,可以直接去写我们上一节说过的最核心的部分shader和渲染状态。这个东西就是Unity Shader。
1、Unity Shader 概述
shader和材质是相关联的。
https://zhuanlan.zhihu.com/p/47163574
https://zhuanlan.zhihu.com/p/47163574
首先还是回到模型渲染上来,模型渲染需要shader来表示他是如何变换的,如何进行着色计算的。而材质,我们之前所说的材质,其实就是BRDF,也就是决定光是如何与物体表面进行作用的。
这里其实还是大致类似的,他把shader赋给材质,然后材质负责一个模型,这里材质也就是给拓宽了一下,可以说是负责物体渲染。
现在创建好了shader和材质,并且把shader给赋到了材质上,还创建了一个物体。
shader的这俩东西很重要!可以设置他的默认纹理。还有是否是固定功能,是否是表面shader。还有一些标签设置。针对固定功能或者表面着色器可以看他们被unity转化生成为顶点/片元着色器的代码。这里是顶点片元着色器就没有相关的show选项。而有一个compile and show code,这个可以看编译的代码。
2、shaderLab
也就是之前说的,unity帮我们提供了一个不需要关心杂七杂八的东西环境,只关心核心的shader和一些渲染状态设置。我们只需要去写Unity Shader。
而UnityLab就是写Unityshader的语言,他有他的语法定义。
这里面不仅仅是shader代码,还有嵌套在花括号内部的语义,把unityshader分成了几个部分。
3、unity shader的结构
Shader名字
我们的材质要绑定shader,而绑定方式之一就是从shader下拉框中找,而我们写好的shader应该在下拉框什么位置呢?就是这一句决定的额。
Shader "Custom/MyShader"这么写,custom是自定义的意思,这样我们自定义的都给放在里面。
property
在shader里property设置的东西会在材质面板中显式出来。就是在写shader的过程中,需要用到一些外面的数据,纹理等等的东西,这在DX中是常量缓冲区来实现的。
这里因为材质是负责整个渲染相关的东西的,所以在材质中显示出来这些量,然后我们设置这些量,shader会有一个变量名来引用这个变量,从而就实现了这种交互。
变量的名(用来在shader代码中引用该变量),显示在面板中的名,变量的类型,当然类似c++设置变量的时候还可以设置初始值。大致就这么一个形式。
Properties
{
_Int ("Int", Int) = 2
_Float("Float",Float) = 3.4
_Range("Range", Range(0.0, 5.0)) = 3.0
_Color("Color", Color) = (1,1,1,1)
_Vector("Vector", Vector) = (2,3,6,1)
_2D("2D", 2D) = ""{}
_Cube("Cube", Cube) = "white"{}
_3D("3D", 3D) = "black"{}
}
这里的color和vector都是4个分量的。
对于2D,cube,3D这三个初始化的时候,最后需要加一个{}。同时引号里面写的是自己准备的纹理名字,要么就是内置的纹理名字。看图可以看出来,分别表示的是2D纹理,cubeMap的纹理,和3D纹理。
对于变量的名字,我们定义用来引用该变量,虽然我们在property定义了,但是不能直接用,需要再定义一下。当然普通变量,非交互性的变量,就不必在property中定义了。
所以property的作用,总的来说就是在材质面板中显示出来,同时可以进行赋值。
SubShader
一个SubShader会包含渲染需要的一套东西,可以有多套,有时候由于硬件平台原因,第一个不能跑可以换下一个,从前往后判断,直到最后有个兜底的fallback
这个玩意的语法:
pass可以有多个,但是多了会性能下降,尽量少。pass内部定义了完整的渲染流程。
状态就是渲染状态
渲染状态设置后,在他后面的所有的shader都是用这个渲染状态去执行,除非出现一个新的渲染状态,这个东西可以写在pass外面,也可以写在pass里面。之前说过,渲染状态的切换CPU很慢,可能会拖后腿,所以没事就少换。
标签是一个键值对的形式,同时键和值都是字符串,也是进行渲染时候的一些设置。
有点看不懂。。。
pass
他的结构:
名字是当别的shader想要用当前shader里的pass时,可以直接叫名字。
这里需要注意在叫pass的名字的时候,需要改成大写的,他才能叫的应,因为他起的名字是小写的,登记的时候unity搞成了大写的,你想让unity帮你叫他,得叫大写的。
pass内部可以有状态设置,刚刚说了。
pass内部还可以有标签,但是不同于外部的标签。也是关于一些渲染的设置的。
上边是关于SubShader的结构,而在几个subshader最后,会有一个Fallback,他的作用:上边subshader都不能用的时候,说明这整个unity shader就废了,所以这里需要指定一个unity shader,指定一个最低级的:
这里的name就是指定一个低级的,而Off就是直接摆烂,跑不了就不跑了。
这个有点看不明白,到阴影再说吧。
https://www.zhihu.com/column/unityTA
4、unity的形式
其实到着,关于subshader中的标签和状态,以及pass中的标签,这些都是对于渲染状态的设置和相关选项的设置。那我们关心的另外一个东西,shader还没有出现。也就是下面要说的了:
表面着色器
这个可以理解为对于顶点+片元着色器的更高一层的抽象。
这个东西写在SubShader内部。不需要卸载pass内部。多了一层pass,其实就是多了一些渲染状态设置,和几个使用多个pass相关的东西,但是unity把这些事都帮我们做好了。所以不需要写在pass里面。表面着色器实际上会被unity转化成一个包含多个pass的顶点/片元着色器。(可以在unity Shader的导入设置面板中show generated code看到。)
这里我们开始写真正的Shader了,虽说这个是更抽象一层的,但是他也是真正的shader,那么我们就应该用shader语言来写,现在处在的是shaderlab语言下,我们做一个区分,就是:把真正的shader代码(用HLSL/CG写的)写在CGPROGRAM 和 ENDCG之间。
这里写的HLSL/CG和真正的HLSL/CG略有差异,毕竟这里是在unity里面,可能略有更改或者一些不支持。
其实这个表面着色器并不是我们关心的重点,重点在于下面的:
顶点/片元着色器
这个就是我们最开始在GPU渲染流程中提到的了。
这个也是有类似的CGPROGRAM和ENDCG的。这个需要写在pass内部,因为这个就不让unity帮我们干太多活了,这样可以让我们主动性更强,程序也更加灵活。
对于不可编程的管线来说,有一个叫固定函数着色器,他都退出历史舞台了,不管了。
针对上边的shader,我们关心的是顶点/片元着色器。
6、相关解释
UnityShader != 真正的shader
UnityShader把多个shader包含在内,包含了渲染状态的设置,对于输入交互数据,这里直接搞到了材质面板中来完成。
unity对于一些高级的shader,曲面细分等等的支持的就相对差。
页:
[1]