在另一个线程中绘制 OpenGL
OpenGL drawing in another thread
我为Windows创建了一个简单的OpenGL应用程序。它创建一个窗口,然后使用 OpenGL 命令为其绘制一个三角形。这按预期工作。
稍后,我想将我的绘图代码封装到 DLL 中,以便可以在 C# WinForms 应用程序中使用它来绘制到 WinForm。 为此,我将绘图代码移动到单独的类和线程中。我的想法是,我可以将我的类"吸引"到任何现有窗口,并让我的线程绘制到它。
可悲的是,事情似乎并不那么简单。一旦我将窗口创建分离并将内容绘制到不同的线程中,屏幕就会保持全黑状态。绘图调用似乎不再有效。
有没有办法让我的绘图完全独立于窗口创建和主 UI 线程?
编辑:这是一些代码:-)
这是我的渲染器(从 UI 线程调用时有效,从后台线程调用时不起作用):
// Constructor
Renderer::Renderer(HWND hwnd, size_t windowWidth, size_t windowHeight)
:
mContext(hwnd)
{
mWindowWidth = windowWidth;
mWindowHeight = windowHeight;
mHdc = GetDC(hwnd);
// From now on everything is similar to initializing a context on any other hdc
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory(&pfd, sizeof(pfd));
pfd.nSize = sizeof(pfd);
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;
int iFormat = ChoosePixelFormat(mHdc, &pfd);
SetPixelFormat(mHdc, iFormat, &pfd);
mHrc = wglCreateContext(mHdc);
wglMakeCurrent(mHdc, mHrc);
// Set up OpenGL
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
glLoadIdentity();
glViewport(0, 0, windowWidth, windowHeight);
glOrtho(0, windowWidth, windowHeight, 0, -1, 1);
}
// Draws the scene
void Renderer::Draw()
{
wglMakeCurrent(mHdc, mHrc);
glClear(GL_COLOR_BUFFER_BIT);
glColor4f(1, 0, 1, 1);
glBegin(GL_QUADS);
glColor3f(1.0, 1.0, 1.0);
glVertex3f(0.0f, float(mWindowHeight/2), 0.0f);
glVertex3f(float(mWindowWidth/2), float(mWindowHeight/2), 0.0f);
glVertex3f(float(mWindowWidth/2), 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glEnd();
glFlush();
SwapBuffers(mHdc);
}
这是我从后台线程调用渲染器的方式:
// Constructor
BackgroundRenderer::BackgroundRenderer(HWND hwnd, uint32_t windowWidth, uint32_t windowHeight)
:
mCancelThread(false)
{
// Initialize OpenGL
mRenderer = std::make_shared<Renderer>(hwnd, windowWidth, windowHeight);
// Start rendering thread
mRenderingThread = std::thread(&BackgroundRenderer::BackgroundLoop, this);
}
// Destructor
BackgroundRenderer::~BackgroundRenderer()
{
// Stop rendering thread
mCancelThread = true;
mRenderingThread.join();
}
// The background rendering loop
void BackgroundRenderer::BackgroundLoop()
{
while (!mCancelThread)
{
// Draw stuff
mRenderer->Draw();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
这是我将它们粘合在一起的主要内容:
// Message loop
LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
// Window creation
HWND CreateApplicationWindow(char* title, int x, int y, int width, int height, int nCmdShow)
{
HWND hWnd;
WNDCLASS wc;
static HINSTANCE hInstance = 0;
if (!hInstance)
{
hInstance = GetModuleHandle(NULL);
wc.style = CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = "OpenGL";
RegisterClass(&wc);
}
hWnd = CreateWindowA("OpenGL", title, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, x, y, width, height, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
return hWnd;
}
// Main entry point of application
int APIENTRY WinMain(HINSTANCE hCurrentInst, HINSTANCE hPreviousInst, LPSTR lpszCmdLine, int nCmdShow)
{
HWND hWnd = CreateApplicationWindow("Test", 0, 0, 640, 480, nCmdShow);
// This renders from another thread (not working)
auto backgroundRenderer = std::make_shared<BackgroundRenderer>(hWnd, 640, 480);
// This would render in the UI thread (works)
//auto renderer = std::make_shared<Renderer>(hWnd, 640, 480);
MSG msg;
while (GetMessage(&msg, hWnd, 0, 0))
{
// This would render in the UI thread (works)
//renderer->Draw();
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DestroyWindow(hWnd);
return msg.wParam;
}
两个基本规则是:
- 任何给定的 OpenGL 渲染上下文一次只能在一个线程上处于活动状态。
- 任何线程一次只能有一个 OpenGL 上下文当前处于活动状态。
但是,特定的 OpenGL 上下文和特定窗口(用于将单个 OpenGL 上下文与多个窗口一起使用的 Win32 测试程序)或特定线程之间没有严格的关联。您始终可以将 OpenGL 上下文从一个线程迁移到另一个线程。
两种常用方法是:
- 在线程 A 中创建一个窗口,将窗口句柄传递给线程 B,然后在那里创建 OpenGL 上下文。
或
- 在线程 A 中创建窗口和 OpenGL 上下文,使上下文在 A 中处于非活动状态,将句柄传递给线程 B 并使它们在那里处于活动状态。
您需要每个目标窗口的OpenGL上下文...而且您还需要在渲染之前交换上下文...查看如何使用wglMakeCurrent
,否则您正在执行的所有渲染都是针对最后选择的上下文完成的。
因此,在渲染之前的每个渲染线程上,您需要为窗口设置wglMakeCurrent(hdc, hrc)
...然后渲染。
还有其他问题需要注意,例如:
- 什么是英特尔核芯 3000 上正确的 OpenGL 初始化?
相关文章:
- 如何在Visual Basic中使用矩形函数OpenGL绘制矩形
- 无法使用VAO和EBO(openGL)绘制多个对象
- 为什么我的点没有在 OpenGL 中绘制鼠标所在的位置?
- 在顶点着色器中使用 OpenGl 的未声明标识符,我在顶点着色器中绘制三角形时遇到问题
- 在现代OpenGL中,绘制GL_TRIANGLE奇怪的z轴行为的填充圆
- OpenGL 着色器不绘制任何东西 - 糟糕的精灵类
- 如何在 3D OpenGL 场景上绘制 SDL 2D 矩形?
- C++ OpenGL 中,绘制形状返回错误
- OpenGL绘制三角形而不是正方形
- 如何使用 mvp 在 opengl 中绘制椭圆
- OpenGL ES 2.0将纹理绘制到屏幕上是行不通的
- Opengl 和 c++ 绘制多个立方体
- 在另一个线程中绘制 OpenGL
- 使用纹理,法线和索引列表从VBO绘制OpenGL对象的问题
- 在3.2中使用VAO和VBO绘制OpenGL线条和正方形
- 用VBO绘制OpenGL图形
- 多对象绘制(OpenGL)
- 如何在Qt 5.7中使用glew绘制OpenGL几何图形
- SFML绘制OpenGL到多个窗口的速度非常慢
- 多次绘制Opengl纹理