使用几何着色器优化体素渲染
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,你可以只用一个数组来存储每个方向的面输出的基向量
我用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,你可以只用一个数组来存储每个方向的面输出的基向量