一个使用 OpenGL 和 MFC 的小型 VRML 查看器






4.96/5 (53投票s)
1999年12月1日
2分钟阅读

439251

16835
图 1. 您可以使用 OpenGL 和 MFC 轻松显示基于 wrl 的地形。
此贡献是一个小的 MFC 示例,用于学习如何
- 显示 VRML 文件
- 使用 OpenGL 显示列表
- 在平面或平滑着色的网格上叠加线框
- 平滑细分 3D 三角形网格(来自 Charles Loop)
- 实现鼠标交互(旋转和平移)
- 从 vrml 2.0 文件构建场景图(手工制作,非基于词法分析)
显示列表
使用显示列表是加速渲染应用程序的好方法。显示列表使用标准的 OpenGL 调用编译一系列 gl 绘图,然后可以使用简单的列表 ID 号稍后调用。因此,生成的列表以预编译模式驻留在主内存中,从而大大加快渲染循环的速度。构建显示列表的一个好的命令序列可能是int list = ::glGenLists(1); // ask for a free id number ::glNewList(list,GL_COMPILE_AND_EXECUTE); ::glBegin(GL_TRIANGLES); // std gl calls here... fill vertices, normals, colors ::glEnd(); ::glEndList();使用显示列表的一个好的命令序列可能是
if(::glIsList(list) == GL_TRUE)
::glCallList(m_ListOpenGL);
该示例从 vrml 2.0 文件(仅通过 3D Studio Max 导出)构建场景图,然后使用显示列表。每个 3D 网格包含一个列表编号,并在构建列表时使用 glCallList 命令代替标准的 glBegin(GL_TRIANGLES) 命令。一个标志 m_Modified 允许在修改网格时重建列表。//******************************************** // The 3D mesh class definition //******************************************** class CMesh2d : public CObject3d { private : // Std datas CArray<CVertex3d> m_ArrayVertex; CArray<CFace3d> m_ArrayFace; // OpenGL-specific unsigned int m_ListOpenGL; BOOL m_ListDone; BOOL m_Modified; .../... public : BOOL glDraw(); .../... } //******************************************** // Mesh drawing //******************************************** BOOL CMesh2d::glDraw() { // Build list at first if(!m_ListDone || m_Modified) glBuildList(); // Is the list valid ? if(::glIsList(m_ListOpenGL)==GL_TRUE) { ::glCallList(m_ListOpenGL); return TRUE; } return FALSE; }
叠加线框
有时您希望查看叠加在平面或平滑着色的网格上的线框。一种好的方法是使用 glPolygonOffset 命令,该命令创建 z 缓冲区偏移。以下代码显示了文档的 RenderScene 函数,如果总结,则需要两个渲染过程,第一个使用光照平面模式渲染网格,第二个关闭光照,设置线模式,设置 z 缓冲区偏移,然后再次绘制网格。//*********************************************** // RenderScene //*********************************************** void CMeshDoc::RenderScene() { // Main drawing m_SceneGraph.glDraw(); // Add wireframe (no light, and line mode) if(m_AddWireframe) { // Set state ::glDisable(GL_LIGHTING); ::glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); ::glEnable(GL_POLYGON_OFFSET_LINE); ::glPolygonOffset(m_PolygonOffset,-1.0f); // Draw again... m_SceneGraph.glDraw(TYPE_MESh2D); // Restore light and mode ::glDisable(GL_POLYGON_OFFSET_LINE); ::glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); ::glEnable(GL_LIGHTING); } ::glFlush(); }
平滑细分
从给定的 3D 网格,我们如何改进平滑模型的几何外观?查尔斯·卢普平滑细分在这里帮助我们。每个三角形被分成四个三角形(见图 3),并且一个过滤函数允许网格被平滑。该命令在文档中易于使用,我让您在网格的代码中发现细节。图 3. 该方法使用的 1 到 4 个三角形细分方案。
//*********************************************** // Smooth subdivision //*********************************************** void CMeshDoc::OnMeshLoop() { BeginWaitCursor(); int NbObject = m_SceneGraph.NbObject(); for(int i=0;i<NbObject;i++) { CObject3d *pObject3d = m_SceneGraph[i]; if(pObject3d->GetType() == TYPE_MESh2D) { CMesh2d *pMesh = (CMesh2d *)pObject3d; pMesh->SubdivisionLoop(); } } UpdateAllViews(NULL); EndWaitCursor(); }
图 5. 参见通过平滑细分方案获得的视觉增强效果。
鼠标交互
插入到视图中的一些变量和命令允许鼠标交互。//*********************************************** // Left button -> x/y translation //*********************************************** void CMeshView::OnLButtonDown(UINT nFlags, CPoint point) { m_LeftButtonDown = TRUE; m_LeftDownPos = point; SetCapture(); CView::OnLButtonDown(nFlags, point); } void CMeshView::OnLButtonUp(UINT nFlags, CPoint point) { m_RightButtonDown = FALSE; m_LeftButtonDown = FALSE; ReleaseCapture(); CView::OnLButtonUp(nFlags, point); } //*********************************************** // Right button : z translation //*********************************************** void CMeshView::OnRButtonDown(UINT nFlags, CPoint point) { m_RightButtonDown = TRUE; m_RightDownPos = point; SetCapture(); CView::OnRButtonDown(nFlags, point); } void CMeshView::OnRButtonUp(UINT nFlags, CPoint point) { m_RightButtonDown = FALSE; m_LeftButtonDown = FALSE; ReleaseCapture(); CView::OnRButtonUp(nFlags, point); } //*********************************************** // Mouse move // Both : rotation // Left : x / y translation // Right : z translation //*********************************************** void CMeshView::OnMouseMove(UINT nFlags, CPoint point) { // Both : rotation if(m_LeftButtonDown && m_RightButtonDown) { if(m_xyRotation) { m_yRotation -= (float)(m_LeftDownPos.x - point.x) * m_SpeedRotation; m_xRotation -= (float)(m_LeftDownPos.y - point.y) * m_SpeedRotation; } else { m_zRotation -= (float)(m_LeftDownPos.x - point.x) * m_SpeedRotation; m_xRotation -= (float)(m_LeftDownPos.y - point.y) * m_SpeedRotation; } m_LeftDownPos = point; m_RightDownPos = point; InvalidateRect(NULL,FALSE); } else // Left : x / y translation if(m_LeftButtonDown) { m_xTranslation -= (float)(m_LeftDownPos.x - point.x) * m_SpeedTranslation; m_yTranslation += (float)(m_LeftDownPos.y - point.y) * m_SpeedTranslation; m_LeftDownPos = point; InvalidateRect(NULL,FALSE); } else // Right : z translation if(m_RightButtonDown) { m_zTranslation += (float)(m_RightDownPos.y - point.y) * m_SpeedTranslation; m_RightDownPos = point; InvalidateRect(NULL,FALSE); } CView::OnMouseMove(nFlags, point); }