Android 上使用 WebGL 的顶点着色器工件
Vertex shader artifacts with WebGL on Android
我在 WebGL 中渲染几何图形,在 Chrome 上 Android(不需要的伪像,左)和 Chrome 上 Windows(右)得到不同的结果):
我试过:
- 使用 WebGL2 和 WebGL 上下文
- 在传递索引缓冲区时使用
gl.UNSIGNED_INT
和 gl.UNSIGNED_SHORT
- 将所有属性值四舍五入到逗号后的 4 位小数
下面是一些代码:
我已经 "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;
解决了这个问题。
lowp
、mediump
和highp
在不同的硬件上对应不同的值,只对OpenGL ES平台有影响。桌面平台充分利用 OpenGL 或 Direct X(与 OpenGL ES 相对),因此,在桌面计算机上,这些限定符都对应于相同的值。 Mobile WebGL 通常利用 OpenGL ES,因此在移动设备上这些限定符对应于不同的值。
在此示例中,lowp
和 mediump
导致 Android 出现故障,需要替换为 highp
。
通常,如果性能很重要,则使用尽可能低的精度是 recommended to reduce shader execution time。
WebGLRenderingContext.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 硬件上,lowp
和 mediump
返回相同的结果(低于 Windows ),而 highp
返回的精度与我的 Windows 机器一样高。
我在 WebGL 中渲染几何图形,在 Chrome 上 Android(不需要的伪像,左)和 Chrome 上 Windows(右)得到不同的结果):
我试过:
- 使用 WebGL2 和 WebGL 上下文
- 在传递索引缓冲区时使用
gl.UNSIGNED_INT
和gl.UNSIGNED_SHORT
- 将所有属性值四舍五入到逗号后的 4 位小数
下面是一些代码:
我已经 "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;
解决了这个问题。
lowp
、mediump
和highp
在不同的硬件上对应不同的值,只对OpenGL ES平台有影响。桌面平台充分利用 OpenGL 或 Direct X(与 OpenGL ES 相对),因此,在桌面计算机上,这些限定符都对应于相同的值。 Mobile WebGL 通常利用 OpenGL ES,因此在移动设备上这些限定符对应于不同的值。
在此示例中,lowp
和 mediump
导致 Android 出现故障,需要替换为 highp
。
通常,如果性能很重要,则使用尽可能低的精度是 recommended to reduce shader execution time。
WebGLRenderingContext.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 硬件上,lowp
和 mediump
返回相同的结果(低于 Windows ),而 highp
返回的精度与我的 Windows 机器一样高。