Jump to content

How to use WEBGL_draw_buffers in babylon.js


Recommended Posts

Hello guys,

I would like to make a debugger such one of these:



I am trying to use multiple rendering target with this webgl extension => (https://www.khronos.org/registry/webgl/extensions/WEBGL_draw_buffers/), so when I am rendering my scene I would like in my fragment shader to write something like this:

#extension GL_EXT_draw_buffers : require

precision highp float;

uniform sampler2D positionSampler;
uniform sampler2D normalSampler;
uniform sampler2D depthSampler;
uniform sampler2D stuffSampler;

varying vec2 vUV2;
void main(void) {

	gl_FragData[0] = texture2D(positionSampler, vUV2);
	gl_FragData[1] = texture2D(normalSampler, vUV2);
	gl_FragData[2] = texture2D(depthSampler, vUV2);
	gl_FragData[3] = texture2D(stuffSampler, vUV2);

with each gl_FragData writing in a webgl texture associated. I didn't find a lots resources about it on the internet...

I am working with babylon.js, any example or help would be great.

Thanks !

Link to comment
Share on other sites


you need to change babylon.js a bit for now and add this kind of code:

var fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT0_WEBGL, gl.TEXTURE_2D, tx[0], 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT1_WEBGL, gl.TEXTURE_2D, tx[1], 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT2_WEBGL, gl.TEXTURE_2D, tx[2], 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT3_WEBGL, gl.TEXTURE_2D, tx[3], 0);


Link to comment
Share on other sites


I have read the article and since I've tried something similar I am stuck right now.

Here are samples of my code:

vertex and fragment shader

/*************************** FRAGMENT ***************************/

#extension GL_EXT_draw_buffers : require

precision highp float;

uniform sampler2D positionSampler;
uniform sampler2D normalSampler;

varying vec2 vUV2;
void main(void) {

	vec4 renderTarget1 = texture2D(normalSampler, vUV2);
	vec4 renderTarget2 = texture2D(positionSampler, vUV2);	

	gl_FragData[0] = vec4(1.0,0.0,0.0,1.0);
	gl_FragData[1] = vec4(0.5,0.5,0.5,1.0);
	gl_FragData[2] = vec4(0.0,1.0,0.0,1.0);
	gl_FragData[3] = vec4(0.0,0.0,1.0,1.0);

/*************************** VERTEX ***************************/

precision highp float;
// Attributes
attribute vec3 position;
attribute vec2 uv2;
// Uniforms
uniform mat4 worldViewProjection;
// Varying
varying vec2 vUV2;
void main(void) {
    gl_Position = worldViewProjection * vec4(position, 1.0);
    vUV2 = uv2;

and the javascript

var interval;

		//material init
		var mrtMaterial = new BABYLON.ShaderMaterial("mrt", _scene, "./src/Components/Demo/Shaders/mrt", {
			attributes: ["position", "uv2"],
			uniforms: ["worldViewProjection"]
		mrtMaterial.setTexture("normalSampler", normalTexture);
		mrtMaterial.setTexture("positionSampler", positionTexture);
		mrtMaterial.backFaceCulling = false;

		//get the size of canvas
		var canvas = document.getElementsByTagName("canvas")[0];
		var size = {
			width: canvas.width / 3, // split 3 times on the width hard coded
			height: canvas.height / 2 // split 2 times on the height hard coded 

		//return a new webgl texture sized to canvas
		var getWebGLTexture = function(gl) {

			var webglTexture = gl.createTexture();

			gl.bindTexture(gl.TEXTURE_2D, webglTexture);
			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, size.width, size.height, 0, gl.RGBA, gl.FLOAT, null);
			gl.bindTexture(gl.TEXTURE_2D, null);

			return webglTexture;

		//meshes list
		var renderList = MESHES.slice(0);

		//render target associate to the material
		var mrtTarget = RenderTargetManager.CreateForEffectOnce("mrtTarget", mrtMaterial, _scene, size, renderList, false);

		//when material + textures used + render target are ready
		function onReady() {
			if (!mrtMaterial.isReady() || !positionTexture.isReady() || !normalTexture.isReady() || !mrtTarget.isReady())


			var gl = _scene.getEngine()._gl;
			var targets = [];
			var nbTargets = 4;//yield 5 screen, so split 3 times on width and 2 on the height

			//activate extension
			var drawBuffersExtension = gl.getExtension('WEBGL_draw_buffers') ||
				gl.getExtension("GL_EXT_draw_buffers") ||

			if (!drawBuffersExtension) {
				throw new Error("draw buffer extension not supported");

			//return an attachment color with a given i
			var getFlag = function(i) {
				var flag;
				switch (i) {
					case 0:
						flag = drawBuffersExtension.COLOR_ATTACHMENT0_WEBGL
					case 1:
						flag = drawBuffersExtension.COLOR_ATTACHMENT1_WEBGL
					case 2:
						flag = drawBuffersExtension.COLOR_ATTACHMENT2_WEBGL
					case 3:
						flag = drawBuffersExtension.COLOR_ATTACHMENT3_WEBGL
					case 4:
						flag = drawBuffersExtension.COLOR_ATTACHMENT4_WEBGL
					case 5:
						flag = drawBuffersExtension.COLOR_ATTACHMENT5_WEBGL
						throw new Error("flag limit");

				return flag;

			//get the framebuffer of the render target and bind it
			gl.bindFramebuffer(gl.FRAMEBUFFER, mrtTarget._texture._framebuffer );

			//init textures
			for (let i = 0; i < nbTargets; i++) {

				if (i === 0) {

					//insert a canvas into html and associate the webgl texture of the render target to it
					var indexDbg = debugger3D.insertWebGLTexture(mrtTarget._texture, _scene, size, (i + 1).toString());

					var target = {
						texture: mrtTarget._texture,
						indexDbg: indexDbg,
						flag: getFlag(i)

				} else {

					//insert a canvas into html and associate a webgl texture to it
					var texture = getWebGLTexture(gl);					
					var indexDbg = debugger3D.insertWebGLTexture(texture, _scene, size, (i + 1).toString());

					var target = {
						texture: texture,
						indexDbg: indexDbg,
						flag: getFlag(i)


				//bind webgl texture to different attachement
				gl.framebufferTexture2D(gl.FRAMEBUFFER, target.flag, gl.TEXTURE_2D, target.texture, 0);


			//return true
			console.log("FrameBuffer status after initialization: ");
			console.log(gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_UNSUPPORTED);

			var buffers = [];

			//map color attachement to draw buffer
			for (let i = 0; i < targets.length; i++) {

			//execute the rendering pass

			gl.bindFramebuffer(gl.FRAMEBUFFER, null);
			gl.bindTexture(gl.TEXTURE_2D, null);

			//for each canvas inserted read the webgl texture pixel by pixel and draw it

			for (let i = targets.length - 1; i >= 0; i--) {
// when webgl textures were empty I had some warning that I don't have anymore


		interval = setInterval(onReady, 250);

 I joined an image of what displayed on the screen.

The gl_FragData[0] associated to the render target webgl texture is drawn ( y are inversed which is normal), but none of the others are rendering...

What I am missing ? 

Thanks for any help.





Link to comment
Share on other sites

This line bind the webgl texture to a color attachment of the framebuffer =>     gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT1_WEBGL, gl.TEXTURE_2D, texture._texture, 0); (line 96 of the playground)

Then color attachment are mapped to draw buffer slots here => ext.drawBuffersWEBGL([ext.COLOR_ATTACHMENT0_WEBGL,ext.COLOR_ATTACHMENT1_WEBGL]); (line 103)

I guess this is how targets are binded.

Link to comment
Share on other sites

I update the playground it looks like this now :

    gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT0_WEBGL, gl.TEXTURE_2D, textures[0]._texture, 0);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT1_WEBGL, gl.TEXTURE_2D, textures[1]._texture, 0);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT2_WEBGL, gl.TEXTURE_2D, textures[2]._texture, 0);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT3_WEBGL, gl.TEXTURE_2D, textures[3]._texture, 0);

then :


Link to comment
Share on other sites

I installed this debugger webgl (https://www.khronos.org/webgl/wiki/Debugging) to console.log all webgl call.

I join a txt file with the output.

The first time I map a framebuffer attachment with a texture I got :

 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, [object WebGLTexture], 0)

then for the others I got :

 gl.framebufferTexture2D(gl.FRAMEBUFFER, /*UNKNOWN WebGL ENUM*/ 0x8ce1, gl.TEXTURE_2D, [object WebGLTexture], 0)

the hexadecimal value is correct according to the documentation but do you think is that normal ?


Link to comment
Share on other sites


So I solved my problem, actually my use of the draw buffer extension was right but I was checking the framebuffer bounded and not webgl textures. Is there a way to read in a webgl texture with a function like gl.readPixel (to read pixel by pixel in a framebuffer) so I could draw in a separate canvas multiple result of my rendering pass. I can't use a solution like describe here (http://stackoverflow.com/questions/13626606/read-pixels-from-a-webgl-texture) because I've already attach my texture to a framebuffer, but I don't know how to access color buffers of this latter. 


Link to comment
Share on other sites

sure here it is !

fragment looks like this => 

#extension GL_EXT_draw_buffers : require

precision highp float;

uniform sampler2D positionSampler;
uniform sampler2D normalSampler;

varying vec2 vUV2;
void main(void) {

	vec4 renderTarget1 = texture2D(normalSampler, vUV2);
	vec4 renderTarget2 = texture2D(positionSampler, vUV2);	

	gl_FragData[0] = vec4(0.0,1.0,0.0,1.0);
	gl_FragData[1] = renderTarget1;
	gl_FragData[2] = renderTarget2;
	gl_FragData[3] = vec4(0.0,0.0,1.0,1.0);	


multiple rendering target.png


Debugger mrt.webm

Link to comment
Share on other sites

  • 5 years later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...