首页 文章

使用OpenGL从使用CUDA生成的顶点缓冲区对象中绘制图像

提问于
浏览
4

我正在使用CUDA生成此ABGR输出图像 . 有问题的图像存储在uchar4数组中 . 数组的每个元素表示图像中每个像素的颜色 . 显然,这个输出数组是一个2D图像,但它在CUDA中被分配为交错字节的线性存储器 .

我知道CUDA可以轻松地将此数组映射到OpenGL顶点缓冲区对象 . 我的问题是,假设我拥有图像中每个像素的RGB值,以及图像的宽度和高度,我如何使用OpenGL将此图像绘制到屏幕?
我知道必须使用某种着色器,但由于我的知识很少,我不知道着色器如何使用每个像素的颜色,而是将其映射到正确的屏幕像素 .

我知道我应该增加我对OpenGL的了解,但这似乎是一项微不足道的任务 . 如果有一个简单的方法来绘制这个图像,我宁愿不花太多时间学习OpenGL .

1 回答

  • 5

    我终于想出了一个简单的方法来做我想做的事情 . 不幸的是,我不知道罗伯特在NVIDIA网站上谈论的样本的存在 .

    简而言之,绘制图像最简单的方法是在OpenGL中定义一个Pixel Buffer Object,用CUDA注册缓冲区并将其作为 uchar4 的输出数组传递给CUDA内核 . 这是一个基于JOGL和JCUDA的快速伪代码,显示了所涉及的步骤 . 大多数代码都是从NVIDIA网站上的样本中获得的:

    1) Creaing the OpenGL buffers

    GL2 gl = drawable.getGL().getGL2();
    
    int[] buffer = new int[1];
    
    // Generate buffer
    gl.glGenBuffers(1, IntBuffer.wrap(buffer));
    glBuffer = buffer[0];
    
    // Bind the generated buffer
    gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, glBuffer);
    // Specify the size of the buffer (no data is pre-loaded in this buffer)
    gl.glBufferData(GL2.GL_ARRAY_BUFFER, imageWidth * imageHeight * 4, (Buffer)null, GL2.GL_DYNAMIC_DRAW);
    gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);
    
    // The bufferResource is of type CUgraphicsResource and is defined as a class field
    this.bufferResource = new CUgraphicsResource();
    
    // Register buffer in CUDA
    cuGraphicsGLRegisterBuffer(bufferResource, glBuffer, CUgraphicsMapResourceFlags.CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE);
    

    2) Initialize the texture and set texture parameters

    GL2 gl = drawable.getGL().getGL2();
    int[] texture = new int[1];
    
    gl.glGenTextures(1, IntBuffer.wrap(texture));
    this.glTexture = texture[0];
    
    gl.glBindTexture(GL2.GL_TEXTURE_2D, glTexture);
    
    gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_LINEAR);
    gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_LINEAR);
    
    
    gl.glTexImage2D(GL2.GL_TEXTURE_2D, 0, GL2.GL_RGBA8, imageWidth, imageHeight, 0, GL2.GL_BGRA, GL2.GL_UNSIGNED_BYTE, (Buffer)null);
    
    gl.glBindTexture(GL2.GL_TEXTURE_2D, 0);
    

    3) Run the CUDA kernel and display the results in OpenGL's display loop.

    this.runCUDA();
    
    GL2 gl = drawable.getGL().getGL2();
    
    gl.glBindBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, glBuffer);
    
    gl.glBindTexture(GL2.GL_TEXTURE_2D, glTexture);
    gl.glTexSubImage2D(GL2.GL_TEXTURE_2D, 0, 0, 0,
                    imageWidth, imageHeight,
                    GL2.GL_RGBA, GL2.GL_UNSIGNED_BYTE, 0); //The last argument must be ZERO! NOT NULL! :-)
    
    gl.glBindBuffer(GL2.GL_PIXEL_PACK_BUFFER, 0);
    gl.glBindBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, 0);
    
    gl.glBindTexture(GL2.GL_TEXTURE_2D, glTexture);
    gl.glEnable(GL2.GL_TEXTURE_2D);
    gl.glDisable(GL2.GL_DEPTH_TEST);
    gl.glDisable(GL2.GL_LIGHTING);
    gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_REPLACE);
    
    gl.glMatrixMode(GL2.GL_PROJECTION);
    gl.glPushMatrix();
    gl.glLoadIdentity();
    gl.glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
    
    gl.glMatrixMode(GL2.GL_MODELVIEW);
    gl.glLoadIdentity();
    
    gl.glViewport(0, 0, imageWidth, imageHeight);
    
    
    gl.glBegin(GL2.GL_QUADS);
        gl.glTexCoord2f(0.0f, 1.0f);
        gl.glVertex2f(-1.0f, -1.0f);
    
    
        gl.glTexCoord2f(1.0f, 1.0f);
        gl.glVertex2f(1.0f, -1.0f);
    
    
        gl.glTexCoord2f(1.0f, 0.0f);
        gl.glVertex2f(1.0f, 1.0f);
    
    
        gl.glTexCoord2f(0.0f, 0.0f);
        gl.glVertex2f(-1.0f, 1.0f);
    gl.glEnd();
    
    gl.glMatrixMode(GL2.GL_PROJECTION);
    gl.glPopMatrix();
    
    gl.glDisable(GL2.GL_TEXTURE_2D);
    

    3.5) The CUDA call:

    public void runCuda(GLAutoDrawable drawable) {
    
        devOutput = new CUdeviceptr();
        // Map the OpenGL buffer to a resource and then obtain a CUDA pointer to that resource
        cuGraphicsMapResources(1, new CUgraphicsResource[]{bufferResource}, null);
        cuGraphicsResourceGetMappedPointer(devOutput, new long[1], bufferResource);
    
        // Setup the kernel parameters making sure that the devOutput pointer is passed to the kernel
        Pointer kernelParams = 
                                .
                                .
                                .
                                .
    
        int gridSize = (int) Math.ceil(imageWidth * imageHeight / (double)DESC_BLOCK_SIZE);
    
        cuLaunchKernel(function,
                gridSize, 1, 1,
                DESC_BLOCK_SIZE, 1, 1,
                0, null,
                kernelParams, null);
        cuCtxSynchronize();
    
        // Unmap the buffer so that it can be used in OpenGL
        cuGraphicsUnmapResources(1, new CUgraphicsResource[]{bufferResource}, null);
    }
    

    PS:我感谢Robert提供样本的链接 . 我也感谢那些在没有任何有用反馈的情况下弃我问题的人!

相关问题