在WebGL着色器中获取准确的整数模运算

9
我希望在WebGL片段着色器中准确计算xy的模数。xy为整数。
绘制mod(x,y),我们得到以下图像:a choppy, irregular chart 生成红黑矩形的实际代码如下:
gl_FragColor = vec4(mod(
  float(int(v_texCoord[0]*15.))/15.,
  float(int(v_texCoord[1]*15.))/15.
), 0, 0, 1);

v_texCoord是一个vec2类型的变量,范围从左上角的0,0到右下角的1,1。float和int都设定为mediump。

通过查看表格,我们可以发现尽管mod(6,6)计算结果正确为0,但是mod(7,7)实际上是7怎么解决这个问题?

我尝试了自己实现mod()函数,然而它也有同样的错误,并产生相同的图形。

int func_mod(int x, int y) {
    return int(float(x)-float(y)*floor(float(x)/float(y)));
}

在JavaScript中,我可以调试它,函数运行得非常完美。然后我尝试了迭代方法,因为我担心自己疯了,并且我也不信任浮点数除法。
int iter_mod(int x, int y) {
    x = int(abs(float(x))); y = int(abs(float(y)));
    for(int i=0; i>-1; i++) {
        if(x < y) break;
        x = x - y;
    }
    return x;
}

这个方法是有效的,但无法制图。当我尝试制图时,会在Linux中出现环0错误并导致崩溃。对于我需要使用的精灵表计算来说,它可以正常工作,但我认为这不是正确的解决方案。
(更新:在我的手机上完美运行。现在不是我的代码出错了,只是我的问题……)
3个回答

9

这里是一个GLSL函数,用(float)参数计算MOD,这些参数应该是整数:

/**
 * Returns accurate MOD when arguments are approximate integers.
 */
float modI(float a,float b) {
    float m=a-floor((a+0.5)/b)*b;
    return floor(m+0.5);
}

请注意,如果a<0且b>0,则返回值将会是>=0,这与其他语言的%运算符不同。

7
你要修改的不是7乘以7,而是7/15乘以7/15。
尝试一下。
gl_FragColor = vec4(mod(
  floor(v_texCoord[0] * 15.),
  floor(v_texCoord[1] * 15.)
) / 15., 0, 0, 1);

你可以在这里看到运行的两个版本。

function render(num) {
  var gl = document.getElementById("c" + num).getContext("webgl");
  var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);

  var arrays = {
    position: [-1, -1, 0, 1, -1, 0, -1, 1, 0, -1, 1, 0, 1, -1, 0, 1, 1, 0],
  };
  var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

  var uniforms = {
    resolution: [gl.canvas.width, gl.canvas.height],
    intMod: num == 1,
  };

  gl.useProgram(programInfo.program);
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  twgl.setUniforms(programInfo, uniforms);
  twgl.drawBufferInfo(gl, bufferInfo);
}

render(0)
render(1);
canvas { margin: 1em; height: 100px; width: 150px; }
div { display: inline-block; }
pre { text-align: center; }
<script src="https://twgljs.org/dist/3.x/twgl.min.js"></script>
  <script id="vs" type="notjs">
attribute vec4 position;

void main() {
  gl_Position = position;
}
  </script>
  <script id="fs" type="notjs">
precision mediump float;

uniform vec2 resolution;
uniform bool intMod;

void main() {
  vec2 v_texCoord = gl_FragCoord.xy / resolution.xy;

  if (!intMod) {
  
    gl_FragColor = vec4(mod(
      float(int(v_texCoord[0]*15.))/15.,
      float(int(v_texCoord[1]*15.))/15.
    ), 0, 0, 1);

  } else {

    gl_FragColor = vec4(mod(
      floor(v_texCoord[0]*15.),
      floor(v_texCoord[1]*15.)
    )/15., 0, 0, 1);
    
  }
  
}
  </script>
<div><canvas id="c0"></canvas><pre>mod with fractions</pre></div>
<div><canvas id="c1"></canvas><pre>mod with ints</pre></div>

你还需要注意mod(取模)0是未定义的,这意味着在不同的GPU上会得到不同的结果。


3
请注意,自从webGL2版本以后,我们现在可以进行int运算,因此这个问题现在可以通过x % y轻松解决。

2
我认为这不正确。如果我尝试执行 x % y,我会得到 '%' : integer modulus operator supported in GLSL ES 3.00 and above only 的错误提示。据我所知,只有在我们在脚本开头指定 #version 130 的情况下,模数运算符才是有效的,但是据我所知,目前没有浏览器支持GLSL ES的这个版本。 - Joshua Walsh
3
我的错,看起来你可以使用#version 300 es并且它可以正常工作。 - Joshua Walsh
当你发现自己写错了什么,那么逻辑上应该删除这篇帖子;-)。 - Fabrice NEYRET
12
我把这个留下来,以防其他人遇到同样的问题。如果你在网上搜索错误信息,人们会建议使用“#version 130”,但这在任何浏览器中都不起作用。我希望我的评论能帮助其他人。 - Joshua Walsh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接