Webgl 奇怪的浮点数数学
Webgl strange float math
当在条件语句中使用统一坐标或纹理坐标时,在条件语句中修改浮点变量的值会得到奇怪的结果。这发生在许多移动设备上的片段着色器中,例如 iPhone 4S、iPhone 5、iPhone 5S、iPhone 6(Safari、Chrome 和 Firefox), Samsung J3(Android 浏览器,Chrome),很可能还有其他。
似乎该值可以与另一个浮点数进行比较,但是如果除数比该值大一半以上,则除数将变为零。
这是一个简单的测试用例。 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
大多数现代手机以牺牲速度为代价支持 highp,但一些较旧的手机(如 iPhone3GS)不支持。
您还可以设置特定变量的精度
#if GL_FRAGMENT_PRECISION_HIGH
highp vec3 someVar;
#else
mediump vec3 someVar;
#else
在台式机上,无论您要求什么,就相关规范而言,您总是会得到 highp。
当在条件语句中使用统一坐标或纹理坐标时,在条件语句中修改浮点变量的值会得到奇怪的结果。这发生在许多移动设备上的片段着色器中,例如 iPhone 4S、iPhone 5、iPhone 5S、iPhone 6(Safari、Chrome 和 Firefox), Samsung J3(Android 浏览器,Chrome),很可能还有其他。 似乎该值可以与另一个浮点数进行比较,但是如果除数比该值大一半以上,则除数将变为零。 这是一个简单的测试用例。 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
大多数现代手机以牺牲速度为代价支持 highp,但一些较旧的手机(如 iPhone3GS)不支持。
您还可以设置特定变量的精度
#if GL_FRAGMENT_PRECISION_HIGH
highp vec3 someVar;
#else
mediump vec3 someVar;
#else
在台式机上,无论您要求什么,就相关规范而言,您总是会得到 highp。