Direct3D初学总结

来源:互联网 发布:数据挖掘与r语言 源码 编辑:程序博客网 时间:2024/05/19 18:13

基本依照Direct3D龙书章节总结。

一、设备初始化之类的都早已封装成现成的东西了,直接用,不需要深入了解。

二、渲染管道

渲染管道就是硬件做好的,只需要给这个硬件输入所有需要的数据(顶点们,纹理,设置这个硬件的一些状态),这个硬件就会自动输出最终在屏幕上能显示的图像。

(1)三个空间(坐标系):

本地空间,每个模型自己的坐标系。

世界空间,整个三维场景统一的一个坐标系。

视图空间,可能在世界空间中摄像机不在原点位置(即使开始在原点位置,也可能为了漫游地图,扛着摄像机到处走),为了简化操作和运算,在每次渲染之前专门把摄像机移动到原点,当然其它的所有物体也要和摄像机进行相同的变化(即保持和摄像机相对位置不变),所以通过视图空间变化的最终结果是虽然所有点的世界坐标变了,但他们相对于摄像机并没有改变。视图变化后的结果就是视图空间(注意视图空间中的点仍然是三维的)。另外视图变换最终的结果仍然是世界空间中的各个点(只不过这些点的世界坐标变化了),这些点让然是三唯的点,具有三维坐标。

(2)举例:

下面的例子是,在世界空间内,在位置(-3,2,6)处放置一个立方体。

//创建一个表示平移变化的矩阵,当然可以直接写出来,但也可以使用D3D提供的函数把0矩阵转换成需要的变化矩阵

D3DXMATRIX cubeWorldMatrix; //全0的4阶矩阵

D3DXMatrixTranslation(&cubeWorldMatrix,-3.0f,2.0f,6.0f);  //这个函数就把cubeWorldMatrix变成需要的平移矩阵了,函数的字面意思就是平移的D3DX矩阵

//进行立方体的世界变换

Device->SetTransform(D3DTS_WORLD,&cubeWorldMatrix);

//SetTransform函数就会让渲染管道硬件内部对所有的顶点进行对应的世界变换

drawCube();  //画出立方体(其它的变换使用默认值,没有显示的设置,即渲染管道其它状态使用默认值)

下面的则是视图变换的例子,假设当前摄像机在世界坐标的原点(默认就是在原点),但我不想再看从原点看到的场景了,我想把摄像机背到(5,3,-10)点并且设置摄像机看向世界空间的原点。然后从这个地方看整个世界空间内的场景(就好像看另一个角度的照片)。那就进行相应的视图变换好了,具体步骤就是其它所有点不变,把摄像机直接搬到(5,3,-10)点并旋转定好看的方向,此时实际上已经完成任务了,摄像机照出来的场景就是所求,但是由于这个时候摄像机不在原点了,所以计算量就会比较大(三维到二纬的投影变换时),所以需要把整个场景一块移动和旋转直到把摄像机放到原点。

代码如下:

D3DXVECTOR3 position(5.0f,3.0f,-10.0f);

D3DXVECTOR3 targetPoint(0.0f,0.0f,0.0f);

D3DXVECTOR3 worldUp(0.0f,1.0f,0.0f);

D3DXMATRIX V;

D3DXMatrixLookAtLH(&V,&positon,&targetPoint,&worldUp); //此函数字面意思就是输出看向LH的变换矩阵(此变换矩阵包括平移和旋转)。

Device->SetTransform(D3DTS_VIEW,&V); //就好像让渲染管道中的摄像机换个角度照相并输出图像。

注意,上面的代码中不要深究worldUp的值,这个值在D3D中一般固定为向上的方向向量,好像在视图变换中没作用,不用管,就设为固定值就好了。

 

(3)渲染流水线以及着色器(渲染器)

变换内容上面说了,略过。

背面裁剪:三角形有两个面,一个为前表面,一个为后表面,一般后表面不可见。说白了就是摄像机看向的那个面为前表面(即视线的反方向和表面的向量成锐角时为前表面)。当然如果允许三角形的背面也可见的话,就不需要使用背面裁剪了。

