MFC單文檔程序中搭建OpenGL框架

本博客計算機圖形學系列文章索引:

地址:《計算機圖形學系列相關文章索引(持續更新)》


一、簡介

Windows GDI是通過設備句柄(Device Context(設備描述表)以下簡稱"DC")來繪圖,而OpenGL則需要繪製環境(Rendering Context(着色描述表),以下簡稱"RC")。每一個GDI命令需要傳給它一個DC,但與GDI不同,OpenGL使用當前繪製環境(RC)。但是RC並不能直接完成繪圖,只能與特定的DC聯繫起來,從而完成具體的繪圖工作。一旦在一個線程中指定了一個當前RC,在此線程中其後所有的OpenGL命令都使用相同的當前RC。雖然在單一窗口中可以使用多個RC,但在單一線程中只有一個當前RC。下面我將首先產生一個OpenGL RC並使之成爲當前RC。這將分爲三個步驟:

1、設置窗口像素格式;

2、產生RC;

3、設置爲當前RC。

用一個圖表示如下所示,圖中介紹了需要在各個函數中設置的信息:


二、搭建MFC中的OpenGL基本框架

本文以一個單文檔程序爲例。新建一個單文檔名字爲VCOpenGL2 其他的默認。

1、添加鏈接庫。

打開菜單欄下的項目->屬性->配置屬性->鏈接器->輸入->附加依賴項里加入OpenGL32.lib GLu32.lib GLaux.lib,如圖

如果不用這種方法添加鏈接庫的話,可以寫上下列代碼,也能達到同樣的效果:

#pragma comment( lib, "opengl32.lib" )	 
#pragma comment( lib, "glu32.lib" )	    
#pragma comment( lib, "glut32.lib" )
#pragma comment( lib, "glaux.lib" )

2、 包含頭文件。

在stdafx裏面添加opengl的頭文件(當然也可以在其他文件中添加,比如繪圖一般都是在視圖中的,可以在xxxView.cpp文件中包含頭文件)。如下代碼所示:

#include <GL\glaux.h>
#include <GL\glut.h>

有幾點說明:

2.1、包含glut.h的同時就把gl.h和glu.h都包括了。因爲打開glut.h你可以看到如下圖所示的,已經包含了gl.h和glu.h。

2.2、關於這OpenGL中的這幾個庫的介紹,看一看我的另一篇文章,地址如下:

http://blog.csdn.net/zhangkaihang/article/details/7459629

3、設置窗口顯示風格。

窗口創建之前我們必須設置窗口風格包含

WS_CLIPCHILDREN(創建父窗口使用的Windows風格,用於重繪時裁剪子窗口所覆蓋的區域)和WS_CLIPSIBLINGS(創建子窗口使用的Windows風格,用於重繪時剪裁其他子窗口所覆蓋的區域),

從而避免OpenGL繪製到其他窗口中去。這些應該放在PreCreateWindow()中。代碼如下:

4、設置窗口像素格式

首先向VCOpenGL2View類中添加幾個保護的成員變量和公共的成員函數。如下:

HGLRC m_hRC;    //Rendering Context着色描述表
CClientDC* m_pDC;        //Device Context設備描述表
BOOL InitializeOpenGL();    //初始化 OpenGL
BOOL SetupPixelFormat();    //設置像素格式
void RenderScene();         //繪製場景

 別忘了在VCOpenGL2View的構造函數中設置 m_hRC = NULL; m_pDC = NULL;

產生一個RC的第一步是定義窗口的像素格式。像素格式決定窗口着所顯示的圖形在內存中是如何表示的。由像素格式控制的參數包括:顏色深度、緩衝模式和所支持的繪畫接口。在下面將在SetupPixelFormat()函數中對這些參數的設置。代碼如下:

BOOL CVCOpenGL2View::SetupPixelFormat(void)
{
 static PIXELFORMATDESCRIPTOR pfd = 
							{
								sizeof(PIXELFORMATDESCRIPTOR),  // pfd結構的大小 
								1,                              // 版本號 
								PFD_DRAW_TO_WINDOW |            // 支持在窗口中繪圖 
								PFD_SUPPORT_OPENGL |            // 支持 OpenGL 
								PFD_DOUBLEBUFFER,               // 雙緩存模式 
								PFD_TYPE_RGBA,                  // RGBA 顏色模式 
								24,                             // 24 位顏色深度 
								0, 0, 0, 0, 0, 0,               // 忽略顏色位 
								0,                              // 沒有非透明度緩存 
								0,                              // 忽略移位位 
								0,                              // 無累計緩存 
								0, 0, 0, 0,                     // 忽略累計位 
								32,                             // 32 位深度緩存     
								0,                              // 無模板緩存 
								0,                              // 無輔助緩存 
								PFD_MAIN_PLANE,                 // 主層 
								0,                              // 保留 
								0, 0, 0                         // 忽略層,可見性和損毀掩模 

							};
    int pixelFormat;
	// 爲設備描述表得到最匹配的像素格式 
    if((pixelFormat = ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd)) == 0)
    {
        MessageBox( _T("ChoosePixelFormat failed") );
        return FALSE;
    }
	// 設置最匹配的像素格式爲當前的像素格式 
    if(SetPixelFormat(m_pDC->GetSafeHdc(), pixelFormat, &pfd) == FALSE)
    {
        MessageBox( _T("SetPixelFormat failed") );
        return FALSE;
    }
    return TRUE;
}


5、產生RC,設置爲當前RC。  

