使用几何着色器优化体素渲染

Voxel rendering optimization with geometry shader

我用C++ + SDL2 + GLEW + Opengl 4.1编程一个有点像Minecraft的体素小游戏。

我正在尽我所能优化体素渲染。

我把世界分成块,然后把这个块分成块。

每个区块包含 16x16x16 个方块。

现在,如果编辑一个块(remove/place 一个块),我将重建完整的块和相邻的块,并将其与 vao 和 vbo 一起上传到显卡。

现在为了最小化我必须从 cpu 传输到 gpu 的顶点数据,我使用几何着色器。

首先,这是个好主意吗?

我的意思是几何着色器必须为每个体素面计算基元的每一帧。

但是,我对顶点着色器进行了编程,因此我只需要为每个块面传递一个顶点。

为了实现这一点,我使用了 vec4。

前 3 个元素 (x, y, z) 我用于方块位置和第 4 个元素 (w) 我用来指示面部显示的方向。

0表示返回, 1表示前面, 2表示左边, 3表示正确, 4表示底部, 5 表示顶部。

请暂时忽略UV和法线。

此外,我上传了 GLbyte 而不是 GLfloat。

这是个好主意吗?

better/faster 方法是什么?

    #version 410 

    uniform mat4 un_Combined; 

    layout(points) in; 
    layout(triangle_strip, max_vertices = 4) out; 

    in vec2 ge_UV[]; 

    out vec2 fr_UV; 
    out vec3 fr_Normal; 

    void main() 
    { 

        vec4 o = gl_in[0].gl_Position.xyzw; 

        if(o.w == 0) 
        { 
            gl_Position = un_Combined * vec4(o.x, o.y, o.z, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x, o.y + 1, o.z, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x + 1, o.y, o.z, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x + 1, o.y + 1, o.z, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, -1); 
            EmitVertex(); 
        } 
        else 
        if(o.w == 1) 
        { 
            gl_Position = un_Combined * vec4(o.x + 1, o.y, o.z + 1, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x + 1, o.y + 1, o.z + 1, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x, o.y, o.z + 1, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x, o.y + 1, o.z + 1, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 
        } 
        else 
        if(o.w == 2) 
        { 
            gl_Position = un_Combined * vec4(o.x, o.y, o.z + 1, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x, o.y + 1, o.z + 1, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x, o.y, o.z, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x, o.y + 1, o.z, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(-1, 0, 0); 
            EmitVertex(); 
        } 
        else 
        if(o.w == 3) 
        { 
            gl_Position = un_Combined * vec4(o.x + 1, o.y, o.z, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x + 1, o.y + 1, o.z, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x + 1, o.y, o.z + 1, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x + 1, o.y + 1, o.z + 1, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(1, 0, 0); 
            EmitVertex(); 
        } 
        else 
        if(o.w == 4) 
        { 
            gl_Position = un_Combined * vec4(o.x + 1, o.y, o.z, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x + 1, o.y, o.z + 1, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x, o.y, o.z, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x, o.y, o.z + 1, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, -1, 0); 
            EmitVertex(); 
        } 
        else 
        { 
            gl_Position = un_Combined * vec4(o.x, o.y + 1, o.z + 1, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x + 1, o.y + 1, o.z + 1, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x, o.y + 1, o.z, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 0, 1); 
            EmitVertex(); 

            gl_Position = un_Combined * vec4(o.x + 1, o.y + 1, o.z, 1); 
            fr_UV = vec2(0, 0); 
            fr_Normal = vec3(0, 1, 0); 
            EmitVertex(); 
        } 

        EndPrimitive(); 
    }

您可以自己测试一下,但一般来说,几何着色器会减慢速度而不是加快速度。据我了解,AMD GPU 有一个特殊情况的几何着色器,它总是输出恰好 4 个顶点(在这种情况下为真),而 Intel GPU 可以具有相对于其他 GPU 的快速几何着色器,但一般来说,没有固定输出大小优化,不同的几何着色器必须同步。因此,您可能会在某些或许多或大多数实现中遇到快速案例,但您必须进行测试。

考虑到顶点数据可能不是很大:比如说,每个顶点 8 个字节,或每个面 32 个字节。您甚至可以为所有块重复使用相同的索引缓冲区(例如 0、1、2、3、0xffff、4、5、6、7、0xffff,...)。这将问题转化为非常传统的时间与 space 权衡。您可以花更多的时间在几何着色器上,或者花更多的时间 space 来存储完整的顶点数据。您的程序 运行 是否进入内存限制?还是您 运行 遇到了计算限制?测试.

请注意,您的几何着色器可以编写成无分支的。不用if/else,你可以只用一个数组来存储每个方向的面输出的基向量