[UnityShader2]图形学与CG基础

来源:互联网 发布:qq群成员提取软件 编辑:程序博客网 时间:2024/05/16 08:05

0.引入齐次坐标的意义:

a.为了将空间向量与空间点进行区分,所以将三元数扩展到四元数,第四元如果是1,则表示点;如果是0,则表示向量。

b.使用四元数后,就可以通过四阶矩阵运算,将空间点的平移、旋转和缩放等操作全部统一到矩阵的乘法运算中。


1.图形绘制管线的三个阶段:

a.应用程序阶段:将几何体数据(顶点坐标/颜色/法向量/纹理坐标、纹理等)通过数据总线传送到图形硬件。

b.几何阶段:主要负责顶点坐标变换、光照、裁剪、投影以及屏幕映射,该阶段基于GPU进行运算,该阶段的末端得到了经过变换和投影之后的顶点坐标、颜色、以及纹理坐标。

c.光栅化阶段:基于几何阶段的输出数据,为像素正确配色,以便绘制完整图像,该阶段进行的都是单个像素的操作,每个像素的信息存储在颜色缓冲器(color buffer或者frame buffer)中。

值得注意的是,光照计算属于几何阶段,因为光照计算涉及视点、光源和物体的世界坐标,所以通常放在世界坐标系中进行计算;而雾化以及涉及物体透明度的计算属于光栅化阶段,因为上述两种计算都需要深度值信息(Z值),而深度值是在几何阶段中计算,并传递到光栅阶段的。


2.几何阶段

a.几何阶段的主要工作是是“变换三维顶点坐标”和“光照计算”。(四个空间)

b.顶点法向量在模型文件中属于模型空间,在GPU的顶点程序中必须将法向量转换到世界空间中才能使用。

c.图元装配,即顶点根据连接关系,还原出网格结构。网格由顶点和索引组成,在之前的流水线中是对顶点的处理,在这个阶段是根据索引将顶点链接在一起,组成线、面单元。之后就是对超出屏幕外的三角形进行裁剪,想象一下:一个三角形其中一个顶点在画面外,另外两个顶点在画面内,这是我们在屏幕上看到的就是一个四边形。然后将该四边形切成两个小的三角形。

d.三角形处理,根据右手定则来决定三角面片的法向量,如果该法向量朝向视点(法向量与到视点的方向的点积为正),则该面是正面。一般是顶点按照逆时针排列。如果该面是反面,则进行背面去除操作( Back-face Culling)。在OpenGL中有专门的函数 enable 和 disable 背面去除操作。


3.光栅化阶段

a.经过几何阶段,已经可以得到每个点的屏幕坐标值,也知道我们需要绘制的图元(点、线、面),接下来就是将点对应到像素,再然后就是给像素赋予颜色值。

b.要赋予像素颜色,那么就要先计算每个像素的颜色值,其中包含的操作有:

b1.消除遮挡面

b2.纹理操作,即根据像素的纹理坐标,查询对应的纹理值

b3.Blending,混色

b4.Filtering,过滤,可以理解为,经过一种数学运算后变成新的颜色值

c.该阶段之后,像素的颜色值被写入帧缓冲中。


4.图像硬件

a.深度缓冲区(Z buffer),其中存放的是视点到每个像素所对应的空间点的距离衡量,称之为 Z 值或者深度值。可见物体的 Z 值范围位于【 0, 1】区间,默认情况下,最接近眼睛的顶点(近裁减面上)其 Z 值为 0.0,离眼睛最远的顶点(远裁减面上)其 Z值为 1.0。使用 z buffer 可以用来判断空间点的遮挡关系。

当 3D 图形处理器将基础图元(点、线、面)渲染到屏幕上时,需要以逐行扫描的方式进行光栅化。图元顶点位置信息是在应用程序中指定的(顶点模型坐标),然后通过一系列的过程变换到屏幕空间,但是图元内部点的屏幕坐标必须由已知的顶点信息插值而来。例如,当画三角形的一条扫描线时,扫描线上的每个像素的信息,是对扫描线左右端点处已知信息值进行插值运算得到的,所以内部点的 Z 值也是插值计算得到的。


b.帧缓冲区(Frame buffer),其中存放的是像素颜色值,也可以叫做颜色缓冲区。

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

1.数据类型

基本数据类型

a.float:32位浮点数据

b.half:16位浮点数据

c.fixed:12位定点数

d.int:32位整形数据

e.bool

f.sampler*:纹理对象的句柄,分为6类:sampler, sampler1D, sampler2D, sampler3D, samplerCUBE和 samplerRECT。


向量类型,如float4,但最长不能超过4元,即可以声明float1,float2,float3,float4

向量初始化:float4 array = float4(1.0,2.0,3.0,4.0)


矩阵类型,不过最大的维数不能超过4*4阶,例如:

float1x1 matrix1;//等价于 float matirx1; x 是字符,并不是乘号!

