WebGL Legge i pixel dalla destinazione di rendering a virgola mobile

C’è una certa confusione, ad esempio in termini di livelli di supporto per il rendering in trame a virgola mobile in WebGL. L’estensione OES_texture_float non sembra imporre di per sé, come per il supporto opzionale per le trame FLOAT come allegati FBO (deprecato) , ma sembra che alcuni fornitori siano andati avanti e implementarlo. Quindi la mia comprensione di base è che il rendering su trame a virgola mobile funziona effettivamente in ambienti desktop non ES. Non sono stato in grado di leggere direttamente dal target di rendering in virgola mobile.

La mia domanda è se c’è un modo per leggere da una trama in virgola mobile utilizzando una chiamata WebGLContext :: readPixels () e una destinazione Float32Array? Grazie in anticipo.

Allegato è uno script che riesce a leggere da una trama di byte, ma non riesce per una trama float:

   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'); }      

ReadPixels è limitato al formato RGBA e al tipo UNSIGNED_BYTE ( specifica WebGL ). Tuttavia ci sono alcuni metodi per “impacchettare” i float in RGBA / UNSIGNED_BYTE qui descritti:

http://concord-consortium.github.io/lab/experiments/webgl-gpgpu/webgl.html

Sfortunatamente sembra ancora che leggere i componenti RGBA come byte sia l’unico modo per WebGL. Se hai bisogno di codificare un float in un valore di pixel, puoi usare quanto segue:

Nel tuo frattale shader (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); 

Poi di nuovo sul lato JavaScript, dopo che è stata effettuata la chiamata di estrazione è ansible estrarre il valore float codificato di ciascun pixel con il seguente:

 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 

Sto aggiungendo le mie recenti scoperte: Chrome ti consente di leggere i float, come parte del formato definito dall’implementazione ( cerca “readPixels” nelle specifiche ), Firefox implementa l’estensione WEBGL_color_buffer_float , quindi puoi semplicemente caricare l’estensione e leggere i tuoi float , Non sono stato in grado di leggere i float con Safari.