要注意的是,还有一种和背面裁剪相似,但觉不是背面裁剪,不用弄混了。就是如果光线方向(一般认为是指向光源的方向)和表面成锐角则表面成灰白色(先假设顶点不带颜色,纯由光源照亮),如果成钝角,则一律成纯黑色,很好理解,如果成钝角的话就相当于背对着光源,根本接收不到光照,典型的例子就是一个圆柱体侧表面一半被光照亮,一半变黑,具体计算公式就是顶点原来的颜色值乘以cosa,a为夹角,0度最亮,90度就没颜色了,即rgb都为0,大于90度为负值看成0,所以rgb也为0(当然设了全局光的时候除外)。

如下图,是个非常经典的(融合背面剔除和光照方向夹角的)例子:

背面剔除和深度测试深入理解
上图中是验证光照和材质的典型例子(精通Directx3d图形与动画程序设计一书)。但是具体代码中禁用了背面剔除模式。具体的顶点创建是圆柱上表面取一个顶点,下表面取一个平行的顶点,所有三角形列表都只代表侧面,并没有包括上下表面,但每个顶点自己计算好了法向量。那么为什么能绘制出上表面的光照效果呢?原来是因为:并没有绘制上表面,只是绘制了后表面(根据后表面顶点的法向量和光线方向,绘制出从黑到白效果,且大部分由于法向量朝后,所以大部分是黑色的),由于禁用了背面剔除模式,所以无论绘制前表面还是后表面都是直接绘制到屏幕的对应像素,只是一个一个三角形的绘制,根本不管三角形的面朝向。由于这种绘制方法好像两个面都绘制成一模一样(当然实际只绘制一个面,也只可能看到一个面),所以又成为双面渲染。

可以试一下如果不禁止背面剔除模式,可以想象的到上表面根本不会绘制出来,和背景一个颜色,也就根本看不到上表面了。

再深入一点,会发现真实世界了怎么可能会出现上面的图像呢?按理说这个图中的光源都是水平方向的平行光,摄像头又在圆柱体的正上方,那最上面的那个圆面应该照亮后半部分啊?怎么照亮的是前半部分了?这样理解就错了,实际上不管摄像头在哪,圆柱体都是垂直放置,而光线又是水平照射,上下表面实际上是根本照不到任何光的。图上之所有上表面有亮出是因为图中场景没有上表面,侧后面由于没禁用背面剔除模式所以后表面一部分也是亮的。

有个好办法,就是分析光照产生的颜色时,根本别考虑摄像机在哪(镜面反射除外),计算好各个顶点的颜色后,再考虑摄像机,各考虑各的,别融合在一起考虑。

 

 

 

 

 

 

 

下面是收集的所有渲染管道相关的图:

先上三个渲染流水线图(包括着色器)

从这个图可以看出,顶点变换和光照计算阶段以及光栅化阶段只是表示运算器运算的阶段,不要把相关的渲染状态设置以及一些数据初始化也包含在这两个阶段中。最常见的就是纹理层设置属于光栅化阶段前的预处理,不属于运算阶段。不要老搞混。

关于从顶点变换和光照计算后出来的带颜色的顶点到光栅化阶段看下面一个更详细的图如下:

从上图可以看出,“顶点变换和光照计算”出来的是三角形列表(在上图就是三角形的三个顶点,而且每个顶点是二维的,因进行过投影变换),这三个顶点(二维的)作为光栅化器的输入数据,而光栅化器中这进行插值运算,也就是说光栅化所做的工作就是计算每个独立像素点的颜色值。光栅化器的输出就是很多点(和最终输出到屏幕上的像素一一对应,所以也有书称为片段)而不止三个点了,这些点都是二维的且带一个颜色值(差值运算出来的),这个颜色值就是整个“顶点变换和光照计算”的唯一最终功绩。当然这些输出的点还带有纹理坐标信息(一般是直接从三维的顶点的信息中直接复制过来)。

在这里补充一下:顶点着色器在功能上取代传统的顶点变换与光照计算阶段,它位于镶嵌阶段(先简单理解为顶点流进入管线)之后,背面剔除和裁剪之前。出来的顶点可以认为暂时存在顶点渲染器(或着色器)中。

而像素着色器在功能上取代传统的多纹理阶段,它位于光栅化阶段之后。所以像素着色器可以从光栅器、顶点渲染器、应用程序、纹理贴图几处获取数据。像素着色器的计算结果可以写入一个渲染目标。

具体见下图,图中顶点渲染和像素渲染分别表示顶点着色器和像素着色器

原创粉丝点击