我正在制作一个执行一些自定义图像处理的应用程序。该程序将由控制台中的简单菜单驱动。用户将输入图像的文件名,该图像将在窗口中使用openGL显示。当用户选择对图像进行一些处理时,处理完成,openGL窗口应该重绘图像。
我的问题是我的图像从未被绘制到窗口,而是窗口始终是黑色的。我认为这可能与我在程序中组织线程的方式有关。主执行线程处理菜单输入/输出和图像处理,并调用Display方法,而第二个线程运行openGL主循环。
这是我的主要代码:
#include <iostream>
#include <GL/glut.h>
#include "ImageProcessor.h"
#include "BitmapImage.h"
using namespace std;
DWORD WINAPI openglThread( LPVOID param );
void InitGL();
void Reshape( GLint newWidth, GLint newHeight );
void Display( void );
BitmapImage* b;
ImageProcessor ip;
int main( int argc, char *argv[] ) {
DWORD threadID;
b = new BitmapImage();
CreateThread( 0, 0, openglThread, NULL, 0, &threadID );
while( true ) {
char choice;
string path = "TestImages\\";
string filename;
cout << "Enter filename: ";
cin >> filename;
path += filename;
b = new BitmapImage( path );
Display();
cout << "1) Invert" << endl;
cout << "2) Line Thin" << endl;
cout << "Enter choice: ";
cin >> choice;
if( choice == '1' ) {
ip.InvertColour( *b );
}
else {
ip.LineThinning( *b );
}
Display();
}
return 0;
}
void InitGL() {
int argc = 1;
char* argv[1];
argv[0] = new char[20];
strcpy( argv[0], "main" );
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowPosition( 0, 0 );
glutInitWindowSize( 800, 600 );
glutCreateWindow( "ICIP Program - Character recognition using line thinning, Hilbert curve, and wavelet approximation" );
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
glClearColor(0.0,0.0,0.0,1.0);
glEnable(GL_DEPTH_TEST);
}
void Reshape( GLint newWidth, GLint newHeight ) {
/* Reset viewport and projection parameters */
glViewport( 0, 0, newWidth, newHeight );
}
void Display( void ) {
glClear (GL_COLOR_BUFFER_BIT); // Clear display window.
b->Draw();
glutSwapBuffers();
}
DWORD WINAPI openglThread( LPVOID param ) {
InitGL();
glutMainLoop();
return 0;
}
这是我对BitmapImage的绘制方法:
void BitmapImage::Draw() {
cout << "Drawing" << endl;
if( _loaded ) {
glBegin( GL_POINTS );
for( unsigned int i = 0; i < _height * _width; i++ ) {
glColor3f( _bitmap_image[i*3] / 255.0, _bitmap_image[i*3+1] / 255.0, _bitmap_image[i*3+2] / 255.0 );
// invert the y-axis while drawing
glVertex2i( i % _width, _height - (i / _width) );
}
glEnd();
}
}
关于这个问题的任何想法?
编辑:这个问题在技术上是通过从openglThread启动一个glutTimer解决的,每隔500ms调用一次glutPostRedisplay()。现在这是可以的,但是我更喜欢一种解决方案,我只需要每次更改位图时重新显示(以节省处理时间),并且我不必再运行另一个线程(定时器)是假设的另一个主题)。这主要是因为主处理线程将进行大量的密集工作,我想将大部分资源专用于此线程,而不是其他任何东西。
答案 0 :(得分:3)
我以前遇到过这个问题 - 这很烦人。问题是所有的OpenGL调用都必须在启动OpenGL上下文的线程中完成。因此,当您希望主(输入)线程在OpenGL线程中更改某些内容时,您需要以某种方式向线程发出信号,告知它需要执行的操作(设置标志或其他内容)。
注意:我不知道你的BitmapImage
加载函数(这里是你的构造函数)是做什么的,但它可能有一些OpenGL调用。以上也适用于此!因此,您需要向另一个线程发出信号,为您创建BitmapImage
,或者至少要创建与位图相关的OpenGL相关部分。
答案 1 :(得分:1)
几点:
通常,如果你要使用多线程路由,最好是你的主线程是你的GUI线程,即它执行最小的任务,保持GUI响应。在您的情况下,我建议将密集图像处理任务移动到一个线程中,并在主线程中执行OpenGL渲染。
为了绘制图像,您使用的是顶点而不是纹理四边形。除非你有充分的理由,否则使用单个纹理四边形(处理过的图像是纹理)会更快 。查看glTexImage2D和glTexSubImage2D。
如果您正在使用加速的OpenGL实现,那么以2fps(500ms,如您所述)的帧速率渲染将对资源产生微不足道的影响,这几乎可以在任何现代系统上得到保证,如果您使用纹理四边形而不是每个像素的顶点。
答案 2 :(得分:0)
您的问题可能出在
行的Display()中B-&GT;绘制();
我没有看到b被传递到Display()的范围。
答案 3 :(得分:0)
您需要在创建上下文的线程(glutInitDisplayMode)上进行OpenGL调用。因此,不会定义在不同线程上的Display方法内的glXX调用。您可以通过转储函数地址轻松地看到这一点,希望它是未定义的或NULL。
答案 4 :(得分:0)
听起来500ms计时器正在定期调用Display(),在2次调用之后,它会使用相同的渲染填充后缓冲区和前缓冲区。继续调用Display(),直到用户输入OpenGL线程从未知道的内容,但是,由于全局变量b现在不同,该线程在Display()中盲目地使用它。
那么如何做Jesse Beder所说的并使用全局int,调用它标志,以标记用户何时输入内容。例如:
set flag = 1;在你执行b = new BitmapImage(path);
之后然后设置flag = 0;从OpenGL线程调用Display()之后。
你循环计时器,但是,现在检查flag = 1.你只需要在flag = 1时调用glutPostRedisplay(),即用户输入的内容。
似乎是一种不使用睡眠/唤醒机制的好方法。在多个线程之间访问全局变量也可能不安全。我认为这里可能发生的最糟糕的事情是OpenGL线程miss-reads flag = 0,当它应该读取flag = 1.它应该在不超过几次迭代后捕获它。如果你得到奇怪的行为去同步。
使用您显示的代码,在main()中调用Display()两次。实际上,main()甚至不需要调用Display(),OpenGL线程就是这样做的。