OpenGL - 如何在屏幕上绘制像素?

6

我希望能够尽可能快地使用OpenGL在屏幕上绘制一个由像素数据(RGB / 灰度值)组成的2D数组,并且该像素数据会经常变化。

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

如何才能最好地完成这个任务呢?


一个像素版本:https://dev59.com/O2025IYBdhLWcg3wNTAV - Ciro Santilli OurBigBook.com
4个回答

10

或许glDrawPixels函数是您要寻找的?但如果数据是静态的,最好使用它创建一个纹理,然后每帧绘制该纹理。


7
我最近遇到了类似的问题,因为我正在尝试将视频渲染到屏幕上(即重复上传像素数据到VRAM),我的方法是:
  • 使用glTexImage2D和glTexSubImage2D将数据上传到纹理中(即在调用之前绑定纹理(如果适用)和纹理单元)

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

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

  • 另外,由于您只使用灰度,您可能希望考虑仅使用GL_LUMINANCE纹理(即每像素1字节)而不是基于RGB的格式,以使上传更快(但这取决于您的纹理数据大小,我正在流式传输高清1920x1080视频,因此需要确保将其保持在较低水平)

  • 还要注意硬件使用的格式,以避免不必要的数据转换(例如,通常似乎最好使用BGRA数据而不是仅使用RGB)

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

希望这有所帮助,如果需要进一步信息,请随时向我发送消息。

2
我认为最快的方法是使用正交投影绘制一个屏幕大小的四边形,并使用像素着色器和纹理缓冲对象直接在像素着色器中绘制到纹理中。由于传输到/从TBO的延迟,您可能需要查看双缓冲是否有所帮助。
如果速度不是太大的问题(您只需要相当交互式的帧率),glDrawPixels易于使用,并且对于许多目的而言效果足够好。

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();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接