2017-06-19 80 views
0

使用WebGL 2,我們現在可以使用統一緩衝區對象。綁定多個統一緩衝區對象

它們看起來像一個偉大的想法,不必共同制服重視每一個程序(如投影和視圖矩陣所共有的每個對象被渲染)。

我創建了一個輔助類,這就是我稱之爲我要綁定一個統一的緩衝對象每次。

class UniformBuffer { 
    constructor(gl, data, boundLocation = 0) { 
     this.boundLocation = boundLocation; 

     this.data = new Float32Array(data); 

     this.buffer = gl.createBuffer(); 
     gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer); 
     gl.bufferData(gl.UNIFORM_BUFFER, this.data, gl.DYNAMIC_DRAW); 
     gl.bindBuffer(gl.UNIFORM_BUFFER, null); 
     gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer); 
    } 

    update(gl, data, offset = 0) { 
     this.data.set(data, offset); 

     gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer); 
     gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, null); 
     gl.bindBuffer(gl.UNIFORM_BUFFER, null); 
     gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer); 
    } 
}; 

如果建立統一的緩衝區這樣

const perScene = new UniformBuffer(gl, [ 
    ...vec4.create(), 
    ...vec4.create(), 
], 0); // and bind it to bind location 0? 

const perObject = new UniformBuffer(gl, [ 
    ...vec4.create(), 
], 1); // and bind it to bind location 1? 

在我的渲染循環,然後我通過調用

perScene.update(gl, [ 
    ...vec4.fromValues(1, 0, 0, 1), 
], 4); // giving an offset to update only the 2nd color. 

然後,我將更新「perScene」校服的想法查看場景中的所有對象,我的想法是更新像這樣的perObject統一緩衝區

for (let i = 0; i < objects.length; i++) { 
    perObject.update(gl, [ 
     ...vec4.fromValues(0, 0, 1, 1), 
    ]); 
} 

我談論vec4只是爲了讓這個例子更容易,但這個想法是有矩陣對perObject上perScene(投影和視圖),和(對象和正常的矩陣)。

在我的着色器我有他們宣佈爲

uniform perScene { 
    vec4 color1; 
    vec4 color2; 
}; 


uniform perModel { 
    vec4 color3; 
}; 

我有一個在這裏工作片斷

class UniformBuffer { 
 
    constructor(gl, data, boundLocation = 0) { 
 
     this.boundLocation = boundLocation; 
 

 
     this.data = new Float32Array(data); 
 

 
     this.buffer = gl.createBuffer(); 
 
     gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer); 
 
     gl.bufferData(gl.UNIFORM_BUFFER, this.data, gl.DYNAMIC_DRAW); 
 
     gl.bindBuffer(gl.UNIFORM_BUFFER, null); 
 
     gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer); 
 
    } 
 

 
    update(gl, data, offset = 0) { 
 
     this.data.set(data, offset); 
 

 
     gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer); 
 
     gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, null); 
 
     gl.bindBuffer(gl.UNIFORM_BUFFER, null); 
 
     gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer); 
 
    } 
 
}; 
 

 
const vertex = `#version 300 es 
 

 
uniform perScene { 
 
\t vec4 color1; 
 
    vec4 color2; 
 
}; 
 

 
uniform perModel { 
 
\t vec4 color3; 
 
}; 
 

 
in vec3 a_position; 
 
out vec3 v_color; 
 

 
void main() { 
 
\t gl_Position = vec4(a_position, 1.0); 
 
\t v_color = color1.rgb + color2.rgb; // WORKS 
 
    // v_color = color1.rgb + color2.rgb + color3.rgb; // DOESNT WORK 
 
} 
 
`; 
 

 
const fragment = `#version 300 es 
 
precision highp float; 
 
precision highp int; 
 

 
in vec3 v_color; 
 
out vec4 outColor; 
 

 
void main() { 
 
\t outColor = vec4(v_color, 1.0); 
 
} 
 
`; 
 

 
const geometry = { 
 
    positions: [-0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0], 
 
    indices: [0, 2, 1, 1, 2, 3], 
 
}; 
 

 
const renderList = []; 
 

 
// STEP 1 (create canvas) 
 
var canvas = document.getElementById("canvas"); 
 
var gl = canvas.getContext("webgl2"); 
 
if (!gl) { 
 
    console.log('no webgl2 buddy'); 
 
} 
 

 
// STEP 2 (create program) 
 
const v = gl.createShader(gl.VERTEX_SHADER); 
 
