【发布时间】:2018-06-10 01:48:40
【问题描述】:
我正在尝试使用 GPU 做一些工作以进行布料模拟,但我在使用不同的硬件时遇到了一些问题。我使用threejs作为框架,但我认为这与我遇到的问题无关。
基本上我要做的是上传一个矩阵和该矩阵的逆矩阵,以便将点从本地坐标转换为世界坐标,在世界坐标中进行一些数学运算(如碰撞检测),然后将它们转换回本地坐标。当我使用浮点纹理时,这在我的笔记本电脑上效果很好,但是我注意到在我的手机上有一些奇怪的伪影:
正确:
不正确:
在进行了一些调试后,我将其范围缩小到了两个问题。它们都与小数精度有关。由于约束(以及约束期间的精度问题)导致的顶点折叠以及使用矩阵乘法和逆矩阵时失去精度。
我认为问题与精度有关的原因是,如果我使用浮点纹理,它可以在我的计算机上运行,但如果我使用半浮点数,我也会遇到同样的问题。我的手机支持浮点纹理,这是我对为什么会在我的手机上发生这种情况感到困惑的原因之一。我缩小了问题的范围,因此禁用了所有布料模拟,如果我在计算机上运行具有半浮动纹理的应用程序,没有任何重力,但通过变换和反转平面会以奇怪的方式闪烁
如果禁用了转换和反转,那么它看起来很正常。
我不知道如何处理这个问题,或者我是否走在正确的道路上。我相信半浮点纹理的小数精度有限,但我不明白为什么这会导致我的问题,因为它应该只影响着色器的输出,而不影响着色器中的数学运算。
着色器的代码如下所示:
' vec2 cellSize = 1.0 / res;',
' vec4 pos = texture2D(vertexPositions, vuv.xy );',
' vec2 newUV;',
' if(type == 0.0){',
' float px = floor(vuv.x * res.x );',
' float spacingx = px- (2.0 * floor(px/2.0));',
' float py = floor(vuv.y * res.y );',
' float spacingy = py- (2.0 * floor(py/2.0));',
' float total = spacingx + spacingy;',
' total = total- (2.0 * floor(total/2.0));',
' if(total == 0.0){',
' newUV = vuv + (direction * cellSize);',
' }',
' else{',
' newUV = vuv - (direction * cellSize);',
' }',
' }',
' if(type == 1.0){',
' float px = floor(vuv.x * res.x );',
' float spacingx = px- (2.0 * floor(px/2.0));',
' float total = spacingx;',
' if(total == 0.0){',
' newUV = vuv + (direction * cellSize);',
' }',
' else{',
' newUV = vuv - (direction * cellSize);',
' }',
' }',
' vec4 totalDisplacement = vec4(0.0);',
' if(newUV.x > 0.0 && newUV.x < 1.0 && newUV.y > 0.0 && newUV.y < 1.0){ ',
' vec4 posOld = texture2D(vertexPositionsStart, vuv);' ,
' vec4 posOld2 = texture2D(vertexPositionsStart, newUV);' ,
' float targetDistance = length(posOld - posOld2);',
' vec4 newPos = texture2D(vertexPositions, newUV);',
' float dx = pos.x - newPos.x;',
' float dy = pos.y - newPos.y;',
' float dz = pos.z - newPos.z;',
' float distance = sqrt(dx * dx + dy * dy + dz * dz);',
' float difference = targetDistance- distance;',
' float percent = difference / distance / 2.0;',
' float offsetX = dx * percent * rigid;',
' float offsetY = dy * percent * rigid;',
' float offsetZ = dz * percent * rigid;',
' totalDisplacement.x += offsetX;',
' totalDisplacement.y += offsetY;',
' totalDisplacement.z += offsetZ;',
' }',
' }',
' }',
' pos += totalDisplacement;',
' if( vuv.x > 1.0 - cellSize.x && topConstrain == 1 ){',
' pos =transformation * texture2D(vertexPositionsStart, vuv.xy );',
' }',
' if( vuv.x < cellSize.x && bottomConstrain == 1 ){',
' pos =transformation * texture2D(vertexPositionsStart, vuv.xy );',
' }',
' if( vuv.y < cellSize.y && leftConstrain == 1 ){',
' pos =transformation * texture2D(vertexPositionsStart, vuv.xy );',
' }',
' if( vuv.y > 1.0 - cellSize.y && rightConstrain == 1 ){',
' pos =transformation * texture2D(vertexPositionsStart, vuv.xy );',
' }',
' gl_FragColor = vec4( pos.xyz , 1.0 );',
【问题讨论】:
-
GLES 的精度要求远低于桌面 GL(尤其是使用 GLES2 时)。当您的着色器 ALU 仍然使用低得多的精度时,如果您使用完整的 fp32 纹理,这将无济于事。
-
我明白了。所以你认为问题在于我手机的着色器 ALU 不支持足够的精度。我不明白为什么如果我在我的计算机上使用半浮动纹理仍然会发生这个问题。不过,这似乎是一个合理的解释
-
尝试使用相对坐标系,这样变换后的顶点就不会离你的matrices origins太远。计算后转换回原始坐标系。这样,您将避免使用高幅度向量与矩阵相乘,这会产生精度问题。欲了解更多信息,请参阅ray and ellipsoid intersection accuracy improvement
-
感谢 Spektre 的建议,我将使用一种方法,避免同时使用转换矩阵。我仍然在精度方面遇到一些问题(我相信这是精度。顶点在手机上缓慢地向中心移动,而在计算机上表现正常。虽然它们都应该支持 highp 浮点数
-
所以最简单的方法是“尝试提高精度”。更好的方法是“找到更好的(数值稳定等)算法”。
标签: matrix three.js glsl webgl