在unity材质检视器中可以看到Shaders的性质及多重shader(SubShaders)的描述,针对不同图 形硬件,每个描述也都完整的说明了图形硬件的彩现状态,fixed function pipeline如何设定、vertex/ fragment programs如何作用。 Vertex and fragment程序可以使用高阶Cg程式语言或低阶shader组合。
在这个教程中,我们将描述如何使用fixed function与programmable pipelines两种方式于ShaderLab中撰写shaders,我们假设读者拥有基本的OpenGL或Direct3D彩现概念,并对cg有 fixed function与programmable pipelines的常识,HLSL或GLSL编程语言技术,一些Shader教程与参考文件可于NVIDIA以及AMD的开发站上取得。
建立一个新的shader有两种方法,可以由菜单Assets->Create->Shader新增,或复制一个既有的shader再进行编辑,新的shader可以透过双击来启动编辑画面(UniSciTE)
下面开始介绍一个基础的shader范例:
-
Shader "Tutorial/Basic" {
-
Properties {
-
_Color ("Main Color", Color) = (1,0.5,0.5,1)
-
}
-
SubShader {
-
Pass {
-
Material {
-
Diffuse [_Color]
-
}
-
Lighting On
- }
着色器:开始
-
}
-
}
-
Shader "Tutorial/Basic" {
-
Properties {
-
_Color ("Main Color", Color) = (1,0.5,0.5,1)
-
}
-
SubShader {
-
Pass {
-
Material {
-
Diffuse [_Color]
-
}
-
Lighting On
-
}
-
}
- }
这个shader范例只是众多shader中最基本的一个,它定义了一个颜色性质,名称为Main Color,并指定了玫瑰色的效果(red=100%
green=50% blue=50% alpha=100%),在调用时会跳过Diffuse的材质设定(_Color)并开启顶点光源。
要测试这个shader,你可以建立一个新的材质,并于Shader下拉菜单选择(Tutorial->Basic),再把这个新材质指定到物件上,拖拉材质检视器的颜色表并查看其变化。是时候研究更复杂的事情了!
假如你开启一个既有的复合shader,刚开始看可能会觉得有点难,在开始以前,我们将详细说明unity内建的VertexLit shader。这个shader使用fixed function pipeline产生标准的per-vertex照明。
-
Shader "VertexLit" {
-
Properties {
-
2
-
_Color ("Main Color", Color) = (1,1,1,0.5)
-
_SpecColor ("Spec Color", Color) = (1,1,1,1)
-
_Emission ("Emmisive Color", Color) = (0,0,0,0)
-
_Shininess ("Shininess", Range (0.01, 1)) = 0.7
-
_MainTex ("Base (RGB)", 2D) = "white" { }
-
}
-
SubShader {
-
Pass {
-
Material {
-
Diffuse [_Color]
-
Ambient [_Color]
-
Shininess [_Shininess]
-
Specular [_SpecColor]
-
Emission [_Emission]
-
}
-
Lighting On
-
SeperateSpecular On
-
SetTexture [_MainTex] {
-
constantColor [_Color]
-
Combine texture * primary DOUBLE, texture * constant
-
}
-
}
-
}
-
}
-
Shader "VertexLit" {
-
Properties {
-
_Color ("Main Color", Color) = (1,1,1,0.5)
-
_SpecColor ("Spec Color", Color) = (1,1,1,1)
-
3
-
_Emission ("Emmisive Color", Color) = (0,0,0,0)
-
_Shininess ("Shininess", Range (0.01, 1)) = 0.7
-
_MainTex ("Base (RGB)", 2D) = "white" { }
-
}
-
SubShader {
-
Pass {
-
Material {
-
Diffuse [_Color]
-
Ambient [_Color]
-
Shininess [_Shininess]
-
Specular [_SpecColor]
-
Emission [_Emission]
-
}
-
Lighting On
-
SeperateSpecular On
-
SetTexture [_MainTex] {
-
constantColor [_Color]
-
Combine texture * primary DOUBLE, texture * constant
-
}
-
}
-
}
- }
所有的shaders都必须以Shader作为开始,接着是这个shader的名称(例如:VertexLit),这个名称将会显示于检视器(Inspector)。所有的语法都必须放在{ }之内。
如果要把shaders放在unity的submenus下面,请使用斜线,例如:MyShaders/Test,你将 会看到有个submenu名为MyShaders,下面有个shader名为Test,或是像这样MyShaders->Test在 Properties block下面接着的是SubShader block,每个描述都在这个段落中
Properties
properties位于shader block一开始的位置,你可以定义任何性质,这些性质将可在材质检视器中编辑,在VertexLit的个范例中,properties block看起来像这样:
properties block内的语法都是单行的,每一个性质描述都由内名称开始(例如:Color,MainTex),在后方的括弧号中所显示的名字也会显示于inspector检视器上,在此之后,描述的是该性质的预设值:
可用的性质类型请参考Properties Reference。预设值与性质有关,以color为例,预设值应该由四个值组成。
现在我们已经定义了四个性质,可以开始撰写实际的shader了在开始以前,先了解shader的结构是如何定义的。
不同的绘图卡有不同的能力,例如:有的绘图卡支援fragment programs但有些没有,有些可以一次处理四个贴图?(four textures)其他的可能只能处理两个或一个,为了要符合所有用户的硬体需求,一个shader可以包涵多个SubShaders,当unity在运 算shader时,它将详细察看所有的subshaders而且使用硬体可支持的第一个。
-
Shader "Structure Example" {
-
Properties { /* ...shader properties... }
-
SubShader {
-
// ...subshader that uses vertex/fragment programs...
-
}
-
SubShader {
-
// ...subshader that uses four textures per pass...
-
}
-
SubShader {
-
// ...subshader that uses two textures per pass...
-
}
-
SubShader {
-
// ...subshader that might look ugly but runs on anything : )
-
}
-
}
-
Shader "Structure Example" {
-
Properties { /* ...shader properties... }
-
SubShader {
-
// ...subshader that uses vertex/fragment programs...
-
}
-
SubShader {
-
// ...subshader that uses four textures per pass...
-
}
-
SubShader {
-
// ...subshader that uses two textures per pass...
-
}
-
SubShader {
-
// ...subshader that might look ugly but runs on anything : )
-
6
-
}
- }
此系统提供unity可以支援现有所有的硬体并取得最佳的品质。它作到了,然而,结果是必须撰写很长的shaders语法在每一个SubShader
block,你可以设定彩现途径的状态;并定义彩现途径本身。完整的SubShader语法请参照SubShader Reference章节
每个subshader等于是一个途径集。要对几何物件进行彩现,至少一定要有一个途径,内定的VertexLit shader里面仅有一个途径:
-
view plaincopy to clipboardprint?
-
// ...snip...
-
Pass {
-
Material {
-
Diffuse [_Color]
-
Ambient [_Color]
-
Shininess [_Shininess]
-
Specular [_SpecColor]
-
Emission [_Emission]
-
}
-
Lighting On
-
SeperateSpecular On
-
SetTexture [_MainTex] {
-
constantColor [_Color]
-
Combine texture * primary DOUBLE, texture * constant
-
}
-
}
-
// ...snip...
-
// ...snip...
-
7
-
Pass {
-
Material {
-
Diffuse [_Color]
-
Ambient [_Color]
-
Shininess [_Shininess]
-
Specular [_SpecColor]
-
Emission [_Emission]
-
}
-
Lighting On
-
SeperateSpecular On
-
SetTexture [_MainTex] {
-
constantColor [_Color]
-
Combine texture * primary DOUBLE, texture * constant
-
}
-
}
- // ...snip...
通过指令可以定义一个特殊的方法,用来驱动绘图硬体彩现指定的几何物件
例如:上方语法中有一个Material block,定义了照明时所需要几项固定参数。而指令LightingOn用来开启该照明设备,SeperateSpecular On则是启用Seperate作为特殊镜射效果。到目前为止的所有命令,皆属于支援OpenGL/Direct3D技术硬体本身可使用的固定功能,您可以 参考OpenGL红皮书,可以找到更多相关资料。
下一个命令是SetTexture,这是个非常重要的命令,这个命令可以定义影像纹理如何混合、组合以及如何运用于我们 的彩现环境里,SetTexture通常跟随于纹理的属性名称之后(我们在这里使用_MainTex ),接下来的combiner block也是定义纹理的应用方式,这个combiner block的命令会在萤幕显示每一个被执行的动作。
让我们开始了一个着色器的一般结构小回顾:
-
Shader "MyShaderName" {
-
Properties {
-
// ... properties here ...
-
}
-
SubShader {
-
// ... subshader for graphics hardware A ...
-
Pass {
-
// ... pass commands ...
-
}
-
// ... more passes if needed ...
-
}
-
SubShader {
-
// ... subshader for graphics hardware B ...
-
}
-
// ... Optional fallback ...
-
FallBack "VertexLit"
- }
最后,我们在这里引入一个新的命令:
FallBack "VertexLit"
回退命令可用于在着色结束,它告诉它应该使用着色如果从目前的着色没有SubShaders可以运行在用户的图形硬件。其效果是一样的,包括在年底从后备
着色所有SubShaders相同。例如,如果你写了颠簸映射着色,然后不要写成一个非常基本的非凹凸映射的老显卡你可以回退到subshader内置
VertexLit着色。
该着色的基本构建块,介绍了在第一渲染教程,而性能完整的文件,SubShaders并通过提供。阿建设 SubShaders快速方法是使用证在其他着色器定义。该命令UsePass正是如此,所以你可以重用一个整洁时尚着色器代码。作为一个例子下面的命令 使用的名称,通过“基地”从内置的镜面着色:
- UsePass "Specular/BASE"
着色:顶点和片段程序
为了UsePass为了工作,这个名字必须考虑的一个传递希望使用。通过名称内的命令给它一个名字:
- Name "MyPassName"
顶点和片段方案
我们描述了通过该只使用一个单一的纹理相结合的第一个教程指令。现在是时候证明我们如何能够通过使用我们的顶点和片段方案。
当您使用顶点和片段程序(即所谓“可编程管线”),大部分的硬编码功能(“固定功能管线”)在图形硬件已关闭。例如,使用一个顶点程序关闭标准3D变换,
灯光和纹理坐标生成完全。同样地,使用任何一个片段程序替换纹理相结合,将在SetTexture命令定义的模式,因此SetTexture命令是没有必
要的。
写作顶点/片断程序需要一个三维转换,照明和协调空间 - 透彻的认识,因为你必须重写固定功能是一样的OpenGL的API将自己的建造。另一方面,你可以做更多比内置的!
利用ShaderLab Cg语言
在ShaderLab着色器通常用Cg语言嵌入“Cg的片段着色器中的文本”的编程语言。 Cg的片段被编译成低级的统一着色器组装编辑,最后着色是在你的游戏的数据中包含的文件只包含这个低层次的组装。
当您选择一个项目视图着色,检查员Cg语言编译后显示,这可能有助于在调试援助着色文本。统一自动编译的CG OpenGL和Direct3D的片段,所以你的着色器,使用Cg的工作都将只。请注意,因为企业管治守则是由编辑编译,你不能创建Cg的从脚本在运行时着色器。
一般来说,Cg的片段放在里面通行证块。他们看起来像这样:
-
Pass {
-
// ... the usual pass state setup ...
-
CGPROGRAM
-
// compilation directives for this snippet, e.g.:
-
#pragma vertex vert
-
#pragma fragment frag
-
// the Cg code itself
-
2
-
ENDCG
-
// ... the rest of pass setup ...
- }
下面的例子演示了一个Cg的方案,使得对象的颜色正常人完全着色:
-
Shader "Tutorial/Display Normals" {
-
SubShader {
-
Pass {
-
CGPROGRAM
-
#pragma vertex vert
-
#pragma fragment frag
-
#pragma fragmentoption ARB_fog_exp2
-
#include "UnityCG.cginc"
-
struct v2f {
-
V2F_POS_FOG;
-
float3 color : COLOR0;
-
};
-
v2f vert (appdata_base v)
-
{
-
v2f o;
-
PositionFog( v.vertex, o.pos, o.fog );
-
o.color = v.normal * 0.5 + 0.5;
-
return o;
-
}
-
half4 frag (v2f i) : COLOR
-
{
-
return half4( i.color, 1 );
-
}
-
ENDCG
-
}
-
}
-
Fallback "VertexLit"
- }
当应用于一个对象,将图像中的结果如下(如果您的图形卡支持顶点和片段的过程中方案):
我们的“平均值显示”着色没有任何属性,包含一个是空的,除了单一的企业管治守则证单SubShader。最后,向在VertexLit着色器内置的定义后备。让我们解剖了部分企业管治守则的一部分:
-
CGPROGRAM
-
#pragma vertex vert
-
#pragma fragment frag
-
#pragma fragmentoption ARB_fog_exp2
-
// ... snip ...
- ENDCG
整个Cg的片段之间写入CGPROGRAM和ENDCG关键字。在开始编译指令#pragma指令给出:
#pragma vertex name告诉该代码包含在给定的函数(垂直这里顶点方案)。
#pragma fragment name告诉该代码包含在给定的函数有关水土这里(片断程序)。
#pragma fragmentoption name加上一个选项来编译OpenGL的片段方案。在这里,我们添加指数平方雾支持。
继汇编指令只是普通企业管治守则。我们首先包括一个内置的Cg的文件:
#include UnityCg.cginc
该文件包含常用UnityCg.cginc宣言等的着色器可以保持较小的功能。该文件本身是统一内发现的应用:/应用程 序/统一/ Unity.app /目录/ CGIncludes / UnityCG.cginc。在这里,我们将使用appdata_base结构,V2F_POS_FOG宏观和PositionFog从该文件助手作用。 我们可以只定义它们直接在着色,不包括文件的过程。
接下来,我们要定义一个片段“顶点”结构(这里命名为v2f) - 什么样的信息是从顶点传递到片断程序。我们通过标准的地位和雾参数和float3颜色参数。颜色将计算出的顶点程序和公正的片段程序的输出。
我们的出发定义的顶点程序 - 垂直功能。在这里,我们在计算标准方法使用UnityCG.cginc辅助功能(位置和雾)输入和输出作为一种颜色正常的:
- ! o.color = v.normal * 0.5 + 0.5;
正常的组成部分是-1 .. 1范围内,而色彩在0 .. 1范围内,所以我们在规模和偏见的代码高于正常。接下来,我们定义一个片断程序 - 有关水土功能,只是颜色和产出计算为alpha组件1:
-
half4 frag (v2f i) : COLOR
-
{
-
return half4( i.color, 1 );
- }
就这样,我们的着色完毕!即使这样简单的着色器是非常有用的可视化网格法线。
当然,这种着色不响应所有灯光,而这其中,事情变得有点复杂,对渲染管线和光衰减有关详细信息,参考网页阅读。
企业管治守则中使用着色性能当你定义的着色性能,你给他们一个像_Color或_MainTex名称。要使用Cg语言你他们只需要定义一
个匹配的名称和类型的变量。统一将自动设置Cg的变量的名称与着色性能匹配。
下面是一个完整的着色器显示的颜色调制纹理。当然,你可以很容易地做一个纹理合成调用相同的,但问题是这里只是为了说明如何使用Cg的属性:
-
Shader "Tutorial/Textured Colored" {
-
Properties {
-
_Color ("Main Color", Color) = (1,1,1,0.5)
-
_MainTex ("Texture", 2D) = "white" { }
-
5
-
}
-
SubShader {
-
Pass {
-
CGPROGRAM
-
#pragma vertex vert
-
#pragma fragment frag
-
#pragma fragmentoption ARB_fog_exp2
-
#include "UnityCG.cginc"
-
float4 _Color;
-
sampler2D _MainTex;
-
struct v2f {
-
V2F_POS_FOG;
-
float2 uv : TEXCOORD0;
-
};
-
v2f vert (appdata_base v)
-
{
-
v2f o;
-
PositionFog( v.vertex, o.pos, o.fog );
-
o.uv = TRANSFORM_UV(0);
-
return o;
-
}
-
half4 frag (v2f i) : COLOR
-
{
-
half4 texcol = tex2D( _MainTex, i.uv );
-
return texcol * _Color;
-
}
-
ENDCG
-
}
-
}
-
Fallback "VertexLit"
- }
这种着色结构是在前面的例子一样。在这里,我们定义两个属性,即_Color和_MainTex。企业管治守则内我们定义相应的变量:
-
float4 _Color;
- sampler2D _MainTex;
访问中看到着色性能更多信息,Cg语言。
顶点和片段程序在这里不做任何幻想;顶点程序使用从UnityCG.cginc的TRANSFORM_UV宏,以确保质 地规模与偏移是正确应用,并片断程序公正样品的质地和颜色属性的繁殖。请注意,因为我们正在编写我们自己的节目片段在这里,我们不需要任何 SetTexture命令。如何纹理着色器适用于完全控制的片断程序。
摘要
我们已经展示了如何定制着色器程序可以在几个简单的步骤产生。虽然这里的例子所示很简单,没有什么阻止你写任意复杂的着色器程序!这可以帮助您采取统一充分利用,实现最佳的渲染效果。
完整的ShaderLab参考手册在这里。我们也有一个阴影在forum.unity3d.com论坛,从而去那里得到你的着色器的帮助!编程快乐,享受团结和Shaderlab权力。
在这个block内我们设定了一个颜色值,并命名为_Color,我们会在后面使用这个颜色
在下个命令,我们指定如何混合纹理以及颜色值。我们用Combine命令来混合其他纹理或颜色,看起来像下面这样:
Combine ColorPart,
AlphaPart在这里ColorPart与AlphaPart定义了混合的颜色(RGB)以及alpha值(A)个别的资料,假如AlphaPart
被省略了,那它将与ColorPart的资料作混合在我们的VertexLit范例中:
- Combine texture * primary DOUBLE, texture * constant
这里的texture来源是当前的纹理(_MainTex),它将与主要的颜色互相搭配(*),主色为照明设备的颜色,它是由Material计算出来的
结果。最终是这两个倍增后的结果会增加照明强度.aplha值(在逗号以后)是由constantColor倍增而得的结果。另一个常用的混合模式称为
previous(在这个shader未使用),这是所有previous SetTexture的结果,并且可以用来混合多种纹理和颜色。