2017-06-19 44 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