現在像素格式已經設定,我們下一步工作是產生繪製環境(RC)並使之成爲當前繪製環境,即編寫InitializeOpenGL()函數。代碼如下:

BOOL CVCOpenGL2View::InitializeOpenGL(void)
{
	PIXELFORMATDESCRIPTOR pfd;
	int n;
	m_pDC=new CClientDC(this);
	ASSERT(m_pDC != NULL);
	// 設置當前的繪圖像素格式
	if(!SetupPixelFormat())
	{
		return FALSE;
	}

	n=::GetPixelFormat(m_pDC->GetSafeHdc());
	::DescribePixelFormat(m_pDC->GetSafeHdc(), n,sizeof(pfd),&pfd);
	 // 創建繪圖描述表
	m_hRC=wglCreateContext(m_pDC->GetSafeHdc());
	if(m_hRC == NULL)
	{
		return FALSE;
	}
	// 使繪圖描述表爲當前調用現程的當前繪圖描述表
	if( wglMakeCurrent(m_pDC->GetSafeHdc(),m_hRC) == FALSE)
	{
		return FALSE;
	}
	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);
	return TRUE;
}

別忘了再OnCreate()函數中調用 InitializeOpenGL()函數。如下所示:


int CVCOpenGL2View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您專用的創建代碼
	if ( InitializeOpenGL())
	{
		return 0;
	}

	return 0;
}


6、設置視口

在OnSize()中一般用來設置視口和視錐,因爲這些是和窗口大小相關的。代碼如下:

void CVCOpenGL2View::OnSize(UINT nType, int cx, int cy)
{
	CView::OnSize(nType, cx, cy);

	// TODO: 在此處添加消息處理程序代碼
	m_wide = cx;    //m_wide爲在CVCOpenGL2View類中添加的表示視口寬度的成員變量
	m_heigth = cy;  //m_height爲在CVCOpenGL2View類中添加的表示視口高度的成員變量
	//避免除數爲0
	if(m_heigth==0)
	{
		m_heigth=1;
	}
	//設置視口與窗口的大小
	glViewport(0,0,m_wide,m_heigth);
}

7、繪製場景(用OpenGL繪圖相關的代碼都在這裏哦!!)

本文以一個三維正方體爲例,代碼如下:

void CVCOpenGL2View::RenderScene(void)
{
	//設置清屏顏色爲黑色
	glClearColor(0.0f,0.0f,0.0f,0.0f);
	//清除顏色緩衝區和深度緩衝區
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/*
	//建立正交變換下的剪切體
	if(w<h)
	{
		glOrtho(-nRange,nRange,-nRange*h/w,nRange*h/w,-nRange,nRange);
	}
	else
	{
		glOrtho(-nRange*w/h,nRange*w/h,-nRange,nRange,-nRange,nRange);
	}
*/
	//透視投影變換
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(m_tFovy, (double)m_wide/(double)m_heigth,m_zNear,m_zFar);
	//視角變換
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(10,10,10,0,0,0,0,1,0);
	//矩陣堆棧函數,和glPopMatrix()相對應
	glPushMatrix();

	glBegin( GL_LINES );
	glColor3d(1.0, 0.0, 0.0);	// X軸 紅色
	glVertex3d(0.0, 0.0, 0.0); 
	glVertex3d(2.0, 0.0, 0.0);
	glColor3d(0.0, 1.0, 0.0);	// Y軸 綠色
	glVertex3d(0.0, 0.0, 0.0);
	glVertex3d(0.0, 2.0, 0.0);
	glColor3d(0.0, 0.0, 1.0);	// Z軸 藍色
	glVertex3d(0.0, 0.0, 0.0); 
	glVertex3d(0.0, 0.0, 2.0);
	glEnd();

	glColor3f(1.0, 1.0, 1.0);
      glutWireCube(0.5);

	glPopMatrix();
	glFinish();
	SwapBuffers(wglGetCurrentDC());
}
別忘了,在OnDraw()函數中調用哦!!!!!如下:

void CVCOpenGL2View::OnDraw(CDC* /*pDC*/)
{
	CVCOpenGL2Doc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;
	// TODO: 在此處爲本機數據添加繪製代碼

	RenderScene();
}

8、一些收尾工作。

8.1 爲了使改變窗口大小時嚴重的閃爍,在OnEraseBkgnd()裏做一些操作,避免windows自己的窗口刷新閃爍。OnEraseBkgnd()函數需要重寫。如下:

BOOL CVCOpenGL2View::OnEraseBkgnd(CDC* pDC)
{
	// TODO: 在此添加消息處理程序代碼和/或調用默認值
	return TRUE;

//	return CView::OnEraseBkgnd(pDC);
}
8.2 爲了避免內存泄露,OnDestroy()函數中加一些代碼,如下:

void CVCOpenGL2View::OnDestroy()
{
	CView::OnDestroy();

	// TODO: 在此處添加消息處理程序代碼

	m_hRC = ::wglGetCurrentContext();
      if(::wglMakeCurrent (0,0) == FALSE)
      {
           MessageBox(_T("Could not make RC non-current"));
      }

	if(m_hRC)
	{
	    if(::wglDeleteContext(m_hRC)==FALSE)
		{
			MessageBox(_T("Could not delete RC"));
		}
	}

	if(m_pDC)
	{
		delete m_pDC;
	}
	m_pDC = NULL;
}

至此一個單文檔的框架就弄好了。下面貼出所畫的立方體,如下圖:








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章