C# 中的 OpenGL 包装器

OpenGL Wrapper in C#

我写了一个简单的 class 来创建 window 并将 OpenGL 初始化为 window。它在本机 c++ 和 CLI 中也能正常工作。但是,一旦我将 CLI class 作为 DLL 导入到我的 C# 项目中,渲染就会失败。出现空白 window 但渲染不起作用。 SwapBuffers 和 GL 渲染被调用但没有任何反应。你知道什么是错的吗? (对不起,我不是母语人士)

OpenGLClass.cpp

#include "OpenGLClass.h"
#include <iostream>

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

OpenGLClass::OpenGLClass()
{
}


OpenGLClass::~OpenGLClass()
{
}

bool OpenGLClass::Initialize()
{
    if (InitWindow())
        return InitOGL();
    else
        return false;
}

void OpenGLClass::Run()
{
    MSG msg = { 0 };
    while (true)
    {
        if (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            Render();
            SwapBuffers(m_hDevContext);
        }
    }
}

void OpenGLClass::Render()
{
    glViewport(0, 0, 800, 600);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, (float)800 / (float)600, 1, 1000);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    gluLookAt(0, 0, 10,
        0, 0, 0,
        0, 1, 0);

    glClearColor(0.2, 0.5, 0.8, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    static float i = 0.01f;

    i += 0.001f;

    float c = cos(i);
    float s = sin(i);

    glBegin(GL_TRIANGLES);
    glColor3f(c, 0, 0);      // red
    glVertex3f(1 + c, 0 + s, 0);

    glColor3f(c, s, 0);      // yellow
    glVertex3f(0 + c, 1 + s, 0);

    glColor3f(s, 0.1f, s);   // magenta
    glVertex3f(-1 + c, 0 + s, 0);
    glEnd();
}

bool OpenGLClass::InitWindow()
{
    WNDCLASSEX wc;

    m_hInstance = GetModuleHandle(NULL);

    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = m_hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
    wc.hIconSm = wc.hIcon;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "OpenGLWindow";
    wc.cbSize = sizeof(WNDCLASSEX);

    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, "RegisterClassEx() failed", "Error", MB_OK);
        return false;
    }

    int screenWidth = GetSystemMetrics(SM_CXSCREEN);
    int screenHeight = GetSystemMetrics(SM_CYSCREEN);

    screenWidth = 800;
    screenHeight = 600;
    int nStyle = WS_POPUP;


    m_hwnd = CreateWindowEx(WS_EX_APPWINDOW, "OpenGLWindow", "WindowTitle", nStyle, 0, 0, screenWidth, screenHeight, NULL, NULL, m_hInstance, NULL);


    ShowWindow(m_hwnd, SW_SHOW);
    return true;
}

bool OpenGLClass::InitOGL()
{
    m_hDevContext = GetDC(m_hwnd);


    PIXELFORMATDESCRIPTOR pfd = { 0 };
    pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 32;
    pfd.cDepthBits = 24;
    pfd.cStencilBits = 24;

    int format = ChoosePixelFormat(m_hDevContext, &pfd);
    if (format == 0)
        return false;

    std::cout << "Pixel Format: " << format << std::endl;

    if (!SetPixelFormat(m_hDevContext, format, &pfd))
        return false;


    m_GLRenderContext = wglCreateContext(m_hDevContext);
    if (!wglMakeCurrent(m_hDevContext, m_GLRenderContext))
        return false;
    GLint GlewInitResult = glewInit();
    if (GLEW_OK != GlewInitResult)
    {
        return false;
    }

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);


    return true;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_KEYDOWN:
        if (wParam == VK_ESCAPE)
        {
            PostQuitMessage(0);
            DestroyWindow(hwnd);
        }
        break;
    case WM_CLOSE:
        PostQuitMessage(0);
        DestroyWindow(hwnd);
        break;

    default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }

    return 0;
}

WrapperClass.h

#pragma once
#include <OpenGLClass.h>

public ref class WrapperClass
{
public:
    WrapperClass() : m_Impl(new OpenGLClass) {}

    ~WrapperClass() {
        delete m_Impl;
    }

protected:
    !WrapperClass() {
        delete m_Impl;
    }

public:

    inline bool Initialize()
    {
        return m_Impl->Initialize();
    }


    inline void Run()
    {
        m_Impl->Run();
    }

private:
    OpenGLClass* m_Impl;
};

C#代码

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        BackgroundWorker bw = new BackgroundWorker();
        WrapperClass wc;

        public Form1()
        {
            InitializeComponent();
            InitOGL();
        }

        private void InitOGL()
        {
            wc = new WrapperClass();
            if(wc.Initialize())
            {
                bw.DoWork += Bw_DoWork;
                bw.RunWorkerAsync();
            }
        }

        private void Bw_DoWork(object sender, DoWorkEventArgs e)
        {
            wc.Run();
        }
    }
}

正如 and 指出的那样,我 运行 InitOGL 和 Render 在不同的线程上。