gl.shaderSource(v, vertex); 
 
gl.compileShader(v); 
 

 
const f = gl.createShader(gl.FRAGMENT_SHADER); 
 
gl.shaderSource(f, fragment); 
 
gl.compileShader(f); 
 

 
const program = gl.createProgram(); 
 
gl.attachShader(program, v); 
 
gl.attachShader(program, f); 
 
gl.linkProgram(program); 
 

 
// STEP 3 (create VAO) 
 
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position'); 
 
const colorUniformLocation = gl.getUniformLocation(program, 'color'); 
 

 
const positionsBuffer = gl.createBuffer(); 
 
const indicesBuffer = gl.createBuffer(); 
 

 
const vao = gl.createVertexArray(); 
 
gl.bindVertexArray(vao); 
 

 
// position & indices 
 
gl.enableVertexAttribArray(positionAttributeLocation); 
 
gl.bindBuffer(gl.ARRAY_BUFFER, positionsBuffer); 
 
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(geometry.positions), gl.STATIC_DRAW); 
 
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0); 
 
gl.bindBuffer(gl.ARRAY_BUFFER, null); 
 

 
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer); 
 
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(geometry.indices), gl.STATIC_DRAW); 
 

 
// STEP 4 (create UBO) 
 

 
// bound to location 0 
 
const perScene = new UniformBuffer(gl, [ 
 
    ...vec4.create(), // color 1 
 
    ...vec4.create(), // color 2 
 
], 0); 
 

 
// bound to location 1 ? 
 
const perModel = new UniformBuffer(gl, [ 
 
    ...vec4.create(), // color 3 
 
], 3); 
 

 
// STEP 5 (add instances) 
 
for (let i = 0; i < 1; i++) { 
 
    renderList.push({ 
 
     id: i, 
 
     vao: vao, 
 
     program: program, 
 
     color: [0, 1, 1], 
 
    }); 
 
} 
 

 
// STEP 6 (draw) 
 
gl.clearColor(0, 0, 0, 0); 
 

 
gl.enable(gl.DEPTH_TEST); 
 

 
gl.viewport(0, 0, canvas.width, canvas.height); 
 

 
perScene.update(gl, [ 
 
    ...vec4.fromValues(1, 0, 0, 1), 
 
    ...vec4.fromValues(0, 1, 0, 1), 
 
]); 
 

 
for (let i = 0; i < renderList.length; i++) { 
 
    const current = renderList[i]; 
 
    gl.useProgram(current.program); 
 
    gl.bindVertexArray(current.vao); 
 

 
    // update perObject 
 
    perModel.update(gl, [ 
 
     ...vec4.fromValues(0, 0, 1, 1), 
 
    ]); 
 

 
    gl.drawElements(gl.TRIANGLES, geometry.indices.length, gl.UNSIGNED_SHORT, 0); 
 

 
    // unbind 
 
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); 
 
} 
 

 
console.log('compiled!');
canvas { 
 
    background-color: black; 
 
}
<canvas id="canvas"></canvas> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script>

我不應該被看到,因爲所有顏色的白色正方形加起來的結果是vec4(1.0, 1.0, 1.0, 1.0)? (jsfiddle line 41)

我在做什麼錯? 謝謝

回答

4

所以,你做錯的第一件事是你不打電話gl.getUniformBlockIndex。就像制服一樣,您必須查詢位置,或者在這種情況下查詢每個區塊的索引。

的第二件事是塊的制服進行間接尋址一個水平,你需要調用gl.uniformBlockBinding(program, uniformBlockIndex, uniformBufferIndex);

uniformBlockIndex是你從gl.getUniformBlockIndex得到了指數。 uniformBufferIndex類似於紋理單元。有N個統一的緩衝區索引。您可以選擇從0MAX_UNIFORM_BUFFER_BINDINGS - 1的任何緩衝區索引。

如果你有一個程序使用塊A,B,另一個使用A和C,那麼這種間接方式會有所幫助。在這種情況下,塊A在2個程序中可能有不同的索引,但是你需要從相同的uniformBufferIndex。

請注意,此狀態是每個程序狀態,以便可以在初始時間大概設置它,如果你打算一直使用相同的統一的緩存指數相同的均勻塊。

拼出來更多。你有一個着色器程序。它有狀態

var someProgram = { 
    uniforms: { 
    projectionMatrix: [1, 0, 0, 0, 0, ... ], // etc 
    }, 
    uniformBlockIndcies[ // one per uniform block 
    0, 
    0, 
    0, 
    ], 
    ... 
} 

