如何在 GLSL 中渲染无限的 2D 网格?
Posted
技术标签:
【中文标题】如何在 GLSL 中渲染无限的 2D 网格?【英文标题】:How can I render an infinite 2D grid in GLSL? 【发布时间】:2015-06-15 10:22:36 【问题描述】:理想情况下,我想做的是绘制一个四边形并让 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);
我已经尝试为此使用纹理坐标和世界空间位置。两者都会产生相同的效果,在某些角度看起来不错,但在其他角度看起来很可怕。
我想也许我可以根据距离缩放 alpha,这样我的网格就会消失得无影无踪,但问题是你可以在第二张图片中看到,从屏幕的中心,你甚至可以看到这些线在渲染时带有间隙(网格的点是为了在原点周围提供一个视觉参考框架)。
有没有更简单的方法来做到这一点?
编辑
按要求截图:
带有 x8 多采样帧缓冲区的 VBO 行
具有 x8 多采样帧缓冲区和 glEnable(GL_LINE_SMOOTH) 的 VBO 行
我决定了
我选择了上述任何一种(实际上并不重要),并简单地将 alpha 降低为与原点的距离的函数。这符合我的目的,尽管这不是我问的问题。
【问题讨论】:
我很想看看 GL_LINES 和多重采样的结果。你能发布一个截图作为更新吗? 结果与您的问题基本相同。然而,最后我发现我并不真的需要一个无限平面,所以我改用了一个小得多的四边形,它从原点开始有一个圆形渐变作为 alpha。它符合我的目的,尽管我承认你的回答对于我提出的实际问题是正确的。 【参考方案1】:aliasing 的简单案例。就像多边形渲染一样,您的片段着色器每个像素运行一次。颜色仅针对单个中心坐标计算,不代表真实颜色。
-
您可以创建多重采样 FBO 并启用超级采样。但这很贵。
您可以用数学方法准确计算每个像素下有多少线条区域和空网格区域,然后在片段着色器中对它们进行相应的着色。鉴于它是一个统一的网格,这可能是可能的,但数学可能仍然会变得相当复杂。
Mipmapping 已经为纹理做到了这一点。只用几条线创建一个网格纹理并映射它,以便它为你真正的大四边形重复(确保设置GL_REPEAT
)。为纹理设置正确的 mipmap 过滤参数并调用glGenerateMipmap
。当您在片段着色器中调用texture2D()
/texture()
时,OpenGL 会根据相邻像素之间的纹理坐标增量自动计算要使用的 mipmap 级别。最后,设置各向异性过滤以获得更令人惊叹的网格。
如果您希望网格真正“无限”,我已经看到一些海洋渲染器使用垂直几何将网格的边缘连接到地平线。如果您在它们之前有足够的网格,则可以将它们设置为一种平面颜色 - mipmap 顶层的颜色。
示例(与 cmets 相关):
1024x2 GL_LINES 从 VBO 绘制45fps(在高清分辨率下绘制 100 次)
请参阅有关多重采样的 cmets 以解决 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);
【讨论】:
纹理根本不能保证无缝线。此外,您引入了许多不必要的片段处理和采样,其中在行方法中仅运行实际对行有贡献的片段。我们的目标是只绘制一个 2D 网格,对我来说,你所有的方法都是一个巨大的矫枉过正。 对于您的第一点,他专门提供了应用纹理以提供抗锯齿线条的技术,然后比较了天真纹理四边形与做得很好。对于第二点,我们仍然需要引用哪种技术更快。 有趣的性能结果。我现在可以问心无愧地对此表示赞同:)【参考方案2】:你可以看到,即使是这些线条也被渲染成间隙
哦,我们当然可以。这是因为你冒犯了 CG 101。(无意冒犯:)
)
我们使用GL_LINES
是有原因的;在计算机屏幕上绘制无缝线并非易事(尽管已解决)。我说的是something like this。您所做的是对浮点数进行计算,并将相当于绘制子像素线(即间隙)。
我的建议是废弃此 GLSL 代码并将线条生成为常规顶点。它可能会更快,并且您会得到正确的结果。
您还可以使用常规雾计算获得所需的“远距离淡入”效果。
【讨论】:
...现在我觉得很傻。您是否知道几何着色器是否可以将三角形作为输入,将线条作为输出? @NeomerArcana 如果您真的想在着色器中执行此操作(您可以在空气中闻到它的味道:D),那么我会绘制一个四边形并将其镶嵌成所需的密度。同样,这将利用预先制作的硬件支持来承担此类负载。除此之外,您可以在几何着色器中修剪图元,因此将每个三角形减少为线应该没问题。就layout(triangles) in; layout(lines) out;
是的,已经在几何着色器中。得到一个 2D 正方形,现在我只需要细分。
@NeomerArcana 澄清一下,我的意思是使用镶嵌着色器;在 GS 中每一个三角形输出一条线就可以了。不会有每个三角形输出数千条线!
似乎无关紧要,我使用的是多重采样缓冲区,到目前为止 GL_LINES 渲染得非常好并且抗锯齿。以上是关于如何在 GLSL 中渲染无限的 2D 网格?的主要内容,如果未能解决你的问题,请参考以下文章