OpenGL - 将像素绘制到屏幕上?

Posted

技术标签:

【中文标题】OpenGL - 将像素绘制到屏幕上?【英文标题】:OpenGL - draw pixels to screen? 【发布时间】:2012-05-30 22:13:45 【问题描述】:

我想使用 OpenGL 尽可能快地在屏幕上绘制像素数据(RGB/灰度值)的二维数组。像素数据变化频繁。

我曾希望我能找到一个简单的函数,让我可以将指针推入表示像素数据的数组,因为这可能是最快的方法。不幸的是,我没有找到这样的功能。

完成这项任务的最佳方法是什么?

【问题讨论】:

一个像素版本:***.com/questions/6151135/… 【参考方案1】:

也许glDrawPixels 是您正在寻找的功能?虽然如果数据是静态的,最好用它创建一个纹理,然后在每一帧中绘制它。

【讨论】:

【参考方案2】:

我最近遇到了类似的问题,因为我正在尝试将视频渲染到屏幕上(即反复将像素数据上传到 VRAM),我的方法是:

使用 glTexImage2D 和 glTexSubImage2D 将数据上传到纹理(即在调用之前绑定纹理(和纹理单元,如果适用))

在我的情况下,由于视频帧速率(通常约为 24 fps)低于我的应用程序的帧速率(目标为 60 fps),为了避免再次上传相同的数据,我使用帧缓冲区对象 (查看 glGenFramebuffers/glBindFramebuffer/glDeleteFramebuffers) 并将我的纹理与帧缓冲区 (glFramebufferTexture2D) 链接。然后我上传该纹理一次,并多次绘制同一帧(只是使用 glBindTexture 进行普通纹理访问)

我不知道您使用的是哪个平台,但由于我针对的是 Mac,我使用了一些 Apple 扩展来确保通过 DMA 将数据传输到 VRAM(即让 glTexSubImage2D 立即返回以让 CPU 执行其他工作)- 如果您也在使用 Mac,请随时向我询问更多信息

1234563纹理数据,我正在流式传输高清 1920x1080 视频,所以我需要确保将其保持在低电平)

还要注意您的硬件使用的格式以避免不必要的数据转换(即通常使用 BGRA 数据似乎比仅使用 RGB 数据更好)

最后,在我的代码中,我用着色器替换了所有固定的管道功能(特别是将数据从灰度或 YUV 格式转换为 RGB),但这又取决于数据的大小,并且CPU 或 GPU 的工作负载

希望对您有所帮助,如果您需要更多信息,请随时给我发消息

【讨论】:

【参考方案3】:

我认为最快的方法是使用正交投影绘制一个屏幕大小的四边形,并使用像素着色器和Texture Buffer Object 直接绘制到像素着色器中的纹理。由于与 TBO 之间的传输延迟,您可能想看看双缓冲是否有帮助。

如果速度不是问题(您只需要相当交互式的帧速率)glDrawPixels 很容易使用并且可以很好地用于多种用途。

【讨论】:

【参考方案4】:

我在 OpenGL 中获取动态变化的图像数据到屏幕的解决方案,

#define WIN32_LEAN_AND_MEAN

#include "wx/wx.h"
#include "wx/sizer.h"
#include "wx/glcanvas.h"
#include "BasicGLPane.h"

// include OpenGL
#ifdef __WXMAC__
#include "OpenGL/glu.h"
#include "OpenGL/gl.h"
#else
#include <GL/glu.h>
#include <GL/gl.h>
#endif
#include "ORIScanMainFrame.h"

BEGIN_EVENT_TABLE(BasicGLPane, wxGLCanvas)
EVT_MOTION(BasicGLPane::mouseMoved)
EVT_LEFT_DOWN(BasicGLPane::mouseDown)
EVT_LEFT_UP(BasicGLPane::mouseReleased)
EVT_RIGHT_DOWN(BasicGLPane::rightClick)
EVT_LEAVE_WINDOW(BasicGLPane::mouseLeftWindow)
EVT_SIZE(BasicGLPane::resized)
EVT_KEY_DOWN(BasicGLPane::keyPressed)
EVT_KEY_UP(BasicGLPane::keyReleased)
EVT_MOUSEWHEEL(BasicGLPane::mouseWheelMoved)
EVT_PAINT(BasicGLPane::render)
END_EVENT_TABLE()

// Test data for image generation.  floats range 0.0 to 1.0, in RGBRGBRGB... order.
// Array is 1024 * 3 long. Note that 32 * 32 is 1024 and is the largest image we can randomly generate.
float* randomFloatRGB;
float* randomFloatRGBGrey;

