作者:Bill Hsu
主页:http://www.graptor.com
剪裁平面
(Clip Plane)
在图形学领域有着重要的作用,比如水面模拟中,渲染折射纹理时,我们就必须将水面以上的顶点通过剪裁平面剪裁掉。
在过去的固定渲染管道时代,剪裁平面的实现较为简单,比如在
DirectX 9
中,可以先设定剪裁平面在世界坐标系下的方程
(ax+by+cz+d=0)
,再调用
SetClipPlane(DWORD Index,CONST float * pPlane)
这个
API
函数就可以了。
附上例子程序:
vPosition=D3DXVECTOR3(0,0,0);
//
平面上一个点
vNormal=D3DXVECTOR3(0,1,0);
//
法向量
D3DXPlaneFromPointNormal( &clipplane, &vPosition, &vNormal );
//
生成剪裁平面
m_pDevice()->SetClipPlane( 0, (
float
*)clipplane);
然而,在现在的可编程管道
(programmable pipeline)
下,设置的剪裁平面会被在剪裁坐标系下处理,而不是在世界坐标系下。
解决这个问题的方法有:
1) 给要剪裁的顶点做标记,在Pixel Shader中把它剪裁掉。
2) 使用近斜平面裁剪(Oblique Near-Plane Clipping),即修改投影矩阵,将要剪裁的顶点放在视截体之外,从而避免了该顶点的绘制。
3) 修改平面方程,使之从世界坐标系转换到剪裁坐标系。
上述方法中,第一种和第二种效率并不高:在Pixel Shader中剪裁没有减少任何不必要的顶点处理,而计算近斜平面裁剪矩阵较为繁琐。所以,方法三是最佳选择。 要将一个平面从世界坐标系转换到剪裁坐标系,必须求出这个变换矩阵。
设平面方程ax+by+cz+d=0,用一个4维向量来n表示(a,b,c,d),设平面上有个点p:(x,y,z,1)。根据平面方程的定义,有:
nTp = ax + by + cz + d = 0
设矩阵R可以让点P从世界坐标系转换到剪裁坐标系,矩阵Q可以让平面n实现同样的变换。那么,有:
p'= Rp
n'= Qn
其中p'、n'分别是转换后的点与平面。
n'Tp'= 0
(Qn)T (Rp) = 0
nTQTRp = 0
如果:QTR = I
那么:
nTQTRp = nTIp = nTp = 0
于是:
QT = R-1
Q = (R-1)T
在DirectX 3D中,将一个点从世界坐标系转换到剪裁坐标系,所用的矩阵为观察矩阵与投影矩阵的乘积,即:
D3DXMATRIX TranMatrix = matView*matProj;
(TranMatrix为所求的变换矩阵,matView和matProj分别为观察矩阵与投影矩阵)
附上在D3D中变换的完整代码:
D3DXPLANE tempPlane = clipplane;
D3DXPlaneNormalize(&tempPlane, &tempPlane);
D3DXMATRIX TranMatrix = matView*matProj;
D3DXMatrixInverse(&TranMatrix, NULL, &TranMatrix);
D3DXMatrixTranspose(&TranMatrix, &TranMatrix);
D3DXPlaneTransform(&tempPlane, &tempPlane, &TranMatrix);
参考资料:
1.Back Face Culling Notes ,Jordan Smith (University of California, Berkeley)
http://www.cs.berkeley.edu/~ug/slide/pipeline/assignments/backfacecull.shtml
2.GameDev Forum
http://www.gamedev.net/community/forums/topic.asp?topic_id=402381
3.Oblique Near-Plane Clipping with Orthographic Camera ,Aras
http://aras-p.info/texts/obliqueortho.html
12-12-2009更新:加入图形化界面
程序下载(含使用说明):http://www.cppblog.com/Files/billhsu/MatInv.rar
感觉线性代数作业里一直少不了矩阵求逆,
写个带输出算逆矩阵的步骤的矩阵求逆程序,希望给即将或正在学线代的同学一点方便。
代码写的不好,大家见谅。
/*
==================================
*
* Copyright (C) Bill Hsu
*
http://hi.baidu.com/probill
* 2009-12-11
**********************************
*/
#include
<
iostream
>
#include
<
vector
>
#include
<
math.h
>
using
namespace
std;
typedef vector
<
float
>
s_line;
//
用来表示一行
s_line line;
typedef vector
<
s_line
>
s_matrix;
//
用来表示一个矩阵
s_matrix matrix;
s_matrix mat;
int
nSize;
//
矩阵维数
int
nSign;
//
标记行列式正负
void
outprint(s_matrix
&
_mat);
void
printstep(s_matrix
&
_mat);
int
step
=
0
;
void
line_add(s_matrix
&
_mat,
int
a,
int
b,
float
k
=
1.0f
)
//
第b行乘k加到第a行
{
int
size
=
_mat[
0
].size();
for
(
int
i
=
0
;i
<
size;
++
i)
{
_mat[a][i]
+=
_mat[b][i]
*
k;
}
//
end for
}
void
work1(s_matrix
&
_mat)
//
主计算函数
{
for
(
int
i
=
1
;i
<
nSize;
++
i)
{
if
(fabs(_mat[i
-
1
][i
-
1
])
<
0.000001
)
{
int
mm;
for
(mm
=
i;mm
<
nSize;
++
mm)
{
if
(fabs(_mat[mm
-
1
][i
-
1
])
>
0.000001
)
break
;
}
//
end for
line_add(_mat,i
-
1
,mm
-
1
);
}
//
end if
for
(
int
j
=
i;j
<
nSize;
++
j)
{
line_add(_mat,j,i
-
1
,
-
_mat[j][i
-
1
]
/
_mat[i
-
1
][i
-
1
]);
}
//
end for j
printstep(_mat);
}
//
end for i
}
void
work2(s_matrix
&
_mat)
//
第二部计算
{
for
(
int
i
=
nSize
-
2
;i
>=
0
;
--
i)
{
for
(
int
j
=
i;j
>=
0
;
--
j)
{
line_add(_mat,j,i
+
1
,
-
_mat[j][i
+
1
]
/
_mat[i
+
1
][i
+
1
]);
}
printstep(_mat);
}
}
void
makeunit(s_matrix
&
_mat)
//
单位化
{
mat.clear();
for
(
int
i
=
0
;i
<
nSize;
++
i)
{
line.clear();
for
(
int
j
=
0
;j
<
nSize
*
2
;
++
j)
{
float
tmp
=
_mat[i][j]
/
_mat[i][i];
if
(fabs(tmp)
<
0.000001
) tmp
=
0
;
line.push_back(tmp);
}
mat.push_back(line);
//
cout<<endl;
}
_mat
=
mat;
}
void
printstep(s_matrix
&
_mat)
//
显示求的过程
{
cout
<<
"
第
"
<<++
step
<<
"
步
"
<<
endl;
for
(
int
i
=
0
;i
<
nSize;
++
i)
{
for
(
int
j
=
0
;j
<
2
*
nSize;
++
j)
{
if
(fabs(_mat[i][j])
<
0.000001
) _mat[i][j]
=
0
;
cout
<<
_mat[i][j]
<<
"
"
;
if
(j
==
nSize
-
1
)cout
<<
"
|
"
;
}
cout
<<
endl;
}
cout
<<
endl;
}
void
outprint(s_matrix
&
_mat)
//
输出函数
{
for
(
int
i
=
0
;i
<
nSize;
++
i)
{
for
(
int
j
=
nSize;j
<
2
*
nSize;
++
j)
{
cout
<<
_mat[i][j]
<<
"
"
;
}
cout
<<
endl;
}
}
int
main()
{
step
=
0
;
matrix.clear();
line.clear();
cout
<<
"
*********矩阵 求逆*********
"
<<
endl;
cout
<<
"
*********Bill Hsu*********
"
<<
endl;
cout
<<
"
http://hi.baidu.com/probill
"
<<
endl
<<
endl;
cout
<<
"
请输入矩阵维数(输入0退出):
"
;
cin
>>
nSize;
if
(nSize
<=
0
)
return
0
;
for
(
int
i
=
0
;i
<
nSize;
++
i)
{
line.clear();
cout
<<
"
输入第
"
<<
i
+
1
<<
"
行:
"
<<
endl;
for
(
int
j
=
0
;j
<
nSize;
++
j)
{
float
tmp;
cin
>>
tmp;
line.push_back(tmp);
//
压入一个数到某行
}
for
(
int
j
=
0
;j
<
nSize;
++
j)
{
if
(i
==
j) line.push_back(
1.0f
);
else
line.push_back(
0.0f
);
}
matrix.push_back(line);
//
压入一行到矩阵
}
cout
<<
endl;
work1(matrix);
work2(matrix);
makeunit(matrix);
cout
<<
endl
<<
"
########################
"
<<
endl
<<
"
求逆结果:
"
<<
endl;
outprint(matrix);
cout
<<
"
########################
"
<<
endl;
main();
return
0
;
}
有图有真相:

