Играя с Mandelbrot set
поколением в WebGl
, я неизбежно наткнулся на небольшую точность 32-битного float
OpenGl
. Тем не менее, новая надежда родилась, когда я нашел эту замечательную статью.< br> Осторожно, я взял функции из вышеупомянутого источника и добавил double
деконструкцию на стороне JS.
Насколько я понимаю, JS имеет 64-битные doubles
для чисел с плавающей запятой, поэтому моя идея состояла в том, чтобы вычислить некоторые подготовительные данные на стороне JS, отправить их в GPU в виде пар чисел с плавающей запятой и перейти к циклу Мандельброта.
К сожалению, в конце я увидел буквально нулевую разницу в полученном изображении, и я действительно не знаю, где моя точка отказа.
JS:
function doubleToFloat(d) {
return new Float32Array([d])[0];
};
function splitDouble(dbl) { //splits JS number to array of 2 32bit floats
var arr = [];
arr[0] = doubleToFloat(dbl);
arr[1] = doubleToFloat(dbl - arr[0]);
//console.log(dbl, arr);
arr = new Float32Array(arr);
dlog(dbl,arr);
return arr;
};
///Somewhere inside rendering function - binding data to webGl
var planeH = 4/settings.magnification;
var planeMult = planeH/canvasH; //screen to complex plane dimensions multiplier
var planeW = canvasW*planeMult;
var planeWOffset = planeW/2; //offset to align plane's 0:0 at screen's center
var planeHOffset = planeH/2;
bindUniforms(program, {
WIDTH: canvasW,
HEIGHT: canvasH,
CENTER_X: {value:splitDouble(settings.centerX),type:"2fv"},
CENTER_Y: {value:splitDouble(settings.centerY),type:"2fv"},
MAGNIFICATION: settings.magnification,
PLANE_W_OFFSET: {value:splitDouble(planeWOffset),type:"2fv"},
PLANE_H_OFFSET: {value:splitDouble(planeHOffset),type:"2fv"},
PLANE_MULT: {value:splitDouble(planeMult),type:"2fv"},
uSampler: {value:texIndex,type:"1i"}
});
--> sent to webGL
WebGL (фрагментный шейдер):
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
precision mediump int;
const int ITER_LIMIT = <%=iterLimit%>;
uniform float MAGNIFICATION;
uniform vec2 CENTER_X;
uniform vec2 CENTER_Y;
uniform float WIDTH;
uniform float HEIGHT;
uniform vec2 PLANE_W_OFFSET;
uniform vec2 PLANE_H_OFFSET;
uniform vec2 PLANE_MULT;
uniform sampler2D uSampler;
const float threshold = 10.0;
///Double emulation functions///////////////
vec2 ds(float a) {
vec2 z;
z.x = a;
z.y = 0.0;
return z;
}
vec2 ds_add(vec2 dsa, vec2 dsb) {
vec2 dsc;
float t1, t2, e;
t1 = dsa.x + dsb.x;
e = t1 - dsa.x;
t2 = ((dsb.x - e) + (dsa.x - (t1 - e))) + dsa.y + dsb.y;
dsc.x = t1 + t2;
dsc.y = t2 - (dsc.x - t1);
return dsc;
}
vec2 ds_mult(vec2 dsa, vec2 dsb) {
vec2 dsc;
float c11, c21, c2, e, t1, t2;
float a1, a2, b1, b2, cona, conb, split = 8193.;
cona = dsa.x * split;
conb = dsb.x * split;
a1 = cona - (cona - dsa.x);
b1 = conb - (conb - dsb.x);
a2 = dsa.x - a1;
b2 = dsb.x - b1;
c11 = dsa.x * dsb.x;
c21 = a2 * b2 + (a2 * b1 + (a1 * b2 + (a1 * b1 - c11)));
c2 = dsa.x * dsb.y + dsa.y * dsb.x;
t1 = c11 + c2;
e = t1 - c11;
t2 = dsa.y * dsb.y + ((c2 - e) + (c11 - (t1 - e))) + c21;
dsc.x = t1 + t2;
dsc.y = t2 - (dsc.x - t1);
return dsc;
}
vec2 ds_sub(vec2 dsa, vec2 dsb) {
return ds_add(dsa, ds_mult(ds(-1.0),dsb));
}
///End of Double emulation functions/////////
///Inside main()////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
float sqThreshold = threshold*threshold;
vec2 X = ds_mult(ds(gl_FragCoord.x),PLANE_MULT);
vec2 Y = ds_mult(ds(gl_FragCoord.y),PLANE_MULT);
vec2 planeX = ds_add(ds_sub(X,PLANE_W_OFFSET),CENTER_X);
vec2 planeY = ds_sub(ds_sub(Y,PLANE_H_OFFSET),CENTER_Y);
vec4 outcome = vec4(ds(0.0),ds(0.0));
vec4 C = vec4(planeX,planeY);
int iters = 0;
for (int i = 0; i < ITER_LIMIT; i++) {
iters = i;
vec2 sqRe = ds_mult(outcome.xy,outcome.xy);
vec2 sqIm = ds_mult(outcome.zw,outcome.zw);
vec4 Zsq = vec4(
ds_sub(sqRe,sqIm),
ds_mult(ds(2.0),ds_mult(outcome.xy,outcome.zw))
);
outcome.xy = ds_add(Zsq.xy,C.xy);
outcome.zw = ds_add(Zsq.zw,C.zw);
vec2 sqSumm = ds_add(ds_mult(outcome.xy,outcome.xy),ds_mult(outcome.zw,outcome.zw));
if (sqSumm.x > sqThreshold) break;
};
--> Proceed to coloring
Результат (данные о местоположении/увеличении справа): Нулевая разница с тем же изображением, отрендеренным без двойной эмуляции.
Спасибо, что прочитали эту стену текста и кода, какие-нибудь подсказки и/или идеи?
precision
для этиfloat
с. Вы, вероятно, захотитеhighp
. - person genpfault   schedule 20.03.2017