接下來,您必須統一緩衝指數是全球性狀態

glState = { 
    textureUnits: [ ... ], 
    uniformBuffers: [ null, null, null ..., ], 
}; 

你告訴每個統一緩衝塊,均勻緩衝指數與gl.uniformBlockBinding要使用的程序。然後,您使用gl.bindBufferBasegl.bindBufferRange將緩衝區綁定到該索引。

這與告訴程序要使用哪個紋理單元然後將紋理綁定到該單元非常相似。當你這樣做時,在初始階段或時間是真的取決於你。在我看來,我更可能在初始化時決定我的perScene素材始終位於索引0處的緩衝區索引0和perModel素材處,因此我可以在init時將它們設置爲程序部分(調用gl.uniformBlockBinding)。

class UniformBuffer { 
 
    constructor(gl, data, boundLocation = 0) { 
 
     this.boundLocation = boundLocation; 
 

 
     this.data = new Float32Array(data); 
 

 
     this.buffer = gl.createBuffer(); 
 
     gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer); 
 
     gl.bufferData(gl.UNIFORM_BUFFER, this.data, gl.DYNAMIC_DRAW); 
 
     gl.bindBuffer(gl.UNIFORM_BUFFER, null); 
 
     gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer); 
 
    } 
 

 
    update(gl, data, offset = 0) { 
 
     this.data.set(data, offset); 
 

 
     gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer); 
 
     gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, null); 
 
     gl.bindBuffer(gl.UNIFORM_BUFFER, null); 
 
     gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer); 
 
    } 
 
}; 
 

 
const vertex = `#version 300 es 
 

 
uniform perScene { 
 
\t vec4 color1; 
 
    vec4 color2; 
 
}; 
 

 
uniform perModel { 
 
\t vec4 color3; 
 
}; 
 

 
in vec3 a_position; 
 
out vec3 v_color; 
 

 
void main() { 
 
\t gl_Position = vec4(a_position, 1.0); 
 
\t v_color = color1.rgb + color2.rgb + color3.rgb; 
 
} 
 
`; 
 

 
const fragment = `#version 300 es 
 
precision highp float; 
 
precision highp int; 
 

 
in vec3 v_color; 
 
out vec4 outColor; 
 

 
void main() { 
 
\t outColor = vec4(v_color, 1.0); 
 
} 
 
`; 
 

 
const geometry = { 
 
    positions: [-0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0], 
 
    indices: [0, 2, 1, 1, 2, 3], 
 
}; 
 

 
const renderList = []; 
 

 
// STEP 1 (create canvas) 
 
var canvas = document.getElementById("canvas"); 
 
var gl = canvas.getContext("webgl2"); 
 
if (!gl) { 
 
    console.log('no webgl2 buddy'); 
 
} 
 

 
// STEP 2 (create program) 
 
const v = gl.createShader(gl.VERTEX_SHADER); 
 
gl.shaderSource(v, vertex); 
 
gl.compileShader(v); 
 

 
const f = gl.createShader(gl.FRAGMENT_SHADER); 
 
gl.shaderSource(f, fragment); 
 
gl.compileShader(f); 
 

 
const program = gl.createProgram(); 
 
gl.attachShader(program, v); 
 
gl.attachShader(program, f); 
 
gl.linkProgram(program); 
 

 
// STEP 3 (create VAO) 
 
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position'); 
 
const colorUniformLocation = gl.getUniformLocation(program, 'color'); 
 

 
const positionsBuffer = gl.createBuffer(); 
 
const indicesBuffer = gl.createBuffer(); 
 

 
const vao = gl.createVertexArray(); 
 
gl.bindVertexArray(vao); 
 

 
// position & indices 
 
gl.enableVertexAttribArray(positionAttributeLocation); 
 
gl.bindBuffer(gl.ARRAY_BUFFER, positionsBuffer); 
 
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(geometry.positions), gl.STATIC_DRAW); 
 
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0); 
 
gl.bindBuffer(gl.ARRAY_BUFFER, null); 
 

 
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer); 
 
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(geometry.indices), gl.STATIC_DRAW); 
 

 
// STEP 4 (create UBO) 
 

 
// bound to location 0 
 
const perScene = new UniformBuffer(gl, [ 
 
    ...vec4.create(), // color 1 
 
    ...vec4.create(), // color 2 
 
], 0); 
 

 
// bound to location 1 ? 
 
