控制台菜单更新OpenGL窗口

时间:2009-02-08 05:46:54

标签: c++ multithreading opengl console

我正在制作一个执行一些自定义图像处理的应用程序。该程序将由控制台中的简单菜单驱动。用户将输入图像的文件名,该图像将在窗口中使用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()。现在这是可以的,但是我更喜欢一种解决方案,我只需要每次更改位图时重新显示(以节省处理时间),并且我不必再运行另一个线程(定时器)是假设的另一个主题)。这主要是因为主处理线程将进行大量的密集工作,我想将大部分资源专用于此线程,而不是其他任何东西。

5 个答案:

答案 0 :(得分:3)

我以前遇到过这个问题 - 这很烦人。问题是所有的OpenGL调用都必须在启动OpenGL上下文的线程中完成。因此,当您希望主(输入)线程在OpenGL线程中更改某些内容时,您需要以某种方式向线程发出信号,告知它需要执行的操作(设置标志或其他内容)。

注意:我不知道你的BitmapImage加载函数(这里是你的构造函数)是做什么的,但它可能有一些OpenGL调用。以上也适用于此!因此,您需要向另一个线程发出信号,为您创建BitmapImage,或者至少要创建与位图相关的OpenGL相关部分。

答案 1 :(得分:1)

几点:

  • 通常,如果你要使用多线程路由,最好是你的主线程是你的GUI线程,即它执行最小的任务,保持GUI响应。在您的情况下,我建议将密集图像处理任务移动到一个线程中,并在主线程中执行OpenGL渲染。

  • 为了绘制图像,您使用的是顶点而不是纹理四边形。除非你有充分的理由,否则使用单个纹理四边形(处理过的图像是纹理)会更快 。查看glTexImage2DglTexSubImage2D

  • 如果您正在使用加速的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线程就是这样做的。