Преобразование текстуры DDS (DXT3) в ImageData с использованием WebGL

Я пытаюсь преобразовать текстуру DDS (в основном DXT1 и DXT3) в ImageData с использованием WebGL. Вот моя попытка...

let ext = <WEBGL_compressed_texture_s3tc>gl.getExtension('WEBGL_compressed_texture_s3tc');

let texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);

let fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);

gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_RGBA_S3TC_DXT3_EXT, width, height, 0, sourceImage);

gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

let data = new Uint8Array(width * height * 4);

gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);
gl.deleteFramebuffer(fb);

let image = new ImageData(new Uint8ClampedArray(data), width, height);

где gl — WebGLRenderingContext, а sourceImage (Uint8Array) — текстура в формате DXT3. Без всяких мипмапов и прочего. Я уверен, потому что я пытался визуализировать эту текстуру с помощью этого фрагмента, и он работал.

Сбой кода в функции readPixels со следующей ошибкой (Google Chrome):

[.Offscreen-For-WebGL-000001F2F3C04690]ОШИБКА GL: GL_INVALID_FRAMEBUFFER_OPERATION: glReadPixels: кадровый буфер неполный

Я ищу ответ, конечно, но безуспешно. Возможно, это может помочь. Я могу предоставить несколько примеров текстур, если они понадобятся.


person Matěj Pokorný    schedule 16.09.2017    source источник


Ответы (1)


Вы не можете выполнять рендеринг в сжатые форматы текстур, поэтому присоединение их к фреймбуферу вызывает ошибку недопустимой операции. Вам нужно прикрепить несжатую текстуру к вашему фреймбуферу, а затем нарисовать четырехъядерную выборку экранного пространства из текстуры DXT. Вот так:

let ext = <WEBGL_compressed_texture_s3tc>gl.getExtension('WEBGL_compressed_texture_s3tc');

// create framebuffer attachment texture
let colorTarget = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, colorTarget);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_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,
    width,
    height,
    0,
    gl.RGBA,
    gl.UNSIGNED_BYTE,
    null
);
// setup framebuffer
let fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorTarget, 0);

// create and upload compressed texture
let compressedTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, compressedTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_RGBA_S3TC_DXT3_EXT, width, height, 0, sourceImage);

gl.viewport(0,0,width,height);
//draw screenspace quad here

// read back uncompressed color data
let data = new Uint8Array(width * height * 4);
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);

gl.deleteFramebuffer(fb);

let imageData = new ImageData(new Uint8ClampedArray(data), width, height);
person LJᛃ    schedule 16.09.2017
comment
Итак... Если я правильно понимаю, я должен каким-то образом преобразовать текстуру DXT WebGL в несжатую текстуру RGBA WebGL, а затем сделать что-то с фреймбуфером и readPixels? Я новичок в WebGL, так что, может быть, я неправильно понимаю...? - person Matěj Pokorný; 17.09.2017
comment
Я добавил измененную версию вашего фрагмента кода в свой ответ, чтобы проиллюстрировать поток - person LJᛃ; 17.09.2017
comment
Не работает :/. Сделал маленькое демо, когда можно будет попробовать. Просто перетащите эту текстуру в результирующее окно этот фрагмент jsfiddle. Он покажет вам текстуру. Но этот фрагмент с преобразованием текстуры в imageData где-то сломан. Все байты в imageData равны нулю... - person Matěj Pokorný; 17.09.2017
comment
да, потому что вы должны заменить //draw screenspace quad here фактическим кодом, рисующим квадрат экрана... - person LJᛃ; 17.09.2017
comment
Да! Это работает! Спасибо! ;-) Значит, нельзя конвертировать DXT-текстуру в ImageData, не переводя все в Canvas? - person Matěj Pokorný; 17.09.2017
comment
Ну, теперь вы выполняете рендеринг в объект кадрового буфера, поэтому его не видно на холсте, с другой стороны, форматы DXT хорошо документированы, вы можете декодировать их в обычном javascript. - person LJᛃ; 17.09.2017