const depthTextures = gl => {
const depthTexture = gl.createTexture();
const depthTextureSize = 512;
gl.bindTexture(gl.TEXTURE_2D, depthTexture);
gl.texImage2D(gl.TEXTURE_2D, // target
0, // mip level
gl.DEPTH_COMPONENT, // internal format
depthTextureSize, // width
depthTextureSize, // height
0, // border
gl.DEPTH_COMPONENT, // format
gl.UNSIGNED_INT, // type
null); // data
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);
const depthFramebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, depthFramebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, // target
gl.DEPTH_ATTACHMENT, // attachment point
gl.TEXTURE_2D, // texture target
depthTexture, // texture
0); // mip level
// create a color texture of the same size as the depth texture
// see article why this is needed_
const unusedTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, unusedTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, depthTextureSize, depthTextureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
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); // attach it to the framebuffer
gl.framebufferTexture2D(gl.FRAMEBUFFER, // target
gl.COLOR_ATTACHMENT0, // attachment point
gl.TEXTURE_2D, // texture target
unusedTexture, // texture
0); // mip level
return [depthFramebuffer, unusedTexture];
};
I found
Note: This extension is only available to WebGL1 contexts. In WebGL2,
the functionality of this extension is available on the WebGL2 context
by default. The constant in WebGL2 is gl.UNSIGNED_INT_24_8.
I change DEPTH_COMPONENT with RGBA but still no attaced framebuffer...
In other combination i get :
gl.texImage2D(
gl.TEXTURE_2D, // target
0, // mip level
gl.RGBA, // internal format
depthTextureSize, // width
depthTextureSize, // height
0, // border
gl.RGBA, // format
gl.UNSIGNED_INT_24_8, // type
null); // data
GL_INVALID_OPERATION: Invalid combination of format, type and internalFormat.
GL_INVALID_OPERATION: Only array uniforms may have count > 1.
Any suggestion ?
This is source which i wanna implement in my own already exist glmatrix project...
The internal format of a depth texture cannot be RGBA, it must be one of the size internal formats e.g. (GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32F) . If the source format is GL_DEPTH_COMPONENT, the source type must be either FLOAT or one of the unsigned integral types. The internal format must be one of the valid combinations together with the format and type, (see OpenGL ES 3.0 glTexImage2D). Valid textures for an gl.DEPTH_ATTACHMENT are therefore e.g.:
gl.texImage2D(
gl.TEXTURE_2D, 0,
gl.GL_DEPTH_COMPONENT24,
depthTextureSize, depthTextureSize, 0,
gl.GL_DEPTH_COMPONENT, gl.GL_UNSIGNED_INT,
null);
gl.texImage2D(
gl.TEXTURE_2D, 0,
gl.GL_DEPTH_COMPONENT32F,
depthTextureSize, depthTextureSize, 0,
gl.GL_DEPTH_COMPONENT, gl.GL_FLOAT,
null);
Alternatively, you can use a combined depth and stencil texture (in this case it is a gl.DEPTH_STENCIL_ATTACHMENT instead of a gl.DEPTH_ATTACHMENT), e.g:
gl.texImage2D(
gl.TEXTURE_2D, 0,
gl.GL_DEPTH24_STENCIL8,
depthTextureSize, depthTextureSize, 0,
gl.GL_DEPTH_STENCIL, gl.GL_UNSIGNED_INT_24_8,
null);
Related
I'm porting a C++/OpenGL application into WebGL and I'm trying to create and configure a framebuffer.
The framebuffer has 3 textures we can write into of types : vec2, vec2, uint. (gl.RG32F, gl.RG32F, gl.R32UI)
Here is how I initialize the framebuffer :
var gbuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, gbuffer);
var z0_texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, z0_texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RG32F, output_canvas.width, output_canvas.height, 0, gl.RG, gl.FLOAT, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, z0_texture, 0);
var zn_texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, zn_texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RG32F, output_canvas.width, output_canvas.height, 0, gl.RG, gl.FLOAT, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, zn_texture, 0);
var n_texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, n_texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.R32UI, output_canvas.width, output_canvas.height, 0, gl.RED_INTEGER, gl.UNSIGNED_INT, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT2, gl.TEXTURE_2D, n_texture, 0);
gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2]);
The framebuffer is incomplete : gl.checkFramebufferStatus(gl.FRAMEBUFFER) returns gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT and shows a warning : Framebuffer not complete. (status: 0x8cd6) COLOR_ATTACHMENT1: Attachment has an effective format of RG32F, which is not renderable.
Any idea on what's wrong with my code?
I don't want to render these textures. I am using them in post-processing. That's what I did in C++ but can't do the same in WebGL.
No. You cannot do the same in WebGL.
The error meassage "Attachment has an effective format of RG32F, which is not renderable" - means that gl.RG32F is not a renderable format. Therefore you cannot use it for a framebuffer. You must use a color renderable format. See OpenGL ES 3.0 Specification - Table 3.13 (WebGL 2.0 conforms closely to OpenGL ES 3.0).
You can't do the same thing in WebGL as you can in desktop OpenGL. In desktop OpenGL RG32F is color renderable. However, WebGL is based on OpenGL ES. In OpenGL ES RG32 is not a color renderable format. "color renderable" does not mean that you can render these textures. That means you can render into a texture using this format.
When you only want the color part of the frame, you can store depth in a render buffer.
This works fine for me on both webgl1 and webgl2.
When you also need to use the depth part you can't use a render buffer, you need to store it in a texture.
In webgl1 you need to get an extension to support it.
This also works fine for me in webgl1.
const gl = canvas.getContext('webgl');
const depthTextureExtension = gl.getExtension('WEBGL_depth_texture');
if (!depthTextureExtension) {
alert('Depth textures not supported');
}
This feature should be available by default in webgl2.
Yet when I replace the code above with this:
const gl = canvas.getContext('webgl2');
My code fails to render and I can not find any explanation why.
this.texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
this.depth = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.depth);
checkGLErrors(gl); // OK
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_SHORT, null);
checkGLErrors(gl); // for webgl2: 1281 INVALID_VALUE
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
this.framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, this.depth, 0);
In WebGL2 DEPTH_COMPONENT is not a valid internal format. Use DEPTH_COMPONENT16, DEPTH_COMPONENT24 or DEPTH_COMPONENT32F
Example from here
const level = 0;
const internalFormat = gl.DEPTH_COMPONENT24;
const border = 0;
const format = gl.DEPTH_COMPONENT;
const type = gl.UNSIGNED_INT;
const data = null;
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
targetTextureWidth, targetTextureHeight, border,
format, type, data);
I am currently facing an error that I cannot seem to figure out.
In chrome I am receiving:
GL ERROR :GL_INVALID_VALUE : glTexImage2D: invalid internal_format GL_FALSE
RENDER WARNING: there is no texture bound to the unit 0
Which is resulting in my 3D object having just a black texture rather than the image I have waiting to be rendered.
internal_format is the 3rd argument to gl.texImage2D. The error mesages says you are passing either 0 or undefined or null. All of those become 0 which is the same as GL_FALSE.
You need to pass a valid internal format like gl.RGBA, gl.RGB, etc...
The second error is because of the first.
So I found the answer after fiddling around with my code a little, turns out its a simple fix (maybe stupidity on my part) but what I used to have was this:
var boxTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, boxTexture);
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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,document.getElementById('crate-image'));
gl.bindTexture(gl.TEXTURE_2D, null);
All that needed changing was the order, so now it looks like this:
var boxTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, boxTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
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, gl.RGBA, gl.UNSIGNED_BYTE,document.getElementById('crate-image'));
gl.bindTexture(gl.TEXTURE_2D, null);
I have a TIFF made up of floating points, not RGBA values, so it shows up as transparent. I used C++ to get the float values, and now I have a matrix of these values. How can I convert them to RGBA using WebGL, then make a texture out of them?
To load floating point values into a texture in WebGL you have to check for and enable floating point textures
var ext = gl.getExtension("OES_texture_float");
if (!ext) {
// Sorry, your browser/GPU/driver doesn't support floating point textures
}
After that you can upload floating point data
var data = new Float32Array(width * height * 4); //
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0 gl.RGBA, gl.FLOAT, data);
But you can't filter that texture so you have to set it to nearest
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
If you want to be able to filter you need to check for and enable that too
var ext = gl.getExtensions("OES_texture_float_linear");
if (!ext) {
// sorry, can't filter floating point textures
}
Rendering to a floating point texture is also an optional feature (using a a floating point textures as a framebuffer attachment). For that you'd attach the texture then check if it works
var fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
// sorry, you can't render to a floating point texture
}
Admittedly i haven't really tried this directly with floating points for a texture, but i have done it with Uint8 values to create a blank texture.
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.FLOAT, new Float32Array(putFloatingPointArrayHere));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.bindTexture(gl.TEXTURE_2D, null);
I am in the process of integrating post processing effects into my 3D engine. I have hit a roadblock with capturing depth data using a FrameBuffer Object with WebGL. There has been no issue capturing color data into the FrameBuffer. However, I am not able to get any Depth data even with the Depth Extension enabled for Chrome/Firefox.
var DepthEXT = webGLContext.getExtension( "WEBKIT_WEBGL_depth_texture" ) ||
webGLContext.getExtension( "MOZ_WEBGL_depth_texture" );
I have tried many different settings to see if it was a configuration issue, but no matter what I try I just see a white texture. The screenshot at the end of the post shows the color attachment rendered as a texture and the depth attachment rendered as a texture. Is there a problem with my initialization of the FrameBuffer or should I be looking elsewhere to solve this issue?
Below is my code for initializing the FrameBuffer Object:
// Frame Buffer
this.m_frameBuffer = gl.createFramebuffer();
gl.bindFramebuffer( gl.FRAMEBUFFER, this.m_frameBuffer );
// Render Buffer
this.m_renderBuffer = gl.createRenderbuffer();
gl.bindRenderbuffer( gl.RENDERBUFFER, this.m_renderBuffer );
gl.renderbufferStorage(
gl.RENDERBUFFER,
gl.DEPTH_COMPONENT16,
sharedRenderer.canvasDOMElement.width,
sharedRenderer.canvasDOMElement.height );
// Diffuse Component
this.m_diffuseComponentTexture = gl.createTexture();
gl.bindTexture( gl.TEXTURE_2D, this.m_diffuseComponentTexture );
gl.texParameteri(
gl.TEXTURE_2D,
gl.TEXTURE_MAG_FILTER,
gl.LINEAR );
gl.texParameteri(
gl.TEXTURE_2D,
gl.TEXTURE_MIN_FILTER,
gl.LINEAR );
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,
sharedRenderer.canvasDOMElement.width,
sharedRenderer.canvasDOMElement.height,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
null );
// Depth
this.m_depthComponentTexture = gl.createTexture();
gl.bindTexture( gl.TEXTURE_2D, this.m_depthComponentTexture );
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.DEPTH_COMPONENT,
sharedRenderer.canvasDOMElement.width,
sharedRenderer.canvasDOMElement.height,
0,
gl.DEPTH_COMPONENT,
gl.UNSIGNED_SHORT,
null );
// FrameBuffer
// Diffuse
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
this.m_diffuseComponentTexture,
0 );
// Depth
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.DEPTH_ATTACHMENT,
gl.TEXTURE_2D,
this.m_depthComponentTexture,
0 );
// RenderBuffer
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER,
gl.DEPTH_ATTACHMENT,
gl.RENDERBUFFER,
this.m_renderBuffer );
// Unbind buffers and textures
gl.bindTexture( gl.TEXTURE_2D, null );
gl.bindRenderbuffer( gl.RENDERBUFFER, null );
gl.bindFramebuffer( gl.FRAMEBUFFER, null );
Here is the code where I render the current scene to the FrameBuffer.
CBRenderer.prototype.renderSceneToGBuffer = function( sceneToRender, GBufferTarget, deltaSeconds )
{
CBMatrixStack.clearMatrixStackAndPushIdentityMatrix();
this.applyProjectionMatrix();
GBufferTarget.bindGBufferFrameBuffer();
this.renderer.enable( this.renderer.DEPTH_TEST );
this.renderer.depthMask( true );
this.renderer.clearDepth( 1.0 );
this.renderer.clearColor( 0.1, 0.1, 0.1, 0.0 );
this.renderer.clear( this.renderer.COLOR_BUFFER_BIT | this.renderer.DEPTH_BUFFER_BIT );
sceneToRender.render( deltaSeconds );
this.renderer.flush();
GBufferTarget.m_dirty = false;
GBufferTarget.unbindGBufferFrameBuffer();
this.renderer.clearColor( 0.0, 0.0, 0.0, 0.0 );
this.renderer.clear( this.renderer.COLOR_BUFFER_BIT | this.renderer.DEPTH_BUFFER_BIT );
this.renderer.bindTexture( this.renderer.TEXTURE_2D, null );
}
You don't have the depth texture attached to your FBO at the end of setting it up. You do set the texture as the depth attachment at some point:
// Depth
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.DEPTH_ATTACHMENT,
gl.TEXTURE_2D,
this.m_depthComponentTexture,
0 );
But immediately following that, you set a renderbuffer as the depth attachment instead:
// RenderBuffer
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER,
gl.DEPTH_ATTACHMENT,
gl.RENDERBUFFER,
this.m_renderBuffer );
You can have only one target attached to each FBO attachment point. So the second call sets a new depth attachment, replacing the one you just set in the first call. So at the end of this, m_depthComponentTexture is not attached to the FBO anymore.
If you want to use a depth texture, I don't see why you would want to create a depth renderbuffer as well. You should only need one of the two. You would normally use a renderbuffer for the depth if you don't need it for sampling later. If you want to sample from the result, you need a texture instead, and there's no need for a renderbuffer anymore.