2013-07-31 9 views
9

Istnieje pewne zamieszanie e.g. pod względem poziomów wsparcia dla renderowania dla tekstur zmiennoprzecinkowych w WebGL. Wydaje się, że rozszerzenie OES_texture_float nie upoważnia go per se, jak na Optional support for FLOAT textures as FBO attachments (deprecated), ale wygląda na to, że niektórzy dostawcy wprowadzili go i wdrożyli. Dlatego moim podstawowym założeniem jest to, że renderowanie tekstur zmiennoprzecinkowych działa w środowiskach graficznych innych niż środowiska ES. Jednak nie byłem w stanie odczytać z celu renderowania zmiennoprzecinkowego bezpośrednio.WebGL Piksele odczytu z ruchomego punktu renderowania celu

Moje pytanie brzmi, czy istnieje sposób na odczyt z tekstury zmiennoprzecinkowej za pomocą wywołania WebGLContext :: readPixels() i miejsca docelowego Float32Array? Z góry dziękuję.

dołączony jest skrypt, który mógłby czytać z bajtów fakturze, ale nie dla tekstur pływaka:

<html> 
<head> 
<script> 
function run_test(use_float) { 
    // Create canvas and context 
    var canvas = document.createElement('canvas'); 
    document.body.appendChild(canvas); 
    var gl = canvas.getContext("experimental-webgl"); 

    // Decide on types to user for texture 
    var texType, bufferFmt; 
    if (use_float) { 
     texType = gl.FLOAT; 
     bufferFmt = Float32Array; 
    } else { 
     texType = gl.UNSIGNED_BYTE; 
     bufferFmt = Uint8Array; 
    } 

    // Query extension 
    var OES_texture_float = gl.getExtension('OES_texture_float'); 
    if (!OES_texture_float) { 
     throw new Error("No support for OES_texture_float"); 
    } 

    // Clear 
    gl.viewport(0, 0, canvas.width, canvas.height); 
    gl.clearColor(1.0, 0.0, 0.0, 1.0); 
    gl.clear(gl.COLOR_BUFFER_BIT); 

    // Create texture 
    var texture = gl.createTexture(); 
    gl.bindTexture(gl.TEXTURE_2D, texture); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 512, 512, 0, gl.RGBA, texType, null); 

    // Create and attach frame buffer 
    var fbo = gl.createFramebuffer(); 
    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); 
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); 
    gl.bindTexture(gl.TEXTURE_2D, null); 
    if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { 
     throw new Error("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE"); 
    } 

    // Clear 
    gl.viewport(0, 0, 512, 512); 
    gl.clear(gl.COLOR_BUFFER_BIT); 
    var pixels = new bufferFmt(4 * 512 * 512); 
    gl.readPixels(0, 0, 512, 512, gl.RGBA, texType, pixels); 

    if (pixels[0] !== (use_float ? 1.0 : 255)) { 
     throw new Error("pixels[0] === " + pixels[0].toString()); 
    } 
} 

function main() { 
    run_test(false); 
    console.log('Test passed using GL_UNSIGNED_BYTE'); 
    run_test(true); 
    console.log('Test passed using GL_FLOAT'); 
} 
</script> 
</head> 
<body onload='main()'> 
</body> 
</html> 

Odpowiedz

11

Niestety to wciąż wydaje się, że odczyt komponenty RGBA jako bajtów jest jedynym sposobem na WebGL. Jeśli trzeba kodować pływaka na wartość piksela można wykorzystać następujące:

W swojej fraktalnej cieniującego (GLSL/HLSL):

float shift_right (float v, float amt) { 
    v = floor(v) + 0.5; 
    return floor(v/exp2(amt)); 
} 
float shift_left (float v, float amt) { 
    return floor(v * exp2(amt) + 0.5); 
} 
float mask_last (float v, float bits) { 
    return mod(v, shift_left(1.0, bits)); 
} 
float extract_bits (float num, float from, float to) { 
    from = floor(from + 0.5); to = floor(to + 0.5); 
    return mask_last(shift_right(num, from), to - from); 
} 
vec4 encode_float (float val) { 
    if (val == 0.0) return vec4(0, 0, 0, 0); 
    float sign = val > 0.0 ? 0.0 : 1.0; 
    val = abs(val); 
    float exponent = floor(log2(val)); 
    float biased_exponent = exponent + 127.0; 
    float fraction = ((val/exp2(exponent)) - 1.0) * 8388608.0; 
    float t = biased_exponent/2.0; 
    float last_bit_of_biased_exponent = fract(t) * 2.0; 
    float remaining_bits_of_biased_exponent = floor(t); 
    float byte4 = extract_bits(fraction, 0.0, 8.0)/255.0; 
    float byte3 = extract_bits(fraction, 8.0, 16.0)/255.0; 
    float byte2 = (last_bit_of_biased_exponent * 128.0 + extract_bits(fraction, 16.0, 23.0))/255.0; 
    float byte1 = (sign * 128.0 + remaining_bits_of_biased_exponent)/255.0; 
    return vec4(byte4, byte3, byte2, byte1); 
} 

// (the following inside main(){}) return your float as the fragment color 
float myFloat = 420.420; 
gl_FragColor = encode_float(myFloat); 

Następnie z powrotem na boku JavaScript, potem rozmowa losowanie ma poczyniono można wyodrębnić zakodowaną wartość pływaka każdego piksela z następujących czynności:

var pixels = new Uint8Array(CANVAS.width * CANVAS.height * 4); 
gl.readPixels(0, 0, CANVAS.width, CANVAS.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); 
pixels = new Float32Array(pixels.buffer); 

// pixels now contains an array of floats, 1 float for each pixel 
+1

Dzięki Adrian. Mogę sprawdzić, czy to działa dobrze. Użyłem twojego rozwiązania w tej pracy: https://github.com/stharding/function-plotter – Stephen

+0

Bez problemu Stephen, świetna robota przy okazji; Jeśli masz więcej pytań, skontaktuj się bezpośrednio z nami: adrian [at] gatosomina [dot] com –

+1

dziękuję za to, zadziałało jak urok i dało mi precyzję potrzebną do symulacji + nie wiedziałem że Float32Array (ArrayBuffer) przekonwertowałby Ints na floats, to bardzo przydatne :) dzięki! – nicoptere

7

Dodaję moich ostatnich odkryć: Chrome pozwoli Ci odczytać pływaków, w ramach realizacji zdefiniowane formacie (search for "readPixels" in the spec), Firefox implementuje WEBGL_color_buffer_float rozszerzenie, więc możesz po prostu załadować rozszerzenie i przeczytać swoje pływaki, nie byłem w stanie przeczytać pływaków z Safari.

+0

Czy wiesz, czy oznacza to, że ta funkcjonalność jest wymagana w webgl 2? – pailhead

+1

https://www.khronos.org/registry/webgl/specs/latest/2.0/#readpixels tak, tak myślę. – nraynaud

+2

Dziękuję bardzo za ten komentarz. Widzę w chrome mogę wywołać 'gl.getContext (" experimental-webgl ")', a następnie wywołać 'gl.readPixels (0,0, szerokość, wysokość, gl.RGB, gl.FLOAT, buf)' podczas gdy mam zmiennoprzecinkowy FBO związany, gdzie 'buf' to' Float32Array'. Ale w jaki sposób mogę użyć rozszerzenia w Firefoksie? Mogę wywołać 'gl.getExtension (" WEBGL_color_buffer_float ")' i zwraca 'WEBGL_color_buffer_float {}', ale co mam z nim zrobić? – matth