BasicGLPane::BasicGLPane(wxFrame* parent, int* args) :
    wxGLCanvas(parent, wxID_ANY, args, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)

    m_context = new wxGLContext(this);

    randomFloatRGB = new float[1024 * 3];
    randomFloatRGBGrey = new float[1024 * 3];
    // In GL images 0,0 is in the lower left corner so the draw routine does a vertical flip to get 'regular' images right side up.
    for (int i = 0; i < 1024; i++) 
        // Red
        randomFloatRGB[i * 3] = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
        // Green
        randomFloatRGB[i * 3 + 1] = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
        // Blue
        randomFloatRGB[i * 3 + 2] = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
        // Telltale 2 white pixels in 0,0 corner.
        if (i < 2) 
            randomFloatRGB[i * 3] = randomFloatRGB[i * 3 + 1] = randomFloatRGB[i * 3 + 2] = 1.0f;
        

        randomFloatRGBGrey[i * 3] = randomFloatRGB[i * 3];
        randomFloatRGBGrey[i * 3 + 1] = randomFloatRGB[i * 3];
        randomFloatRGBGrey[i * 3 + 2] = randomFloatRGB[i * 3];
    

    // To avoid flashing on MSW
    SetBackgroundStyle(wxBG_STYLE_CUSTOM);


BasicGLPane::~BasicGLPane()

    delete m_context;


void BasicGLPane::resized(wxSizeEvent& evt)

    //  wxGLCanvas::OnSize(evt);
    Refresh();


int BasicGLPane::getWidth()

    return GetSize().x;


int BasicGLPane::getHeight()

    return GetSize().y;


void BasicGLPane::render(wxPaintEvent& evt)

    assert(GetParent());
    assert(GetParent()->GetParent());
    ORIScanMainFrame* mf = dynamic_cast<ORIScanMainFrame*>(GetParent()->GetParent());
    assert(mf);

    switch (mf->currentMainView) 
    case ORIViewSelection::ViewCamera:
        renderCamera(evt);
        break;
    case ORIViewSelection::ViewDepth:
        renderDepth(evt);
        break;
    case ORIViewSelection::ViewPointCloud:
        renderPointCloud(evt);
        break;
    case ORIViewSelection::View3DModel:
        render3DModel(evt);
        break;
    default:
        renderNone(evt);
    


void BasicGLPane::renderNone(wxPaintEvent& evt) 
    if (!IsShown())
        return;
    SetCurrent(*(m_context));
    glPushAttrib(GL_ALL_ATTRIB_BITS);
    glClearColor(0.08f, 0.11f, 0.15f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glFlush();
    SwapBuffers();
    glPopAttrib();


GLuint makeOpenGlTextureFromDataLuninanceFloats(int width, int height, float* f) 
    GLuint textureID;

    glEnable(GL_TEXTURE_2D);
    glGenTextures(1, &textureID);

    // "Bind" the newly created texture : all future texture functions will modify this texture
    glBindTexture(GL_TEXTURE_2D, textureID);

    // Give the image to OpenGL
    glTexImage2D(GL_TEXTURE_2D, 0, GL_FLOAT, width, height, 0, GL_FLOAT, GL_LUMINANCE, f);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    return textureID;


GLuint makeOpenGlTextureFromRGBInts(int width, int height, unsigned int* f) 
    GLuint textureID;


    glEnable(GL_TEXTURE_2D);
    glGenTextures(1, &textureID);

    // "Bind" the newly created texture : all future texture functions will modify this texture
    glBindTexture(GL_TEXTURE_2D, textureID);

    // Give the image to OpenGL
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_INT, f);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    return textureID;


/// <summary>
/// Range of each float is 0.0f to 1.0f
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="floatRGB"></param>
/// <returns></returns>
GLuint makeOpenGlTextureFromRGBFloats(int width, int height, float* floatRGB) 
    GLuint textureID;

    // 4.6.0 NVIDIA 457.30  (R Keene machine, 11/25/2020)
    // auto sss = glGetString(GL_VERSION);

    glGenTextures(1, &textureID);

    // "Bind" the newly created texture : all future texture functions will modify this texture
    glBindTexture(GL_TEXTURE_2D, textureID);

    // Give the image to OpenGL
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, floatRGB);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    return textureID;


