大规模模型绘制的优化思路





思路:

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;
        }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值