I have read a previous StackOverflow post regarding this issue and am still having issues in getting two different shader sets to work correctly in one WebGL program.
Below is a skeleton framework of the code. I have two separate sets of shaders, with different variable names to prevent cross-contamination. I create two initShader() functions, one for each set of shaders, and then call the initBuffers() and the draw() functions. The result is that only the second shader takes effect and only the item draw with this shader is shown, despite the shaders being separately identified and called within drawscene().
Any suggestions for how to fix this issue will be appreciated.
Declare: vertexShaderA
Declare: fragmentShaderA
Declare: vertexShaderB (use distinct variable names to that of vertexShaderA)
Declare: fragmentShaderB (use distinct variable names to that of fragmentShaderA)
// main script
initShadersA(){
…
gl.useProgram(shaderProgramA);
…
}
initShadersB(){
…
gl.useProgram(shaderProgramB);
…
}
…
setMatrixUniformsA();
setMatrixUniformsB();
function initBuffers(){
…
}
function drawScene(){
…
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
gl.vertexAttribPointer(shaderProgramA.vertexPositionAttributeA, vertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
…
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer);
gl.uniform1i(shaderProgramA.samplerUniform, 0);
setMatrixUniformsA();
gl.drawElements(gl.TRIANGLES, vertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
…
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
gl.vertexAttribPointer(shaderProgramB.vertexPositionAttributeB, vertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
…
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer);
gl.uniform1i(shaderProgramB.samplerUniform, 0);
setMatrixUniformsB();
gl.drawElements(gl.TRIANGLES, vertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
}
function draw() {
requestAnimFrame(draw);
animate();
drawScene();
}
function animate() {
…
}
function webGLStart() {
initGL(canvas);
initShadersA();
initShadersB();
initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
draw();
}
gl.useProgram(shaderProgram);
basically says: "okay, from this moment on, I will use this material to shade objects".
That being said, what you are doing is in the webGLStart()
is: initGL, use A material, use B material, create objects, draw objects with current material (and that is material B).
OpenGL/webGL works as a state machine. When you try to render something, it's drawn with the parameters of that state machine that are currently set. For example, you say draw
to OpenGL, but that method is looking for a buffer that is bound to the state machine, it also looks for material (shader) currently used... etc. So you set everything up and that initiate some action.
So, if you want to render two objects with two different shaders, you should bind one object (it's buffers actually), use first material (gl.useProgram(shaderProgramA)
) and dispatch draw. Then bind second object, set second material (gl.useProgram(shaderProgramB)
) and dispatch another call. It's how OpenGL/WebGL works.
Also, one suggestion. Try naming functions differently. initShadersA
should do things like taking source string, creating vertex and fragment shader, creating program and linking everything, whilst some function like useShadersA
should set current material to exact shader program.
I hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With