const perModel = new UniformBuffer(gl, [ 
 
    ...vec4.create(), // color 3 
 
], 1); 
 

 
gl.uniformBlockBinding(program, gl.getUniformBlockIndex(program, "perScene"), perScene.boundLocation); 
 
gl.uniformBlockBinding(program, gl.getUniformBlockIndex(program, "perModel"), perModel.boundLocation); 
 

 

 
// STEP 5 (add instances) 
 
for (let i = 0; i < 1; i++) { 
 
    renderList.push({ 
 
     id: i, 
 
     vao: vao, 
 
     program: program, 
 
     color: [0, 1, 1], 
 
    }); 
 
} 
 

 
// STEP 6 (draw) 
 
gl.clearColor(0, 0, 0, 0); 
 

 
gl.enable(gl.DEPTH_TEST); 
 

 
gl.viewport(0, 0, canvas.width, canvas.height); 
 

 
perScene.update(gl, [ 
 
    ...vec4.fromValues(1, 0, 0, 1), 
 
    ...vec4.fromValues(0, 1, 0, 1), 
 
]); 
 

 
for (let i = 0; i < renderList.length; i++) { 
 
    const current = renderList[i]; 
 
    gl.useProgram(current.program); 
 
    gl.bindVertexArray(current.vao); 
 

 
    // update perObject 
 
    perModel.update(gl, [ 
 
     ...vec4.fromValues(0, 0, 1, 1), 
 
    ]); 
 

 
    gl.drawElements(gl.TRIANGLES, geometry.indices.length, gl.UNSIGNED_SHORT, 0); 
 

 
    // unbind 
 
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); 
 
} 
 

 
console.log('compiled!');
canvas { 
 
    background-color: black; 
 
}
<canvas id="canvas"></canvas> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script>

this example有5個均勻塊。

  1. 共享基質如projectionviewviewProjection
  2. 每模型基質如world和每樣lightPositionlightColor光信息的worldInverseTransform
  3. 有2個LED燈,便於4塊類似於第三
  4. 像環境色,鏡面等材料數據..

我並不是說這是完美的設置。我真的不知道。但是製作稱爲「素材」的東西很常見,並且在多個模型中共享該素材,這就像perMaterial區塊與perModel區塊不同。分享照明信息也很常見。我不知道理想的設置是什麼,只是指出perSceneperModel可能不足以滿足相當常見的情況。

的另一件事,這條線

// unbind 
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); 

是沒有意義的。 ELEMENT_ARRAY_BUFFER是VAO州的一部分。

+0

哦jsfiddle的第41行被註釋掉了。如果我取消註釋,您應該看到該錯誤。或者如果我曾是一個好孩子,並稱之爲'getUniformBlockIndex',我會看到一個白色的方塊不是黃色的? :) – andrevenancio

+0

更新爲WebGL2中沒有錯誤。我忘了你需要調用'uniformBlockBinding' – gman

+0

,所以在我的代碼中最好的地方添加這個?這應該在for循環內的渲染循環上嗎?因爲我們想要爲每個對象/程序更新相同的統一緩衝區......? – andrevenancio

0

正如GMAN說,獲得均勻的塊的索引,然後與gl.bindBufferBase

其綁定你更新的類應該是這個樣子:

class UniformBuffer { 
    constructor(gl, data, program, uniformName, targetIndex = 0) { 

     this.data = new Float32Array(data); 
     const boundLocation = gl.getUniformBlockIndex(program, uniformName); 

     this.buffer = gl.createBuffer(); 
     gl.bindBufferBase(gl.UNIFORM_BUFFER, boundLocation, this.buffer); 

     gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer); 
     gl.bufferData(gl.UNIFORM_BUFFER, this.data, gl.DYNAMIC_DRAW); 
     gl.bindBuffer(gl.UNIFORM_BUFFER, null); 
    } 

    update(gl, data, offset = 0) { 
     this.data.set(data, offset); 

     gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer); 
     gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, null); 
     gl.bindBuffer(gl.UNIFORM_BUFFER, null); 
    } 

};

+0

不知道它是否是我的GPU,但是即使添加這個改變,我也無濟於事。https://jsfiddle.net/m9qchtdb/11/在Chrome上沒有錯誤,但Firefox日誌緩衝區的統一塊小於UNIFORM_BLOCK_DATA_SIZE。 :/你可以在你的機器上運行jsfiddle這個評論嗎? – andrevenancio