65.9K
CodeProject 正在变化。 阅读更多。
Home

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

1999年12月1日

2分钟阅读

viewsIcon

439251

downloadIcon

16835

  • 下载文件 - 669 Kb
  • 图 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(); 
    }

    图 2. 您可以使用第二种渲染循环和线条选项在平面渲染的网格上叠加线框。

    平滑细分

    从给定的 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(); 
    }

    图 4. 连续两次 1 到 4 个三角形细分方案。

    图 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); 
    }
    © . All rights reserved.