基本知识:
Windows应用程序是使用设备描述表(Device Context,简写为"DC")进行图形的绘制输出,
但OpenGL并不使用标准的设备描述表,它使用渲染描述表(Rendering Context,简写为"RC")完成图形图像的映射。
描述表的映射核心是像素格式的设置。
当进行OpenGL的绘图操作时,实际上是在进行设备像素的操作。OpenGL将数据转化为像素操作写入帧缓存中,就需要知道Windows的像素格式, 或者说需要与其一致起来。在初始化OpenGL时,需要一个叫做PIXELFORMATDESCRIPTOR的结构来完成对像素属性的设置。
Win32提 供了4个函数管理像素格式。
①ChoosePixelFormat():返回和&pfd指定的像素格式最匹配的DC支持的像素格式的索引值,如果函数调用不成功,返回零。
②SetPixelFormat():在DC中设置pixelformat指定的设备的像素格式,如果函数调用成功,返回TRUE。
③GetPixelFormat():返回当前DC的像素格式的索引,如果函数调用不成功,返回零。
④DescribePixelFormat():用n指定的DC的像素格式的数据设置&pfd,如果函数调用成功,返回DC的像素格式的最大索引,否则,返回零。
OpenGL的渲染描述表保存着在窗口中用来渲染一个场景所需的信息。一个OpenGL应用程序必须有一个渲染描述表,并且在OpenGL进行绘制之前它应该是当前的。在渲染描述表存入信息后,OpenGL就可以在Windows系统中更新一个窗口的图形状态。
Win32提供了5个函数管理渲染描述表。
①wglCreateContext():创建一个与给定的DC兼容的RC,调用成功返回RC的句柄,不成功返回NULL。
②wglMakeCurrent():使指定的RC成为当前的,调用成功返回GL_TRUE,否则返回GL_FALSE。
③wglGetCurrentCotext():获得当前的RC句柄。
④wglGetCurrentDC():获得和当前的RC联系的DC句柄。
⑤wglDeleteContext():删除RC。
/* Pixel format descriptor */
typedef struct tagPIXELFORMATDESCRIPTOR
{
WORD nSize;
WORD nVersion;
DWORD dwFlags;
BYTE iPixelType;
BYTE cColorBits;
BYTE cRedBits;
BYTE cRedShift;
BYTE cGreenBits;
BYTE cGreenShift;
BYTE cBlueBits;
BYTE cBlueShift;
BYTE cAlphaBits;
BYTE cAlphaShift;
BYTE cAccumBits;
BYTE cAccumRedBits;
BYTE cAccumGreenBits;
BYTE cAccumBlueBits;
BYTE cAccumAlphaBits;
BYTE cDepthBits;
BYTE cStencilBits;
BYTE cAuxBuffers;
BYTE iLayerType;
BYTE bReserved;
DWORD dwLayerMask;
DWORD dwVisibleMask;
DWORD dwDamageMask;
} PIXELFORMATDESCRIPTOR, *PPIXELFORMATDESCRIPTOR, FAR *LPPIXELFORMATDESCRIPTOR;
=============================================================
创建一个单文档结构的MFC程序
=============================================================
窗口创建之前我们须设置窗口风格
WS_CLIPCHILDREN(创建父窗口使用的Windows风格,用于重绘时裁剪子窗口所覆盖的区域)和
WS_CLIPSIBLINGS(创建子窗口使用的Windows风格,用于重绘时剪裁其他子窗口所覆盖的区域)
从而避免OpenGL绘制到其他窗口中去。
这些应该在窗口创建之前设置好,则代码位于PreCreateWindow()中。
BOOL CLDSView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
return CView::PreCreateWindow(cs);
}
=============================================================
在窗口创建时,则需要将OpenGL的环境设置好
int CLDSView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
InitializeOpenGL();
return 0;
}
定义如下变量:
CClientDC* m_pDC; // Device Context 设备描述表
HGLRC m_hRC; // Render Context 着色描述表
Windows应用程序是使用设备描述表(Device Context,简写为"DC")进行图形的绘制输出,
但OpenGL并不使用标准的设备描述表,它使用渲染描述表(Rendering Context,简写为"RC")
初始化OpenGL如下:
BOOL CLDSView::InitializeOpenGL()
{
m_pDC = new CClientDC (ThisWindow());
if(m_pDC == NULL)
{
AfxMessageBox(_T("new CClientDC ERROR!"));
return FALSE;
}
// 设置当前的绘图像素格式
if(!SetupPixelFormat()) // 在初始化OpenGL环境的时候,必须要设置好像素格式
{
return FALSE;
}
// 创建绘图描述表
m_hRC = ::wglCreateContext (m_pDC->GetSafeHdc ()); // DC转RC
if(m_hRC == 0)
{
AfxMessageBox(_T("创建绘图描述表失败"));
return FALSE;
}
// 使绘图描述表为当前调用线程的当前绘图描述表
if(::wglMakeCurrent (m_pDC->GetSafeHdc (), m_hRC)==FALSE)
{
AfxMessageBox(_T("调用线程的当前绘图描述表失败"));
return FALSE;
}
glClearColor(0.28f,0.28f,0.28f,0.0f); // 设置清屏颜色
glClearDepth(1.0f); // 指定清除深度缓存时使用的深度值
glEnable(GL_DEPTH_TEST);
return TRUE;
}
一般,应用程序先调用函数wglCreateContext,然后调用函数wglMakeCurrent使渲染上下文和设备显示相联系,因此OpenGL 的绘图操作都可以在设备显示上完成。完成绘图以后,通过再一次调用函数wglMakeCurrent(参数设置为NULL)可以使渲染上下文释放DC,最后调用函数wglDeleteContext来删除渲染上下文。(http://blog.csdn.net/chuajiang/article/details/1767520)
在创建一个图形操作表之前,首先必须设置像素格式。即产生一个RC的第一步是定义窗口的像素格式。
像素格式含有设备绘图界面的属性,这些属性包括绘图界面是用RGBA模式还是颜色表模式,像素缓存是用单缓存还是双缓存,以及颜色位数、深度缓存和模板缓存所用的位数,还有其它一些属性信息。像素格式决定窗口着所显示的图形在内存中是如何表示的。
在下面将在SetupPixelFormat()函数中对这些参数的设置。代码如下:
BOOL CLDSView::SetupPixelFormat()
{
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 m_nPixelFormat = ::ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd);
if ( m_nPixelFormat == 0 )
{
return FALSE;
}
// 设置最匹配的像素格式为当前的像素格式
if ( ::SetPixelFormat(m_pDC->GetSafeHdc(), m_nPixelFormat, &pfd) == FALSE)
{
return FALSE;
}
return TRUE;
}
上述则将OpenGL初始化成功
=============================================================
视图设置
在初始化OpenGL环境后,我们需要对视图进行设置
窗口创建好后,我们需要将OpoenGL的视口与MFC的客户区进行覆盖,进行OpenGL的图形显示
而且当主程序窗口进行缩放后,会影响到OpenGL图形的显示
void CLDSView::OnSize( int cx, int cy )
{
if ( 0 >= cx || 0 >= cy )
{
return;
}
glViewport(0, 0, cx, cy); // 视口覆盖整个程序的客户区
glMatrixMode(GL_PROJECTION); // 随后的矩阵操作glOrtho应用于投影矩阵中
glLoadIdentity(); // 重置当前指定的矩阵为单位矩阵
glOrtho(-cx , cx, -cy, cy,-10000000,10000000); // 正交投影
}
我们还可以用透视投影操作:
将
glOrtho(-cx , cx, -cy, cy,-10000000,10000000); // 正交投影
转换为:
GLfloat aspect_ratio = (GLdouble)cx/(GLdouble)cy; // 防止窗口缩放变形
gluPerspective(60.0f, aspect_ratio, 0.01f, 1500.0f);
=============================================================
将环境搭建好了,视口也设置好了,下面,我们就要进行图形绘制了
在
void CLDSView::OnDraw(CDC* /*pDC*/)
{
CLDSDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
RenderScene(); // 绘制图形
}
void CLDSView::RenderScene()
{
//清除颜色缓冲区和深度缓冲区
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
if (!::wglMakeCurrent(m_pDC->GetSafeHdc(), m_hRC))
{
return;
}
glMatrixMode(GL_MODELVIEW); // 图形绘制在模型视图中绘制
glLoadIdentity();
// 后面描述
glTranslated(m_dXPos, m_dYPos, 0.0); // XY方向上的移动
glRotated(m_dXAngle, 1.0, 0.0, 0.0); // 旋转
glRotated(m_dYAngle, 0.0, 1.0, 0.0);
glScalef(m_dScale, m_dScale, m_dScale); // 缩放
GLfloat dAsixLen = 600.0f;
glBegin(GL_LINES); // 绘制三根轴
glColor3f(1.0f, 0.0f, 0.0f); // X
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(dAsixLen, 0.0f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f); // Y
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, dAsixLen, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f); // Z
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, dAsixLen);
glEnd();
SwapBuffers( m_pDC->GetSafeHdc() );
}
=============================================================
按住左键后移动旋转图形
定义一个成员变量用于保存状态点
CPoint m_ptCurrent;
void CLDSView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
SetCapture();
m_ptCurrent = point;
__super::OnLButtonDown(nFlags, point);
}
void CLDSView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
ReleaseCapture();
mouseOpt = OTHER;
__super::OnLButtonUp(nFlags, point);
}
void CLDSView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_dXAngle += (point.y - m_ptCurrent.y)/6; // 注意 X Y
m_dYAngle += (point.x - m_ptCurrent.x)/6;
m_ptCurrent = point;
RedrawWindow();
__super::OnMouseMove(nFlags, point);
}
在RedrawWindow();后,程序窗口进行刷新,刷新时,绘制图形,
则就调用了
glRotated(m_dXAngle, 1.0, 0.0, 0.0);
glRotated(m_dYAngle, 0.0, 1.0, 0.0);
进行旋转
=============================================================
按住中键后移动移动图形
void CLDSView::OnMButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
SetCapture();
m_ptCurrent = point;
__super::OnMButtonDown(nFlags, point);
}
void CLDSView::OnMButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
ReleaseCapture();
__super::OnMButtonUp(nFlags, point);
}
void CLDSView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//if(this == GetCapture())
// return;
m_dXPos += (point.x - m_ptCurrent.x)*2; // 因为glOrtho截取的是两倍的屏幕大小
m_dYPos += (m_ptCurrent.y - point.y )*2;
m_ptCurrent = point;
RedrawWindow();
__super::OnMouseMove(nFlags, point);
}
还可以通过键盘的方向键进行图形的移动
void CLDSView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
switch(nChar)
{
case VK_UP:
m_dYPos += 1;
break;
case VK_DOWN:
m_dYPos -= 1;
break;
case VK_LEFT:
m_dXPos -= 1;
break;
case VK_RIGHT:
m_dXPos += 1;
break;
}
RedrawWindow();
__super::OnKeyDown(nChar, nRepCnt, nFlags);
}
在RedrawWindow();后,程序窗口进行刷新,刷新时,绘制图形,
则就调用了
glTranslated(m_dXPos, m_dYPos, -10.0); // 移动
进行移动
=============================================================
鼠标中键滚动进行图形缩放
BOOL CLDSView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (m_dScale > 0.06 && m_dScale <8 )
{
m_dScale += (zDelta /120) * 0.02;
}
else
{
m_dScale -= (zDelta /120) * 0.02;
}
CString strDebugOut;
strDebugOut.Format(_T("%.3f\n"), m_dScale);
OutputDebugString(strDebugOut);
mouseOpt = SCALE;
RedrawWindow();
return __super::OnMouseWheel(nFlags, zDelta, pt);
}
=============================================================
可以绘制,并进行一些简单的操作后,程序退出后呢?
我们需要做一些清理工作
void CLDSView::OnDestroy()
{
CView::OnDestroy();
// TODO: 在此处添加消息处理程序代码
if(::wglMakeCurrent (0,0) == FALSE) // 使渲染上下文释放DC
{
AfxMessageBox(_T("wglMakeCurrent Error!"));
}
if(::wglDeleteContext (m_hRC)==FALSE) // 删除渲染上下文
{
AfxMessageBox(_T("不能删除RC-- wglDeleteContext Error"));
}
if(m_pDC)
{
delete m_pDC;
}
m_pDC = NULL;
}
=============================================================
源代码下载:
http://download.csdn.net/detail/yulinxx/7911397