任何人都可以帮助我解决以下问题,我很困惑并且会感激任何建议。我会尝试尽可能简洁。
我有一个简单的WebGL页面,它呈现两个对象。我可以通过单击按钮加载一个着色器程序或另一个,以更改这两个对象的渲染方式。但是,我想用第一个着色器程序渲染一个对象,用另一个着色器程序渲染另一个对象。
这是我的绘图功能(每次用户拖动鼠标时调用):
WebGLViewer.prototype.DrawGLScene = function()
{
this.SetupWebGLContextViewport();
this.SetForProgram();
this.Draw3DObjects();
}
因此,调用了三个函数。他们在这里:
首先设置投影和相机:
WebGLViewer.prototype.SetupWebGLContextViewport = function()
{
this.m_WebGLContext.viewport(0, 0, this.m_WebGLContext.viewportWidth, this.m_WebGLContext.viewportHeight);
this.m_WebGLContext.clear(this.m_WebGLContext.COLOR_BUFFER_BIT | this.m_WebGLContext.DEPTH_BUFFER_BIT);
if(this.m_projection == PROJECTION.ORTHOGRAPHIC)
{
mat4.ortho(this.m_orthoXMin, this.m_orthoXMax, this.m_orthoYMin, this.m_orthoYMax, 0.1, 100, this.m_pMatrix);
}
else
{
mat4.perspective(45, this.m_WebGLContext.viewportWidth / this.m_WebGLContext.viewportHeight, 0.1, 100.0, this.m_pMatrix);
}
mat4.identity(this.m_mvMatrix);
this.m_mvMatrix = mat4.lookAt(this.m_eye, this.m_centre, this.m_up);
}
其次,选择使用哪个着色器程序(如果只有一个对象则使用程序0,否则程序1 - 我一次添加一个按钮单击一个对象) - (我应该补充一点,m_arrProgramSettingCallbacks是一个数组两个函数,用于设置顶点和片段着色器统一变量的值):
WebGLViewer.prototype.SetForProgram = function()
{
if(this.m_numObjects.length == 1)
{
this.UseShaderProgram(0,false);
}
else
{
this.UseShaderProgram(1,false);
}
this.m_WebGLContext.uniformMatrix4fv(this.m_shaderProgram.m_pMatrixUniform, false, this.m_pMatrix);
this.m_WebGLContext.uniformMatrix4fv(this.m_shaderProgram.m_mvMatrixUniform, false, this.m_mvMatrix);
var normalMatrix = mat3.create();
mat4.toInverseMat3(this.m_mvMatrix, normalMatrix);
mat3.transpose(normalMatrix);
this.m_WebGLContext.uniformMatrix3fv(this.m_shaderProgram.m_nMatrixUniform, false, normalMatrix);
this.m_WebGLContext.useProgram(this.m_shaderProgram);
var context = this.m_nShaderProgramID == 0 ? this : this.m_callingObject;
this.m_arrProgramSettingCallbacks[this.m_nShaderProgramID].call(context);
}
你可以看到上面的函数调用另一个名为UseShaderProgram的函数,它只是设置全局变量m_nShaderProgramID和m_shaderProgram,如下所示(我将false作为第二个参数传递,因此不会重绘):
WebGLViewer.prototype.UseShaderProgram = function(nShaderProgramID, bRedraw)
{
this.EnableVertexAttribArray(nShaderProgramID);
this.m_nShaderProgramID = nShaderProgramID;
this.m_shaderProgram = this.m_arrShaderPrograms[this.m_nShaderProgramID];
if(bRedraw)
{
this.Draw();
}
}
最后,第三个函数呈现场景:
WebGLViewer.prototype.Draw3DObjects = function()
{
this.m_WebGLContext.activeTexture(this.m_WebGLContext.TEXTURE0);
for(var n3DObject = 0; n3DObject < this.m_arrVertexPositionBuffers.length; n3DObject++)
{
this.m_preRenderObjectCallback.call(this.m_callingObject, n3DObject);
this.m_WebGLContext.bindBuffer(this.m_WebGLContext.ARRAY_BUFFER, this.m_arrVertexPositionBuffers[n3DObject]);
this.m_WebGLContext.vertexAttribPointer(this.m_shaderProgram.m_vertexPositionAttribute, this.m_arrVertexPositionBuffers[n3DObject].itemSize, this.m_WebGLContext.FLOAT, false, 0, 0);
this.m_WebGLContext.bindBuffer(this.m_WebGLContext.ARRAY_BUFFER, this.m_arrVertexNormalBuffers[n3DObject]);
this.m_WebGLContext.vertexAttribPointer(this.m_shaderProgram.m_vertexNormalAttribute, this.m_arrVertexNormalBuffers[n3DObject].itemSize, this.m_WebGLContext.FLOAT, false, 0, 0);
this.m_WebGLContext.bindBuffer(this.m_WebGLContext.ARRAY_BUFFER, this.m_arrVertexColourBuffers[n3DObject]);
this.m_WebGLContext.vertexAttribPointer(this.m_shaderProgram.m_vertexColourAttribute, this.m_arrVertexColourBuffers[n3DObject].itemSize, this.m_WebGLContext.FLOAT, false, 0, 0);
this.m_WebGLContext.bindBuffer(this.m_WebGLContext.ARRAY_BUFFER, this.m_arrVertexTextureCoordBuffers[n3DObject]);
this.m_WebGLContext.vertexAttribPointer(this.m_shaderProgram.m_textureCoordAttribute, this.m_arrVertexTextureCoordBuffers[n3DObject].itemSize, this.m_WebGLContext.FLOAT, false, 0, 0);
this.m_WebGLContext.bindBuffer(this.m_WebGLContext.ELEMENT_ARRAY_BUFFER, this.m_arrVertexIndicesBuffers[n3DObject]);
this.m_WebGLContext.drawElements(this.m_WebGLContext.TRIANGLES, this.m_arrVertexIndicesBuffers[n3DObject].numItems, this.m_WebGLContext.UNSIGNED_SHORT, 0);
}
}
因此,可以看出,在任何给定的渲染代码运行中,只使用一个着色器程序,具体取决于是否有一个或两个对象。
这一切都很完美。
希望每个对象使用一个着色器程序,我基本上将调用SetForProgram放在Draw3DObjects函数中,如下所示:
WebGLViewer.prototype.Draw3DObjects = function()
{
this.m_WebGLContext.activeTexture(this.m_WebGLContext.TEXTURE0);
for(var n3DObject = 0; n3DObject < this.m_arrVertexPositionBuffers.length; n3DObject++)
{
this.UseShaderProgram(n3DObject,false);
this.m_WebGLContext.uniformMatrix4fv(this.m_shaderProgram.m_pMatrixUniform, false, this.m_pMatrix);
this.m_WebGLContext.uniformMatrix4fv(this.m_shaderProgram.m_mvMatrixUniform, false, this.m_mvMatrix);
var normalMatrix = mat3.create();
mat4.toInverseMat3(this.m_mvMatrix, normalMatrix);
mat3.transpose(normalMatrix);
this.m_WebGLContext.uniformMatrix3fv(this.m_shaderProgram.m_nMatrixUniform, false, normalMatrix);
this.m_WebGLContext.useProgram(this.m_shaderProgram);
var context = this.m_nShaderProgramID == 0 ? this : this.m_callingObject;
this.m_arrProgramSettingCallbacks[this.m_nShaderProgramID].call(context);
this.m_preRenderObjectCallback.call(this.m_callingObject, n3DObject);
this.m_WebGLContext.bindBuffer(this.m_WebGLContext.ARRAY_BUFFER, this.m_arrVertexPositionBuffers[n3DObject]);
this.m_WebGLContext.vertexAttribPointer(this.m_shaderProgram.m_vertexPositionAttribute, this.m_arrVertexPositionBuffers[n3DObject].itemSize, this.m_WebGLContext.FLOAT, false, 0, 0);
this.m_WebGLContext.bindBuffer(this.m_WebGLContext.ARRAY_BUFFER, this.m_arrVertexNormalBuffers[n3DObject]);
this.m_WebGLContext.vertexAttribPointer(this.m_shaderProgram.m_vertexNormalAttribute, this.m_arrVertexNormalBuffers[n3DObject].itemSize, this.m_WebGLContext.FLOAT, false, 0, 0);
this.m_WebGLContext.bindBuffer(this.m_WebGLContext.ARRAY_BUFFER, this.m_arrVertexColourBuffers[n3DObject]);
this.m_WebGLContext.vertexAttribPointer(this.m_shaderProgram.m_vertexColourAttribute, this.m_arrVertexColourBuffers[n3DObject].itemSize, this.m_WebGLContext.FLOAT, false, 0, 0);
this.m_WebGLContext.bindBuffer(this.m_WebGLContext.ARRAY_BUFFER, this.m_arrVertexTextureCoordBuffers[n3DObject]);
this.m_WebGLContext.vertexAttribPointer(this.m_shaderProgram.m_textureCoordAttribute, this.m_arrVertexTextureCoordBuffers[n3DObject].itemSize, this.m_WebGLContext.FLOAT, false, 0, 0);
this.m_WebGLContext.bindBuffer(this.m_WebGLContext.ELEMENT_ARRAY_BUFFER, this.m_arrVertexIndicesBuffers[n3DObject]);
this.m_WebGLContext.drawElements(this.m_WebGLContext.TRIANGLES, this.m_arrVertexIndicesBuffers[n3DObject].numItems, this.m_WebGLContext.UNSIGNED_SHORT, 0);
}
}
当只有一个对象时工作正常,但是当我添加第二个时,程序停止响应。
对不起所有的代码。感谢任何人可以建议我在OpenGL渲染循环中使用每个对象的useProgram函数。
米奇。
答案 0 :(得分:1)
你应该注意的一些事情。
统一地点不会在各个计划之间共享。
如果你有2个程序都有一个名为u_matrix
的制服,你需要为每个程序查找单独的位置对象
不保证属性位置相同
除非您致电gl.bindAttribLocation
(webgl1和webgl2)或使用layout (location = <loc>)
(webgl2 glsl 300 es),否则每个程序的属性位置可能会有所不同。
所以,当你想要切换程序时,你需要启用和设置特定程序的属性(除非你使用上面的方法确保它们使用相同的属性位置),你需要为该特定程序设置所有制服,因为不共享制服。
另请注意,gl.uniform???
函数适用于当前程序,即使用gl.useProgram
设置的函数。因此,为了在特定程序上设置制服,您必须先为该程序调用{{1}}。
您应该在JavaScript控制台中看到使用错误的统一位置和错误的程序的错误。例如:
gl.useProgram
&#13;
当我运行上面的代码时,我在JavaScript控制台中看到了这个
const gl = document.createElement("canvas").getContext("webgl"); const vs = createShader(gl, gl.VERTEX_SHADER, ` void main() { gl_Position = vec4(0,0,0,1); gl_PointSize = 10.; } `); const fs = createShader(gl, gl.FRAGMENT_SHADER, ` precision mediump float; uniform vec4 color; void main() { gl_FragColor = color; } `); // create 2 programs using the exact same shaders const prg1 = createProgram(gl, vs, fs); const prg2 = createProgram(gl, vs, fs); // look up the location of 'color' on prg1 const colorLoc = gl.getUniformLocation(prg1, "color"); // use that location with prg2 BAD!! gl.useProgram(prg2); gl.uniform4fv(colorLoc, [1, 0, 0, 1]); // SHOULD GET ERROR function createProgram(gl, vs, fs) { const p = gl.createProgram(); gl.attachShader(p, vs); gl.attachShader(p, fs); gl.linkProgram(p); // TODO: check for errors; return p; } function createShader(gl, type, src) { const s = gl.createShader(type); gl.shaderSource(s, src); gl.compileShader(s); // TODO: should check for errors return s; }
查看您的代码我看到了
WebGL: INVALID_OPERATION: uniform4fv: location is not from current program
代码正在调用 this.UseShaderProgram(n3DObject,false);
this.m_WebGLContext.uniformMatrix4fv(this.m_shaderProgram.m_pMatrixUniform, false, this.m_pMatrix);
this.m_WebGLContext.uniformMatrix4fv(this.m_shaderProgram.m_mvMatrixUniform, false, this.m_mvMatrix);
var normalMatrix = mat3.create();
mat4.toInverseMat3(this.m_mvMatrix, normalMatrix);
mat3.transpose(normalMatrix);
this.m_WebGLContext.uniformMatrix3fv(this.m_shaderProgram.m_nMatrixUniform, false, normalMatrix);
this.m_WebGLContext.useProgram(this.m_shaderProgram);
,但这不是调用this.UseShaderProgram
。然后在以前制作的任何节目上设置制服。最后,它会调用useProgram
,但此时为时已晚。