着色器和shade

Properties块内的语法都是单行的。每个属性都是由内部名称 开始,后面括号中是显示在检视面板(Inspector)中的名字和该属性的类型。等号后边跟的是默认值。

零基础Unity3D游戏开发系列 第四章:游戏资源导入导出(三)材质与着色器


已经定义好了需要的属性,可以开始写自己的着色器(shader)了

不同的显卡有不同的渲染能力。例如,某些显卡支持片段程序( fragment programs)和别人不一样,有的可以放下四个纹理通道(textures per pass)而有些只放下一个或两个。为了适应不同的显卡,一个着色器可以包含多个子着色器(SubShaders)。Unity在渲染着色器时,它会根据显卡去匹配合适的子着色器(subshaders)。

通道Passes

每个子着色器(subshader)都是一个通道集合。对于每一个pass,都要渲染几何对象,所以必须至少有一个pass。我们的VertexLit着色器只有一个pass .在pass内定义的任何一个命令都会在显卡上渲染指定的几何图形。

在上面的例子中,我们有一个材质(Material)块,定义了照明时所需要几项固定参数。 Lighting On 这个命令是打开顶点光照明设备,SeparateSpecular On是指允许使用一个单独的颜色作为镜面高光。

SetTexture是非常重要的。这个命令可以定义影像纹理如何混合、组合以及如何运用于我们的渲染里,SetTexture之后通常跟随纹理的属性名称(我们在这里使用_MainTex ),接下来是combiner 块,定义纹理的处理方式,这个combiner 块的命令会对屏幕显示每一个像素执行的运算。

在这个块(Material块)内我们设定了一个常量颜色值(_Color)。接下来Combine命令,我们指定如何混合纹理以及颜色值。用Combine命令格式:

Combine ColorPart, AlphaPart

在这里ColorPart与AlphaPart定义了混合的颜色(RGB)和alpha值(A)的信息,如果AlphaPart被省略了它将与颜色部分做同样的混合。

这个例子中,颜色部分的结果来着当前纹理(_MainTex)的颜色,乘以顶点光照颜色,再乘以2以增强照明强度。aplha值(在逗号以后)是由texture*constant得来(constant由之前constantColor设置)。

在Unity着色器中编写自定义顶点(vertex)和片段程序( fragment programs)。

顶点(vertex)和片段程序( fragment programs)

快速创建一个子着色器(SubShaders)的方法是使用其他定义好的通道(passes)。UsePass就是这样的一个命令,所以你可以重复使用优秀的着色器代码。下面的例子将教你使用该命令实现调用Unity内置的名字为“BASE”的镜面着色器(Specular shader):

UsePass “Specular/BASE”

为了让UsePass起作用,必须给通道(pass)一个你所想要的名字。Name这个命令是给当前所在的通道(pass)赋予一个名字。

Name “MyPassName”

当你使用顶点(vertex)和片断程序(fragment programs)(即所谓的“可编程管线)时,显卡的大部分硬编码(”固定功能管线“)功能将关闭。例如,使用一个顶点(vertex)程序完全可以做到关闭标准的3D变换,灯光和纹理坐标的功能。类似的,使用一个片段程序( fragment programs)可以替换任何纹理混合模式。编写顶点(vertex)/片断程序(fragment programs)需要对3D转换,照明和坐标空间有透彻的了解 。 因为你要自己写出API 实现的效果就像OpenGL的自身的固定功能一样。另外,还可以实现比置功能以外自己需要的功能。//Unity3D教程手册:www.unitymanual.com

着色器通过在着色器文本中嵌入”Cg片段“实现在着色器语言(ShaderLab)中写入Cg编程语言。Cg片段是通过Unity编辑被编译成低级的着色器,最终的着色器是包含在你的游戏数据文件内的,游戏数据文件只包含低级指令集合。当你在项目视图( Project View)中选择一个着色器,在检视面板(Inspector)中的着色器文本后会显示Cg 编译(Cg compilation),这可能有助于帮助调试。Unity将自动编译OpenGL和Direct3D的Cg片段,所以你写的着色器使用了Cg也可以在这两个环境下运行。要注意的是,Cg代码是要通过编译的,所以你不能在运行的时候创建Cg着色器代码。

一般情况下,Cg片段是放在通道(Pass)块的。就像下面这样:

[C#] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<font style= "color:rgb(85, 85, 85)" ><font face= "微软雅黑" >Pass {
 
// ... the usual pass state setup ...
 
  
CGPROGRAM
 
// compilation directives for this snippet, e.g.:
 
#pragma vertex vert
 
#pragma fragment frag
 
  
// the Cg code itself
 
  
ENDCG
 
// ... the rest of pass setup ...
 
}
</font></font>

下面的例子演示了一个完整Cg程序的着色器,它渲染对象的结果是颜色随着法线而变化:

[C#] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<font style= "color:rgb(85, 85, 85)" ><font face= "微软雅黑" >Shader "Tutorial/Display Normals" {
 
SubShader {
 
Pass {
 
  
CGPROGRAM
 
#pragma vertex vert
 
#pragma fragment frag
 
#include "UnityCG.cginc"
 
  
struct v2f {
 
float4 pos : SV_POSITION;
 
float3 color : COLOR0;
 
};
 
  
v2f vert (appdata_base v)
 
{
 
v2f o;
 
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
 
o.color = v.normal * 0.5 + 0.5;
 
return o;
 
}
 
  
half4 frag (v2f i) : COLOR
 
{
 
return half4 (i.color, 1);
 
}
 
ENDCG
 
  
}
 
}
 
Fallback "VertexLit"
 
}
</font></font>

