如何在 GLSL 中渲染无限二维网格?
How can I render an infinite 2D grid in GLSL?
理想情况下,我想做的是绘制一个四边形并让 GLSL 处理实际网格线的创建。
到目前为止,在我尝试的顶点着色器中:
#version 400
layout (location = 0) in vec4 in_position;
layout (location = 2) in vec3 in_UV;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
smooth out vec3 ex_UV;
smooth out vec3 ex_originalPosition;
void main()
{
gl_Position = projection * view * model * in_position;
ex_UV = in_UV;
ex_originalPosition = vec3(in_position.xyz);
}
模型矩阵将四边形按一定比例放大,例如 10,000。
#version 400
layout (location = 0) out vec4 color;
smooth in vec3 ex_UV;
smooth in vec3 ex_originalPosition;
uniform vec4 lineColor;
void main(void)
{
if(fract(ex_UV.x / 0.001f) < 0.01f || fract(ex_UV.y / 0.001f) < 0.01f)
color = lineColor;
else
color = vec4(0);
}
我尝试为此使用纹理坐标和世界 space 位置。两者都会产生相同的效果,在某些角度看起来不错,但在其他角度看起来就很糟糕。
我想也许我可以根据距离缩放 alpha,这样我的网格就会消失,但问题是你可以在第二张图片中看到,从屏幕中央,你可以看到即使是那些线条也被渲染成有间隙(网格的点是在原点周围给出一个视觉参考框架)。
有更简单的方法吗?
编辑
要求的截图:
具有 x8 多重采样帧缓冲区的 VBO 行
带有 x8 多重采样帧缓冲区和 glEnable 的 VBO 行(GL_LINE_SMOOTH)
我选择了
我选择了上面的任何一个(哪个并不重要)并简单地降低了 alpha 作为与原点距离的函数。这符合我的目的,尽管这不是我问的问题。
you can see that even those lines are being rendered with gaps through them
哦,当然可以。这是因为你得罪了CG 101。(无罪:)
)
我们使用 GL_LINES
是有原因的;在计算机屏幕上绘制无缝线条并非易事(尽管已解决)。我说的是 something like this。你所做的是对浮点数进行计算,并将相当于绘制子像素线(即间隙)。
我的建议是废弃此 GLSL 代码并将线条生成为常规顶点。它可能会更快,你会得到正确的结果。
您还可以使用常规的雾计算获得您想要的 "fade in the distance" 效果。
aliasing 的一个简单案例。就像多边形渲染一样,您的片段着色器是每个像素一次 运行。颜色仅针对单个中心坐标计算,并不代表真实颜色。
- 您可以创建多样本 FBO 并启用 super-sampling。但这很贵。
- 您可以用数学方法精确计算出每个像素下有多少线条区域和空白网格区域,然后在片段着色器中相应地为它们着色。鉴于它是一个统一的网格,这可能在可能的范围内,但数学可能仍然非常复杂。
Mipmapping 已经为纹理做了这个。创建一个只有几条线的网格纹理并对其进行映射,以便它为您非常大的四边形重复(确保设置 GL_REPEAT
)。为纹理设置正确的 mipmap 过滤参数并调用 glGenerateMipmap
。当您在片段着色器中调用 texture2D()
/texture()
时,OpenGL 会根据相邻像素之间的纹理坐标增量自动计算要使用的 mipmap 级别。最后,设置各向异性过滤以获得更加惊人的网格。
如果您希望网格真正成为 "infinite",我已经看到一些海洋渲染器将网格的边缘连接到具有垂直几何形状的 horizon。如果您在它们之前有足够的网格,您可以将它们设置为一种纯色 - 您的 mipmap 顶层的颜色。
示例(与评论相关):
1024x2 GL_LINES 取自 VBO
45fps(在高清分辨率下绘制 100 次作为基准)
查看关于多重采样解决 GL_LINES 混叠问题的评论。
使用 mipmapping 映射到四边形的 32^2 纹理
954fps(在高清分辨率下绘制 100 次作为基准)
Image img;
int w = 128;
int h = 128;
img.resize(w, h, 1);
for (int j = 0; j < h; ++j)
for (int i = 0; i < w; ++i)
img.data[j*w + i] = (i < w / 16 || j < h / 16 ? 255 : 0);
tex = img.upload();
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
...
//for the quick and dirty, immediate mode
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex);
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex3f(0, 0, 0);
glTexCoord2f(1024, 0); glVertex3f(1, 0, 0);
glTexCoord2f(1024, 1024); glVertex3f(1, 0, 1);
glTexCoord2f(0, 1024); glVertex3f(0, 0, 1);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
理想情况下,我想做的是绘制一个四边形并让 GLSL 处理实际网格线的创建。
到目前为止,在我尝试的顶点着色器中:
#version 400
layout (location = 0) in vec4 in_position;
layout (location = 2) in vec3 in_UV;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
smooth out vec3 ex_UV;
smooth out vec3 ex_originalPosition;
void main()
{
gl_Position = projection * view * model * in_position;
ex_UV = in_UV;
ex_originalPosition = vec3(in_position.xyz);
}
模型矩阵将四边形按一定比例放大,例如 10,000。
#version 400
layout (location = 0) out vec4 color;
smooth in vec3 ex_UV;
smooth in vec3 ex_originalPosition;
uniform vec4 lineColor;
void main(void)
{
if(fract(ex_UV.x / 0.001f) < 0.01f || fract(ex_UV.y / 0.001f) < 0.01f)
color = lineColor;
else
color = vec4(0);
}
我尝试为此使用纹理坐标和世界 space 位置。两者都会产生相同的效果,在某些角度看起来不错,但在其他角度看起来就很糟糕。
我想也许我可以根据距离缩放 alpha,这样我的网格就会消失,但问题是你可以在第二张图片中看到,从屏幕中央,你可以看到即使是那些线条也被渲染成有间隙(网格的点是在原点周围给出一个视觉参考框架)。
有更简单的方法吗?
编辑
要求的截图:
具有 x8 多重采样帧缓冲区的 VBO 行
带有 x8 多重采样帧缓冲区和 glEnable 的 VBO 行(GL_LINE_SMOOTH)
我选择了
我选择了上面的任何一个(哪个并不重要)并简单地降低了 alpha 作为与原点距离的函数。这符合我的目的,尽管这不是我问的问题。
you can see that even those lines are being rendered with gaps through them
哦,当然可以。这是因为你得罪了CG 101。(无罪:)
)
我们使用 GL_LINES
是有原因的;在计算机屏幕上绘制无缝线条并非易事(尽管已解决)。我说的是 something like this。你所做的是对浮点数进行计算,并将相当于绘制子像素线(即间隙)。
我的建议是废弃此 GLSL 代码并将线条生成为常规顶点。它可能会更快,你会得到正确的结果。
您还可以使用常规的雾计算获得您想要的 "fade in the distance" 效果。
aliasing 的一个简单案例。就像多边形渲染一样,您的片段着色器是每个像素一次 运行。颜色仅针对单个中心坐标计算,并不代表真实颜色。
- 您可以创建多样本 FBO 并启用 super-sampling。但这很贵。
- 您可以用数学方法精确计算出每个像素下有多少线条区域和空白网格区域,然后在片段着色器中相应地为它们着色。鉴于它是一个统一的网格,这可能在可能的范围内,但数学可能仍然非常复杂。
Mipmapping 已经为纹理做了这个。创建一个只有几条线的网格纹理并对其进行映射,以便它为您非常大的四边形重复(确保设置
GL_REPEAT
)。为纹理设置正确的 mipmap 过滤参数并调用glGenerateMipmap
。当您在片段着色器中调用texture2D()
/texture()
时,OpenGL 会根据相邻像素之间的纹理坐标增量自动计算要使用的 mipmap 级别。最后,设置各向异性过滤以获得更加惊人的网格。
如果您希望网格真正成为 "infinite",我已经看到一些海洋渲染器将网格的边缘连接到具有垂直几何形状的 horizon。如果您在它们之前有足够的网格,您可以将它们设置为一种纯色 - 您的 mipmap 顶层的颜色。
示例(与评论相关):
1024x2 GL_LINES 取自 VBO
45fps(在高清分辨率下绘制 100 次作为基准)
查看关于多重采样解决 GL_LINES 混叠问题的评论。
使用 mipmapping 映射到四边形的 32^2 纹理
954fps(在高清分辨率下绘制 100 次作为基准)
Image img;
int w = 128;
int h = 128;
img.resize(w, h, 1);
for (int j = 0; j < h; ++j)
for (int i = 0; i < w; ++i)
img.data[j*w + i] = (i < w / 16 || j < h / 16 ? 255 : 0);
tex = img.upload();
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
...
//for the quick and dirty, immediate mode
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex);
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex3f(0, 0, 0);
glTexCoord2f(1024, 0); glVertex3f(1, 0, 0);
glTexCoord2f(1024, 1024); glVertex3f(1, 0, 1);
glTexCoord2f(0, 1024); glVertex3f(0, 0, 1);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);