2017-08-07 63 views
0

任何人都可能幫助我以下,我很困難,並會感謝任何建議。我會盡量保持簡潔。如何在WebGL渲染函數中加載多個着色器程序

我有一個簡單的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 - I添加對象的一個在一個按鈕點擊時間) - (I要補充的是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); 
} 

可以看到的是,上述的函數調用另一個稱爲UseS的函數haderProgram這只是設置全局變量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你需要查找單獨的位置對象爲每個程序

  2. 屬性位置都不能保證是同一

    除非你調用gl.bindAttribLocation(統一webgl1和webgl2)或使用layout (location = <loc>)(webgl2glsl 300 es),​​那麼對於每個程序,屬性位置可以/將是不同的。

所以短期的是,當你想切換程序需要啓用和設置該特定程序的屬性(除非你用上述方法,以確保他們所使用的相同屬性的位置),並且,您需要爲該特定程序設置所有制服,因爲制服不會共享。

另外請記住gl.uniform???函數在當前程序上工作,這是您用gl.useProgram設置的程序。所以,爲了在一個特定的節目上設置制服,你必須先爲該節目撥打gl.useProgram

您應該在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; 
 
}

當我運行上面的代碼我看到這個在JavaScript控制檯

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。然後在任何以前製作的節目上設置制服。最後它叫useProgram,但那時已經太晚了。

+0

非常感謝你。你徹底的回答讓我想起了我的一些電話的順序,過了一段時間,我看到了錯誤,現在一切正常。感謝您抽出時間,非常感謝。米奇, – user3738290