C#中使用OpenGL:(七)创建OpenGL渲染环境

之前的文章介绍了如何在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。欲知道更详细的操作,可以浏览我的源码

上一篇:C#中使用OpenGL:(六)C#中调用C函数时的参数传递问题

下一篇:C#中使用OpenGL:(八)OpenGL扩展函数

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页