思路:
1,使用VBO和IBO减少主机内存和显存之间的数据传递
2,只绘制摄像机可视范围内的模型
具体实现:
1,加载模型时创建每个模型的VBO和IBO,初始化每个模型的AABB包围盒
/**
* 加载模型数据
*/
bool load(const char* fileName)
{
/// 初始化shader
_shader.initialize();
/// 读文件
size_t length = 0;
char* xmlData = readFile(fileName,length);
if (xmlData == 0)
{
return false;
}
try
{
rapidxml::xml_document<> doc;
rapidxml::xml_node<>* rootNode = 0;
rapidxml::xml_node<>* meshNode = 0;
rapidxml::xml_node<>* faceRoot = 0;
rapidxml::xml_node<>* vertRoot = 0;
doc.parse<0>(xmlData);
rootNode = doc.first_node("MeshRoot");
if (rootNode == 0)
{
return false;
}
meshNode = rootNode->first_node();
if (meshNode == 0)
{
return false;
}
/// 解析面索引
faceRoot = meshNode->first_node("faceIndex");
parseFaceIndex(faceRoot);
vertRoot = meshNode->first_node("vertex");
/// 解析顶点数据
parseVertex(vertRoot);
delete [] xmlData;
return true;
}
catch (...)
{
return false;
}
}
/**
* 解析面信息--IBO
*/
void parseFaceIndex(rapidxml::xml_node<>* faceRoot)
{
std::vector<short> arIndex;
rapidxml::xml_node<>* pFaceIndex = faceRoot->first_node();
for ( ; pFaceIndex ; pFaceIndex = pFaceIndex->next_sibling())
{
const char* pzFace = pFaceIndex->value();
int a,b,c;
sscanf(pzFace,"%d %d %d",&a,&b,&c);
arIndex.push_back(short(a));
arIndex.push_back(short(b));
arIndex.push_back(short(c));
}
_indexSize = arIndex.size();
glGenBuffers(1,&_indexBufferId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,_indexBufferId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,arIndex.size() * sizeof(short),&arIndex.front(),GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
}
/**
* 解析顶点信息--VBO
*/
void parseVertex(rapidxml::xml_node<>* vertRoot)
{
std::vector<Vertex> arVert;
rapidxml::xml_attribute<>* attrSize = vertRoot->first_attribute("size");
rapidxml::xml_node<>* vertNode = vertRoot->first_node();
for ( ; vertNode ; vertNode = vertNode->next_sibling())
{
const char* pzVert = vertNode->value();
Vertex vertex;
sscanf(pzVert,"%f %f %f %f %f %f %f %f",&vertex.x,&vertex.y,&vertex.z,&vertex.u,&vertex.v,&vertex.nx,&vertex.ny,&vertex.nz);
arVert.push_back(vertex);
}
/// 初始化包围盒子
_aabb.setExtents(real3(FLT_MAX,FLT_MAX,FLT_MAX),real3(-FLT_MAX,-FLT_MAX,-FLT_MAX));
/// 得到实际的大小
for (size_t i = 0 ;i < arVert.size() ; ++ i )
{
_aabb._minimum.x = min(_aabb._minimum.x,arVert[i].x);
_aabb._minimum.y = min(_aabb._minimum.y,arVert[i].y);
_aabb._minimum.z = min(_aabb._minimum.z,arVert[i].z);
_aabb._maximum.x = max(_aabb._maximum.x,arVert[i].x);
_aabb._maximum.y = max(_aabb._maximum.y,arVert[i].y);
_aabb._maximum.z = max(_aabb._maximum.z,arVert[i].z);
}
glGenBuffers(1,&_vertexBufferId);
glBindBuffer(GL_ARRAY_BUFFER,_vertexBufferId);
glBufferData(GL_ARRAY_BUFFER,arVert.size() * sizeof(Vertex),&arVert.front(),GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER,0);
}
2,模型绘制时判断每个模型的AABB包围盒是否在摄像机的可视范围内,若不在则不绘制.
virtual void render(const CELLFrameEvent& evt)
{
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glViewport(0,0,_winWidth,_winHeight);
//! 更新定时器
_timeStamp.update();
_camera3rd.setTarget(_role._pos);
_camera3rd.update();
CELL::matrix4 matView = _camera3rd.getView();
CELL::matrix4 matProj = _camera3rd.getProject();
CELL::matrix4 matMVP = matProj * matView;
CELL::matrix4 temp = matMVP.transpose();
//根据MVP矩阵产生一个摄像机的可视范围-也就是摄像机的视椎体
_frustum.loadFrustum(temp);
float gSize = 100;
float gPos = -5;
float rept = 100;
Vertex grounds[] =
{
{ -gSize, gPos,-gSize,0.0f, 0.0f,1.0f, 1.0f, 1.0f,1.0f },
{ gSize, gPos,-gSize,rept, 0.0f,1.0f, 1.0f, 1.0f,1.0f },
{ gSize, gPos, gSize,rept, rept,1.0f, 1.0f, 1.0f,1.0f },
{ -gSize, gPos,-gSize,0.0f, 0.0f,1.0f, 1.0f, 1.0f,1.0f },
{ gSize, gPos, gSize,rept, rept,1.0f, 1.0f, 1.0f,1.0f },
{ -gSize, gPos, gSize,0.0f, rept,1.0f, 1.0f, 1.0f,1.0f },
};
renderGround();
glBindTexture(GL_TEXTURE_2D,_textureId);
//! 绘制角色
size_t hasRender = 0;
_role.render(evt._sinceLastFrame,_camera3rd,_shader);
//遍历要绘制的模型
for (size_t i = 0 ;i < _arNodes.size() ; ++ i )
{
CELLNode& node = _arNodes[i];
//改变每个模型的位置坐标
float3 pos(evt._sinceLastFrame * 1,0,evt._sinceLastFrame * 1);
node.setPosition(node.getPosition() + pos);
node.update(false);
//判断每个模型的AABB包围盒是否在摄像机的可视范围内,若不在,则不绘制,节省系统资源
if (!_frustum.cubeInFrustum(node._aabb._minimum,node._aabb._maximum))
{
continue;
}
++hasRender;
node.doRender(evt,_camera3rd,node._local);
}
char szBuf[128];
sprintf_s(szBuf,"共%d个对象,绘制了 %d ",_arNodes.size(),hasRender);
HWND hWnd = GetActiveWindow();
if (hWnd)
{
SetWindowTextA(hWnd,szBuf);
}
}
3,在渲染模型时,首先根据模型的MVP矩阵来获取当前摄像机的可视范围
void loadFrustum(const tmat4x4<T> &mvp)
{
const T* dataPtr = mvp.data();
_planes[FRUSTUM_LEFT ] = Plane<T>(dataPtr[12] - dataPtr[0], dataPtr[13] - dataPtr[1], dataPtr[14] - dataPtr[2], dataPtr[15] - dataPtr[3]);
_planes[FRUSTUM_RIGHT ] = Plane<T>(dataPtr[12] + dataPtr[0], dataPtr[13] + dataPtr[1], dataPtr[14] + dataPtr[2], dataPtr[15] + dataPtr[3]);
_planes[FRUSTUM_TOP ] = Plane<T>(dataPtr[12] - dataPtr[4], dataPtr[13] - dataPtr[5], dataPtr[14] - dataPtr[6], dataPtr[15] - dataPtr[7]);
_planes[FRUSTUM_BOTTOM] = Plane<T>(dataPtr[12] + dataPtr[4], dataPtr[13] + dataPtr[5], dataPtr[14] + dataPtr[6], dataPtr[15] + dataPtr[7]);
_planes[FRUSTUM_FAR ] = Plane<T>(dataPtr[12] - dataPtr[8], dataPtr[13] - dataPtr[9], dataPtr[14] - dataPtr[10], dataPtr[15] - dataPtr[11]);
_planes[FRUSTUM_NEAR ] = Plane<T>(dataPtr[12] + dataPtr[8], dataPtr[13] + dataPtr[9], dataPtr[14] + dataPtr[10], dataPtr[15] + dataPtr[11]);
}
4,根据每个模型的AABB包围盒判断其是否在摄像机可视范围内
//判断点是否在视椎体中
bool pointInFrustum(const tvec3<T> &pos) const
{
for (int i = 0; i < 6; i++)
{
if (_planes[i].distance(pos) <= 0)
return false;
}
return true;
}
//判断球是否在视椎体中
bool sphereInFrustum(const tvec3<T> &pos, const float radius) const
{
for (int i = 0; i < 6; i++)
{
if (_planes[i].distance(pos) <= -radius)
return false;
}
return true;
}
//判断立方体是否在视椎体中
bool cubeInFrustum(tvec3<T> vmIn,tvec3<T> vMax) const
{
return cubeInFrustum(vmIn.x,vMax.x,vmIn.y,vMax.y,vmIn.z,vMax.z);
}
bool cubeInFrustum(T minX,T maxX,T minY,T maxY,T minZ,T maxZ) const
{
for (int i = 0; i < 6; i++)
{
if (_planes[i].distance(tvec3<T>(minX, minY, minZ)) > 0) continue;
if (_planes[i].distance(tvec3<T>(minX, minY, maxZ)) > 0) continue;
if (_planes[i].distance(tvec3<T>(minX, maxY, minZ)) > 0) continue;
if (_planes[i].distance(tvec3<T>(minX, maxY, maxZ)) > 0) continue;
if (_planes[i].distance(tvec3<T>(maxX, minY, minZ)) > 0) continue;
if (_planes[i].distance(tvec3<T>(maxX, minY, maxZ)) > 0) continue;
if (_planes[i].distance(tvec3<T>(maxX, maxY, minZ)) > 0) continue;
if (_planes[i].distance(tvec3<T>(maxX, maxY, maxZ)) > 0) continue;
return false;
}
return true;
}