Problem multi-texturing my water quad.

I’m attempting to make water using color, depth frame buffers and a quad. When I bind the frame buffer’s color texture, I actually see the tex rendered on the quad, but once I try binding TEXTURE1 (refraction), I get the following warning:

I understand what the warning is telling me, but I’m not sure at what point I’m making this mistake, or even if this is what’s going on. This only happens as soon as I try binding TEXTURE1, so for now I comment those two lines out, and just bind TEXTURE0 and my scene is rendered properly. But I’d love to bind the second texture (TEXTURE1) too :doh:

I’m almost sure this is due to my lack of multi-texturing knowledge, I just started reading on it, so I’m suspecting there’s something I’m doing wrong. The idea is to obviously reflect the scene on to the reflection sampler2D, and mix it with the refraction sampler2D which will show what’s “under the water” with depth.

I don’t think it’s my shader code because I’m including all the attributes and uniforms, and I’m getting 0 shader compiler errors. Let me show what I’m doing so far, hopefully it’s not too much code. I’ve condensed, and by the way thank you for reading.

This is how I’m drawing the quad water on every frame:


/* DRAW */
var shader = this.shader;
shader.bind();
gl.bindTexture(gl.TEXTURE_2D, null);

// Vertex Postion
gl.bindBuffer(gl.ARRAY_BUFFER, this.vboVertex);
gl.vertexAttribPointer(this.vertexAttrib, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(this.vertexAttrib);
gl.bindBuffer(gl.ARRAY_BUFFER, null);

// Normals
gl.bindBuffer(gl.ARRAY_BUFFER, this.vboNormal);
gl.vertexAttribPointer(this.normalsAttrib, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(this.normalsAttrib);
gl.bindBuffer(gl.ARRAY_BUFFER, null);

// Indices
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.vboIndices);


gl.uniform1i(this.reflection, 0);
gl.uniform1i(this.refraction, 1);

// Texture
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this.reflectionColorTexture);

gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, this.refractionColorTexture);

gl.bindBuffer(gl.ARRAY_BUFFER, this.vboTex);
gl.vertexAttribPointer(this.texAttrib, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(this.texAttrib);

// Uniforms
gl.uniformMatrix4fv(this.mvp, false, mvp);
gl.uniformMatrix4fv(this.model, false, model);
gl.uniformMatrix4fv(this.view, false, view);
gl.uniformMatrix4fv(this.normMat, false, normMat);  

gl.drawElements(gl.TRIANGLES, this.numIndices, gl.UNSIGNED_SHORT, 0);

This is how I initialize the water quad before rendering (only runs once):


depthExt = gl.getExtension("WEBGL_depth_texture");
if (!depthExt) { throw "Ahh"; }

let mesh    = Forge.Model.plane(3, 3, 1);
this.width  = size;
this.height = size;
this.shader = shader;
/*
MESH!
*/
// Vertex 
this.vboVertex = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vboVertex);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(mesh.vertex.coords), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
this.vertexPosAttrib = gl.getAttribLocation(shader.program, "a_position");
// Normal
this.vboNormal = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vboNormal);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(mesh.normals.coords), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);	
this.normalsAttrib 	= gl.getAttribLocation(shader.program, "a_normals");
// Indices
this.vboIndices = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.vboIndices);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(mesh.indices.coords), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);		
this.numIndices = mesh.indices.coords.length;	
// Tex
this.vboTex = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vboTex);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(mesh.texture.coords), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
this.texAttrib = gl.getAttribLocation(shader.program, "a_texCoords");

// Uniforms
this.mvp 		= gl.getUniformLocation(shader.program, "mvp");
this.model 		= gl.getUniformLocation(shader.program, "model");
this.view 		= gl.getUniformLocation(shader.program, "view");
this.normMat 	= gl.getUniformLocation(shader.program, "normMat"); 

/*
  WATER REFLECTION FRAME BUFFER
*/
this.reflectionFrameBuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, this.reflectionFrameBuffer);

// Color Texture
this.reflectionColorTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.reflectionColorTexture);
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, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.reflectionColorTexture, 0);
gl.bindTexture(gl.TEXTURE_2D, null);

