【问题标题】:How to draw OpenGL pixels with the GPU如何使用 GPU 绘制 OpenGL 像素
【发布时间】:2019-01-13 23:37:28
【问题描述】:

简介:
我正在制作一个粉末玩具,它利用并行处理来进行游戏物理,它处理 500 x 500 区域的粉末。游戏主要使用 GPU 上的粒子完成所有操作,但它使用 CPU 来渲染粒子(大大降低了速度)。如何在 GPU 而不是 CPU 上渲染粒子?我主要将我的粒子数据保存在 GPU 上,因为大多数操作都发生在那里,而且 cudaMemcpy 非常慢,当它在主机内存上时,项目会无法控制地滞后。

代码:
这是我的显示功能

void display()
{
    // Measure performance
    mainloopMeasurePerformanceStart(1);

    // Clear the screen
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    // Copy particle data to render
    cudaMemcpy(&particles, d_particles, sizeof(particles), cudaMemcpyDeviceToHost);

    // Loop over the sand particles
    for(int i=0;i<250000;i++)
    {
        // Is the sand particle alive
        if(particles[i].alive)
        {
            // Get the position
            int pos[2];
            id_to_pos(i,pos);

            // Draw the pixel
            glColor3f(particles[i].color[0],particles[i].color[1],particles[i].color[2]);
            glBegin(GL_QUADS);
                glVertex2d((pos[0]/500.0-0.5)*2,(pos[1]/500.0-0.5)*2);
                glVertex2d((pos[0]/500.0-0.5+0.002)*2,(pos[1]/500.0-0.5)*2);
                glVertex2d((pos[0]/500.0-0.5+0.002)*2,(pos[1]/500.0-0.5+0.002)*2);
                glVertex2d((pos[0]/500.0-0.5)*2,(pos[1]/500.0-0.5+0.002)*2);
            glEnd();
        }
    }

    // Get the mouse position
    int m_posX, m_posY;
    mousePos(&m_posX, &m_posY);

    // Draw the cursor
    glColor3f(1.0f, 1.0f, 1.0f);
    for(int i=0;i<360;i++)
    {
        // Calculate the position
        double pos[2];
        pos[0] = sin(2*PI/360*i)*cursor_radius+m_posX;
        pos[1] = cos(2*PI/360*i)*cursor_radius+m_posY;

        glBegin(GL_QUADS);
            glVertex2d((pos[0]/500.0-0.5)*2,(pos[1]/500.0-0.5)*2);
            glVertex2d((pos[0]/500.0-0.5+0.002)*2,(pos[1]/500.0-0.5)*2);
            glVertex2d((pos[0]/500.0-0.5+0.002)*2,(pos[1]/500.0-0.5+0.002)*2);
            glVertex2d((pos[0]/500.0-0.5)*2,(pos[1]/500.0-0.5+0.002)*2);
        glEnd();
    }

    // Swap the front and back frame buffers
    glutSwapBuffers();

    // Measure performance
    mainloopMeasurePerformanceEnd();
}

以及处理沙子的地方:

__global__ void do_sand(
    Sand *particles, bool *mouseStates, unsigned long seed,
    int m_pos_x, int m_pos_y, double cursor_radius
){
    // Get the overall ID
    int id = blockIdx.x*100+threadIdx.x;

    // Convert the ID to a position
    int pos[2];
    id_to_pos(id,pos);

    // Convert the mouse position to an array
    int m_pos[2];
    m_pos[0] = m_pos_x;
    m_pos[1] = m_pos_y;

    // Is the sand particle alive
    if(particles[id].alive)
    {
        // Is there sand being cleared and is this particle in range
        if(mouseStates[GLUT_RIGHT_BUTTON] && distance_between(pos, m_pos) < cursor_radius)
        {
            // Delete this particle
            particles[id].alive = false;
        }

        // Do physics
        bool done = false;
        int check;

        switch(particles[id].model)
        {
            // Powder
            case 'P':
            {
                // Is vertical movement valid
                if(pos[1]-1 >= 0 && !done)
                {
                    // Get the ID
                    check = pos_to_id(pos[0], pos[1]-1);

                    // Is this space free
                    if(!particles[check].alive)
                    {
                        // Move the particle
                        particles[check] = particles[id];
                        particles[id].alive = false;
                        done = true;
                    }
                }

                // Randomly pick the sands course
                int choice;
                if((seed * id * 5423) % 2 == 0) choice=1;
                else choice=-1;

                // Check left movement
                if(pos[0]-choice < 500 && pos[0]-choice >= 0 && pos[1]-1 >= 0 && !done)
                {
                    // Get the ID
                    check = pos_to_id(pos[0]-choice,pos[1]-1);

                    // Is this space free
                    if(
                        !particles[check].alive &&
                        !particles[pos_to_id(pos[0]-choice,pos[1])].alive &&
                        !(
                            particles[pos_to_id(pos[0]-choice*2,pos[1])].alive &&
                            particles[pos_to_id(pos[0]-choice*2,pos[1]-1)].alive
                        )
                    ){
                        // Move the particle
                        particles[check] = particles[id];
                        particles[id].alive = false;
                        done = true;
                    }
                }

                // Check right movement
                if(pos[0]+choice < 500 && pos[0]+choice >= 0 && pos[1]-1 >= 0 && !done)
                {
                    // Get the ID
                    check = pos_to_id(pos[0]+choice,pos[1]-1);

                    // Is this space free
                    if(
                        !particles[check].alive &&
                        !particles[pos_to_id(pos[0]+choice,pos[1])].alive &&
                        !(
                            particles[pos_to_id(pos[0]+choice*2,pos[1])].alive &&
                            particles[pos_to_id(pos[0]+choice*2,pos[1]-1)].alive
                        )
                    ){
                        // Move the particle
                        particles[check] = particles[id];
                        particles[id].alive = false;
                        done = true;
                    }
                }
            }

            // Fluid
            case 'F':
            {

            }
        }
    }

    // Is there sand being added and is this particle in range
    else if(mouseStates[GLUT_LEFT_BUTTON] && distance_between(pos, m_pos) < cursor_radius)
    {
        // Make this particle
        particles[id].alive = true;
        particles[id].color[0] = 0.0f;
        particles[id].color[1] = 0.0f;
        particles[id].color[2] = 0.6f;
        particles[id].model = 'P';
    }
}