应用到对象上时,会看到和下图类似的效果。(需要你的显卡支持顶点(vertex)和片段程序(fragment programs))

零基础Unity3D游戏开发系列 第四章:游戏资源导入导出(三)材质与着色器


分析一下这段Cg代码。

整个Cg片段写在CGPROGRAM与ENDCG关键字中间。开始编译的指令是 #pragma 给出:

#pragma vertex name告诉该代码在提供的函数里包含了一个顶点(vertex)程序。(上面的例子中是在vert 函数里)。

#pragma fragment name告诉代码在提供的函数里包含了一个片段(fragment)程序。(上面的例子中是在frag 函数里)。

接下来编译的指令只是普通的Cg代码。首先需要导入一个内置的Cg文件:

#include UnityCg.cginc

UnityCg.cginc文件包含了常用的声明,所以可以保持着色器程度简短。这个文件可以在Unity里找到(Windows系统下:{Unity安装路径} /Data /CGIncludes /UnityCG.cginc;Mac系统下:/Applications/Unity/Unity.app/Contents/CGIncludes/UnityCG.cginc)。在这里,我们将通过这个文件使用appdata_base结构。我们也可以不导入这个文件,直接在着色器中定义他们。

接下来,我们定义一个“顶点(vertex)片段(fragment)”结构(这里命名为V2F) -什么信息是从顶点(vertex)传递到片断(fragment)程序?通过位置(position)和颜色(color)参数。颜色将在顶点(vertex)程序中计算,只在片断(fragment)程序中输出。

先定义顶点(vertex)程序-在这里是vert 函数。在这个函数内,我们计算位置(position)和颜色(color)然后返回:

[C#] 纯文本查看 复制代码
?
1
2
<font style= "color:rgb(85, 85, 85)" ><font face= "微软雅黑" >o.color = v.normal * 0.5 + 0.5;
</font></font>

法线(normal)的取值范围是[-1,1],而颜色的取值范围是[0,1]。所以我们要通过上述计算微调一下,才能符合颜色的取值范围。接下来我们定义了一个片段(fragment)程序-frag 函数。它只返回计算好的颜色和透明值。

[C#] 纯文本查看 复制代码
?
1
2
3
4
5
6
7
8
<font style= "color:rgb(85, 85, 85)" ><font face= "微软雅黑" >half4 frag (v2f i) : COLOR
 
{
 
return half4 (i.color, 1);
 
}
</font></font>

在Cg代码中使用着色器属性

当你在着色器中定义属性(properties)的时候,你给他们起这样的名字:_Color或_MainTex。但在Cg代码中使用着色器属性(shader properties),你就必须定义一个变量,变量的的名字和类型要与它相匹配。Unity会自动设置的CG变量,名字与着色器属性(Shader properties)匹配。//Unity3D教程手册:www.unitymanual.com

下面是一个完整的着色器,它显示了一张调整了颜色的纹理。当然,你可以很容易地在纹理合成中调用类似的指令,但现在只是为了展示一下如何在Cg代码中使用属性(properties):

[C#] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<font style= "color:rgb(85, 85, 85)" ><font face= "微软雅黑" >Shader "Tutorial/Textured Colored" {
 
Properties {
 
_Color ( "Main Color" , Color) = (1,1,1,0.5)
 
_MainTex ( "Texture" , 2D) = "white" { }
 
}
 
SubShader {
 
Pass {
 
  
CGPROGRAM
 
#pragma vertex vert
 
#pragma fragment frag
 
  
#include "UnityCG.cginc"
 
  
float4 _Color;
 
sampler2D _MainTex;
 
  
struct v2f {
 
float4 pos : SV_POSITION;
 
float2 uv : TEXCOORD0;
 
};
 
  
float4 _MainTex_ST;
 
  
v2f vert (appdata_base v)
 
{
 
v2f o;
 
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
 
o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
 
return o;
 
}
 
  
half4 frag (v2f i) : COLOR
 
{
 
half4 texcol = tex2D (_MainTex, i.uv);
 
return texcol * _Color;
 
}
 
ENDCG
 
  
}
 
}
 
Fallback "VertexLit"
 
}
</font></font>

在这里,我们定义了两个属性(properties),即_Color和_MainTex。在Cg代码内,我们定义相应的变量:

[C#] 纯文本查看 复制代码
?
1
2
3
4
<font style= "color:rgb(85, 85, 85)" ><font face= "微软雅黑" >float4 _Color;
 
sampler2D _MainTex;
</font></font>

这里的顶点(vertex)和片断(fragment)程序没有做别的事情;顶点(vertex)程序使用UnityCG.cginc里的TRANSFORM_TEX,用来保证纹理(texture)正确的缩放和偏移。片断(fragment)程序只是对纹理(texture)进行采样然后乘以颜色值。

请注意,因为这是我们自己编写的片段(fragment)程序,我们并不需要任何SetTexture命令,就演示了如何在着色器中利用片段(fragment)程序完美的控制纹理(texture)。

编写表面着色器 Writing Surface Shaders

表面着色器示例 Surface Shader Examples

本文来自:Unity3D教程手册QQ群(290248177)作者:傻瓜(QQ:4073

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值