Billboard(
广告牌
,
公告牌
)
:就是使物体永远面对摄像机
,
摄像机旋转
,
物体也跟着旋转
,
使其始终朝向摄像机
.
我们在
渲染树木
,
灌木丛
,
云,烟等效果时都会用到
.
下面是一个效果演示
:billboard
原理
:
问题的实质就是我们知道结果是物体面对摄像机
,
然后求物体的世界变换矩阵
Plocal(0,0,0,1):
局部坐标原点
Pworld:
世界空间内的坐标
Pview:在
视图空间内的坐标
Mworld:
世界变换矩阵
Mview:
视图变换矩阵
PviewOri:
视图空间原点
Mvtransform:
在视图空间内作的变换
关键是求
Mview
的逆矩阵,根据线性代数的知识我们知道正交矩阵的逆矩阵等于其转置矩阵
,
所以我们只要把
Mview
正交化,那么它的逆就等于它的转置矩阵了
,
所以
由上面的公式可以理解为:
求
billboard
物体的世界变换矩阵其实就是用物体在视图空间内的变换矩阵乘以视图矩阵的逆
(
转置
)
代码
下面是代码
BOOL Do_Frame()
{
D3DXMATRIX mat_view, mat_world;
// clear device back buffer
g_d3d_device -> Clear( 0 , NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA( 0 , 64 , 128 , 255 ), 1.0f , 0 );
// update the view position
float angle = ( float ) timeGetTime() / 2000.0 ;
// 构建视图矩阵
D3DXMatrixLookAtLH( & mat_view, & D3DXVECTOR3(cos(angle) * 400.0 , 200.0 , sin(angle) * 400.0 ),
& D3DXVECTOR3( 0.0 , 0.0 , 0.0 ), & D3DXVECTOR3( 0.0 , 1.0 , 0.0 ));
// set view matrix
g_d3d_device -> SetTransform(D3DTS_VIEW, & mat_view);
// Begin scene
if (SUCCEEDED(g_d3d_device -> BeginScene()))
{
#pragma region draw ground
// binds a vertex buffer to a device data stream
g_d3d_device -> SetStreamSource( 0 , g_floor_vb, 0 , sizeof (VERTEX));
// set the current vertex stream declation
g_d3d_device -> SetFVF(VERTEX_FVF);
// assigns a texture to a stage for a device
g_d3d_device -> SetTexture( 0 , g_floor_texture);
// build world matrix, we only need identity matrix, because we do not need to change original floor position.
D3DXMatrixIdentity( & mat_world);
// set world matrix
g_d3d_device -> SetTransform(D3DTS_WORLD, & mat_world);
// renders a sequence of noindexed, geometric primitives of the specified type from the current set
// of data input stream.
g_d3d_device -> DrawPrimitive(D3DPT_TRIANGLESTRIP, 0 , 2 );
#pragma endregion
// 2) draw the billboards
g_d3d_device -> SetStreamSource( 0 , g_billboard_vb, 0 , sizeof (VERTEX));
g_d3d_device -> SetTexture( 0 , g_billboard_texture);
// enable alpha test
g_d3d_device -> SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
// 用当前试图矩阵的转置矩阵作为世界变换矩阵(实际上应该是逆矩阵,只是因为正交矩阵的转置等于逆)
D3DXMatrixTranspose( & mat_world, & mat_view);
// 用这个也是可以的,不过求逆比求转置费D3DXMatrixInverse(&mat_world, &mat_view);
// draw all billboard images
for ( short i = 0 ; i < 5 ; i ++ )
{
// 对每一个树都先在视图空间内移动到理性的位置,再乘以上面求得的世界矩阵
for ( short j = 0 ; j < 5 ; j ++ )
{
mat_world._41 = i * 80.0 - 160.0 ;
mat_world._42 = 0.0 ;
mat_world._43 = j * 80.0 - 160.0 ;
g_d3d_device -> SetTransform(D3DTS_WORLD, & mat_world);
// 这里也可以用下面的代码代替
/*
D3DXMATRIX tempTrans, tempWorld;
D3DXMatrixIdentity(&tempTrans);
D3DXMatrixTranslation(&tempTrans, i*80.0-160.0, 0, j*80.0-160.0);
D3DXMatrixMultiply(&tempWorld, &mat_world, &tempTrans);
g_d3d_device->SetTransform(D3DTS_WORLD, &tempWorld); */
// draw polygon
g_d3d_device -> DrawPrimitive(D3DPT_TRIANGLESTRIP, 0 , 2 );
}
}
// disable alpha test
g_d3d_device -> SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
// release texture
g_d3d_device -> SetTexture( 0 , NULL);
// end the scene
g_d3d_device -> EndScene();
}
// present the contents of the next buffer in the sequence of back buffers owned by the device
g_d3d_device -> Present(NULL, NULL, NULL, NULL);
return TRUE;
}
{
D3DXMATRIX mat_view, mat_world;
// clear device back buffer
g_d3d_device -> Clear( 0 , NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA( 0 , 64 , 128 , 255 ), 1.0f , 0 );
// update the view position
float angle = ( float ) timeGetTime() / 2000.0 ;
// 构建视图矩阵
D3DXMatrixLookAtLH( & mat_view, & D3DXVECTOR3(cos(angle) * 400.0 , 200.0 , sin(angle) * 400.0 ),
& D3DXVECTOR3( 0.0 , 0.0 , 0.0 ), & D3DXVECTOR3( 0.0 , 1.0 , 0.0 ));
// set view matrix
g_d3d_device -> SetTransform(D3DTS_VIEW, & mat_view);
// Begin scene
if (SUCCEEDED(g_d3d_device -> BeginScene()))
{
#pragma region draw ground
// binds a vertex buffer to a device data stream
g_d3d_device -> SetStreamSource( 0 , g_floor_vb, 0 , sizeof (VERTEX));
// set the current vertex stream declation
g_d3d_device -> SetFVF(VERTEX_FVF);
// assigns a texture to a stage for a device
g_d3d_device -> SetTexture( 0 , g_floor_texture);
// build world matrix, we only need identity matrix, because we do not need to change original floor position.
D3DXMatrixIdentity( & mat_world);
// set world matrix
g_d3d_device -> SetTransform(D3DTS_WORLD, & mat_world);
// renders a sequence of noindexed, geometric primitives of the specified type from the current set
// of data input stream.
g_d3d_device -> DrawPrimitive(D3DPT_TRIANGLESTRIP, 0 , 2 );
#pragma endregion
// 2) draw the billboards
g_d3d_device -> SetStreamSource( 0 , g_billboard_vb, 0 , sizeof (VERTEX));
g_d3d_device -> SetTexture( 0 , g_billboard_texture);
// enable alpha test
g_d3d_device -> SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
// 用当前试图矩阵的转置矩阵作为世界变换矩阵(实际上应该是逆矩阵,只是因为正交矩阵的转置等于逆)
D3DXMatrixTranspose( & mat_world, & mat_view);
// 用这个也是可以的,不过求逆比求转置费D3DXMatrixInverse(&mat_world, &mat_view);
// draw all billboard images
for ( short i = 0 ; i < 5 ; i ++ )
{
// 对每一个树都先在视图空间内移动到理性的位置,再乘以上面求得的世界矩阵
for ( short j = 0 ; j < 5 ; j ++ )
{
mat_world._41 = i * 80.0 - 160.0 ;
mat_world._42 = 0.0 ;
mat_world._43 = j * 80.0 - 160.0 ;
g_d3d_device -> SetTransform(D3DTS_WORLD, & mat_world);
// 这里也可以用下面的代码代替
/*
D3DXMATRIX tempTrans, tempWorld;
D3DXMatrixIdentity(&tempTrans);
D3DXMatrixTranslation(&tempTrans, i*80.0-160.0, 0, j*80.0-160.0);
D3DXMatrixMultiply(&tempWorld, &mat_world, &tempTrans);
g_d3d_device->SetTransform(D3DTS_WORLD, &tempWorld); */
// draw polygon
g_d3d_device -> DrawPrimitive(D3DPT_TRIANGLESTRIP, 0 , 2 );
}
}
// disable alpha test
g_d3d_device -> SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
// release texture
g_d3d_device -> SetTexture( 0 , NULL);
// end the scene
g_d3d_device -> EndScene();
}
// present the contents of the next buffer in the sequence of back buffers owned by the device
g_d3d_device -> Present(NULL, NULL, NULL, NULL);
return TRUE;
}