1

我在 WebGL 中渲染几何图形,并在 Chrome for Android(不需要的工件,左)和 Chrome for Windows(右)上得到不同的结果:

安卓 视窗

我试过了:

  • 使用 WebGL2 和 WebGL 上下文
  • 在传递索引缓冲区时使用gl.UNSIGNED_INTgl.UNSIGNED_SHORT
  • 在逗号后将所有属性值四舍五入到小数点后 4 位

这是一些代码:

我已经“简化”了我的顶点着色器以缩小问题范围:

#version 100
precision mediump float;

attribute vec3 aPosition;
attribute vec3 aColor;
attribute vec3 aNormal;
varying vec3 vColor;
varying vec3 vNormal;
varying vec3 vPosition;
varying mat4 vView;
uniform mat4 uWorld;
uniform mat4 uView;
uniform mat4 uProjection;
uniform mat3 uNormal;
uniform float uTime;

void main() {
    vColor = aColor;
    vNormal = uNormal * aNormal;
    vPosition = (uWorld * vec4(aPosition, 1.0)).xyz;
    gl_Position = uProjection * uView * uWorld * vec4(aPosition, 1.0);
}

我通过交错缓冲区传递属性(所有值在逗号后四舍五入到小数点后四位):

gl.bindBuffer(gl.ARRAY_BUFFER, this.interleaved.buffer)
const bytesPerElement = 4
gl.vertexAttribPointer(this.interleaved.attribLocation.position, 3, gl.FLOAT, gl.FALSE, bytesPerElement * 9, bytesPerElement * 0)
gl.vertexAttribPointer(this.interleaved.attribLocation.normal, 3, gl.FLOAT, gl.FALSE, bytesPerElement * 9, bytesPerElement * 3)
gl.vertexAttribPointer(this.interleaved.attribLocation.color, 3, gl.FLOAT, gl.FALSE, bytesPerElement * 9, bytesPerElement * 6)

我正在使用索引缓冲区来绘制几何图形:

gl.drawElements(gl.TRIANGLES, this.indices.length, gl.UNSIGNED_INT, 0)

指数范围从0..3599,因此gl.UNSIGNED_INT应该足够大。

我没有收到任何错误消息。在 Windows 上一切都很好,只有 Android 上的 Chrome 有工件。

4

1 回答 1

3

这些伪影是由不同设备上的着色器缺乏精度引起的。使用precision highp float;解决了这个问题。

lowp,对应不同硬件上的不同值mediumphighp只对OpenGL ES平台有影响。桌面平台利用了 OpenGL 或 Direct X(相对于 OpenGL ES)的完整实现,因此,在桌面机器上,这些限定符都对应于相同的值。移动 WebGL 通常利用 OpenGL ES,因此在移动设备上,这些限定符对应于不同的值。

在此示例中lowpmediump会导致 Android 出现故障,需要替换为highp.

通常,如果性能很重要,建议使用尽可能低的精度来减少着色器执行时间WebGLRenderingContext.getShaderPrecisionFormat()返回着色器数据类型的精度,分别用于顶点和片段着色器 ( MDN )。要使用尽可能低的精度,可以在使用的着色器前面加上所需的精度限定符,基于WebGLRenderingContext.getShaderPrecisionFormat().

例如

const lowPrecisionFormat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT)
const mediumPrecisionFormat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT)
const highPrecisionFormat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT)

if (lowPrecisionFormat.precision >= 23)
    shaderString = "precision lowp float;" + shaderString
else if (mediumPrecisionFormat.precision >= 23)
    shaderString = "precision mediump float;" + shaderString
else
    shaderString = "precision highp float;" + shaderString

在我测试过的 Android 硬件gl.getShaderPrecisionFormat()上,lowp返回mediump了相同的结果(低于 Windows),同时highp返回了与我的 Windows 机器上一样高的精度。

于 2019-08-24T23:42:57.557 回答