我也是一个学习者,有错误请指出。 References: 《Unity Shader 入门精要》
Unity中,我们需要配合使用材质和unity shader,它们的关系和流程是:
首先创建Unity Shader,它定义了各种着色器(如顶点着色器、片元着色器)、属性和指令,将其赋给材质。可以在材质面板上调整Unity Shader的属性,最后将其赋给某个要渲染的对象(模型)来得到最终的效果。
创建好一个材质后,一般要结合一个GameObject的Mesh或者Particle System来工作。
从Unity 5.0版本开始,默认情况下创建的Material使用的是Unity内置的Standard Shader。这种默认行为一直持续到现在。Standard Shader提供了丰富的物理材质(PBR)功能,允许用户创建各种复杂和逼真的材质效果。
材质的Inspector界面中,这个选可以简单调整光照,并且这个界面本身有预览功能,可以按住右键移动查看效果。
Unity Shader与传统的vs和fs有很大不同。
Unity中提供了5种Unity Shader模板:
材质可以选择使用的shader,这样在材质的Inspector界面可以设置shader的各种属性。
在Unity Shader的Inspector界面中,可以看到和设置很多信息,在Compiled Code选项中,可以看到该Shader针对不同图形API接口编译的代码。
如果直接使用某一个图形API进行渲染,要进行很多工作,一不注意就会出错(当初我就是设置错了一个渲染状态导致我调试了2小时OpenGL)
而Unity Shader为开发者提供了一个渲染抽象层,使得开发者可以更加轻松。
所有的Unity Shader都是使用ShaderLab来编写的,ShaderLab是Unity提供的编写Unity Shader的一种说明性语言,它使用了一些嵌套在花括号内的语义来描述Unity Shader文件的结构,这些结构包含了许多渲染所需的数据,例如Properties语句块中定义了着色器所需的各种属性,这些属性会出现材质面板中。
之前我们提到了Unity Shader是通过Shader Lab分为了多个语义块,下面将分块介绍Unity Shader。
这个语义块包含了一系列属性,这些属性会出现在材质面板中。
代码一般为这样的形式:
Properties {
Name ("display name", Type) = DefaultValue
// 更多其他属性
}
Properties的属性类型:
语义块中定义的属性的作用就是为了在材质面板中显示,这样开发者可以在面板上直接调整属性的值。shader中要想使用这些变量,需要直接在shader中使用相应的Uniform变量,属性和Uniform变量的名字通常是一致的,这样系统才会正确将属性传给Uniform变量;或者通过脚本传递。
每个Unity Shader文件可以包含多个SubShader块,至少要有一个SubShader块。当Unity加载这个Unity Shader时,Unity会扫描所有的SubShader块,然后选择第一个能够在目标平台上运行的SubShader。
这样做的原因是,不同的显卡具有不同的能力,我们希望在旧显卡上运行计算复杂度较低的着色器,在高级的显卡上运行复杂度较高的着色器。
SubShader语义块的定义如下:
SubShader{
// 可选的
[Tags]
// 可选的
[RenderSetup]
Pass{
}
// 其他Pass
}
SubShader中定义了一系列的Pass以及[RenderSetup]状态和[Tags]标签。
每个Pass定义了一个完整的渲染流程,过多的Pass会导致性能的下降。标签和状态同样也可以在Pass中定义,但是SubShader的标签与Pass中使用的标签是不一样的,SubShader中的一些标签是特定的;状态两者使用的语法相同,但如果在SubShader中设置了状态,那么会用于全部的Pass
SubShader提供了一系列的指令,这些指令用于设置显卡的各种状态。
一些常见的状态设置
就像之前提到的,SubShader中设置的状态会应用到全部的Pass,要想不同Pass使用不同的设置,需要在每个Pass中单独进行状态设置。
SubShader的标签是一个键值对,键和值都是字符串。
结构如下:
Tags { "TagName1" = "Value1" "TagName2" = "Value2" }
Pass {
[Name]
[Tags]
[RenderSetup]
// other
}
可以在Pass中定义该Pass的名称:
Name "MyPassName"
这样,我们可以在其他Unity Shader中调用ShaderLab的UsePass命令使用Pass
UsePass "MyShader/MYPASSNAME"
Unity内部会将所有Pass的名称转换为大写字母,所以使用UsePass时要使用大写形式
Pass中的标签不同于SubShader的标签
RequireOptions现在有很多选项,例如Shadows, Directional, Point等等,使用时查阅相关文档即可。
Unity Shader中还支持一些特殊的Pass:
Unity Shader以及ShaderLab还能实现很多功能,比如自定义材质面板等等,但不太常用。
上面讲解了这么多,但基本没有涉及到Unity Shader是如何处理传统的像是顶点着色器或者片元着色器。这些着色器代码可以写在SubShader语义块中(表面着色器),也可以写在Pass语义块中(顶点/片元着色器和固定函数着色器)
Shader "ShaderName" {
Properties {
}
SubShader {
// shader code
}
SubShader {
}
}
这是Unity自创的一种shader类型,它需要的代码量很少,这就意味着Unity在背后为它做了很多工作(一般这种需要软件背后做工作的,性能都不会太好)。它本质上还是一个vs或fs,Unity会对其进行转换。
它的意义在于处理了很多光照细节,不需要开发者自己编写。
Shader "Custom/Simple Surface Shader" {
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float4 color : COLOR;
};
void surf(Input IN, inout SurfaceOutput o) {
o.Albedo = IN.color.rgb;
}
ENDCG
}
}
需要注意的点有:
CGPROGRAM
与ENDCG
之间的代码是使用Unity封装后的CG/HLSL编写,与原生的CG/HLSL仅有细微的不同。同样是使用CG/HLSL语言来编写,定义在CGPROGRAM
与ENDCG
,但需要写在Pass语义块内,我们需要自己定义每个Pass使用的Shader代码。
如果想要使用GLSL编写Unity shader,需要将GLSL代码嵌套在GLSLPROGRAM
和ENDGLSL
之间。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。