OpenGL - 绘制像素到屏幕?

时间:2012-05-30 22:13:46

标签: opengl

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

我原本希望能找到一个简单的函数让我按下指向像素数据的数组,因为这可能是最快的方法。不幸的是,我没有找到这样的功能。

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

4 个答案:

答案 0 :(得分:10)

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

答案 1 :(得分:7)

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

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

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

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

  • 同样在使用灰度时,您可能需要考虑仅使用GL_LUMINANCE纹理(即每像素1个字节)而不是基于RGB的格式来加快上传速度(但这取决于您的大小)纹理数据,我正在播放高清1920x1080视频,所以我需要确保将其保持下来)

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

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

希望这有帮助,如果您需要更多信息,请随时给我留言

答案 2 :(得分:2)

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

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

答案 3 :(得分:0)

我的将图像数据动态更改到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();
}