Unity之Shader学习基础篇《一》
几何阶段:主要负责顶点坐标变换、光照、裁剪、投影以及屏幕映射,该阶段基于GPU进行运算,在该阶段之后得到了经过变化和投影之后的顶点坐标、颜色以及纹理坐标——《实时计算机图像学》
光栅阶段:基于几何阶段的输出数据,为像素正确配色,以便绘制完整的图形,该阶段进行的都是单个像素的操作,每个像素的信息存储在颜色缓冲器(Color buffer或者Frame buffer)中,光栅化得到的屏幕坐标值通常都是浮点型的而像素都是整数表示,通常最后绘制到屏幕上的都是这两个整数点之间进行插值得到线段上某些点上。
(光栅化:将几何图元变为二维图像的过程)
Fragment Program:将VertexProgram的输出作为输入对每个片段的颜色进行计算,最后将处理后的数据送光栅操作模块进行光栅化。
什么是片段,它和像素有什么不一样?:片段其实就是所有的三维顶点在光栅化之后得到的数据集合,这些数据没有经过深度值的比较,而在屏幕上显示的像素都是经过深度值比较的。
(Fragment Program还有一个突出的特点就是拥有检索纹理的能力,纹理其实就是数组)
World Space,世界坐标空间:顶点法向量的计算在此过程
Eye Space,观察坐标空间,在Unity中即为虚拟摄像机坐标空间:以虚拟摄像机为原点,由视线方向、视角和远近平面共同组成一个梯形的三维空间
Clip and Project Space,屏幕坐标空间
并非是先裁剪再投影,因为在不规则的物体中进行裁剪并非容易的事情,裁剪被单独安排在一个单位立方体中进行,这个立方体被称为CVV,CVV的近平面的X,Y坐标对应屏幕像素坐标,Z坐标则代表画面像素深度。视点坐标到屏幕坐标由三部分组成:用透视变换矩阵把顶点从视锥体中变换到裁剪空间的CVV中、在CVV进行图元裁剪、屏幕映射。
half,16位浮点数据
int,32位整形数据
fixed,12位定点数
bool,布尔数据
sampler*,纹理对象的句柄: sampler、sampler1D,sampler2D,sampler3D,samplerCUBE,samplerRECT
- string,字符类型
Cg中向量、矩阵与数据是完全不同的、向量和矩阵是内置的数据类型,而数据是一种数据结构,在其他高级语言中数组,向量和矩阵都是一种数据结构
Cg表达式与控制语句:关系操作符、逻辑操作符、数学操作符、位移操作符以及Swizzle操作符:
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 等价于 floatd
中floata 和float1 a是基本等价的,两者可以进行类
型转换;float、bool、half等基本类型声明的变量也可以使用swizzle操作符。例
如:
float a = 1.0;
float4 b = a.xxxx;
Cg语言的其他一些特性
数组形参:Cg中不存在指针机制,数组作为函数形参传递的是数组的完全拷贝注意:形参数组不必指定长度,如果指定了长度在调用函数是实参数组的长度和形参数组的长度必须一致,最好不要指定长度。CG函数重载方式和C++基本一致
由于着色程序分为定点程序很片段程序,两者对应着图形流水线上的不同阶段,所以两个程序各有且只有一个入口函数,当程序今夕编译时必须要指定入口函数,除非入口函数为main.
如何确定定点和片段程序的入口函数?顶点程序:接收应用程序传递的顶点数据(通常位于模型坐标空间)进行坐标空间转换和光照处理,输出投影坐标和计算得到的光照颜色片段程序:接收从顶点程序输出的数据并进行像素颜色计算。
所以通常通过观察程序的输入输出语义绑定就可以区分函数对应的是顶点程序还是片段程序
齐次坐标的本质是什么?
1、两条平行线是否可以相交一点?
在欧氏几何空间,同一平面的两条平行线不能相交,这是我们都熟悉的一种场景。
然而,在透视空间里面,两条平行线可以相交,例如:火车轨道随着我们的视线越来越窄,最后两条平行线在无穷远处交于一点。
2、为什么叫齐次坐标?
简而言之,齐次坐标就是用N+1维来代表N维坐标
3、齐次坐标的本质是什么?
我们可以在一个2D笛卡尔坐标末尾加上一个额外的变量w来形成2D齐次坐标,因此,一个点(X,Y)在齐次坐标里面变成了(x,y,w),并且有
X = x/w
Y = y/w
例如,笛卡尔坐标系下(1,2)的齐次坐标可以表示为(1,2,1),如果点(1,2)移动到无限远处,在笛卡尔坐标下它变为(∞,∞),然后它的齐次坐标表示为(1,2,0),因为(1/0, 2/0) = (∞,∞),我们可以不用”∞"来表示一个无穷远处的点了
证明两条直线可以相交:
3、Unity中如何使用Cg编写Shader
Shader程序的基本结构:首先是一些属性定义,用来指定这段代码将有哪些输入。接下来是一个或者多个的子着色器,在实际运行中,哪一个子着色器被使用是由运行的平台所决定的。子着色器是代码的主体,每一个子着色器中包含一个或者多个的Pass。在计算着色时,平台先选择最优先可以使用的着色器,然后依次运行其中的Pass,然后得到输出的结果。最后指定一个回滚,用来处理所有Subshader都不能运行的情况(比如目标设备实在太老,所有Subshader中都有其不支持的特性)。
3.2,Shader的程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | Shader "Cg basic shader" { // 定义Shader的名字 SubShader{ // Unity会选择最适合GPU的subshader块 Pass{ // 一个shader会有多个Pass块 CGPROGRAM // 开始Unity的shader #pragma vertex vert //定义一个顶点程序,名为vert #pragma fragment frag//定义一个片段程序,名为frag float4 vert(float4 vertexPos : POSITION) : SV_POSITION { return mul(UNITY_MATRIX_MVP, vertexPos); //这行代码的作用是使用Unity内置的矩阵UNITY_MATRIX_MVP将顶点输入参数vertexPos进行转换,并将转换后的值作为返回顶点程序的输出参数,片段程序的输入参数 } float4 frag( void ) : COLOR // f片段程序 { return float4(1.0, 0.0, 0.0, 1.0);) //该片段程序返回一个片段输出参加(也即语义COLOR),该代码的意思是设置一个不透明的红色((red = 1, green = 0, blue = 0, alpha = 1)) } ENDCG // here ends the part in Cg } } } |