void BasicGLPane::DrawTextureToScreenFloat(int w, int h, float* floatDataPtr, GLuint (*textureFactory)(int width, int height, float* floatRGB)) 
    if (w <= 0 || h <= 0 || floatDataPtr == NULL || w > 5000 || h > 5000) 
        assert(false);
        return;
    

    SetCurrent(*(m_context));

    glPushAttrib(GL_ALL_ATTRIB_BITS);
    glPushMatrix();
    glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);

    glClearColor(0.15f, 0.11f, 0.02f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glEnable(GL_TEXTURE_2D);

    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    // 4.6.0 NVIDIA 457.30  (R Keene machine, 11/25/2020)
    // auto sss = glGetString(GL_VERSION);

    float onePixelW = (float)getWidth() / (float)w;
    float onePixelH = (float)getHeight() / (float)h;
    float orthoW = w;
    float orthoH = h;
    if (onePixelH > onePixelW) 
        orthoH = h * onePixelH / onePixelW;
    
    else 
        orthoW = w * onePixelW / onePixelH;
    
    // We want the image at the top of the window, not the bottom if the window is too tall.
    int topOfScreen = (float)getHeight() / onePixelH;

    // If the winjdow resizes after creation you need to change the viewport.
    glViewport(0, 0, getWidth(), getHeight());
    gluOrtho2D(0.0, orthoW, (double)topOfScreen - (double)orthoH, topOfScreen);

    GLuint myTextureName = textureFactory(w, h, floatDataPtr);

    glBegin(GL_QUADS);
    
        // This order of UV coords and verticies will do the vertical flip of the image to get the 'regular' image 0,0
        // in the top left corner.
        glTexCoord2f(0.0f, 1.0f); glVertex3f(0.0f, 0.0f, 0.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f(0.0f + w, 0.0f, 0.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f(0.0f + w, 0.0f + h, 0.0f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f(0.0f, 0.0f + h, 0.0f);
    
    glEnd();

    glDeleteTextures(1, &myTextureName);

    glFlush();
    SwapBuffers();


    glPopClientAttrib();
    glPopMatrix();
    glPopAttrib();


void BasicGLPane::DrawTextureToScreenMat(wxPaintEvent& evt, cv::Mat m, float brightness) 
    m.type();
    if (m.empty()) 
        renderNone(evt);
        return;
    

    if (m.type() == CV_32FC1)  // Grey scale.
        DrawTextureToScreenFloat(m.cols, m.rows, (float*)m.data, makeOpenGlTextureFromDataLuninanceFloats);
    
    if (m.type() == CV_32FC3)  // Color.
        DrawTextureToScreenFloat(m.cols, m.rows, (float*)m.data, makeOpenGlTextureFromRGBFloats);
    
    else 
        renderNone(evt);
    


void BasicGLPane::renderCamera(wxPaintEvent& evt) 
    if (!IsShown())
        return;
    DrawTextureToScreenMat(evt, ORITopControl::Instance->im_white);


void BasicGLPane::renderDepth(wxPaintEvent& evt) 
    if (!IsShown())
        return;
    DrawTextureToScreenMat(evt, ORITopControl::Instance->depth_map);


void BasicGLPane::render3DModel(wxPaintEvent& evt) 
    if (!IsShown())
        return;
    SetCurrent(*(m_context));
    glPushAttrib(GL_ALL_ATTRIB_BITS);
    glPushMatrix();

    glClearColor(0.08f, 0.11f, 0.15f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);



    glFlush();
    SwapBuffers();

    glPopMatrix();
    glPopAttrib();


void BasicGLPane::renderPointCloud(wxPaintEvent& evt) 
    if (!IsShown())
        return;
    boost::unique_lock<boost::mutex> lk(ORITopControl::Instance->pointCloudCacheMutex);

    SetCurrent(*(m_context));
    glPushAttrib(GL_ALL_ATTRIB_BITS);
    glPushMatrix();

    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glViewport(0, 0, getWidth(), getHeight());

    glClearColor(0.08f, 0.11f, 0.15f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    if (ORITopControl::Instance->pointCloudCache.size() > 0) 
        glMatrixMode(GL_PROJECTION);
        gluPerspective( /* field of view in degree */ 40.0,
            /* aspect ratio */ 1.0,
            /* Z near */ 1.0, /* Z far */ 500.0);
        glMatrixMode(GL_MODELVIEW);
        gluLookAt(100, 70, 200, // Eye
            25, 25, 25, // Look at pt
            0, 0, 1); // Up Vector

        glPointSize(2.0);
        glBegin(GL_POINTS);
        // Use explicit for loop because pointCloudFragments can grow asynchronously.
        for (int i = 0; i < ORITopControl::Instance->pointCloudCache.size(); i++) 
            auto frag = ORITopControl::Instance->pointCloudCache[i];
            auto current_point_cloud_ptr = frag->cloud;
            glPushMatrix();
            // glMultMatrixf(frag->xform.data());
            for (size_t n = 0; n < current_point_cloud_ptr->size(); n++) 
                glColor3ub(255, 255, 255);
                glVertex3d(current_point_cloud_ptr->points[n].x, current_point_cloud_ptr->points[n].y, current_point_cloud_ptr->points[n].z);
            
            glPopMatrix();
        
        glEnd();
    

    glFlush();
    SwapBuffers();

    glPopMatrix();
    glPopAttrib();

【讨论】:

以上是关于OpenGL - 将像素绘制到屏幕上?的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL-坐标系

OpenGL Stencil - 排除透明像素

如何在OpenGL中将像素作为纹理绘制到多边形?

将 OpenGL 后台缓冲区直接复制到 GDI DC 像素数据上

如何使用 OpenGL 制作渐变到黑色的效果?

opengl绘制三角形