今天,我们讲一下平常心,平常心对于我们做技术的来说,尤其重要,心里的波澜,会让自己开始浮躁,开始冲动,我们都知道冲动是魔鬼,说来说去,还是由于自己的内心太弱小,害怕改变,害怕失去,其实所有的东西,都是有失就有得,因为我们目前看不到,改变后会得到的东西,是不是比现在拥有的东西少,我们无法去衡量,所以就开始害怕,如果我们有足够强大的内心,其实就是自信,我们就不会害怕。那么多的创业者,难道他们一开始就知道,或者注定,他们会成功,会得到很多东西吗?没有,正是因为,不可预知性,才更加的有神秘的诱惑感,很多人都不敢尝试,所以,成功注定跟他们无缘,当然,即使敢于尝试,失败的几率也是成功的几率的几百倍,但是,因为我们有强大的内心,勇敢的去尝试,必有成功的可能。
我们今天看一下粒子系统,许多自然现象都包含了大量行为相似的微小粒子(例如飘落的雪花,焰火的火星以及未来武器所发射的子弹等).粒子系统常用来模拟此类现象。粒子是一种微小的物体,在数学上通常用点来表示其模型。所以显示粒子时,使用点图元(由D3DPRIMITIVETYPE类型的D3DPT_POINTLIST枚举常量表示)是一个很好的选择。但是,光栅化时,点图元被映射为一个单个像素。这样就无法为我们提供很大的灵活性,因为实际应用中我们可能需要各种尺寸的粒子甚至希望能够对这些粒子进行纹理映射。从Direct3D 8.0引入了一种特别的点图元---点精灵。之前使用的广告牌需要4个顶点描述,点精灵只需要一个顶点。
我们看一下代码,首先看一下pSystem.h的代码:
//
//
// File: pSystem.cpp
//
// Author: Frank Luna (C) All Rights Reserved
//
// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0
//
// Desc: Represents a geneal particle system.
//
//
#include <cstdlib>
#include "pSystem.h"
using namespace psys;
const DWORD Particle::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE;
PSystem::PSystem()
{
_device = 0;
_vb = 0;
_tex = 0;
}
PSystem::~PSystem()
{
d3d::Release<IDirect3DVertexBuffer9*>(_vb);
d3d::Release<IDirect3DTexture9*>(_tex);
}
bool PSystem::init(IDirect3DDevice9* device, char* texFileName)
{
// vertex buffer's size does not equal the number of particles in our system. We
// use the vertex buffer to draw a portion of our particles at a time. The arbitrary
// size we choose for the vertex buffer is specified by the _vbSize variable.
_device = device; // save a ptr to the device
HRESULT hr = 0;
hr = device->CreateVertexBuffer(
_vbSize * sizeof(Particle),
D3DUSAGE_DYNAMIC | D3DUSAGE_POINTS | D3DUSAGE_WRITEONLY,
Particle::FVF,
D3DPOOL_DEFAULT, // D3DPOOL_MANAGED can't be used with D3DUSAGE_DYNAMIC
&_vb,
0);
if(FAILED(hr))
{
::MessageBox(0, "CreateVertexBuffer() - FAILED", "PSystem", 0);
return false;
}
hr = D3DXCreateTextureFromFile(
device,
texFileName,
&_tex);
if(FAILED(hr))
{
::MessageBox(0, "D3DXCreateTextureFromFile() - FAILED", "PSystem", 0);
return false;
}
return true;
}
void PSystem::reset()
{
std::list<Attribute>::iterator i;
for(i = _particles.begin(); i != _particles.end(); i++)
{
resetParticle( &(*i) );
}
}
void PSystem::addParticle()
{
Attribute attribute;
resetParticle(&attribute);
_particles.push_back(attribute);
}
void PSystem::preRender()
{
_device->SetRenderState(D3DRS_LIGHTING, false);
_device->SetRenderState(D3DRS_POINTSPRITEENABLE, true);
_device->SetRenderState(D3DRS_POINTSCALEENABLE, true);
_device->SetRenderState(D3DRS_POINTSIZE, d3d::FtoDw(_size));
_device->SetRenderState(D3DRS_POINTSIZE_MIN, d3d::FtoDw(0.0f));
// 控制了点精灵的尺寸如果随距离发生变化,这里的距离是指点精灵到摄像机的距离。
// 因为我们不断的变换了点精灵在世界坐标系的位置,所以点精灵的尺寸也一直在改变
_device->SetRenderState(D3DRS_POINTSCALE_A, d3d::FtoDw(0.0f));
_device->SetRenderState(D3DRS_POINTSCALE_B, d3d::FtoDw(0.0f));
_device->SetRenderState(D3DRS_POINTSCALE_C, d3d::FtoDw(1.0f));
// use alpha from texture
_device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
_device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
_device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
}
void PSystem::postRender()
{
_device->SetRenderState(D3DRS_LIGHTING, true);
_device->SetRenderState(D3DRS_POINTSPRITEENABLE, false);
_device->SetRenderState(D3DRS_POINTSCALEENABLE, false);
_device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
}
void PSystem::render()
{
//
// Remarks: The render method works by filling a section of the vertex buffer with data,
// then we render that section. While that section is rendering we lock a new
// section and begin to fill that section. Once that sections filled we render it.
// This process continues until all the particles have been drawn. The benifit
// of this method is that we keep the video card and the CPU busy.
if( !_particles.empty() )
{
//
// set render states
//
preRender();
_device->SetTexture(0, _tex);
_device->SetFVF(Particle::FVF);
_device->SetStreamSource(0, _vb, 0, sizeof(Particle));
//
// render batches one by one
//
// start at beginning if we're at the end of the vb
if(_vbOffset >= _vbSize)
_vbOffset = 0;
Particle* v = 0;
_vb->Lock(
_vbOffset * sizeof( Particle ),
_vbBatchSize * sizeof( Particle ),
(void**)&v,
_vbOffset ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD);
DWORD numParticlesInBatch = 0;
//
// Until all particles have been rendered.
//
std::list<Attribute>::iterator i;
for(i = _particles.begin(); i != _particles.end(); i++)
{
if( i->_isAlive )
{
//
// Copy a batch of the living particles to the
// next vertex buffer segment
//
v->_position = i->_position;
v->_color = (D3DCOLOR)i->_color;
v++; // next element;
numParticlesInBatch++; //increase batch counter
// if this batch full?
if(numParticlesInBatch == _vbBatchSize)
{
//
// Draw the last batch of particles that was
// copied to the vertex buffer.
//
_vb->Unlock();
_device->DrawPrimitive(
D3DPT_POINTLIST,
_vbOffset,
_vbBatchSize);
//
// While that batch is drawing, start filling the
// next batch with particles.
//
// move the offset to the start of the next batch
_vbOffset += _vbBatchSize;
// don't offset into memory thats outside the vb's range.
// If we're at the end, start at the beginning.
if(_vbOffset >= _vbSize)
_vbOffset = 0;
_vb->Lock(
_vbOffset * sizeof( Particle ),
_vbBatchSize * sizeof( Particle ),
(void**)&v,
_vbOffset ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD);
numParticlesInBatch = 0; // reset for new batch
}
}
}
_vb->Unlock();
// its possible that the LAST batch being filled never
// got rendered because the condition
// (numParticlesInBatch == _vbBatchSize) would not have
// been satisfied. We draw the last partially filled batch now.
if( numParticlesInBatch )
{
_device->DrawPrimitive(
D3DPT_POINTLIST,
_vbOffset,
numParticlesInBatch);
}
// next block
_vbOffset += _vbBatchSize;
//
// reset render states
//
postRender();
}
}
bool PSystem::isEmpty()
{
return _particles.empty();
}
bool PSystem::isDead()
{
std::list<Attribute>::iterator i;
for(i = _particles.begin(); i != _particles.end(); i++)
{
// is there at least one living particle? If yes,
// the system is not dead.
if( i->_isAlive )
return false;
}
// no living particles found, the system must be dead.
return true;
}
void PSystem::removeDeadParticles()
{
std::list<Attribute>::iterator i;
i = _particles.begin();
while( i != _particles.end() )
{
if( i->_isAlive == false )
{
// erase returns the next iterator, so no need to
// incrememnt to the next one ourselves.
i = _particles.erase(i);
}
else
{
i++; // next in list
}
}
}
//*****************************************************************************
// Snow System
//***************
Snow::Snow(d3d::BoundingBox* boundingBox, int numParticles)
{
_boundingBox = *boundingBox;
_size = 0.25f;
_vbSize = 2048;
_vbOffset = 0;
_vbBatchSize = 512;
for(int i = 0; i < numParticles; i++)
addParticle();
}
void Snow::resetParticle(Attribute* attribute)
{
attribute->_isAlive = true;
// get random x, z coordinate for the position of the snow flake.
d3d::GetRandomVector(
&attribute->_position,
&_boundingBox._min,
&_boundingBox._max);
// no randomness for height (y-coordinate). Snow flake
// always starts at the top of bounding box.
attribute->_position.y = _boundingBox._max.y;
// snow flakes fall downwards and slightly to the left
attribute->_velocity.x = d3d::GetRandomFloat(0.0f, 1.0f) * -3.0f;
attribute->_velocity.y = d3d::GetRandomFloat(0.0f, 1.0f) * -10.0f;
attribute->_velocity.z = 0.0f;
// white snow flake
attribute->_color = d3d::WHITE;
}
void Snow::update(float timeDelta)
{
std::list<Attribute>::iterator i;
for(i = _particles.begin(); i != _particles.end(); i++)
{
i->_position += i->_velocity * timeDelta;
// is the point outside bounds?
if( _boundingBox.isPointInside( i->_position ) == false )
{
// nope so kill it, but we want to recycle dead
// particles, so respawn it instead.
resetParticle( &(*i) );
}
}
}
//*****************************************************************************
// Explosion System
//********************
Firework::Firework(D3DXVECTOR3* origin, int numParticles)
{
_origin = *origin;
_size = 0.9f;
_vbSize = 2048;
_vbOffset = 0;
_vbBatchSize = 512;
for(int i = 0; i < numParticles; i++)
addParticle();
}
void Firework::resetParticle(Attribute* attribute)
{
attribute->_isAlive = true;
attribute->_position = _origin;
D3DXVECTOR3 min = D3DXVECTOR3(-1.0f, -1.0f, -1.0f);
D3DXVECTOR3 max = D3DXVECTOR3( 1.0f, 1.0f, 1.0f);
d3d::GetRandomVector(
&attribute->_velocity,
&min,
&max);
// normalize to make spherical
D3DXVec3Normalize(
&attribute->_velocity,
&attribute->_velocity);
attribute->_velocity *= 100.0f;
attribute->_color = D3DXCOLOR(
d3d::GetRandomFloat(0.0f, 1.0f),
d3d::GetRandomFloat(0.0f, 1.0f),
d3d::GetRandomFloat(0.0f, 1.0f),
1.0f);
attribute->_age = 0.0f;
attribute->_lifeTime = 2.0f; // lives for 2 seconds
}
void Firework::update(float timeDelta)
{
std::list<Attribute>::iterator i;
for(i = _particles.begin(); i != _particles.end(); i++)
{
// only update living particles
if( i->_isAlive )
{
i->_position += i->_velocity * timeDelta;
i->_age += timeDelta;
if(i->_age > i->_lifeTime) // kill
i->_isAlive = false;
}
}
}
//Firework系统在绘制时使用了不同的融合因子。而且,它禁止对深度缓存进行操作。我们只需重载preRender和postRender,即
//很容易改变了融合因子和写状态。
void Firework::preRender()
{
PSystem::preRender();
_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
// read, but don't write particles to z-buffer
_device->SetRenderState(D3DRS_ZWRITEENABLE, false);
}
void Firework::postRender()
{
PSystem::postRender();
_device->SetRenderState(D3DRS_ZWRITEENABLE, true);
}
//*****************************************************************************
// Laser System
//****************
ParticleGun::ParticleGun(Camera* camera)
{
_camera = camera;
_size = 0.8f;
_vbSize = 2048;
_vbOffset = 0;
_vbBatchSize = 512;
}
//这里的resetParticle方法将粒子设置在与摄像即所在的位置,并将粒子的速度取为摄像机的观察方向乘以100.
void ParticleGun::resetParticle(Attribute* attribute)
{
attribute->_isAlive = true;
D3DXVECTOR3 cameraPos;
_camera->getPosition(&cameraPos);
D3DXVECTOR3 cameraDir;
_camera->getLook(&cameraDir);
// change to camera position
attribute->_position = cameraPos;
attribute->_position.y -= 1.0f; // slightly below camera
// so its like we're carrying a gun
// travels in the direction the camera is looking
attribute->_velocity = cameraDir * 100.0f;
// green
attribute->_color = D3DXCOLOR(0.0f, 1.0f, 0.0f, 1.0f);
attribute->_age = 0.0f;
attribute->_lifeTime = 1.0f; // lives for 1 seconds
}
//update方法对粒子的位置进行更新,并杀死那些超过生命期的粒子。然后搜索粒子列表,移除所有处于死亡状态的粒子。
void ParticleGun::update(float timeDelta)
{
std::list<Attribute>::iterator i;
for(i = _particles.begin(); i != _particles.end(); i++)
{
i->_position += i->_velocity * timeDelta;
i->_age += timeDelta;
if(i->_age > i->_lifeTime) // kill
i->_isAlive = false;
}
removeDeadParticles();
}
再来看一下,pSystem.cpp的代码:
//
//
// File: pSystem.cpp
//
// Author: Frank Luna (C) All Rights Reserved
//
// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0
//
// Desc: Represents a geneal particle system.
//
//
#include <cstdlib>
#include "pSystem.h"
using namespace psys;
const DWORD Particle::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE;
PSystem::PSystem()
{
_device = 0;
_vb = 0;
_tex = 0;
}
PSystem::~PSystem()
{
d3d::Release<IDirect3DVertexBuffer9*>(_vb);
d3d::Release<IDirect3DTexture9*>(_tex);
}
bool PSystem::init(IDirect3DDevice9* device, char* texFileName)
{
// vertex buffer's size does not equal the number of particles in our system. We
// use the vertex buffer to draw a portion of our particles at a time. The arbitrary
// size we choose for the vertex buffer is specified by the _vbSize variable.
_device = device; // save a ptr to the device
HRESULT hr = 0;
hr = device->CreateVertexBuffer(
_vbSize * sizeof(Particle),
D3DUSAGE_DYNAMIC | D3DUSAGE_POINTS | D3DUSAGE_WRITEONLY,
Particle::FVF,
D3DPOOL_DEFAULT, // D3DPOOL_MANAGED can't be used with D3DUSAGE_DYNAMIC
&_vb,
0);
if(FAILED(hr))
{
::MessageBox(0, "CreateVertexBuffer() - FAILED", "PSystem", 0);
return false;
}
hr = D3DXCreateTextureFromFile(
device,
texFileName,
&_tex);
if(FAILED(hr))
{
::MessageBox(0, "D3DXCreateTextureFromFile() - FAILED", "PSystem", 0);
return false;
}
return true;
}
void PSystem::reset()
{
std::list<Attribute>::iterator i;
for(i = _particles.begin(); i != _particles.end(); i++)
{
resetParticle( &(*i) );
}
}
void PSystem::addParticle()
{
Attribute attribute;
resetParticle(&attribute);
_particles.push_back(attribute);
}
void PSystem::preRender()
{
_device->SetRenderState(D3DRS_LIGHTING, false);
_device->SetRenderState(D3DRS_POINTSPRITEENABLE, true);
_device->SetRenderState(D3DRS_POINTSCALEENABLE, true);
_device->SetRenderState(D3DRS_POINTSIZE, d3d::FtoDw(_size));
_device->SetRenderState(D3DRS_POINTSIZE_MIN, d3d::FtoDw(0.0f));
// 控制了点精灵的尺寸如果随距离发生变化,这里的距离是指点精灵到摄像机的距离。
// 因为我们不断的变换了点精灵在世界坐标系的位置,所以点精灵的尺寸也一直在改变
_device->SetRenderState(D3DRS_POINTSCALE_A, d3d::FtoDw(0.0f));
_device->SetRenderState(D3DRS_POINTSCALE_B, d3d::FtoDw(0.0f));
_device->SetRenderState(D3DRS_POINTSCALE_C, d3d::FtoDw(1.0f));
// use alpha from texture
_device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
_device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
_device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
}
void PSystem::postRender()
{
_device->SetRenderState(D3DRS_LIGHTING, true);
_device->SetRenderState(D3DRS_POINTSPRITEENABLE, false);
_device->SetRenderState(D3DRS_POINTSCALEENABLE, false);
_device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
}
void PSystem::render()
{
//
// Remarks: The render method works by filling a section of the vertex buffer with data,
// then we render that section. While that section is rendering we lock a new
// section and begin to fill that section. Once that sections filled we render it.
// This process continues until all the particles have been drawn. The benifit
// of this method is that we keep the video card and the CPU busy.
if( !_particles.empty() )
{
//
// set render states
//
preRender();
_device->SetTexture(0, _tex);
_device->SetFVF(Particle::FVF);
_device->SetStreamSource(0, _vb, 0, sizeof(Particle));
//
// render batches one by one
//
// start at beginning if we're at the end of the vb
if(_vbOffset >= _vbSize)
_vbOffset = 0;
Particle* v = 0;
_vb->Lock(
_vbOffset * sizeof( Particle ),
_vbBatchSize * sizeof( Particle ),
(void**)&v,
_vbOffset ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD);
DWORD numParticlesInBatch = 0;
//
// Until all particles have been rendered.
//
std::list<Attribute>::iterator i;
for(i = _particles.begin(); i != _particles.end(); i++)
{
if( i->_isAlive )
{
//
// Copy a batch of the living particles to the
// next vertex buffer segment
//
v->_position = i->_position;
v->_color = (D3DCOLOR)i->_color;
v++; // next element;
numParticlesInBatch++; //increase batch counter
// if this batch full?
if(numParticlesInBatch == _vbBatchSize)
{
//
// Draw the last batch of particles that was
// copied to the vertex buffer.
//
_vb->Unlock();
_device->DrawPrimitive(
D3DPT_POINTLIST,
_vbOffset,
_vbBatchSize);
//
// While that batch is drawing, start filling the
// next batch with particles.
//
// move the offset to the start of the next batch
_vbOffset += _vbBatchSize;
// don't offset into memory thats outside the vb's range.
// If we're at the end, start at the beginning.
if(_vbOffset >= _vbSize)
_vbOffset = 0;
_vb->Lock(
_vbOffset * sizeof( Particle ),
_vbBatchSize * sizeof( Particle ),
(void**)&v,
_vbOffset ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD);
numParticlesInBatch = 0; // reset for new batch
}
}
}
_vb->Unlock();
// its possible that the LAST batch being filled never
// got rendered because the condition
// (numParticlesInBatch == _vbBatchSize) would not have
// been satisfied. We draw the last partially filled batch now.
if( numParticlesInBatch )
{
_device->DrawPrimitive(
D3DPT_POINTLIST,
_vbOffset,
numParticlesInBatch);
}
// next block
_vbOffset += _vbBatchSize;
//
// reset render states
//
postRender();
}
}
bool PSystem::isEmpty()
{
return _particles.empty();
}
bool PSystem::isDead()
{
std::list<Attribute>::iterator i;
for(i = _particles.begin(); i != _particles.end(); i++)
{
// is there at least one living particle? If yes,
// the system is not dead.
if( i->_isAlive )
return false;
}
// no living particles found, the system must be dead.
return true;
}
void PSystem::removeDeadParticles()
{
std::list<Attribute>::iterator i;
i = _particles.begin();
while( i != _particles.end() )
{
if( i->_isAlive == false )
{
// erase returns the next iterator, so no need to
// incrememnt to the next one ourselves.
i = _particles.erase(i);
}
else
{
i++; // next in list
}
}
}
//*****************************************************************************
// Snow System
//***************
Snow::Snow(d3d::BoundingBox* boundingBox, int numParticles)
{
_boundingBox = *boundingBox;
_size = 0.25f;
_vbSize = 2048;
_vbOffset = 0;
_vbBatchSize = 512;
for(int i = 0; i < numParticles; i++)
addParticle();
}
void Snow::resetParticle(Attribute* attribute)
{
attribute->_isAlive = true;
// get random x, z coordinate for the position of the snow flake.
d3d::GetRandomVector(
&attribute->_position,
&_boundingBox._min,
&_boundingBox._max);
// no randomness for height (y-coordinate). Snow flake
// always starts at the top of bounding box.
attribute->_position.y = _boundingBox._max.y;
// snow flakes fall downwards and slightly to the left
attribute->_velocity.x = d3d::GetRandomFloat(0.0f, 1.0f) * -3.0f;
attribute->_velocity.y = d3d::GetRandomFloat(0.0f, 1.0f) * -10.0f;
attribute->_velocity.z = 0.0f;
// white snow flake
attribute->_color = d3d::WHITE;
}
void Snow::update(float timeDelta)
{
std::list<Attribute>::iterator i;
for(i = _particles.begin(); i != _particles.end(); i++)
{
i->_position += i->_velocity * timeDelta;
// is the point outside bounds?
if( _boundingBox.isPointInside( i->_position ) == false )
{
// nope so kill it, but we want to recycle dead
// particles, so respawn it instead.
resetParticle( &(*i) );
}
}
}
//*****************************************************************************
// Explosion System
//********************
Firework::Firework(D3DXVECTOR3* origin, int numParticles)
{
_origin = *origin;
_size = 0.9f;
_vbSize = 2048;
_vbOffset = 0;
_vbBatchSize = 512;
for(int i = 0; i < numParticles; i++)
addParticle();
}
void Firework::resetParticle(Attribute* attribute)
{
attribute->_isAlive = true;
attribute->_position = _origin;
D3DXVECTOR3 min = D3DXVECTOR3(-1.0f, -1.0f, -1.0f);
D3DXVECTOR3 max = D3DXVECTOR3( 1.0f, 1.0f, 1.0f);
d3d::GetRandomVector(
&attribute->_velocity,
&min,
&max);
// normalize to make spherical
D3DXVec3Normalize(
&attribute->_velocity,
&attribute->_velocity);
attribute->_velocity *= 100.0f;
attribute->_color = D3DXCOLOR(
d3d::GetRandomFloat(0.0f, 1.0f),
d3d::GetRandomFloat(0.0f, 1.0f),
d3d::GetRandomFloat(0.0f, 1.0f),
1.0f);
attribute->_age = 0.0f;
attribute->_lifeTime = 2.0f; // lives for 2 seconds
}
void Firework::update(float timeDelta)
{
std::list<Attribute>::iterator i;
for(i = _particles.begin(); i != _particles.end(); i++)
{
// only update living particles
if( i->_isAlive )
{
i->_position += i->_velocity * timeDelta;
i->_age += timeDelta;
if(i->_age > i->_lifeTime) // kill
i->_isAlive = false;
}
}
}
//Firework系统在绘制时使用了不同的融合因子。而且,它禁止对深度缓存进行操作。我们只需重载preRender和postRender,即
//很容易改变了融合因子和写状态。
void Firework::preRender()
{
PSystem::preRender();
_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
// read, but don't write particles to z-buffer
_device->SetRenderState(D3DRS_ZWRITEENABLE, false);
}
void Firework::postRender()
{
PSystem::postRender();
_device->SetRenderState(D3DRS_ZWRITEENABLE, true);
}
//*****************************************************************************
// Laser System
//****************
ParticleGun::ParticleGun(Camera* camera)
{
_camera = camera;
_size = 0.8f;
_vbSize = 2048;
_vbOffset = 0;
_vbBatchSize = 512;
}
//这里的resetParticle方法将粒子设置在与摄像即所在的位置,并将粒子的速度取为摄像机的观察方向乘以100.
void ParticleGun::resetParticle(Attribute* attribute)
{
attribute->_isAlive = true;
D3DXVECTOR3 cameraPos;
_camera->getPosition(&cameraPos);
D3DXVECTOR3 cameraDir;
_camera->getLook(&cameraDir);
// change to camera position
attribute->_position = cameraPos;
attribute->_position.y -= 1.0f; // slightly below camera
// so its like we're carrying a gun
// travels in the direction the camera is looking
attribute->_velocity = cameraDir * 100.0f;
// green
attribute->_color = D3DXCOLOR(0.0f, 1.0f, 0.0f, 1.0f);
attribute->_age = 0.0f;
attribute->_lifeTime = 1.0f; // lives for 1 seconds
}
//update方法对粒子的位置进行更新,并杀死那些超过生命期的粒子。然后搜索粒子列表,移除所有处于死亡状态的粒子。
void ParticleGun::update(float timeDelta)
{
std::list<Attribute>::iterator i;
for(i = _particles.begin(); i != _particles.end(); i++)
{
i->_position += i->_velocity * timeDelta;
i->_age += timeDelta;
if(i->_age > i->_lifeTime) // kill
i->_isAlive = false;
}
removeDeadParticles();
}
再来看一下,DirectX3D.h的代码:
#ifndef __DirectX3DH__
#define __DirectX3DH__
#include <d3dx9.h>
#include <string>
#include <limits>
namespace d3d
{
bool InitD3D(
HINSTANCE hInstance, // [in] Application instance.
int width, int height, // [in] Backbuffer dimensions.
bool windowed, // [in] Windowed (true)or full screen (false).
D3DDEVTYPE deviceType, // [in] HAL or REF
IDirect3DDevice9** device);// [out]The created device.
int EnterMsgLoop(
bool (*ptr_display)(float timeDelta));
LRESULT CALLBACK WndProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam);
template<class T> void Release(T t)
{
if( t )
{
t->Release();
t = 0;
}
}
template<class T> void Delete(T t)
{
if( t )
{
delete t;
t = 0;
}
}
const D3DXCOLOR WHITE( D3DCOLOR_XRGB(255, 255, 255) );
const D3DXCOLOR BLACK( D3DCOLOR_XRGB( 0, 0, 0) );
const D3DXCOLOR RED( D3DCOLOR_XRGB(255, 0, 0) );
const D3DXCOLOR GREEN( D3DCOLOR_XRGB( 0, 255, 0) );
const D3DXCOLOR BLUE( D3DCOLOR_XRGB( 0, 0, 255) );
const D3DXCOLOR YELLOW( D3DCOLOR_XRGB(255, 255, 0) );
const D3DXCOLOR CYAN( D3DCOLOR_XRGB( 0, 255, 255) );
const D3DXCOLOR MAGENTA( D3DCOLOR_XRGB(255, 0, 255) );
//
// Lights
//
D3DLIGHT9 InitDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color);
D3DLIGHT9 InitPointLight(D3DXVECTOR3* position, D3DXCOLOR* color);
D3DLIGHT9 InitSpotLight(D3DXVECTOR3* position, D3DXVECTOR3* direction, D3DXCOLOR* color);
//
// Materials
//
D3DMATERIAL9 InitMtrl(D3DXCOLOR a, D3DXCOLOR d, D3DXCOLOR s, D3DXCOLOR e, float p);
const D3DMATERIAL9 WHITE_MTRL = InitMtrl(WHITE, WHITE, WHITE, BLACK, 2.0f);
const D3DMATERIAL9 RED_MTRL = InitMtrl(RED, RED, RED, BLACK, 2.0f);
const D3DMATERIAL9 GREEN_MTRL = InitMtrl(GREEN, GREEN, GREEN, BLACK, 2.0f);
const D3DMATERIAL9 BLUE_MTRL = InitMtrl(BLUE, BLUE, BLUE, BLACK, 2.0f);
const D3DMATERIAL9 YELLOW_MTRL = InitMtrl(YELLOW, YELLOW, YELLOW, BLACK, 2.0f);
//
// 外接体对象
//
struct BoundingBox
{
BoundingBox();
bool isPointInside(D3DXVECTOR3& p);
D3DXVECTOR3 _min;
D3DXVECTOR3 _max;
};
struct BoundingSphere
{
BoundingSphere();
D3DXVECTOR3 _center;
float _radius;
};
//
// Constants
//
const float INFINITY = FLT_MAX;
const float EPSILON = 0.001f;
// Function references "desert.bmp" internally. This file must
// be in the working directory.
bool DrawBasicScene(
IDirect3DDevice9* device,// Pass in 0 for cleanup.
float scale); // uniform scale
struct Vertex
{
Vertex(){}
Vertex(float x, float y, float z,
float nx, float ny, float nz, float u, float v)
{
_x = x; _y = y; _z = z;
_nx = nx; _ny = ny; _nz = nz;
_u = u; _v = v;
}
float _x, _y, _z, _nx, _ny, _nz, _u, _v;
static const DWORD FVF;
};
//
// Randomness
//
// Desc: Return random float in [lowBound, highBound] interval.
float GetRandomFloat(float lowBound, float highBound);
// Desc: Returns a random vector in the bounds specified by min and max.
void GetRandomVector(
D3DXVECTOR3* out,
D3DXVECTOR3* min,
D3DXVECTOR3* max);
//
// Conversion
//
DWORD FtoDw(float f);
}
#endif
再来看一下,DirectX3D.cpp的代码:
#include "DirectX3D.h"
// vertex formats
const DWORD d3d::Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
bool d3d::InitD3D(
HINSTANCE hInstance,
int width, int height,
bool windowed,
D3DDEVTYPE deviceType,
IDirect3DDevice9** device)
{
//
// Create the main application window.
//
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)d3d::WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = "Direct3D9App";
if( !RegisterClass(&wc) )
{
::MessageBox(0, "RegisterClass() - FAILED", 0, 0);
return false;
}
HWND hwnd = 0;
hwnd = ::CreateWindow("Direct3D9App", "Direct3D9App",
WS_EX_TOPMOST,
0, 0, width, height,
0 /*parent hwnd*/, 0 /* menu */, hInstance, 0 /*extra*/);
if( !hwnd )
{
::MessageBox(0, "CreateWindow() - FAILED", 0, 0);
return false;
}
::ShowWindow(hwnd, SW_SHOW);
::UpdateWindow(hwnd);
//
// Init D3D:
//
//第一步
//要初始化IDirect3D 首先必须获取IDirect3D9的指针,使用一个专门的Direct3D函数就可以很容易做到
IDirect3D9 * _d3d9;
//这个对象的主要有两个用途:设备枚举以及创建IDirect3DDevice9类型的对象。设备枚举是指获取系统中可用的的每块图形卡的
//性能,显示模型,格式以及其他信息。这个函数调用失败会返回一个NULL指针。
if(NULL == (_d3d9 = Direct3DCreate9(D3D_SDK_VERSION))){
return FALSE;
}
//第二步
//创建一个代表主显卡的IDirect3DDevice9类型对象时,必须指定使用该对象进行顶点运算的类型。如果可以,我们希望使用硬件顶点运算
//但是由于并非所有的显卡都支持硬件顶点运算,我们必须首先检查图形卡是否支持该类型的运算。
//要进行检查,必须先根据主显卡的性能参数初始化一个IDirect3DDevice9类型的对象。我们使用如下方法来完成初始化:
/*
HRESULT IDirect3D9:GetDeviceCaps(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DCAPS9 * pCaps;
)
Adapter : 指定物理显卡的序号。
DeviceType:指定设备类(例如硬件设备(D3DDEVTYPE_HAL)或软件设备(D3DDEVTYPE_REF));
pCaps 返回已初始化的设备性能结构实例。
*/
D3DCAPS9 caps;
int vp = 0; //代表顶点如何操作
_d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);
if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT){
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
}
else
{
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
}
//第三步是填充D3DPRESENT_PARAMETER结构
//该结构用于指定所要创建的IDirect3DDevice9类型对象的一些特性,该结构定义如下:
/*
typedef struct _D3DPRESENT_PARAMETERS_{
UINT BackBufferWidth;
UINT BackBufferHeight;
UINT BackBufferFormat;
UINT BackBufferCount;
D3DMULTISAMPLE_TYPE MultiSampleType;
DWORD MultiSampleQuality;
D3DSWAPEFFECT SwapEffect;
HWND hDeviceWindow;
BOOL Windowed;
BOOL EnableAutoDepthStencil;
D3DFORMAT AutoDepthStencilFormat;
DWORD Flags;
UINT FullScreen_RefreshRateInHz;
UINT PresentationInterval;
};
*/
/*
BackBufferWidth: 后台缓存中表面的宽度,单位为像素。
BackBufferHeight:后台缓存中表面的高度,单位为像素。
BackBufferFormat:后台缓存的像素格式(如32位像素格式:D3DFMT_A8R8G8B8);
BackBufferCount: 所需使用的后台缓存的个数,通常指定为1,表明我们仅需要一个后台缓存。
MultiSampleType: 后台缓存所使用的多重采样类型。
MultiSampleQuality:多重采样的质量水平。
SwapEffect:D3DSWAPEFFECT 枚举类型的一个成员。该枚举类型指定了交换链中的缓存的页面置换方式。指定D3DSWAPEFFECT_DISCARD时效率最高。
hDeviceWindow:与设备相关的窗口句柄。指定了所要进行绘制的应用程序窗口。
Windowed:为true时表示窗口模式,false时为全屏模式
EnableAutoDepthStencil:设为true,则Direct3D自动创建并维护深度缓存或模板缓存。
AutoDepthStencilFormat:深度缓存或模板缓存的像素格式(例如,用24位表示深度并将8位保留供模板缓存使用,D3DFMT_D24S8).
Flags:一些附加的特性。可以指定为0,表示无标记,或D3DPRESENTFLAG集合中的一个成员,其中两个成员较常用。
D3DPRESENTFLAG_LOCKABLE_DEPTHBUFFER 指定为可锁定的后台缓存。注意,使用一个可锁定的后台缓存会降低性能。
D3DPRESENTFLAG_DISCARD_DEPTHBUFFER 指定当下一个后台缓存提交时,哪个深度或模块缓存将被丢弃。丢弃的意思是深度或模板缓存存储区
中的内容别丢弃或无效。这样可以提升性能。
FullScreen_RefreshRateInHz: 刷新频率,如果想使用默认的刷新频率,则可将该参数指定为D3DPRESENT_RATE_DEFAULT;
PresentationInterval:D3DPRESENT集合的一个成员,其中有两个比较常用。
D3DPRESENT_INTERVAL_IMMEDIATE 立即提交。
D3DPRESENT_INTERVAL_DEFAULT 由Direct3D来选择提交频率,通常该值等于刷新频率。
*/
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.BackBufferWidth = 800;
d3dpp.BackBufferHeight = 600;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.BackBufferCount = 1;
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dpp.MultiSampleQuality = 0;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hwnd;
d3dpp.Windowed = true;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz = 0;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
//第四步 创建IDirectDevice9类型的对象
/*
HRESULT IDirect3D9::CreateDevice(
UINT Adapter,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS *pPresentationParameters,
IDirect3DDevice9 ** ppReturnedDeviceInterface
);
Adapter:指定我们希望用已创建的IDirect3DDevice9对象代表哪块物理显卡。
DeviceType:指定需要使用的设备类型()如,硬件设备用D3DDEVTYPE_HAL,或D3DDEVTYPE_REF代表软件设备。
hFocusWindow:与设备相关的窗口句柄。通常情况下是指设备所要进行绘制的目标窗口。
为了达到预期的目的,该句柄与D3DPRESENT_PARAMETER结构的数据成员hDeviceWindow应为同一个句柄。
BehaviorFlags:该参数可为D3DCREATE_HARDWARE_VERTEXPROCESSING或D3DCREATE_SOFTWARE_VERTEXPROCESSING.
pPresentationParameters:一个已经完成初始化的D3DPRESENT_PARAMETERS类型的实例,该实例定义了设备的一些特性。
ppReturnedDeviceInterface:返回所创建的设备。
*/
if(FAILED(_d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
hwnd, vp, &d3dpp, device)))
return FALSE;
_d3d9->Release();
return TRUE;
}
int d3d::EnterMsgLoop( bool (*ptr_display)(float timeDelta) )
{
MSG msg;
::ZeroMemory(&msg, sizeof(MSG));
static float lastTime = (float)timeGetTime();
while(msg.message != WM_QUIT)
{
if(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
else
{
float currTime = (float)timeGetTime();
float timeDelta = (currTime - lastTime)*0.001f;
ptr_display(timeDelta);
lastTime = currTime;
}
}
return msg.wParam;
}
D3DLIGHT9 d3d::InitDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color)
{
D3DLIGHT9 light;
::ZeroMemory(&light, sizeof(light));
light.Type = D3DLIGHT_DIRECTIONAL;
light.Ambient = *color * 0.4f;
light.Diffuse = *color;
light.Specular = *color * 0.6f;
light.Direction = *direction;
return light;
}
D3DLIGHT9 d3d::InitPointLight(D3DXVECTOR3* position, D3DXCOLOR* color)
{
D3DLIGHT9 light;
::ZeroMemory(&light, sizeof(light));
light.Type = D3DLIGHT_POINT;
light.Ambient = *color * 0.4f;
light.Diffuse = *color;
light.Specular = *color * 0.6f;
light.Position = *position;
light.Range = 1000.0f;
light.Falloff = 1.0f;
light.Attenuation0 = 1.0f;
light.Attenuation1 = 0.0f;
light.Attenuation2 = 0.0f;
return light;
}
D3DLIGHT9 d3d::InitSpotLight(D3DXVECTOR3* position, D3DXVECTOR3* direction, D3DXCOLOR* color)
{
D3DLIGHT9 light;
::ZeroMemory(&light, sizeof(light));
light.Type = D3DLIGHT_SPOT;
light.Ambient = *color * 0.4f;
light.Diffuse = *color;
light.Specular = *color * 0.6f;
light.Position = *position;
light.Direction = *direction;
light.Range = 1000.0f;
light.Falloff = 1.0f;
light.Attenuation0 = 1.0f;
light.Attenuation1 = 0.0f;
light.Attenuation2 = 0.0f;
light.Theta = 0.5f;
light.Phi = 0.7f;
return light;
}
D3DMATERIAL9 d3d::InitMtrl(D3DXCOLOR a, D3DXCOLOR d, D3DXCOLOR s, D3DXCOLOR e, float p)
{
D3DMATERIAL9 mtrl;
mtrl.Ambient = a;
mtrl.Diffuse = d;
mtrl.Specular = s;
mtrl.Emissive = e;
mtrl.Power = p;
return mtrl;
}
d3d::BoundingBox::BoundingBox()
{
// infinite small
_min.x = d3d::INFINITY;
_min.y = d3d::INFINITY;
_min.z = d3d::INFINITY;
_max.x = -d3d::INFINITY;
_max.y = -d3d::INFINITY;
_max.z = -d3d::INFINITY;
}
bool d3d::BoundingBox::isPointInside(D3DXVECTOR3& p)
{
if( p.x >= _min.x && p.y >= _min.y && p.z >= _min.z &&
p.x <= _max.x && p.y <= _max.y && p.z <= _max.z )
{
return true;
}
else
{
return false;
}
}
d3d::BoundingSphere::BoundingSphere()
{
_radius = 0.0f;
}
bool d3d::DrawBasicScene(IDirect3DDevice9* device, float scale)
{
static IDirect3DVertexBuffer9* floor = 0;
static IDirect3DTexture9* tex = 0;
static ID3DXMesh* pillar = 0;
HRESULT hr = 0;
if( device == 0 )
{
if( floor && tex && pillar )
{
// they already exist, destroy them
d3d::Release<IDirect3DVertexBuffer9*>(floor);
d3d::Release<IDirect3DTexture9*>(tex);
d3d::Release<ID3DXMesh*>(pillar);
}
}
else if( !floor && !tex && !pillar )
{
// 创建顶点缓存,并设置6个顶点坐标,两个三角形组成一个矩形
device->CreateVertexBuffer(
6 * sizeof(Vertex),
0,
d3d::Vertex::FVF,
D3DPOOL_MANAGED,
&floor,
0);
Vertex* v = 0;
floor->Lock(0, 0, (void**)&v, 0);
v[0] = Vertex(-20.0f, -2.5f, -20.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
v[1] = Vertex(-20.0f, -2.5f, 20.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
v[2] = Vertex( 20.0f, -2.5f, 20.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
v[3] = Vertex(-20.0f, -2.5f, -20.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
v[4] = Vertex( 20.0f, -2.5f, 20.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
v[5] = Vertex( 20.0f, -2.5f, -20.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f);
floor->Unlock();
//创建一个圆柱体
D3DXCreateCylinder(device, 0.5f, 0.5f, 5.0f, 20, 20, &pillar, 0);
//创建纹理
D3DXCreateTextureFromFile(
device,
"desert.bmp",
&tex);
}
else
{
//
// 处理纹理
//
device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);
//启用光照
D3DXVECTOR3 dir(0.707f, -0.707f, 0.707f);
D3DXCOLOR col(1.0f, 1.0f, 1.0f, 1.0f);
D3DLIGHT9 light = d3d::InitDirectionalLight(&dir, &col);
device->SetLight(0, &light);
device->LightEnable(0, true);
device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
device->SetRenderState(D3DRS_SPECULARENABLE, true);
//
// Render
//
D3DXMATRIX T, R, P, S;
D3DXMatrixScaling(&S, scale, scale, scale);
// used to rotate cylinders to be parallel with world's y-axis
D3DXMatrixRotationX(&R, -D3DX_PI * 0.5f);
// 渲染地板
D3DXMatrixIdentity(&T);
T = T * S;
device->SetTransform(D3DTS_WORLD, &T);
device->SetMaterial(&d3d::WHITE_MTRL);
device->SetTexture(0, tex);
device->SetStreamSource(0, floor, 0, sizeof(Vertex));
device->SetFVF(Vertex::FVF);
device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
// 6个圆柱体
device->SetMaterial(&d3d::BLUE_MTRL);
device->SetTexture(0, 0);
for(int i = 0; i < 5; i++)
{
D3DXMatrixTranslation(&T, -5.0f, 0.0f, -15.0f + (i * 7.5f));
P = R * T * S;
device->SetTransform(D3DTS_WORLD, &P);
pillar->DrawSubset(0);
D3DXMatrixTranslation(&T, 5.0f, 0.0f, -15.0f + (i * 7.5f));
P = R * T * S;
device->SetTransform(D3DTS_WORLD, &P);
pillar->DrawSubset(0);
}
}
return true;
}
float d3d::GetRandomFloat(float lowBound, float highBound)
{
if( lowBound >= highBound ) // bad input
return lowBound;
// get random float in [0, 1] interval
float f = (rand() % 10000) * 0.0001f;
// return float in [lowBound, highBound] interval.
return (f * (highBound - lowBound)) + lowBound;
}
void d3d::GetRandomVector(
D3DXVECTOR3* out,
D3DXVECTOR3* min,
D3DXVECTOR3* max)
{
out->x = GetRandomFloat(min->x, max->x);
out->y = GetRandomFloat(min->y, max->y);
out->z = GetRandomFloat(min->z, max->z);
}
DWORD d3d::FtoDw(float f)
{
return *((DWORD*)&f);
}
再来看一下,camera.h的代码:
#ifndef __cameraH__
#define __cameraH__
#include <d3dx9.h>
class Camera
{
public:
enum CameraType { LANDOBJECT, AIRCRAFT };
Camera();
Camera(CameraType cameraType);
~Camera();
void strafe(float units); // left/right
void fly(float units); // up/down
void walk(float units); // forward/backward
void pitch(float angle); // rotate on right vector
void yaw(float angle); // rotate on up vector
void roll(float angle); // rotate on look vector
void getViewMatrix(D3DXMATRIX* V);
void setCameraType(CameraType cameraType);
void getPosition(D3DXVECTOR3* pos);
void setPosition(D3DXVECTOR3* pos);
void getRight(D3DXVECTOR3* right);
void getUp(D3DXVECTOR3* up);
void getLook(D3DXVECTOR3* look);
private:
CameraType _cameraType;
D3DXVECTOR3 _right;
D3DXVECTOR3 _up;
D3DXVECTOR3 _look;
D3DXVECTOR3 _pos;
};
#endif // __cameraH__
再来看一下,camera.cpp的代码:
//
//
// File: camera.cpp
//
// Author: Frank Luna (C) All Rights Reserved
//
// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0
//
// Desc: Defines a camera's position and orientation.
//
//
#include "camera.h"
/*
这里我们增加了一个Camera类的设计,首先使用了4个摄像机向量,右向量,上向量,观察向量以及位置向量来定义摄像机相对于
世界坐标系的位置和朝向。这些向量实质上为相对世界坐标系描述的摄像机定义了一个局部坐标系。由于右向量,上向量和观察向量定义了
摄像机在世界坐标系中的朝向,有时我们也将这三个向量统称为方向向量。方向向量必须是标准正交的。如果一个向量
集中的向量都彼此正交,并且均为1,则称该向量是标准正交的。前面,我们提到,标准正交矩阵的一个重要性质是其逆矩阵与其转置矩阵相等。
用上述4个向量来描述摄像机,我们可对摄像机实施6种变换:
绕向量right的旋转(俯仰,pitch)
绕向量up的旋转(偏航,yaw)
绕向量look的旋转(滚动,roll)
沿向量right方向的扫视(strafe)
沿向量up方向的升降(fly)
沿向量look的平动。
*/
/*
平移的实现:
将摄像机的位置向量平移到原点可通过将其与向量-p做向量加法轻松实现,因为p-p=0.
旋转:
要想使摄像机各向量与世界坐标系各轴重合的工作量大一些,我们需要一个3x3的旋转矩阵A以使向量right,up和look分别与世界坐标系的
x,y,z轴重合。
绕任意轴的旋转:
实现摄像机的旋转方法时,我们应使能够绕任意轴进行旋转,D3DX库提供了如下函数恰好具备该功能。
D3DXMATRIX *D3DXMatrixRotationAxis(
D3DXMATRIX *pOut,
CONST D3DXVECTOR *pV,
FLOAT Angle
);
例如,若我们想绕由向量(0.707f,0.707f,0.0f)所确定的轴旋转π/2,可这样做:
D3DXMATRIX R;
D3DXVECTOR3 axis(0.707f,0.707f,0.0f)
D3DXMatrixRotationAxis(&R,&axis,D3DX_PI/2);
*/
Camera::Camera()
{
_cameraType = AIRCRAFT;
_pos = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
_right = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
_up = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
_look = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
}
Camera::Camera(CameraType cameraType)
{
_cameraType = cameraType;
_pos = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
_right = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
_up = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
_look = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
}
Camera::~Camera()
{
}
void Camera::getPosition(D3DXVECTOR3* pos)
{
*pos = _pos;
}
void Camera::setPosition(D3DXVECTOR3* pos)
{
_pos = *pos;
}
void Camera::getRight(D3DXVECTOR3* right)
{
*right = _right;
}
void Camera::getUp(D3DXVECTOR3* up)
{
*up = _up;
}
void Camera::getLook(D3DXVECTOR3* look)
{
*look = _look;
}
void Camera::walk(float units)
{
// move only on xz plane for land object
if( _cameraType == LANDOBJECT )
_pos += D3DXVECTOR3(_look.x, 0.0f, _look.z) * units;
if( _cameraType == AIRCRAFT )
_pos += _look * units;
}
void Camera::strafe(float units)
{
// move only on xz plane for land object
if( _cameraType == LANDOBJECT )
_pos += D3DXVECTOR3(_right.x, 0.0f, _right.z) * units;
if( _cameraType == AIRCRAFT )
_pos += _right * units;
}
void Camera::fly(float units)
{
// move only on y-axis for land object
if( _cameraType == LANDOBJECT )
_pos.y += units;
if( _cameraType == AIRCRAFT )
_pos += _up * units;
}
void Camera::pitch(float angle)
{
D3DXMATRIX T;
D3DXMatrixRotationAxis(&T, &_right, angle);
// rotate _up and _look around _right vector
D3DXVec3TransformCoord(&_up,&_up, &T);
D3DXVec3TransformCoord(&_look,&_look, &T);
}
void Camera::yaw(float angle)
{
D3DXMATRIX T;
// rotate around world y (0, 1, 0) always for land object
if( _cameraType == LANDOBJECT )
D3DXMatrixRotationY(&T, angle);
// rotate around own up vector for aircraft
if( _cameraType == AIRCRAFT )
D3DXMatrixRotationAxis(&T, &_up, angle);
// rotate _right and _look around _up or y-axis
D3DXVec3TransformCoord(&_right,&_right, &T);
D3DXVec3TransformCoord(&_look,&_look, &T);
}
void Camera::roll(float angle)
{
// only roll for aircraft type
if( _cameraType == AIRCRAFT )
{
D3DXMATRIX T;
D3DXMatrixRotationAxis(&T, &_look, angle);
// rotate _up and _right around _look vector
D3DXVec3TransformCoord(&_right,&_right, &T);
D3DXVec3TransformCoord(&_up,&_up, &T);
}
}
void Camera::getViewMatrix(D3DXMATRIX* V)
{
// Keep camera's axes orthogonal to eachother
D3DXVec3Normalize(&_look, &_look);
D3DXVec3Cross(&_up, &_look, &_right);
D3DXVec3Normalize(&_up, &_up);
D3DXVec3Cross(&_right, &_up, &_look);
D3DXVec3Normalize(&_right, &_right);
// Build the view matrix:
float x = -D3DXVec3Dot(&_right, &_pos);
float y = -D3DXVec3Dot(&_up, &_pos);
float z = -D3DXVec3Dot(&_look, &_pos);
(*V)(0,0) = _right.x; (*V)(0, 1) = _up.x; (*V)(0, 2) = _look.x; (*V)(0, 3) = 0.0f;
(*V)(1,0) = _right.y; (*V)(1, 1) = _up.y; (*V)(1, 2) = _look.y; (*V)(1, 3) = 0.0f;
(*V)(2,0) = _right.z; (*V)(2, 1) = _up.z; (*V)(2, 2) = _look.z; (*V)(2, 3) = 0.0f;
(*V)(3,0) = x; (*V)(3, 1) = y; (*V)(3, 2) = z; (*V)(3, 3) = 1.0f;
}
void Camera::setCameraType(CameraType cameraType)
{
_cameraType = cameraType;
}
最后看一下 wmain.cpp的代码:
#include "DirectX3D.h"
#include "camera.h"
#include "pSystem.h"
#include <cstdlib>
#include <ctime>
//
// Globals
//
IDirect3DDevice9* Device = 0;
const int Width = 640;
const int Height = 480;
//psys::PSystem* Sno = 0;
//psys::PSystem* Exp = 0;
psys::PSystem* Gun = 0;
//
// Framework Functions
//
Camera TheCamera(Camera::AIRCRAFT);
bool Setup()
{
// seed random number generator
srand((unsigned int)time(0));
//
// Create Snow System.
//
/*
d3d::BoundingBox boundingBox;
boundingBox._min = D3DXVECTOR3(-10.0f, -10.0f, -10.0f);
boundingBox._max = D3DXVECTOR3( 10.0f, 10.0f, 10.0f);
Sno = new psys::Snow(&boundingBox, 5000);
Sno->init(Device, "snowflake.dds");
d3d::DrawBasicScene(Device, 0.0f);
*/
/*
D3DXVECTOR3 origin(0.0f, 10.0f, 50.0f);
Exp = new psys::Firework(&origin, 6000);
Exp->init(Device, "flare.bmp");
d3d::DrawBasicScene(Device, 1.0f);
*/
Gun = new psys::ParticleGun( &TheCamera );
Gun->init(Device, "flare_alpha.dds");
d3d::DrawBasicScene(Device, 1.0f);
//
// Set projection matrix.
//
D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(
&proj,
D3DX_PI * 0.25f, // 45 - degree
(float)Width / (float)Height,
1.0f,
5000.0f);
Device->SetTransform(D3DTS_PROJECTION, &proj);
return true;
}
//将之前分配的内存进行清理,也就是顶点缓存和索引缓存
void Cleanup()
{
// d3d::Delete<psys::PSystem*>( Sno );
d3d::Delete<psys::PSystem*>( Gun );
// d3d::DrawBasicScene(0, 1.0f);
d3d::DrawBasicScene(0, 0.0f);
}
bool Display(float timeDelta)
{
if( Device )
{
//
// 通过用户输入修改摄像机的位置
//
if( ::GetAsyncKeyState('W') & 0x8000f )
TheCamera.walk(4.0f * timeDelta);
if( ::GetAsyncKeyState('S') & 0x8000f )
TheCamera.walk(-4.0f * timeDelta);
if( ::GetAsyncKeyState('A') & 0x8000f )
TheCamera.strafe(-4.0f * timeDelta);
if( ::GetAsyncKeyState('D') & 0x8000f )
TheCamera.strafe(4.0f * timeDelta);
if( ::GetAsyncKeyState('R') & 0x8000f )
TheCamera.fly(4.0f * timeDelta);
if( ::GetAsyncKeyState('F') & 0x8000f )
TheCamera.fly(-4.0f * timeDelta);
if( ::GetAsyncKeyState(VK_UP) & 0x8000f )
TheCamera.pitch(1.0f * timeDelta);
if( ::GetAsyncKeyState(VK_DOWN) & 0x8000f )
TheCamera.pitch(-1.0f * timeDelta);
if( ::GetAsyncKeyState(VK_LEFT) & 0x8000f )
TheCamera.yaw(-1.0f * timeDelta);
if( ::GetAsyncKeyState(VK_RIGHT) & 0x8000f )
TheCamera.yaw(1.0f * timeDelta);
if( ::GetAsyncKeyState('N') & 0x8000f )
TheCamera.roll(1.0f * timeDelta);
if( ::GetAsyncKeyState('M') & 0x8000f )
TheCamera.roll(-1.0f * timeDelta);
// 这里通过用户的输入处理,不断的设置取景矩阵并进行设置。
//
D3DXMATRIX V;
TheCamera.getViewMatrix(&V);
Device->SetTransform(D3DTS_VIEW, &V);
// Sno->update(timeDelta);
/*
Exp->update(timeDelta);
if( Exp->isDead() )
Exp->reset();
*/
Gun->update(timeDelta);
//
// Render
//
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0);
Device->BeginScene();
D3DXMATRIX I;
D3DXMatrixIdentity(&I);
Device->SetTransform(D3DTS_WORLD, &I);
d3d::DrawBasicScene(Device, 1.0f);
// order important, render snow last.
Device->SetTransform(D3DTS_WORLD, &I);
// Sno->render();
// Exp->render();
Gun->render();
Device->EndScene();
Device->Present(0, 0, 0, 0);
}
return true;
}
//
// WndProc
//
LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch( msg )
{
case WM_DESTROY:
::PostQuitMessage(0);
break;
case WM_KEYDOWN:
if( wParam == VK_ESCAPE )
::DestroyWindow(hwnd);
if( wParam == VK_SPACE )
Gun->addParticle();
break;
}
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
//
// WinMain
//
int WINAPI WinMain(HINSTANCE hinstance,
HINSTANCE prevInstance,
PSTR cmdLine,
int showCmd)
{
if(!d3d::InitD3D(hinstance,
640, 480, true, D3DDEVTYPE_HAL, &Device))
{
::MessageBox(0, "InitD3D() - FAILED", 0, 0);
return 0;
}
if(!Setup())
{
::MessageBox(0, "Setup() - FAILED", 0, 0);
return 0;
}
d3d::EnterMsgLoop( Display );
Cleanup();
Device->Release();
return 0;
}
bool ComputeBoundingSphere(ID3DXMesh* mesh, d3d::BoundingSphere* sphere)
{
HRESULT hr = 0;
BYTE* v = 0;
mesh->LockVertexBuffer(0, (void**)&v);//得到网格的顶点缓存
/*
我们来看一下D3DX库提供的用来计算一个网格的外接球的函数
HRESULT D3DXComputeBoundingSphere(
__in const D3DXVECTOR3 *pFirstPosition,
__in DWORD NumVertices,
__in DWORD dwStride,
__in D3DXVECTOR3 *pCenter,
__in FLOAT *pRadius
);
pFirstPosition:指向顶点数组(该数组的每个元素都描述了对应顶点)中第一个顶点的位置向量的指针。我们可以通过网格对象得到顶点缓存的指针,最后
可转化为该值。
NumVertices:该网格中顶点数组中顶点的个数。可通过网格mesh->GetNumVertices()得到
dsStride:每个顶点的大小,单位为字节。该值很重要,因为一种顶点结构可能包含了许多该函数所不需要的附加信息,如法向量和纹理坐标等。这样该函数
就需要知道应跳过多少字节才能找到下一个顶点的位置。mesh->GetFVF()可以返回一个描述了顶点格式的DWORD类型值。D3DXGetFVFVertexSize这个
函数可以得到该顶点占用多少个字节。
pCenter:返回的外接球的球心位置。
pRadius: 返回外接球的半径。
*/
hr = D3DXComputeBoundingSphere(
(D3DXVECTOR3*)v,
mesh->GetNumVertices(),
D3DXGetFVFVertexSize(mesh->GetFVF()),
&sphere->_center,
&sphere->_radius);
mesh->UnlockVertexBuffer();
if( FAILED(hr) )
return false;
return true;
}
bool ComputeBoundingBox(ID3DXMesh* mesh, d3d::BoundingBox* box)
{
HRESULT hr = 0;
BYTE* v = 0;
mesh->LockVertexBuffer(0, (void**)&v);
//这里是D3DX中计算一个网格外接体的函数,跟外接球的函数很类似,只是最后两个参数,返回外接体的最大点和最小点。
hr = D3DXComputeBoundingBox(
(D3DXVECTOR3*)v,
mesh->GetNumVertices(),
D3DXGetFVFVertexSize(mesh->GetFVF()),
&box->_min,
&box->_max);
mesh->UnlockVertexBuffer();
if( FAILED(hr) )
return false;
return true;
}
最后来看一下程序的截图:
每日总结:
点精灵是一种灵活而方便的显示粒子的方式。它们的从尺寸可调,还能进行纹理映射。更重要的是,我们只用一个单个顶点就能描述点精灵。
粒子系统维护了一个粒子集,并负责粒子的创建,销毁,更新和显示。
除这3个粒子系统外,我们还可实现烟雾,火箭轨迹,喷泉,火焰,光照,爆炸和雨等系统。