虽然Surface Shader是Unity极力推动的编写Shader的方式,但由于Surface Shader做了大量的封装,屏蔽了很多底层实现细节,包括光照、顶点变换、材质使用等,在提供了更简单的使用之余也丧失了灵活性,下面我们只对vertex shaders 和 fragment shaders进行学习,读者可以参阅官方文档以了解更多Surface Shader细节。
一、vertex & fragment shaders基本结构
Shader "DavidWang/vertexAndFragment"{
SubShader{
pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
void vert(in float2 objPos:POSITION, out float4 pos:POSITION)
{
pos = float4(objPos,0,1);
}
void frag(out float4 col:COLOR)
{
col = float4(1,0,0,1);
}
ENDCG
}
}
Fallback "Mobile/VertexLit"
}
Vertex & Frament Shader至少有一个pass通道,一个pass就是对场景的一次渲染。在ShaderLab语法中需要使用关键词“CGPROGRAM”和“ENDCG”将Cg代码段包含起来才能编译使用,需要注意的是这两个关键词都是大写的。#pragma vertex vert指令的意思是使用vert函数来做Vertex shader的处理工作, #pragma fragment fragt指令的意思是使用fragt函数来做Fragment shader的处理工作,即这是分别指定了执行顶点和片段处理的函数。
Vertex shader会对顶点做一系列的处理,最主要的是把顶点坐标从物体Local空间转换成齐次裁减空间的齐次坐标,同时进行必要的光照处理,把处理后的数据传递给Fragment shader程序,Fragment shader拿到顶点程序处理后的数据继续进行最终的计算。Fragment shader最主要的工作是对纹理进行采样、对颜色进行最终的合成,其输出是颜色值。
在上例中,“void vert(in float2 objPos:POSITION, out float4 pos:POSITION)” 中有两个参数,其中第一个参数中的“in”表示引擎提供的输入,“float2”表示数据的类型为二阶的向量,参数名“objPos”冒号后需要带上语义,语义指的是顶点程序和片段程序能够被识别的变量,这里使用“POSITION”。第二个参数中“out”表示输出,数据类型为“float4”,语义也是“POSITION”。如果顶点程序要输出一个“POSITION”语义的变量,这个变量必须要以“float4”四阶向量的形式。“void frag(out float4 col:COLOR)”使用语义为“COLOR”的变量作为输出,这里使用“out float4 col:COLOR”作为颜色变量的输出,语义“COLOR”就是指“COLOR0”。
语义词“POSITION”,“COLOR”不仅仅是表示数据类型,更重要的是指示出数据存放的位置,这些语义词都有特定的含义。
Cg 语言所有 vertex profile 支持的输入语义关键词有: POSITION、BLENDWEIGHT、NORMAL、 TANGENT、 BINORMAL、 PSIZE、BLENDINDICES、TEXCOORD0—TEXCOORD7。
Cg 语言所有 vertex profile 支持的输出和 fragment profile 输入语义关键词有:POSITION 、PSIZE 、FOG,COLOR0-COLOR1、TEXCOORD0-TEXCOORD7 。
Cg 语言所有 fragment profile 支持的输出语义关键词有:COLOR 。
示例shader是最简单但又是结构完整的vertex shaders 和 fragment shaders,读者务必了解。关于输入输出语义更详细的说明超出了本章的内容,请读者查阅相关资料。
二、profile
一个Cg profile定义了一个“被特定图形硬件或API所支持的Cg语言子集”,任意一种shader language都是基于可编程图形硬件的(寄存器、指令集等),这也就意味着,不同的图形硬件对应着不同的功能子集。这些可选的语言功能包括某些控制结构和标准库函数。profile还定义了数据类型的精度,并且指定数据类型是否全部支持或仅部分支持。profile按照功能可以划分为vertex profile和fragment profile,而vertex profile和fragment profile又基于OpenGL和DirectX的不同版本或扩展,划分为各种版本。
profile是语言的一种特性,在不同的处理方法中会拥有不同的特性。在特殊情况下,语言profile就是关于一个使用for或者while循环的限制,意思就是有些情况下它不支持for或者while循环,有些profile仅仅loop循环。Cg不支持goto、switch、case、default等保留关键字;也不支持指针和指针相关的运算能力;Cg支持数组,但是有尺寸和维度的限制;Cg没有枚举和联合;Cg在结构体当中没有位成员;Cg中所有的整型都是有符号,没有signed关键字。
Cg有绑定语义,比如POSITON、COLOR0等;Cg内建有swizzle操作;对于一个值来说,swizzle操作可以允许进行拼凑一个向量或矩阵;swizzle操作可以去访问所有的分量;数据有不同的有限的类型,主要的类型有float、half、fixed,片段profile必须支持这三种数据类型,但是可以选择使用half或fixed去实现float,顶点profile要求实现half和float,但是可以选择实现halp和float,顶点profile可以忽略支持fixed,Cg允许profile去忽略支持运行时的int,实际上会把int转换成float;Cg中的操作很多都是对于向量的操作,可以对向量的基本元素进行操作,Cg中也有?问号表达式,没有全局的非静态变量;Cg有新的sampler*集合;函数拥有默认参数;函数和它们的操作可以被重载;变量可以定义在任何地方,但要在使用之前定义就行等等。
三、Cg基本数据类型
Cg支持7种基本的数据类型
数据类型 | 说明 |
---|---|
float | 32 位浮点数据,一个符号位。浮点数据类型被所有的 profile 支持。 |
half | 16 位浮点数据。 |
int | 32 位整形数据,有些 profile 会将 int 类型作为 float 类型使用。 |
fixed | 12 位定点数,被所有的 fragment profiles 所支持。 |
bool | 布尔数据,通常用于 if 和条件操作符( ?: ) ,布尔数据类型被所有的profiles 支持。 |
simpler* | 纹理对象的句柄( the handle to a texture object ) ,分为 6 类: sampler, sampler1D, sampler2D, sampler3D, samplerCUBE, 和 samplerRECT 。 |
Cg还提供了内置的向量数据类型 (built-in vector data types) ,内置的向量数据类型基于基础数据类型。 例如: float4, 表示 float 类型的 4 元向量; bool4, 表示 bool类型 4 元向量。
注意: 向量最长不能超过 4 元, 即在 Cg 程序中可以声明 float1 、 float2 、 float3 、float4 类型的数组变量,但是不能声明超过 4 元的向量。
参考文献
1、Cg语言的基础 Cg语言的基础
2、profile和基本数据类型 profile和基本数据类型