输入矩阵数据

计算步骤

计算结果
执行文件下载:http://www.cppblog.com/Files/billhsu/%E7%9F%A9%E9%98%B5%E6%B1%82%E9%80%86.rar
.
今天终于把模型导出插件最基本的功能--导出网格给写好了。
c++和max sdk一起用的感觉很怪,系统崩溃了N次,不过,总算能导出网格了~
有图有真相,截图纪念
(注意:模型是从value的csol中弄出来的)
在d3d中渲染导出的模型
在d3d中渲染导出的模型
我的模型导出插件
导出数据样本:
[这是一个带贴图的立方体的导出数据]
8// 8个顶点
-6.70302 -21.2068 -5.46039
17.1883 -6.46757 0.162097
-22.4049 2.22378 -0.162097
1.48644 16.963 5.46039
-8.48039 -28.3257 20.7542
15.411 -13.5865 26.3767
-24.1823 -4.89514 26.0525
-0.290929 9.84411 31.675
12// 12个纹理坐标
0 1 0
1 1 0
0 0 0
1 0 0
0 1 0
1 1 0
0 0 0
1 0 0
0 1 0
1 1 0
0 0 0
1 0 0
12// 12个面引索
0 9 2 11 3 10
3 10 1 8 0 9
4 8 5 9 7 11
7 11 6 10 4 8
0 4 1 5 5 7
5 7 4 6 0 4
1 0 3 1 7 3
7 3 5 2 1 0
3 4 2 5 6 7
6 7 7 6 3 4
2 0 0 1 4 3
4 3 6 2 2 0
低手交流,高手勿入
当初觉得DX中设备丢失很讨厌,差点就投奔OpenGL了。
不过现在发现其实也没那么麻烦啦,写点东西,给不清楚
设备丢失怎么处理的同学参考下。
在创建时使用D3DPOOL_MANAGED标志的资源可以不需要重新载入,但D3DPOOL_DEFAULT加载的资源就需要先释放,后重建。
通常需要这样处理的有ID3DXFont和ID3DXSprite,而.X模型什么的就不需要。
在发现设备丢失时,我们要调用 OnLostDevice(void)函数让D3DPOOL_DEFAULT加载的资源释放掉。
好在ID3DXFont和ID3DXSprite有设备丢失处理函数,直接调用就好了
void OnLostDevice(void)
{
m_sprite->OnLostDevice();
m_font->OnLostDevice();
}
可是,怎么知道设备丢失了呢?
如果设备丢失,Present()函数就会“出问题”,返回值是D3DERR_DEVICELOST。
m_pIDirect3DDevice->EndScene();
HRESULT hr;
hr = m_pIDirect3DDevice->Present(NULL, NULL, NULL, NULL);
if(hr == D3DERR_DEVICELOST)
{
if(m_pIDirect3DDevice->TestCooperativeLevel() == D3DERR_DEVICENOTRESET)
{
OnLostDevice();
OnResetDevice();
}
}
TestCooperativeLevel()== D3DERR_DEVICENOTRESET时,就可以恢复设备了。
于是,我们调用OnLostDevice()让D3DPOOL_DEFAULT加载的资源释放掉,之后,调用
OnResetDevice()来恢复设备就可以了。
怎么恢复设备呢?
void OnResetDevice(void)
{
if(FAILED(m_pIDirect3DDevice->Reset(&d3dpp)))
{
return;
}
m_sprite->OnResetDevice();
m_font->OnResetDevice();
InitDevice();
}
先让D3D设备reset,然后调用ID3DXFont和ID3DXSprite的恢复函数,最后,需要把
D3D设备中的状态啊,矩阵变换啊这些重新设置下就完成了,也就是调用InitDevice()。
void InitDevice()
{
m_pIDirect3DDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
m_pIDirect3DDevice->SetRenderState( D3DRS_AMBIENT, 0xffffffff );
m_pIDirect3DDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
m_pIDirect3DDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
m_pIDirect3DDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 1000.0f );
m_pIDirect3DDevice->SetTransform( D3DTS_PROJECTION, &matProj );
vEyeVec=D3DXVECTOR3(0.0f,0.0f,-1.0f);
vLookatVec=D3DXVECTOR3(0.0f,0.0f,0.0f);
vUpVec=D3DXVECTOR3(0.0f,1.0f,0.0f);
D3DXMatrixLookAtLH( &matView, &vEyeVec, &vLookatVec, &vUpVec );
m_pIDirect3DDevice->SetTransform( D3DTS_VIEW, &matView );
}
啊,这样设备丢失就处理好了
//
tag:DirectX 3D d3d lost device TestCooperativeLevel OnLostDevice 恢复设备 设备丢失 DeviceLost