关于RenderTarget类的具体信息,还是放在后面比较好,因为我们还没有设计好shaders的支持、多重纹理的支持,基本场景管理(中的结点)等等。
先来考虑简单的场景管理,最简单的应该是用Axis-Aligned-box(包围体盒),具体的算法就不说了(当然好像网上很少有讲的很清楚的,还是写完了octree的场景管理之后在总结一下),我们要有一用一个box(8个顶点)来约束我们要渲染的一个物体(或者物体和包围体的集合),简单一点,我们先给每个RenderTarget加上一个AxisAlignedBox,在变换的时候,我们计算这样一个box,然后进行简单的透视体剪裁(在不在视锥体内),再然后就是进行可视性剔除(看不看得见)。我们这里先不涉及剪裁的算法。
设计基本的AxisAlignedBox的接口:
namespace UHEngine
{
class _UHE_Export AxisAlignedBox
{
public:
inline AxisAlignedBox()
{
this->m_bInit = false;
}
inline AxisAlignedBox( Vector3 & minZpoint, Vector3 & maxZpoint )
{
this->m_bInit = false;
Init( minZpoint, maxZpoint );
}
virtual ~AxisAlignedBox();
/// return true if the box is not initialized.
inline bool IsNull(){ return !m_bInit; }
/// fresh vertex data of the corners.
inline void Update();
/// get the corner with the max z val.
inline const Vector3 & GenMaxZPoint(){ return m_maxZpoint; }
/// get the corner with the min z val.
inline const Vector3 & GenMinZPoint(){ return m_minZpoint; }
/// update maxZpoint, after changes, usually internal uses.
inline void GenMaxZPoint(UHEngine::Vector3 &point);
/// update minZpoint, after changes, usually internal uses.
inline void GenMinZPoint(UHEngine::Vector3 &point);
/// do NOT use GenMaxZPoint after this, it already did that
inline void SetMaxZPoint(Vector3 &point);
/// do NOT use GenMinZPoint after this, it already did that
inline void SetMinZPoint(UHEngine::Vector3 &point);
/// create the box, and init all 8 corners
inline void Init(UHEngine::Vector3 &minZpoint, UHEngine::Vector3 &maxZpoint);
/// make the box bigger with a point
inline void MergeBox(UHEngine::Vector3 &point);
/// make the box bigger with another box
inline void MergeBox(UHEngine::AxisAlignedBox &box);
/// get maxZpoint
inline Vector3 & GetMaxZPoint(){ return m_maxZpoint; }
/// get minxZpoint
inline Vector3 & GetMinZPoint(){ return m_minZpoint; }
private:
bool m_bInit;
Vector3 m_maxZpoint;
Vector3 m_minZpoint;
Vector3 m_corners[8];
};
}
在RenderTarget中,我们可以将变换后的点传给AxisAlignedBox的接口MergeBox(Vector3& point );以得到最后的包围体。(场景具体的实现我还没有完成,里面有很多的细节还没有想清楚,网上这个方面的资料比较少,要么就是只讲到原理,所以我还没有找到很有效的办法,来实现RenderTarget生成AxisAlignedBox;还有一个问题是,如何判断一个物理是否可见,要用到Ray来判断,那么Ray的粒度要达到什么程度呢?如果一个Box看不见那么它里面的子树结点也都不可见,但是,如果一个Box把另一个Box遮住,不一定后面的一个 Box就看不见了,这些都是比较麻烦的问题,大家可以想一下自己的实现方法,然后再去看一下网上或者书上是怎么写的)
关于多重纹理,也是很难管理的东西,还涉及到很多的参数要设置,当然最起码的,我们要定义一些基本的类型,我们把一次pass里面用到的材质,多重纹理(最多8张)看成一个Skin(Skin是卡马克比较喜欢的术语,也有些引擎把这看成材质Material,比如torque)。定义一个枚举类,表示 multi-texture的类型:
typedef enum _multiTexInfo
{
BLENDING_ORIGIN = 0,
BLENDING_DARK_MAPPING,
BLENDING_ANIMATED_DARK_MAPPING,
BLENDING_MAT,
BLENDING_DARKMAP_BLENDING_MAT,
BLENDING_GLOW_MAPPING,
BLENDING_DETAIL_MAPPING,
BLENDING_MODULATE_ALPHA,
BLENDING_FRAME_BUFFER,
BLENDING_BUMP,
BLENDING_HEIGHTMAP
}MultiTexInfo;
在后面,渲染器还需要根据不同的类型来设置参数。
Skin类要包含八张纹理的信息,一张材质的信息(也许后面还需要加入纹理采样的参数信息):
namespace UHEngine
{
class _UHE_Export Skin
{
public:
typedef struct _texInfo
{
MultiTexInfo multiTexInfo;
UHETEXTURE *pTex;
} TEXINFO;
enum TEX_CONST
{
MAX_NUM_TEX_PASS = 8, // max num of tex in one pass
};
public:
Skin( TextureMng *pTexMng, MaterialMng *pMatMng );
virtual ~Skin();
TextureMng * GetTexMng();
MaterialMng * GetMatMng();
/// set num of multi-tex in a pass
void SetNumTex( int numTex );
/// get num of multi-tex in a pass
int GetNumTex();
/*
set tex info and create it in TextureMng,
feel free to use this interface
without thingking whether the tex is already created.
*/
void SetTexInfo( char *chFileName,
int texIndex,
MultiTexInfo multiTexInfo = BLENDING_ORIGIN,
bool bAlpha = false,
float fAlpha = 1.0f );
/// set mat value of the skin
/// FIX ME!!! no check for equal mat value
void SetMatInfo( const UHEMATERIAL *pMat );
/// get tex by index in a pass
UHETEXTURE * GetTexInMng(int texIndex);
/// get mat value
UHEMATERIAL * GetMatInMng();
protected:
TextureMng *m_pTexMng;
MaterialMng *m_pMatMng;
UHEMATERIAL m_Mat;
int m_numTex;
TEXINFO m_TexInfoGroup[8];
};
}
简单的实现一下:
//File: UHESkin.cpp
#include "UHESkin.h"
namespace UHEngine
{
Skin::Skin(UHEngine::TextureMng *pTexMng, UHEngine::MaterialMng *pMatMng)
: m_pTexMng(pTexMng), m_pMatMng(pMatMng)
{
for( int i = 0; i < MAX_NUM_TEX_PASS; i++ )
{
m_TexInfoGroup[i].pTex = NULL;
m_TexInfoGroup[i].multiTexInfo = BLENDING_ORIGIN;
/*m_TexInfoGroup[i].texUsage = TEXUSE_ORIGIN;
m_TexInfoGroup[i].name = "";*/
}
}
Skin::~Skin()
{
}
inline MaterialMng * Skin::GetMatMng()
{
return this->m_pMatMng;
}
inline TextureMng * Skin::GetTexMng()
{
return this->m_pTexMng;
}
inline void Skin::SetNumTex(int numTex)
{
numTex = numTex > MAX_NUM_TEX_PASS ? MAX_NUM_TEX_PASS : numTex;
this->m_numTex = numTex > 0 ? numTex : 0;
}
inline int Skin::GetNumTex()
{
return this->m_numTex;
}
void Skin::SetMatInfo(const UHEngine::UHEMATERIAL *pMat)
{
if( pMat != NULL )
{
memcpy( &this->m_Mat, pMat, sizeof( UHEMATERIAL ) );
}
}
void Skin::SetTexInfo(char *chFileName,
int texIndex,
MultiTexInfo multiTexInfo,
bool bAlpha,
float fAlpha)
{
if( texIndex > 0 && texIndex < MAX_NUM_TEX_PASS )
{
/*strcpy( this->m_TexInfoGroup[texIndex].name, chFileName );
this->m_TexInfoGroup[texIndex].texUsage = texUsage;*/
this->m_pTexMng->Create( TEXCOORD_2D, TEXUSE_ORIGIN, chFileName, bAlpha, fAlpha );
this->m_TexInfoGroup[texIndex].pTex = m_pTexMng->GetTexByName( TEXCOORD_2D, chFileName );
this->m_TexInfoGroup[texIndex].multiTexInfo = multiTexInfo;
}
}
UHETEXTURE * Skin::GetTexInMng(int texIndex)
{
if( texIndex > 0 && texIndex < MAX_NUM_TEX_PASS )
{
return this->m_TexInfoGroup[texIndex].pTex;
}
return NULL;
}
UHEMATERIAL * Skin::GetMatInMng()
{
return &m_Mat;
}
}
(今天就写到这里。再往后面,有很多的东西,我还没有实现,或者实现得很不好,或者还有一些地方想改进,所以后面的文章会以每个星期一两篇的速度更新,不会再快了。欢迎大家的建议!还是多多交流,just for FUN 的原则。)