Эмуляция двойной точности Webgl с двумя числами с плавающей запятой не имеет эффекта

Играя с 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

Результат (данные о местоположении/увеличении справа): введите описание изображения здесь Нулевая разница с тем же изображением, отрендеренным без двойной эмуляции.

Спасибо, что прочитали эту стену текста и кода, какие-нибудь подсказки и/или идеи?


person Max Yari    schedule 20.03.2017    source источник
comment
Как этот фрагментный шейдер вообще скомпилировался? В нем отсутствует директива precision для эти floatс. Вы, вероятно, захотите highp.   -  person genpfault    schedule 20.03.2017
comment
Конечно, я оставил кучу кода за бортом. Это точный highp float, поместит его во фрагмент кода.   -  person Max Yari    schedule 20.03.2017
comment
Так мило, когда кто-то голосует за закрытие без каких-либо объяснений или советов, что не так с вопросом или как его улучшить.   -  person Max Yari    schedule 21.03.2017
comment
Я предполагаю, что видимые артефакты вызваны ограниченной точностью пикселей. gl_FragCoord является vec2 и поэтому использует только 32 бита на координату. Это также объясняет, почему все артефакты представляют собой маленькие прямоугольники, а для webgl все пиксели в прямоугольнике — одни и те же пиксели.   -  person Toonijn    schedule 02.08.2017
comment
Я недавно столкнулся с точно такой же проблемой, интересно, вы когда-нибудь решили это? Маловероятно, что это gl_FragCoord, так как он имеет значение от 0,0 до размера холста. похоже, что вы затем используете ds_mult() для получения комплексного числа в этой точке на экране. Таким образом, вы должны иметь двойную точность в этот момент. Мои эксперименты показывают, что результатом всех функций ds_ является vec2 с элементом .y, всегда равным 0.   -  person user1055212    schedule 16.03.2018
comment
@ user1055212 Нет, на самом деле я думаю, что никогда не решал эту проблему, отчасти потому, что вычисление GPU в этот момент было действительно неудобно в браузере, так как он отключал webgl после того, как GPU был занят более ~ 2 секунд, и разделение всего на куски звучало слишком уж слишком большая часть PITA для этого хобби-проекта ... хотя, возможно, я вернусь к нему однажды, так что будет действительно здорово, если вы опубликуете решение проблемы точности, когда / если вы это выясните.   -  person Max Yari    schedule 19.03.2018