novalain
novalain

Reputation: 2209

Layered rendering to 3D texture using glFrameBufferTextureLayer

My goal is to render slices to a 3D texture using gl.frameBufferTextureLayer. However, nothing gets rendered when I read back the data with gl.readPixels.

When I'm changing gl.TEXTURE_3Dto gl.TEXTURE_2D_ARRAY it works fine, but in the tests here they are certainly attaching a layer of a 3D texture to the FBO. The WebGL docs doesn't say anything about what texture should be, but GLES3 docs allows texture to be a 3D texture for sure. I've checked framebuffer complete status and all that stuff..

Are there any resources for attaching a 3D texture layer to a FBO in WebGL2, is this even possible? Some code:

this.fbo= gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);

this.colorTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_3D, this.colorTexture);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texImage3D(gl.TEXTURE_3D, 0, gl.RGBA8, 64, 64, 64, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

this.renderBuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 64, 64);    
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.renderBuffer);

// Use level 1 in 3D texture
// Works when texture is GL_TEXTURE_2D_ARRAY
gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, this.texture, 0, 1);

// ... Render scene

let data = new Uint8Array(64 * 64 * 4);
gl.readPixels(0, 0, 64, 64, gl.RGBA, gl.UNSIGNED_BYTE, data, 0); // data is empty

Upvotes: 1

Views: 1097

Answers (1)

user128511
user128511

Reputation:

First let's try a simple one no rendering

const gl = document.createElement("canvas").getContext("webgl2");
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);

const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_3D, tex);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texImage3D(gl.TEXTURE_3D, 0, gl.RGBA8, 2, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex, 0, 1);

gl.clearColor(.25, .5, .75, 1);
gl.clear(gl.COLOR_BUFFER_BIT);

const data = new Uint8Array(2 * 2 * 4);
gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, data);
console.log(data);

That works

Now let's try rendering

const gl = document.createElement("canvas").getContext("webgl2");
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);

const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_3D, tex);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texImage3D(gl.TEXTURE_3D, 0, gl.RGBA8, 2, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex, 0, 1);

const vs = `
#version 300 es
void main() {
  gl_Position = vec4(0, 0, 0, 1);
  gl_PointSize = 4.0;
}
`;

const fs = `
#version 300 es
precision mediump float;
out vec4 color;
void main() {
  color = vec4(.75, .5, .25, 1);
}
`

const prg = twgl.createProgram(gl, [vs, fs]);
gl.useProgram(prg);
gl.viewport(0, 0, 2, 2);
gl.drawArrays(gl.POINTS, 0, 1);

const data = new Uint8Array(2 * 2 * 4);
gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, data);
console.log(data);
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

That works too.

Let's try with a depth buffer

const gl = document.createElement("canvas").getContext("webgl2");
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);

const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_3D, tex);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texImage3D(gl.TEXTURE_3D, 0, gl.RGBA8, 2, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex, 0, 1);

const rb = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 2, 2);    
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb);


const vs = `
#version 300 es
uniform float depth;
void main() {
  gl_Position = vec4(0, 0, depth, 1);
  gl_PointSize = 4.0;
}
`;

const fs = `
#version 300 es
precision mediump float;
uniform vec4 color;
out vec4 outColor;
void main() {
  outColor = color;
}
`

const prgInfo = twgl.createProgramInfo(gl, [vs, fs]);
gl.useProgram(prgInfo.program);
gl.viewport(0, 0, 2, 2);
gl.enable(gl.DEPTH_TEST);

// draw in middle
twgl.setUniforms(prgInfo, {
  depth: 0,
  color: [1, 0, 0, 1],
});
gl.drawArrays(gl.POINTS, 0, 1);


// draw in front
twgl.setUniforms(prgInfo, {
  depth: -.5,
  color: [0, 1, 0, 1],
});
gl.drawArrays(gl.POINTS, 0, 1);

// draw in back
twgl.setUniforms(prgInfo, {
  depth: .5,
  color: [0, 0, 1, 1],
});
gl.drawArrays(gl.POINTS, 0, 1);

// should be front color (0, 255, 0, 255)

const data = new Uint8Array(2 * 2 * 4);
gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, data);
console.log(data);
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

Seems to work

Maybe you're forgetting to set the viewport? I make that mistake often

Upvotes: 1

Related Questions