之前的文章介绍了如何在C#中调用1.1版本的OpenGL函数,但是光有OpenGL函数还不能绘制图形,就像一个画家,他即使拥有绘画的技巧,还有画笔和颜料,如果没有画布,他也没有地方画画。有了画布,画家还需要画板把画布支起来才能画。OpenGL渲染环境就类似于画布和画板,只有创建了渲染环境,使用OpenGL函数才会起作用。它不是OpenGL的一部分,而是隶属于操作系统,所以不同的操作系统,创建OpenGL渲染环境的方法也不同。由于是在C#中使用OpenGL,所以本文将使用winAPI创建OpenGL渲染环境。
C# OpenGL接口源码、C# OpenGL编程例子可在百度网盘下载:链接:https://pan.baidu.com/s/1dnIo1s-l6aqlE3IgaMJA5g
提取码:wc0x
一、选择显示窗口
显示窗口就类似于画家的画板,它的作用就是支撑画布,使得我们能够看到画布上的图画。在使用C/C++语言开发OpenGL程序,都需要自己创建显示窗口,当然,glut、glfw等库提供了在Windows系统中创建窗口的函数,这使得创建窗口变得很方便。然而,用C#语言开发OpenGL程序的时候,完全没有必要自己创建显示窗口,因为C#本身提供了大量可视控件,这些可视控件都有一个窗口,我们只需选择一个控件作为显示窗口即可。
上图展现的是在文本框、图片框和按钮上绘制三角形。只要控件是可视的(像Timer这类控件是非可视控件),它都可以作为显示窗口。只要你愿意,你可以把图形绘制在窗体(Form)、图片框(pictureBox)、标签(Label)…甚至是文本框(TextBox)。本文选择PictureBox控件作为显示窗口。
二、用到的winAPI函数
创建OpenGL渲染环境需要用到五个windows API。
1.取得设备上下文(DC,Device Context)
HDC GetDC(HWND hWnd);
参数是窗口句柄,返回值是设备上下文句柄。
2.选择像素格式
int ChoosePixelFormat(HDC hdc, CONST PIXELFORMATDESCRIPTOR *ppfd);
第一个参数是设备上下文句柄,第二个参数是像素格式描述器(Pixel Format Descriptor),返回值是一个整型,如果成功则返回像素格式索引号,否则返回0;
其中,PIXELFORMATDESCRIPTOR类型是一个结构体,其C语言描述如下:
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;//alpha分量比特位数
BYTE cAlphaShift;//alpha分量偏移量
BYTE cAccumBits;//累积缓冲区一个像素占用比特位数
BYTE cAccumRedBits;//累积缓冲区红色分量的比特位数
BYTE cAccumGreenBits;//累积缓冲区绿色分量的比特位数
BYTE cAccumBlueBits;//累积缓冲区蓝色分量的比特位数
BYTE cAccumAlphaBits;//累积缓冲区alpha分量的比特位数
BYTE cDepthBits;//深度缓冲区中一个像素占用的比特位数
BYTE cStencilBits;//模板缓冲区中一个像素占用的比特位数
BYTE cAuxBuffers;//辅助缓冲区
BYTE iLayerType;//用于早期的OpenGL,现已忽略
BYTE bReserved;//指定表层与底层的平面数目
DWORD dwLayerMask;//用于早期的OpenGL,现已忽略
DWORD dwVisibleMask;//指定一个底层平面的透明颜色或索引
DWORD dwDamageMask;//用于早期的OpenGL,现已忽略
} PIXELFORMATDESCRIPTOR
3.设置像素格式
BOOL SetPixelFormat(HDC hdc,int format, CONST PIXELFORMATDESCRIPTOR * ppfd);
第一个参数是设备上下文句柄,通过GetDC获得;第二个参数是像素格式索引号,通过ChoosePixelFormat获得;第三个是像素格式描述器,需要用户自行设置;如果设置成功则返回true,否则返回false。
4.创建OpenGL渲染上下文
HGLRC wglCreateContext( HDC hdc );
参数是设备上下文句柄(HDC),返回值是OpenGL渲染上下文句柄(HGLRC)。HGLRC与HDC具有一样的像素格式。
5.指定OpenGL当前渲染上下文
BOOL wglMakeCurrent( HDC hdc, HGLRC hglrc);
第一个参数是设备上下文句柄,第二个参数是OpenGL渲染上下文句柄,如果成功则返回true,否则返回false。
三、C#语言声明windows API
windows API都是用C语言写的,在C#中调用winAPI需要先对这些外部函数进行声明。
1.数据类型的变换
凡是句柄类型,在C#中都用IntPtr类型表示。如HWND类型、HDC类型、HGLRC类型都统一用IntPtr类型表示。对于PIXELFORMATDESCRIPTOR 类型,其C#版的结构体声明如下:
[StructLayout(LayoutKind.Sequential,Pack =1)]
public struct PIXELFORMATDESCRIPTOR
{
public ushort nSize;
public ushort nVersion;
public uint dwFlags;
public byte iPixelType;
public byte cColorBits;
public byte cRedBits;
public byte cRedShift;
public byte cGreenBits;
public byte cGreenShift;
public byte cBlueBits;
public byte cBlueShift;
public byte cAlphaBits;
public byte cAlphaShift;
public byte cAccumBits;
public byte cAccumRedBits;
public byte cAccumGreenBits;
public byte cAccumBlueBits;
public byte cAccumAlphaBits;
public byte cDepthBits;
public byte cStencilBits;
public byte cAuxBuffers;
public byte iLayerType;
public byte bReserved;
public uint dwLayerMask;
public uint dwVisibleMask;
public uint dwDamageMask;
};
2.C#方法声明
[DllImport("user32.dll", ExactSpelling = false, EntryPoint = "GetDC", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("gdi32.dll", ExactSpelling = false, EntryPoint = "ChoosePixelFormat", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
internal static extern int ChoosePixelFormat(IntPtr hdc, IntPtr ppfd);
[DllImport("gdi32.dll", ExactSpelling = false, EntryPoint = "SetPixelFormat", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
internal static extern bool SetPixelFormat(IntPtr hdc, int format, IntPtr ppfd);
[DllImport("opengl32.dll", ExactSpelling = false, EntryPoint = "wglCreateContext", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr wglCreateContext(IntPtr hdc);
[DllImport("opengl32.dll", ExactSpelling = false, EntryPoint = "wglMakeCurrent", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
internal static extern bool wglMakeCurrent(IntPtr hdc, IntPtr hglrc);
四、创建OpenGL渲染环境
下面通过C#代码来说明如何创建OpenGL渲染环境。
要调用windows API需要先引用名称空间:
using System.Runtime.InteropServices;
由于还会用到不安全代码,所以还要在“项目->属性”中勾选允许不安全代码。
示例代码:
IntPtr hWnd=pictureBox1.Handle;//图片框的窗口句柄
IntPtr hdc =GetDC(hWnd);//取得设备上下文
PIXELFORMATDESCRIPTOR pfd;//定义一个像素格式描述器
//设置像素格式
pfd.nSize = 40;
pfd.nVersion = 1;
pfd.dwFlags = PFD.PFD_DRAW_TO_WINDOW | PFD.PFD_SUPPORT_OPENGL | PFD.PFD_DOUBLEBUFFER;
pfd.iPixelType = (byte)PFD.PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cRedBits = 0;
pfd.cRedShift = 0;
pfd.cGreenBits = 0;
pfd.cGreenShift = 0;
pfd.cBlueBits = 0;
pfd.cBlueShift = 0;
pfd.cAlphaBits = 0;
pfd.cAlphaShift = 0;
pfd.cAccumBits = 0;
pfd.cAccumRedBits = 0;
pfd.cAccumGreenBits = 0;
pfd.cAccumBlueBits = 0;
pfd.cAccumAlphaBits = 0;
pfd.cDepthBits = 16;
pfd.cStencilBits = 0;
pfd.cAuxBuffers = 0;
pfd.iLayerType = (byte)PFD.PFD_MAIN_PLANE;
pfd.bReserved = 0;
pfd.dwLayerMask = 0;
pfd.dwVisibleMask = 0;
pfd.dwDamageMask = 0;
int pixelFormat;
unsafe
{
PIXELFORMATDESCRIPTOR* ppfd=&pfd;
pixelFormat =ChoosePixelFormat(hdc,(IntPtr)ppfd);//选择像素格式
SetPixelFormat(hdc, pixelFormat, (IntPtr)ppfd);//设置像素格式
IntPtr hglrc;
hglrc =wglCreateContext(hdc);//建立OpenGL渲染上下文
wglMakeCurrent(hdc, hglrc);//激活当前渲染上下文
}
通过以上一小段代码,OpenGL渲染环境的搭建就完成了。为验证正确性,做了一个实验,该实验是在pictureBox上绘制图形,结果如下图:
结语
之前的一些文章已经探讨过OpenGL常量、1.1版本的OpenGL,还有参数传递问题。本文着重介绍了如何在C#中创建OpenGL渲染环境,到目前为止,已经可以利用1.1版本的OpenGL进行图形绘制。下一步是探讨如何在C#中调用高版本的OpenGL。欲知道更详细的操作,可以浏览我的源码。