简体   繁体   English

如何使用GPU绘制OpenGL像素

[英]How to draw OpenGL pixels with the GPU

Brief: 简要:
I'm making a Powder Toy that makes use of Parallel Processing to do the game physics, its dealing with a 500 x 500 area of powder. 我正在制作一个粉末玩具,该玩具利用并行处理进行游戏物理处理,处理的是500 x 500的粉末区域。 The game does mostly everything with the particles on the GPU but it uses the CPU to render the particles (decreases speed by a lot). 该游戏几乎对GPU上的粒子执行所有操作,但它使用CPU渲染粒子(速度大大降低)。 How would I render the particles on the GPU instead of the CPU? 如何在GPU而不是CPU上渲染粒子? I'm mostly keeping my particle data on the GPU because most of the operations happen there, and cudaMemcpy is quite slow, making by project uncontrollably lag when its on host memory. 我主要将粒子数据保留在GPU上,因为大多数操作都在GPU上进行,而cudaMemcpy相当慢,导致项目在主机内存上时无法控制地滞后。

Code: 码:
Here's my display function 这是我的显示功能

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

And where the processing for the sand happens: 以及在哪里进行砂处理:

__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';
    }
}

Since it was first released, CUDA has had support for OpenGL interoperability (Direct3D also). 自从首次发布以来,CUDA就已经支持OpenGL互操作性(也支持Direct3D)。 It is well documented , and if you have installed the CUDA examples, you have several compete sample codes you can study. 它有据可查 ,并且,如果您已安装CUDA示例,则可以研究几种竞争示例代码

In short, you can map an existing OpenGL buffet object into the CUDA address space so that a compute kernel can read and write to the OpenGL memory, release the memory from CUDA, and then render from that CUDA modified buffer as normal. 简而言之,您可以将现有的OpenGL自助对象映射到CUDA地址空间中,以便计算内核可以读写OpenGL内存,从CUDA释放内存,然后照常从CUDA修改后的缓冲区进行渲染。 There are significant overheads in doing this, but performance may still be better than copying data to the host for rendering. 这样做有很多开销,但是性能可能仍然比将数据复制到主机进行渲染要好。

As suggested, you can read a thorough introduction in this Nvidia supplied presentation. 至于建议,你可以读取一个全面介绍这个 Nvidia公司提供的演示文稿。

I worked out how to create textures and render them using CUDA for extra speed, if I create an array of bytes (could also be int or something else) and upload the RGB values using 3 values (or RGBA using 4 values) positioned after each other to form an image I can load it into OpenGL. 如果我创建了一个字节数组(也可以是int或其他东西),并使用每个之后放置的3个值(或RGBA使用4个值)上载RGB值,我想出了如何创建纹理并使用CUDA渲染它们以提高速度的方法。其他形成图像,我可以将其加载到OpenGL中。

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

As talonmies mentioned I could have used an OpenGL buffer object but an image seems to work for displaying each individual pixel on the screen, and I was having trouble finding information about buffer objects online. 正如前面所说的,我本可以使用OpenGL缓冲区对象,但是图像似乎可以在屏幕上显示每个像素,并且我在网上查找有关缓冲区对象的信息时遇到了麻烦。

Heres a snippet of my display code: 以下是我的显示代码的一段代码:

// 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 code: 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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM