控制台菜单更新 OpenGL 窗口

Posted

技术标签:

【中文标题】控制台菜单更新 OpenGL 窗口【英文标题】:Console menu updating OpenGL window 【发布时间】:2009-02-08 05:46:54 【问题描述】:

我正在制作一个执行一些自定义图像处理的应用程序。该程序将由控制台中的一个简单菜单驱动。用户将输入图像的文件名,该图像将使用 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();
    

对这个问题有什么想法吗?

编辑:通过从每 500 毫秒调用 glutPostRedisplay() 的 openglThread 启动一个 glutTimer 从技术上解决了这个问题。现在这没问题,但我更喜欢这样一种解决方案,在这种解决方案中,每次我对位图进行更改时我只需要重新显示(以节省处理时间),并且我不必运行另一个线程(计时器是我假设的另一个线程)。这主要是因为主处理线程将做大量密集的工作,我想将大部分资源专用于该线程而不是其他任何东西。

【问题讨论】:

投票结束,为什么这段代码不起作用。 【参考方案1】:

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

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

【讨论】:

这并不完全准确,要求 OpenGL 上下文只能是当前到任何单个线程。然而,上下文可以在与其当前所在的线程不同的线程中创建。例如,这对于屏幕外渲染很有用。 澄清一下,这种情况下的问题肯定是多线程渲染,因为 OP 使用相同的上下文从不同的线程进行 gl 调用,或者更确切地说是不存在的上下文(在主线程)。 重新评论 #1,这是真的,但我认为这与这个特定示例无关(并且是不必要的复杂化)。【参考方案2】:

几点:

1234563在你的情况下,我建议将密集的图像处理任务转移到一个线程中,并在你的主线程中进行 OpenGL 渲染。

为了绘制图像,您使用的是顶点而不是带纹理的四边形。除非您有充分的理由,否则使用单个带纹理的四边形(处理后的图像即纹理)会快得多。查看glTexImage2D 和glTexSubImage2D。

如果您使用加速的 OpenGL 实现(在任何现代系统上几乎可以保证)并且如果您使用带纹理的四边形,而不是每个像素的顶点。

【讨论】:

【参考方案3】:

你的问题可能出在 Display() 行

b->画图();

我没有看到 b 在哪里传递到 Display() 的范围内。

【讨论】:

b 在 main() 上方全局声明 是的,我现在在晨光中看到它。【参考方案4】:

您需要在创建上下文的线程上进行 OpenGL 调用 (glutInitDisplayMode)。因此,将不会定义位于不同线程上的 Display 方法内部的 glXX 调用。您可以通过转储函数地址轻松看到这一点,希望它是未定义的或 NULL。

【讨论】:

【参考方案5】:

听起来 500 毫秒计时器定期调用 Display(),在 2 次调用后,它以相同的渲染填充后缓冲区和前缓冲区。 Display() 会继续被调用,直到用户输入了一些 OpenGL 线程永远不知道的内容,但是由于全局变量 b 现在不同,线程在 Display() 中盲目地使用它。

那么如何按照 Jesse Beder 所说的那样使用全局 int(称为标志)来标记用户输入内容的时间。例如:

设置标志 = 1;在你做 b = new BitmapImage( path );

然后设置标志 = 0;从 OpenGL 线程调用 Display() 之后。

你在计时器上循环,但是,现在检查 flag = 1。你只需要在 flag = 1 时调用 glutPostRedisplay(),即用户输入了一些东西。

似乎是不使用睡眠/唤醒机制的好方法。在多个线程之间访问全局变量也可能是不安全的。我认为这里可能发生的最坏情况是 OpenGL 线程未读标志 = 0,而它应该读取标志 = 1。然后它应该在不超过几次迭代后捕获它。如果你有奇怪的行为去同步。

使用您显示的代码,您在 main() 中调用了两次 Display()。其实main()甚至不需要调用Display(),OpenGL线程就可以了。

【讨论】:

以上是关于控制台菜单更新 OpenGL 窗口的主要内容,如果未能解决你的问题,请参考以下文章

SDL OpenGL 窗口无响应,透明

OpenGL应用程序控制台未关闭[关闭]

使用 Opengl 在窗口上显示菜单

OpenGL如何使用固定管线下的着色器渲染一个正方形,并用特殊键控制移动

OpenGl窗口大小定位相关

让Win10控制面板在右键菜单中安家