【问题讨论】:

标签: c++ opengl cuda


【解决方案1】:

自首次发布以来,CUDA 已支持 OpenGL 互操作性(也支持 Direct3D)。很好documented,如果你已经安装了CUDA的例子,你有几个竞争sample codes你可以学习。

简而言之,您可以将现有的 OpenGL Buffet 对象映射到 CUDA 地址空间,以便计算内核可以读取和写入 OpenGL 内存,从 CUDA 释放内存,然后从该 CUDA 修改后的缓冲区正常渲染。这样做有很大的开销,但性能可能仍然比将数据复制到主机进行渲染要好。

按照建议,您可以阅读this Nvidia 提供的演示文稿中的详尽介绍。

【讨论】:

    【解决方案2】:

    如果我创建一个字节数组(也可以是 int 或其他)并使用 3 个值(或使用 4 个值的 RGBA)上传 RGB 值,我研究了如何创建纹理并使用 CUDA 渲染它们以提高速度一个接一个地定位以形成一个图像,我可以将它加载到 OpenGL 中。

    GLubyte data[width*height*3] = {
        R, G, B,
        R, G, B,
        R, G, B
    }
    

    正如 talonmies 所提到的,我本可以使用 OpenGL 缓冲区对象,但图像似乎可以在屏幕上显示每个单独的像素,而且我在在线查找有关缓冲区对象的信息时遇到了麻烦。

    这是我的显示代码的 sn-p:

    // Setup the pixel varibles
    GLubyte *pixels = new GLubyte[sxy[0]*sxy[1]*3]();
    
    // Get the mouse pos
    int m_x, m_y;
    mousePos(&m_x,&m_y);
    
    // Render on CPU
    if(cpu_only) render_pixels_cpu(
        particles,pixels,sxy,
        m_x,m_y,cursor_radius
    );
    
    else
    {
        // Load the pixels on the GPU
        int N = 512;
        render_pixels<<<2048,N>>>(
            N,d_particles,d_pixels,
            d_sxy,m_x,m_y,cursor_radius
        );
    
        // Copy the pixel data over
        cudaMemcpy(pixels, d_pixels, sizeof(GLubyte)*sxy[0]*sxy[1]*3, cudaMemcpyDeviceToHost);
    }
    
    // Generate and bind the texture
    GLuint tex;
    glGenTextures(1, &tex);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, sxy[0], sxy[1], 0, GL_RGB, GL_UNSIGNED_BYTE, pixels );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    
    // Free the pixels
    delete pixels;
    
    // Draw quads
    glBegin(GL_QUADS);
        glTexCoord2d( 0.0, 0.0);    glVertex2d(-1.0,-1.0);
        glTexCoord2d( 1.0, 0.0);    glVertex2d( 1.0,-1.0);
        glTexCoord2d( 1.0, 1.0);    glVertex2d( 1.0, 1.0);
        glTexCoord2d( 0.0, 1.0);    glVertex2d(-1.0, 1.0);
    glEnd();
    
    // Unbind the texture
    glBindTexture(GL_TEXTURE_2D, NULL);
    
    // Delete the texture
    glDeleteTextures(1, &tex);
    

    Cuda 代码:

    __global__ void render_pixels(
        int N, Sand* particles, GLubyte* pixels, int* sxy,
        int m_x, int m_y, double m_radius
    ){
        // Get the overall ID
        int id = blockIdx.x*N+threadIdx.x;
    
        // Return if out of range
        if(i>sxy[0]*sxy[1])return;
    
        // Get the position
        int pos[2];
        id_to_pos(i,pos,sxy);
    
        // Calculate the image id
        int id = (pos[1]*sxy[0])+pos[0];
    
        // Convert the mouse pos to a position
        int mpos[2] = {m_x, m_y};
    
        // Calculate the distance
        double distance = distance_between(pos, mpos);
    
        // Is the position in range with the mouse
        if((int)distance==(int)m_radius&&m_x>-1&&m_y>-1)
        {
            // Create a circle here
            pixels[(id*3)+0] = (GLubyte)255;
            pixels[(id*3)+1] = (GLubyte)255;
            pixels[(id*3)+2] = (GLubyte)255;
        }
    
        else
        {
            // Set the colours
            pixels[(id*3)+0] = (GLubyte)(particles[i].color[0]*255);
            pixels[(id*3)+1] = (GLubyte)(particles[i].color[1]*255);
            pixels[(id*3)+2] = (GLubyte)(particles[i].color[2]*255);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多