float2x3 matrix2;// 表示 2*3 阶矩阵,包含 6 个 float 类型数据

float4x2 matrix3;// 表示 4*2 阶矩阵,包含 8 个 float 类型数据

float4x4 matrix4;//表示 4*4 阶矩阵,这是最大的维数

矩阵初始化:float2x3 matrix5 = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};


数组类型,在cg中,数组是一种数据结构,不是内置数据类型。数组数据类型在 Cg 程序中的作用是:作为函数的形参,用于大量数据的转递。

声明:float4 b[10];//声明了一个数组,包含 10 个 float4 类型向量数据

初始化:float a[4] = {1.0, 2.0, 3.0, 4.0}; //初始化一个数组

获取长度:int length = a.length;//获取数组长度


结构类型,结构体的声明以关键字 struct 开始,然后紧跟结构体的名字,接下来是一个大括号,并以分号结尾(不要忘了分号)。大括号中是结构体的定义,分为两大类:成员变量和成员函数。

struct myAdd

{    float val;

     float add(float x)  {return val + x;}

};

myAdd s;

float a = s.value;

float b = s.add(a);
一般来说,Cg 的源代码都会在文件首部定义二个结构体,分别用于定义输人和输出的类型,这二个结构体定义与普通的 C 结构定义不同,除了定义结构体成员的数据类型外,还定义了该成员的绑定语义类型( Binding Semantics),所谓绑定语义类型是为了与宿主环境进行数据交换的时候识别不同数据类型的。目前Cg 支持的绑定语义类型包括 POSTION 位置), COLOR(颜色), NORMAL(法向量), Texcoord(纹理坐标)等类型。
当顶点着色程序向片段着色程序传递的数据类型较多的情况下, 使用结构体可以大大的方便代码的编写和维护。

类型转换

float a = 1.0;

half b = 2.0;

float c = a+b; //等价于 float c = a + (float)b;

常量的类型后缀有 3 种:
f:表示 float;
h:表示 half;
x: 表示 fixed


2.表达式与控制语句

swizzle操作符

swizzle操作符后接x、 y、 z、 w,分别表示原始向量的第一个、第二个、第三个、第四个元素; 

swizzle操作符后接r、 g、 b和a的含义与前者等同。不过为了程序的易读性,建议对于表示颜色值的向量,使用swizzle操作符后接r、 g、 b和a的方式。

float4(a, b, c, d).xyz 等价于 float3(a, b, c)
float4(a, b, c, d).xyy 等价于 float3(a, b, b)
float4(a, b, c, d).wzyx 等价于 float4(d, c, b, a)
float4(a, b, c, d).w 等价于 float d


3.输入/输出和语义绑定


uniform

Cg 语言将输入数据流分为两类
1. Varying inputs,即数据流输入图元信息的各种组成要素。 从应用程序输入到 GPU 的数据除了顶点位置数据,还有顶点的法向量数据,纹理坐标数据等。 Cg 语言提供了一组语义词,用以表明参数是由顶点的哪些数据初始化的。


2. Uniform inputs,表示一些与三维渲染有关的离散信息数据,这些数据通常由应用程序传入,并通常不会随着图元信息的变化而变化,如材质对光的反射信息、运动矩阵等。 Uniform 修辞一个参数,表示该参数的值由外部应用程序初始化并传入;例如在参数列表中写:
uniform float brightness,uniform float4x4 modleWorldProject


表示从“外部”传入一个 float 类型数据,和一个 4 阶矩阵。“外部”的含义通常是用 OpenGL 或者 DirectX 所编写的应用程序。使用 Uniform 修辞的变量, 除了数据来源不同外, 与其他变量是完全一样的。需要注意的一点是: uniform 修辞的变量的值是从外部传入的,所以在 Cg 程序(顶点程序和片段程序)中通常使用 uniform 参数修辞函数形参,不容许声明一个用 uniform 修辞的局部变量!否则编译时会出现错误提示信息


in / out / inout(输入/输出修饰符,用来修饰形参。输入/输出修辞符通常和语义词一起使用,表示顶点着色程序和片段着色程序的输入输出)

in: 修辞一个形参只是用于输入,进入函数体时被初始化,且该形参值的改变不会影响实参值,这是典型的值传递方式。

out: 修辞一个形参只是用于输出的,进入函数体时并没有被初始化,这种类型的形参一般是一个函数的运行结果。
inout: 修辞一个形参既用于输入也用于输出,这是典型的引用传递。


void myFunction(out float x); //形参 x,只是用于输出

void myFunction(inout float x); //形参 x,即用于输入时初始化,也用于输出数据

void myFunction(in float x); //形参 x,只是用于输入

void myFunction(float x); /等价与 in float x,这种用法和 C\C++完全一致


语义词与语义绑定

