Android 上使用 WebGL 的顶点着色器工件

Vertex shader artifacts with WebGL on Android

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

我试过:

下面是一些代码:

我已经 "dumbed down" 我的顶点着色器来缩小问题范围:

#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 上一切都很好,只有 Chrome 在 Android 上有瑕疵。

这些瑕疵是由于不同设备上的着色器精度不够造成的。使用 precision highp float; 解决了这个问题。

lowpmediumphighp在不同的硬件上对应不同的值,只对OpenGL ES平台有影响。桌面平台充分利用 OpenGL 或 Direct X(与 OpenGL ES 相对),因此,在桌面计算机上,这些限定符都对应于相同的值。 Mobile WebGL 通常利用 OpenGL ES,因此在移动设备上这些限定符对应于不同的值。

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

通常,如果性能很重要,则使用尽可能低的精度是 recommended to reduce shader execution timeWebGLRenderingContext.getShaderPrecisionFormat() returns 着色器数据类型的精度,分别用于顶点和片段着色器 (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

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