1.Viewport和Frustum有什么区别?
2.Viewport和Frustum的NearClipPl?ane有什么区别?
3.Vertex,Point,Vector有什么含义上的区别? Plane,Face和Mesh呢?
4.为什么顶点会有W分量,它怎么算出来的?干什么用的?
5.在一次完整的顶点渲染管线中,最多会出现多少种坐标系?(例如世界坐标系)
6.上面的每个坐标系的实际意义是什么?互相之间怎么转换?转换矩阵怎么写,意义又是什么?
7.光照是怎么计算的?光源可以设置Ambient,Diffuse...为什么Material也可以设置Ambient,Diffuse...?为什么顶点数据中还可以再指定Diffuse,Specular?
8.在每帧的颜色混合过程中,我们可以在SetTexture?StageState?中指定AlphaOp,ColorOp,在SetRenderS?tate中指定SrcBlend和DestBlend,它们互相是什么关系?
9.几种AlphaBlend?方式对应的实际含义是什么?
10.AlphaTest和AlphaBlend?有什么区别? AlphaBlend?可以完全取代AlphaTest吗?
11.TextureSam?plerMode中,可以分别对MIN,MAG,MIP指定Point和Linear,它们可以有哪些组合?各代表什么意义?
12.全屏抗锯齿和TextureSam?plerMode有关系吗? 和MontionBlu?r有关系吗?
13.Cull_CW,Cull_CCW是干什么用的? 不做Cull行吗? Cull和Clip有区别吗?
14.ShadingMod?e可以指定FLAT,GOURAUD,PHONG,这发生在渲染过程中的哪一步? 和FillMode有什么关系?
15.UV有什么用?UV和ST有什么区别?UV在光栅化时需要被插值吗?插值完了再做什么?
16.VertexShad?er和PixelShade?r分别对应于FixedPipel?ine的哪些步骤? 先搞清楚FixedPipel?ine对进一步研究Programmab?lePipeline?很有好处,你同意吗?为什么?
17.如果上面的问题你都搞清楚了, 你能在心中清晰地描述出每一个三角形, 是如何从刚开始的模型数据, 成为屏幕上的象素点?
18.BSP,OSP,Voxel...它们处于RenderPipe?line的什么地位上?
19.现在,请用Vertex和PixelShade?r,实现出和DX9的FixedPipel?ine一样的功能.
20.思考第19个问题,然后说说看,这其中可以玩哪些花招?(Shader是用来玩的...)
1.2.
Viewport是定义在"屏幕坐标系"下的,它是渲染目标表面的一个区域.渲染到这个区域以外的图形将被Clip掉.
ViewFrustu?m 是定义在"世界坐标系"下的,它是一个四楞台.顶面就叫做NearClipPl?ane,底面就叫做FarClipPla?ne.落在 ViewFrustu?m之外的物体,默认情况下会被Clip.ViewFrustu?m的顶点就是Camera也就是眼睛所在的位置,顶角的一半成为 FOV(视域,即视野范围)
那么,ViewFrustu?m实际上就是从"世界坐标系"下的眼睛位置去观察Viewport...
3.
Point,Vector(向量)是数学名词,当我们提到它们,往往是在做一些物理和几何运算,比如碰撞检测.而Vertex则是具有图形意义的顶点,往往带有除了空间位置以外的其他信息,例如Normal,Diffuse...
类似的Plane也属于数学概念,我们常常在BSP树的切分面时提到它.Face是带有一组顶点和边信息的面,是进行Shading的基本单位.Mesh是一组面,又叫网格,含有更多的图形信息,比如Texture...
4.
当我们使用标准4x4的Matrix(特别是Projection?Matrix,它必须是齐次的,它的3行4列元素必须=1.0f)进行坐标变换,而为每个初始顶点额外指定一个W=1.0f时,那么在变换到齐次坐标系下时,W分量可以用来进行Clip:
-W < X <= W, -W < Y <= W, 0 < Z <= W
凡是不满足上式的点将被Clip.(注意Clip不简单等于不画)
此外W还被用于Fog和DepthBuffe?r.
最后就是不要把XYZW和四元数搞混了,后者是为了解决GimbalLock?和平滑的旋转插值等问题引入的数学方式.
5.6.
会顺次出现"ModelSpace?"(LocalSpace?)->"WorldSpace?"->"ViewSpace"(CameraSpac?e)->"Projection?Space"->"ScreenSpac?e"
在 ModelSpace?下,各个Object之间没有相互关系,也不知道Camera和Frustum的存在.为了把孤立的Object全部放在 WorldSpace?下,通常会乘以一些最初的变换矩阵.另外,ModelSpace?并不是一个必须的概念,OpenGL就没有 ModelSpace?.
在WorldSpace?下,所有Object都有了统一的坐标定义.所以在这里开始可以做很多工作.比如场景数据结构 的遍历,碰撞检测等等,光源也在这里被初始化.在这里乘以ViewMatrix?就可以变换到ViewSpace.通常用eye,look,up来定义一 个ViewMatrix?.
在ViewSpace下,坐标原点位于Camera的点也就是ViewFrustu?m的顶点,而方向对齐Camera的方向.这里进行Back-face Culling:
Visibility? = planeNorma?l.dotProduct?(ViewVector?) > 0
此时乘以Projection?Matrix变换到Projection?Space.
在Projection?Space下,坐标系变成方形.这里进行Frustum Culling(使用W),和利用DepthBuffe?r来隐面消除.
在做完ViewportSc?ale之后,就到了ScreenSpac?e.此时实际上3D的顶点已经成为2D的顶点.接下来就可以进行光照计算,光栅化,Alpha混合和贴图了.
变换矩阵:
平移: 缩放: 旋转(以X为例): 投影:
| 1 0 0 0 | | Sx 0 0 0 | | 1 0 0 0 | | w 0 0 0 |
| 0 1 0 0 | | 0 Sy 0 0 | | 0 cos sin 0 | | 0 h 0 0 |
| 0 0 1 0 | | 0 0 Sz 0 | | 0 -sin cos 0 | | 0 0 Q 1 |
| Tx Ty Tz 1 | | 0 0 0 1 | | 0 0 0 1 | | 0 0 -QZn 0 |
其中:w=cot(FOVh/2). h=cot(FOVv/2). Q=Zf/(Zf-Zn)
如果使用DX或者GL的话,往往有非常方便的接口可以调用,不需要牢记这些数学公式.
7.
光分为全局环境光,平光,点光,聚光.
光照计算公式比较复杂,总的来说就是
Lighting = Ambient + Diffuse + Specular + Emissive
也就是四个分量.而其中的每个分量,简单来说就是光源和Materail以某种方式相乘累加的结果.
Ambient Lighting = Ca*[Ga + sum(La*Atten*Spot)]
Diffuse Lighting = sum[Cd*Ld*(N.Ldir)*Atten*Spot]
Specular Lighting = Cs*sum[Ls*(N.H)P*Atten*Spot]
Emissive Lighting = Ce
上面Ca代表Material的属性, La代表Light的属性, 从上面式子我们可以看得出光照计算的大概过程.
一些细节:
Ga:全局环境光,我们在RenderStat?e里可以设置.
Atten:光的衰减因子,可以分为常数,线性,和平方衰减.
Atten = 1/( att0i + att1i * d + att2i * d2) d表示Vertex到光源的距离.
对于平行光,Atten=1; 如果光源超出范围,Atten=0
Spot:光的聚光因子.这个计算比较复杂,对于非聚光灯,Spot=1.
N:Vertex Normal.
Ldir:从顶点到光源的向量.
H:被称为Half way vector(中间向量)
H = norm(norm(Cp - Vp) + Ldir) Cp:Camera位置 Vp:顶点位置. norm表示单位化.
P:Specular的反射强度.也称为Shiness.在指定Material的Specular时通常会一起指定.
总结一下:
对于Ambient和Emissive的计算,是不用考虑Normal和光的方向的.尤其是Emissive(自发光)只和Material有关,光源没有这个属性.
注意所有光源都可以有Amient分量,它和全局环境光不是一个概念.
最重要的是:
光照计算的结果,是由Light和Material共同决定的.Light的Ambient,Diffuse...指定了光源本身的属性.而Material的Ambient,Diffuse...指定了被照亮的物体以何种程度受到光的影响.
一定还有一个疑问:
为什么顶点的数据中还可以指定Diffuse和Specular呢?这其实是逐顶点指定了Material.你可以通过RenderStat?e的设置,而决定Material的来源:从顶点数据得到,或者从全局设置的Material得到.
默认情况下Ambient和Emissive从Material得到,Diffuse和Specular从Vertex得到.这时你设置Material的Diffuse和Specular是没用的.
8.
Alpha和Color信息从哪里来? 可以来自于Vertex,Material,Texture.
Alpha和Color信息到哪里去? 我们最终的视觉效果,也就是FrameBuffe?r中的像素点.
在FrameBuffe?r的Alpha混合的过程中,我们可以指定各种各样的混合公式,这通过指定SrcBlend和DestBlend实现.
例如DX默认的混合公式:
Final Color = SrcColor * SrcAlpha + DestColor * (1-SrcAlpha)
这实际上是指定 SrcBlend = D3DBLEND_S?RCALPHA; DestBlend = D3DBLEND_I?NVSRCALPHA?.
而SrcBlend和DestBlend这两个因数,无非来自于以下四个可能的来源.
SrcColor,SrcAlpha,DestColor,DestAlpha.
这四个因素本身可以取反,如InvSrcAlph?a.再加上Zero和One,就形成了各种各样的混合方式.
DestColor,DestAlpha的来源显然是FrameBuffe?r目前的Pixel数据,那么SrcColor,SrcAlpha呢?它们来源于纹理混合的AlphaOp,ColorOp.
以Alpha为例,假设指定AlphaArg1=Texture,AlphaArg2=Diffuse,AlphaOp=Modulate,那么该点的Alpha值实际上是由贴图的Alpha通道的Alpha值,与Diffuse色的Alpha值相乘得到.
而Diffuse的来源又可以是Vertex或者Material.(参看光照计算)
Color的情况一样,只不过来源为贴图的颜色通道,和Diffuse的Color值.
总结一下:
对于Alpha或者Color:
Vertex/Material ---| blend
|-------| blend
Texture -----------| |-------FinalPixel?
FrameBuffe?r----------------|
注意:
Texture的Blend公式,只对当前描画的Primitive有效,而FrameBlend?公式,则对所有Primitive都有作用.
为了进行AlphaBlend?,当然要先把EnableAlph?aBlending打开,否则...
9.
通过AlphaBlend?可以实现非常多的常见特效,比如火,烟,云雾,爆炸等等.与粒子系统也有着非常密切的关系.而一切特效,无非都是通过指定不同的Alpha混合方式实现的.
打个比方,如果要实现火的效果,首先Texture上应该有Alpha通道,然后把Diffuse的Alpha和Color都指定为255.
此时
AlphaArg1=Texture,AlphaArg2=Diffuse,AlphaOp=Modulate
ColorArg1=Texture,ColorArg2=Diffuse,ColorOp=Modulate
SrcBlend = SrcAlpha, DestBlend = One
就可以了.结合粒子系统,你会看到耀眼的火光把屏幕照亮.
而且这种FrameBlend?方式还有一个优点:因为DestBlend=One,所以混合顺序不影响描画结果.对于带Alpha粒子Z排序通常是比较麻烦的,利用这个公式可以避免这一点.
又比如:
SrcBlend = SrcAlpha, DestBlend = InvSrcAlph?a对应单色滤镜.
SrcBlend = SrcColor, DestBlend = InvSrcColo?r对应彩色滤镜.
总之可能实现的效果非常多,各位不妨试试看各种有趣的组合,这里就不多说了.
10.
AlphaTest指的是,对于SrcAlpha值(参看上面AlphaBlend?的介绍),与某个参考值进行比较,如果不满足某种条件,那么该点就不会被描画.
虽然AlphaTest也利用到了SrcAlpha,但是却不是用来混合.
AlphaTest可以实现镂空(比如2D中经典的透明色问题),消除Texture拉伸引起的黑边等效果,它和AlphaBlend?不是等价的,AlphaBlend?也不能完全代替AlphaTest.
举一个两者可以互相等价的例子:
假如有一个BillBoard显示的树.它的Texture具有Alpha通道,而且透明部分的Alpha=0,其余部分=255.
那么设定AlphaTest参考值为0,比较公式为NotEqual,就可以实现透明色.
有趣的是,这个结果和采用AlphaBlend?得到的结果相同.
可是,如果Texture的Alpha通道有Alpha渐变,那么AlphaTest就显得力不从心了...
理解了AlphaTest的基本思想,就可以很容易理解DepthTest和StencilTes?t.
11.
在纹理映射的过程中,插值得到的Texel的地址一般都不是一个整数,而是个小数.也就是没有一个Texel能够和这个地址精确对应.
怎么办?默认的方式是采用Nearest-Point-Sample,在这种方式下,会找到最近的Texel,直接作为最终结果.
下边举个例子,在此之前,顺便提下TextureAdd?ressMode.分为3种:Wrap,Clamp,Mirror,Border
不太严谨地表示下:
Wrap: EEEEEE (顺延下去)
Clamp: E===== (拉伸出去)
Mirror: E3E3E3 (顺延且不断取反)
Border: E????? (你想填啥色就填啥色)
还有,U的范围[0.0, 1.0],对应到水平具有N个Texel的贴图,范围就是[-0.5, N-0.5].
假 设现在取Wrap方式,那么对于一个U值0.999,按照Nearest-Point-Sample,取到的Texel就是贴图的最右边的那一点,而永远 不会是贴图最左边的那个点.这时如果下一个插出的U值对应到贴图最左边的第二个点.那么就产生了跳跃,贴图效果就会有断裂感.
当贴图比被贴图的表面大(Texel采样不足),或者小很多时(Texel过度采样),类似结果尤其明显.
现在Linear-Sample就很容易解释:对于一个不能精确对应的Texel地址,我们取以下四个Texel:
Nearest-Point上,下,左,右.
并计算Weight(权值):
W1 = (1 - Ufrac) * (1 - Vfrac)
W2 = Ufrac * (1 - Vfrac)
W3 = (1 - Ufrac) * Vfrac
W4 = Ufrac * Vfrac
而混合出最终的结果.
这就是Linear-Sample.
Nearest-Point-Sample和Linear-Sample都分别可以应用于MIN(贴图比被贴图的表面大),MAG(贴图比被贴图的表面小)方式上,产生想要的结果.
MIP的情况有所不同.
首先MIP是指MipMap.什么是MipMap呢?
假设你看着CS的一面墙,墙上有花纹.
现在你向这面墙走近,是不是觉得本来细致的花纹变成了大的色斑?
现在你离开这面墙走远,是不是觉得花纹好像揉皱了看不清楚?
其实这个道理还是Texel采样不足和Texel过度采样.同一个表面,在不同的距离观察,应该采用不同分辨率和精度级别的贴图.
于是我们从具有最大精度的贴图开始,逐步降低分辨率(通常是长宽除2),直到到达最低分辨率(通常是8级),这样产生的一组贴图,就称为MipMap.
注意MipMap会同时提高渲染效率和渲染效果,而付出在于Memory.
对于MIP指定Nearest-Point-Sample,意味着你将直接选择一个最接近的精度级别的MipMap作为贴图,然后再应用上面提过的方式进行映射.
对于MIP指定Linear-Sample,意味着你将选择两个最接近的精度级别的MipMap作为贴图,然后再应用上面提过的方式进行映射,然后结果再根据权值再混合.
这是最高质量的纹理映射方式,有时被戏称为"三线滤波".(MIN:LINEAR, MAG:LINEAR, MIP:LINEAR)