DX的Transform Engine的渲染过程
World->View->Project->Clip->Screen coordinates, 整个过程完成了将物体从世界坐标系转换为屏幕坐标系
一个更详细的过程如下:
local space -> world space -> view space -> backface culling -> lighting -> clipping -> projection -> viewport space -> resterization
点积和叉积的几何意义
假设u和v是两个三维向量
点积
u·v = ux*vx + uy*vy + uz*vz =|u|*|v|*cos(α)
乍一看,几何意义不十分明显,注意:当u和v都是单位向量时,点积就是两个向量夹角的余弦值
叉积
叉积的结果同时垂直于两个向量
叉积的结果值是以u和v为邻边的平行四边形的面积?
规范化坐标系
坐标原点在视口中央,三个坐标轴的坐标范围都是-1.0 - 1.0
将屏幕坐标转换为世界坐标
1. Convert screen coordinates, in pixels, to normalized coordinates, with an origin at the center of the viewport and values on each axis ranging from -1.0 to 1.0.
2. Scale the normalized screen coordinates to the field of view. The X and Y values attained will define the slope of the ray away from the center of the frustum in relation to depth.
3. Calculate two points on the line that correspond to the near and far clipping planes. These will be expressed in 3D coordinates in view space.
4. Create a matrix that expresses an inverse of the current view transformation.
5. Multiply these coordinates with the inverse matrix to transform them into world space.
#define NEAR 10.0f
#define FAR 4000.0f
#define FOV 0.8f
#define WIDTH 640.0f
#define HEIGHT 480.0f
#define WIDTH_DIV_2 (WIDTH*0.5f)
#define HEIGHT_DIV_2 (HEIGHT*0.5f)
#define ASPECT 1.3333f
void calcRay(int x,int y,D3DVECTOR &p1,D3DVECTOR &p2)
{
float dx,dy;
D3DMATRIX invMatrix,viewMatrix;
dx=tanf(FOV*0.5f)*(x/WIDTH_DIV_2-1.0f)/ASPECT;
dy=tanf(FOV*0.5f)*(1.0f-y/HEIGHT_DIV_2);
lpDevice->GetTransform(D3DTRANSFORMSTATE_VIEW,&viewMatrix);
D3DMath_MatrixInvert(invMatrix,viewMatrix);
p1=D3DVECTOR(dx*NEAR,dy*NEAR,NEAR);
p2=D3DVECTOR(dx*FAR,dy*FAR,FAR);
D3DMath_VectorMatrixMultiply(p1,p1,invMatrix);
D3DMath_VectorMatrixMultiply(p2,p2,invMatrix);
}
模型无法显示的原因
1. 变换矩阵设置不正确,导致模型超出视口范围
2. 没有设置光照效果
DirectX的投影平面是 z = 1 的平面
设备类型
D3D的两种设备类型:HAL 设备 REF 设备
比较:
HAL设备支持硬件加速,能实现硬件/软件顶点处理,运行速度快,支持全部/部分D3D特性
REF设备不支持硬件加速,不能做顶点处理,运行速度慢,但支持全部3D3特性
REF设备通常是为了调试而使用,所以只有安装了DirectX SDK的机器才能使用该设备
另外如果想实现某些高级特效或者显卡不支持的特效,那么也需要使用REF设备
D3D中常用的旋转函数
1. 绕坐标轴旋转
D3DXMatrixRotationX
D3DXMatrixRotationY
D3DXMatrixRotationZ
2. 绕任意轴旋转
D3DXMatrixRotationAxis
3. 绕Quaternion旋转
D3DXMatrixRotationQuaternion
其中Quaternion前三个参数表示旋转轴向量,第四个参数表示旋转的角度,这个函数本质上与D3DXMatrixRotationAxis相同
OpenGL使用右手系 DirectX使用左手系 XNA使用右手系
当移植程序时,需要考虑坐标系的变化,比如将程序从OpenGL移植到DirectX上时
使用D3DXCreateFont注意事项
使用D3DXCreateFont创建字体时,这个函数不要放在BeginScene()和EndScene()之间,那样会被调用多次,会有内存泄漏产生,把它单独做成一个函数,在进入消息循环之前调用一次就行了。
Shading
着色发生在光栅化阶段,它实际上是一个利用Vertex Color计算Pixel Color的过程
Flat Shading只使用第一个顶点的颜色作为像素的最终颜色,而平滑着色中,所有的顶点都参与像素的颜色计算
绕不过原点的轴旋转
DX 中的旋转函数都是绕过原点的轴而旋转,比如坐标轴,那么如何绕不过原点的轴旋转呢,可以这样,现将旋转轴平移到原点(待旋转物体也要相应平移),然后旋转物体,再将旋转轴和物体平移回去就可以了,编程的乐趣就在于灵活!
旋转摄像机代替旋转物体
有时候需要旋转物体作出特殊效果,但是如果物体太多并且每个物体有自己的世界坐标,旋转起来很麻烦,因为每个物体都要旋转,这时可以变通一下,旋转摄像机,因为物体和摄像机是相对的,旋转一个也就相当于旋转了另一个,编程的乐趣就在于变通!
只有new和malloc才动态分配内存么?
并不是只有new 和 malloc才会动态分配内存,COM中的Create也会动态分配内存,而Release则对应释放内存,感谢VCK星星的指点,不过好像所有这些,最终都是靠C函数malloc实现的,new也不例外。
如何draw? -- 一个函数只做一件事!
draw就是draw,只做draw该做的事情,比如
pd3dDevice->SetStreamSource( 0, pVB, 0, sizeof(CUSTOMVERTEX) );
pd3dDevice->SetIndices(pIB) ;
pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
pd3dDevice->DrawIndexedPrimitive(D3DPT_LINELIST, 0, 0, 8, 0, 12) ;
不要做创建顶点和索引数组的事情,那些操作会动态分配内存,应该放到初始化操作中去做,如果有特殊需要非要放到draw里面,那么在draw函数的最后要Release他们,像这样
if( pVB != NULL )
pVB->Release() ;
if( pIB != NULL )
pIB->Release() ;
否则你的程序会内存泄漏直至死机,感谢VCK vamos的指点,这个问题困扰了我很久,再次感谢他!
小心拷贝构造函数
在C++中,下面三种对象需要调用拷贝构造函数:
1) 一个对象以值传递的方式传入函数体;
2) 一个对象以值传递的方式从函数返回;
3) 一个对象需要通过另外一个对象进行初始化;
当你自己定义拷贝构造函数时,一定要确保所有字段都正确的初始化了,否则你会死得很惨
慎用 Cull Mode
SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE )
在使用纹理的时候要小心使用上面这个函数,最好不使用它,如果使用不当,将会是物体成镂空状,即从物体外部能看见物体的内部。
VS中的默认当前路径
当前路径是工程文件所在的路径,而不是源文件所在的路径,比如工程文件在C:\projects\, 而源文件在D:\source\,那么当前路径就是C:\project\, 如果在程序中读取文件而不加路径,那么这个文件必须在C:\projects\,否则会读取失败。
u×v = [(uyvz - uzvy), (uzvx - uxvz), (uxvy - uyvx)]