当条件语句中使用统一坐标或纹理坐标时,修改条件语句内浮点变量的值会得到奇怪的结果。这种情况发生在许多移动设备上的片段着色器内,例如 iPhone 4S、iPhone 5、iPhone 5S、iPhone 6(Safari、Chrome 和 Firefox)、Samsung J3(Android 浏览器、Chrome)以及很可能其他设备。 似乎该值可以与另一个 float 进行比较,但是当除以该值时,如果除数比该值大一半以上,则该值为零。 这是一个简单的测试用例。 R、G 和 B 颜色的所有值预计都为相同的 128,因此输出应为灰色,但是在这些移动设备上,R 等于 0,因此输出为海色。 jsfiddle 是 here
html 是:
<html>
<head>
<title>Hello</title>
</head>
<body>
<script src="js/shadertest.js"></script>
</body>
js代码为:
function test(){
var create3DContext = function(canvas, opt_attribs) {
var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
var context = null;
for (var ii = 0; ii < names.length; ++ii) {
try {
context = canvas.getContext(names[ii], opt_attribs);
} catch(e) {}
if (context) {
break;
}
}
return context;
}
var compileShader = function(type, source) {
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if( !gl.getShaderParameter(shader, gl.COMPILE_STATUS) ) {
throw new Error(gl.getShaderInfoLog(shader));
}
return shader;
};
var canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = 1;
canvas.style.width = '100%';
canvas.style.height = '100%';
document.body.appendChild(canvas);
var gl = create3DContext(canvas, {});
// init buffers
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]), gl.STATIC_DRAW);
// The main IDWT Shader
var webglProgram = gl.createProgram();
gl.attachShader(webglProgram, compileShader(gl.VERTEX_SHADER, SHADER_VERTEX_IDENTITY));
gl.attachShader(webglProgram, compileShader(gl.FRAGMENT_SHADER, SHADER_FRAGMENT));
gl.linkProgram(webglProgram);
if( !gl.getProgramParameter(webglProgram, gl.LINK_STATUS) ) {
throw new Error(gl.getProgramInfoLog(webglProgram));
}
gl.useProgram(webglProgram);
var vertexAttr = gl.getAttribLocation(webglProgram, 'vertex');
gl.enableVertexAttribArray(vertexAttr);
gl.vertexAttribPointer(vertexAttr, 2, gl.FLOAT, false, 0, 0);
gl.uniform1i(gl.getUniformLocation(webglProgram, 'vertical'), 0);
gl.viewport(0, 0, 1, 1);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
var pixels = new Uint8Array(4);
var imgData = gl.readPixels(0,0,1,1,gl.RGBA,gl.UNSIGNED_BYTE, pixels);
console.log('R = '+pixels[0]);
console.log('G = '+pixels[1]);
console.log('B = '+pixels[2]);
console.log('A = '+pixels[3]);
}
var SHADER_FRAGMENT = [
'precision mediump float;',
'uniform int vertical;',
'void main() {',
'float valueLow = 60.0;',
'float valueHigh = 60.0;',
'if ( vertical == 1)',
'{',
'valueLow = 60.0;',
'}',
'gl_FragColor.rgba = vec4((valueLow+10000.0)/20000.0, (valueHigh+10000.0)/20000.0, (60.0+10000.0)/20000.0, 1.0);',
'}'
].join('\n'),
SHADER_VERTEX_IDENTITY = [
'attribute vec2 vertex;',
'varying vec2 texCoord;',
'void main() {',
'texCoord = vertex;',
'gl_Position = vec4((vertex * 2.0 - 1.0) * vec2(1, -1), 0.0, 1.0);',
'}'
].join('\n');
test();
编辑 我找到了一种解决方法,即将数学与相关变量放在同一条件语句中,如 jsfiddle 所示。 ,但我希望有更优雅的方式。
最佳答案
我不知道如何解决低精度问题,除了改变你的数学以免遇到问题,但你可以有条件地要求高精度
#if GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#else
大多数现代手机都支持高性能,但会牺牲速度,但一些较旧的手机(例如 iPhone3GS)则不支持。
您还可以设置特定变量的精度
#if GL_FRAGMENT_PRECISION_HIGH
highp vec3 someVar;
#else
mediump vec3 someVar;
#else
在桌面上,无论您要求什么,就规范而言都可以,您总是会得到高分。
关于floating-point - Webgl 奇怪的 float 学,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44361299/