在 GLSL 片段着色器中使用整数 - 遇到无法解释的伪像
Using integers in GLSL fragment shaders - experiencing unexplained artifacts
我有一个六边形网格,我想对其进行纹理处理。我想使用一个纹理,其中有 16 个不同的子纹理排列在 4x4 网格中。网格中的每个“节点”都有一个图像类型,我想在它们之间平滑地融合。我实现它的方法是成对渲染三角形,并在两个面的所有顶点上编码 4 种图像类型,以及一组 4 个加权因子(它们是两个三角形的重心坐标)。然后我可以使用这两个东西在任何图像类型组合之间平滑地混合。
这是我正在使用的片段着色器。这些问题是由于使用 int
类型引起的,但我不明白为什么。如果我只使用前四个子纹理,那么我可以将 idx
更改为 float
并将 Y 坐标硬编码为 0,然后它会按我预期的那样工作。
vec2 offset(int idx) {
vec2 v = vec2(idx % 4, idx / 4);
return v / 4.0;
}
void main(void) {
//
// divide the incoming UVs into one of 16 regions. The
// offset() function should take an integer from 0..15
// and return the offset to that region in the 4x4 map
//
vec2 uv = v_uv / 4.0;
//
// The four texture regions involved at
// this vertex are encoded in vec4 t_txt. The same
// values are stored at all vertices, so this doesn't
// vary across the triangle
//
int ia = int(v_txt.x);
int ib = int(v_txt.y);
int ic = int(v_txt.z);
int id = int(v_txt.w);
//
// Use those indices in the offset function to get the
// texture sample at that point
//
vec4 ca = texture2D(txt, uv + offset(ia));
vec4 cb = texture2D(txt, uv + offset(ib));
vec4 cc = texture2D(txt, uv + offset(ic));
vec4 cd = texture2D(txt, uv + offset(id));
//
// Merge them with the four factors stored in vec4 v_tfact.
// These vary for each vertex
//
fragcolour = ca * v_tfact.x
+ cb * v_tfact.y
+ cc * v_tfact.z
+ cd * v_tfact.w;
}
事情是这样的:
(我的“一对三角形”实际上是20个左右,你可以在神器中看到它们的结构,但效果是一样的)
这种伪影的行为有点像 z-fighting:四处移动场景使它闪闪发光并疯狂移动。
为什么这不像我预期的那样工作?
我可以退而求其次的一个解决方案是简单地使用一维纹理贴图,所有 16 个子图像都在一条水平线上,然后我可以将所有内容切换为浮点,因为我不需要 modulo/integer-divide 映射 idx->x,y
的过程,但这感觉很笨拙,我至少想了解这里发生了什么。
它应该是这样的,尽管只使用了 4 个子图像:
见OpenGL Shading Language 4.60 Specification - 5.4.1. Conversion and Scalar Constructors
When constructors are used to convert a floating-point type to an integer type, the fractional part of the floating-point value is dropped.
因此 int(v_txt.x)
不舍入 v_txt.x
,而是截断 v_txt.x
在构造整数值之前,您必须 round
将值设为最接近的整数:
int ia = int(round(v_txt.x));
int ib = int(round(v_txt.y));
int ic = int(round(v_txt.z));
int id = int(round(v_txt.w));
或者在构造积分值之前加0.5:
int ia = int(v_txt.x + 0.5);
int ib = int(v_txt.y + 0.5);
int ic = int(v_txt.z + 0.5);
int id = int(v_txt.w + 0.5);
我有一个六边形网格,我想对其进行纹理处理。我想使用一个纹理,其中有 16 个不同的子纹理排列在 4x4 网格中。网格中的每个“节点”都有一个图像类型,我想在它们之间平滑地融合。我实现它的方法是成对渲染三角形,并在两个面的所有顶点上编码 4 种图像类型,以及一组 4 个加权因子(它们是两个三角形的重心坐标)。然后我可以使用这两个东西在任何图像类型组合之间平滑地混合。
这是我正在使用的片段着色器。这些问题是由于使用 int
类型引起的,但我不明白为什么。如果我只使用前四个子纹理,那么我可以将 idx
更改为 float
并将 Y 坐标硬编码为 0,然后它会按我预期的那样工作。
vec2 offset(int idx) {
vec2 v = vec2(idx % 4, idx / 4);
return v / 4.0;
}
void main(void) {
//
// divide the incoming UVs into one of 16 regions. The
// offset() function should take an integer from 0..15
// and return the offset to that region in the 4x4 map
//
vec2 uv = v_uv / 4.0;
//
// The four texture regions involved at
// this vertex are encoded in vec4 t_txt. The same
// values are stored at all vertices, so this doesn't
// vary across the triangle
//
int ia = int(v_txt.x);
int ib = int(v_txt.y);
int ic = int(v_txt.z);
int id = int(v_txt.w);
//
// Use those indices in the offset function to get the
// texture sample at that point
//
vec4 ca = texture2D(txt, uv + offset(ia));
vec4 cb = texture2D(txt, uv + offset(ib));
vec4 cc = texture2D(txt, uv + offset(ic));
vec4 cd = texture2D(txt, uv + offset(id));
//
// Merge them with the four factors stored in vec4 v_tfact.
// These vary for each vertex
//
fragcolour = ca * v_tfact.x
+ cb * v_tfact.y
+ cc * v_tfact.z
+ cd * v_tfact.w;
}
事情是这样的:
(我的“一对三角形”实际上是20个左右,你可以在神器中看到它们的结构,但效果是一样的)
这种伪影的行为有点像 z-fighting:四处移动场景使它闪闪发光并疯狂移动。
为什么这不像我预期的那样工作?
我可以退而求其次的一个解决方案是简单地使用一维纹理贴图,所有 16 个子图像都在一条水平线上,然后我可以将所有内容切换为浮点,因为我不需要 modulo/integer-divide 映射 idx->x,y
的过程,但这感觉很笨拙,我至少想了解这里发生了什么。
它应该是这样的,尽管只使用了 4 个子图像:
见OpenGL Shading Language 4.60 Specification - 5.4.1. Conversion and Scalar Constructors
When constructors are used to convert a floating-point type to an integer type, the fractional part of the floating-point value is dropped.
因此 int(v_txt.x)
不舍入 v_txt.x
,而是截断 v_txt.x
在构造整数值之前,您必须 round
将值设为最接近的整数:
int ia = int(round(v_txt.x));
int ib = int(round(v_txt.y));
int ic = int(round(v_txt.z));
int id = int(round(v_txt.w));
或者在构造积分值之前加0.5:
int ia = int(v_txt.x + 0.5);
int ib = int(v_txt.y + 0.5);
int ic = int(v_txt.z + 0.5);
int id = int(v_txt.w + 0.5);