语义词,表示输入图元的数据含义(是位置信息,还是法向量信息),也表明这些图元数据存放的硬件资源(寄存器或者纹理缓冲区)。顶点着色程序和片段着色程序中 Varying inputs 类型的输入,必须和一个语义词相绑定,这称之为绑定语义( binding semantics)。

由于 Cg 语言并不支持指针机制,且图形硬件处理过程中,数据通常暂存在寄存器中,故而在 Cg 语言中,通过引入语义绑定( binding semantics)机制,指定数据存放的位置,实际上就是将输入\输出数据和寄存器做一个映射关系(在 OpenGL Cg profiles 中是这样的,但在DirectX-based Cg profiles 中则并没有这种映射关系)。根据输入语义,图形处理器从某个寄存器取数据;然后再将处理好的数据,根据输出语义,放到指定的寄存器。


记住这一点:语义,是两个处理阶段(顶点程序、片段程序)之间的输入\输出数据和寄存器之间的桥梁,同时语义通常也表示数据的含义,如 POSITION,一般表示参数种存放的数据是顶点位置。

语义,分为输入语义和输入语义;输入语义和输出语义是有区别的。虽然一些参数经常会使用相同的绑定语义词, 例如: 顶点 Shader 的输入参数, POSITION指应用程序传入的顶点位置, 而输出参数使用 POSITION 语义就表示要反馈给硬件光栅器的裁剪空间位置,光栅器把 POSITION 当成一个位置信息。虽然两个语
义都命名为 POSITION,但却对应着图形流水线上不同的寄存器。


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

a.顶点着色程序的输入语义:

语义词关键字:

POSITION                   BLENDWEIGHT
NORMAL                     TANGENT
BINORMAL                 PSIZE
BLENDINDICES       TEXCOORD0---TEXCOORD7

语义词 POSITION0 等价于 POSITION, 其他的语义词也有类似的等价关系。

in float4 modelPos: POSITION    表示该参数中的数据是顶点位置坐标(通常位于模型空间),属于输入参数,语义词 POSITION 是输入语义,如果在 OpenGL 中则对应为接受应用程序传递的顶点数据的寄存器(图形硬件上)。

in float4 modelNormal: NORMAL  表示该参数中的数据是顶点法向量坐标(通常位于模型空间),属于输入参数,语义词 NORMAL 是输入语义,如果在 OpenGL 中则对应为接受应用程序传递的顶点法向量的寄存器(图形硬件上)。




b.顶点着色程序的输出语义:

语义词关键字:

POSITION          PSIZE, 

FOG                    COLOR0-COLOR1

TEXCOORD0-TEXCOORD7

顶点程序的输出数据被传入到片断程序中,所以顶点着色程序的输出语义词,通常也是片段程序的输入语义词,不过语义词POSITION除外。
顶点着色程序必须声明一个输出变量,并绑定POSITION语义词,该变量中的数据将被用于,且只被用于光栅化! 

为了保持顶点程序输出语义和片段程序输入语义的一致性, 通常使用相同的struct类型数据作为两者之间的传递,这是一种非常方便的写法,推荐使用。
struct VertexScreen

{

float4 oPosition : POSITION;

float4 objectPos : TEXCOORD0;

float4 objectNormal : TEXCOORD1;

};

注意:当使用struct结构中的成员变量绑定语义时,需要注意到顶点着色程序中使用的POSITION语义词,是不会被片段程序所使用的。

如果需要从顶点着色程序向片段程序传递数据,例如顶点投影坐标、光照信息等,则可以声明另外的参数,绑定到TEXCOORD系列的语义词进行数据传递,实际上TEXCOORD系列的语义词通常都被用于从顶点程序向片段程序之间传递数据

当然,你也可以选择不使用struct结构,而直接在函数形参中进行语义绑定。无论使用何种方式,都要记住vertex program中的绑定语义( POSITION除外)的
输出形参中的数据会传递到fragment program中绑定相同语义的输入形参中。


c.片段着色程序的输出语义:

片段着色程序的输出语义词较少,通常是COLOR。这是因为片段着色程序运行完毕后,就基本到了GPU流水线的末端了。 片段程序必须声明一个out向量(三元或四元),绑定语义词COLOR,这个值将被用作该片断的最终颜色值。



语义绑定方法:

a.绑定语义放在函数的参数列表的参数声明后面中

void mian_v(float4 position_obj : POSITION,float3 normal_obj : NORMAL,out float4 oPosition : POSITION,out float4 oColor : COLOR,uniform float4x4 modelViewProj){………………}

b.绑定语义可以放在结构体( struct)的成员变量后面

struct C2E1v_Output {float4 position : POSITION;float3 color : COLOR;};

c.绑定语义词可以放在函数声明的后面

float4 main_v(float4 position: POSITION,out float4 oposition : POSITION,uniform float4x4 modelViewProj):COLOR

{oposition = mul(modelViewProj,position);

float4 ocolor = float4(1.0,0,0,0);

return ocolor;}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


0 0