// Depth Buffer Attachment
this.reflectionDepthBuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, this.reflectionDepthBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.depthBuffer);

// Unbind
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);


/*
  WATER REFRACTION FRAME BUFFER
*/
this.refractionFrameBuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, this.refractionFrameBuffer);

// Color Texture
this.refractionColorTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.refractionColorTexture);
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, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.refractionColorTexture, 0);
gl.bindTexture(gl.TEXTURE_2D, null);

// Depth Buffer Attachment
this.refractionDepthTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.refractionDepthTexture);
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.DEPTH_COMPONENT, size, size, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_SHORT, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, this.refractionDepthTexture, 0);

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

if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { throw "oh no"; }

// Reflection and Refraction Uniform
this.reflection = gl.getUniformLocation(shader.program, "reflection");
this.refraction = gl.getUniformLocation(shader.program, "refraction");
// Unbind
gl.bindFramebuffer(gl.FRAMEBUFFER, null);

and this how I render the water quad after I’ve drawn the scene two previous times, one for the reflection texture, the other for refraction:



/*
    Render Function!
*/
{
    frameBuffer.bindReflection();
    clear();

    drawScene(objs, numObjs);
    frameBuffer.unbind();


    frameBuffer.bindRefraction();
    clear();

    drawScene(objs, numObjs);
    frameBuffer.unbind();
}
        
projection = Mathf.perspective(currentCam.fov, Forge.app.aspect, currentCam.near, currentCam.far);
clear();

water.draw(projection, view);	
finalDrawScene(objs, numObjs);

Oh and almost forgot to include the bind and unbind functions, although they’re quite straight forward:


    bindReflection = function () {
        gl.bindFramebuffer(gl.FRAMEBUFFER, this.reflectionFrameBuffer);
        gl.viewport(0, 0, this.width, this.height);
    };

    bindRefraction = function () {
        gl.bindFramebuffer(gl.FRAMEBUFFER, this.refractionFrameBuffer);
        gl.viewport(0, 0, this.width, this.height);
    };

    unbind = function () {
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
    };

from what the “warnings” say, i guess that you try to read and write from the same texture, afaik thats not allowed. the warnings also say that there isnt a texture bountd at unit 0, so before you gl.bindtexture(…), call gl.activetexture(GL.texture0 + 0/* 0 being the unit */), by the way its sufficient to do that once and let’em bound there

remember: when you call gl.bindtexture(blabla, 0), you unbind the texture at the current “texture unit”, just to make sure that textures ae in place you can bind them each again to their respective unit once you’ve initialized them:

init texture 1
init texture 2
init texture 3
init texture …

/* bind to unit */
gl.activetexture(GL.texture0 + 1)
glBindtexture(blabla, texture1);

gl.activetexture(GL.texture0 + 2)
glBindtexture(blabla, texture2);

gl.activetexture(GL.texture0 + 3)
glBindtexture(blabla, texture3);

/* reset marker */
gl.activetexture(GL.texture0)

[QUOTE=john_connor;1287427]from what the “warnings” say, i guess that you try to read and write from the same texture, afaik thats not allowed. the warnings also say that there isnt a texture bountd at unit 0, so before you gl.bindtexture(…), call gl.activetexture(GL.texture0 + 0/* 0 being the unit */), by the way its sufficient to do that once and let’em bound there

remember: when you call gl.bindtexture(blabla, 0), you unbind the texture at the current “texture unit”, just to make sure that textures ae in place you can bind them each again to their respective unit once you’ve initialized them:

init texture 1
init texture 2
init texture 3
init texture …

/* bind to unit */
gl.activetexture(GL.texture0 + 1)
glBindtexture(blabla, texture1);

gl.activetexture(GL.texture0 + 2)
glBindtexture(blabla, texture2);

gl.activetexture(GL.texture0 + 3)
glBindtexture(blabla, texture3);

/* reset marker */
gl.activetexture(GL.texture0)[/QUOTE]

John, amazing. Now I understand how multi-texturing works, and I tried several sites. but your explanation did it. Oh and the problem I mostly had was that I was missing the reset marker. As soon as I read that part of your code, I added it and now it’s working. Thanks again.