WebGL-实例化绘制
Posted giserYZ2SS
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WebGL-实例化绘制相关的知识,希望对你有一定的参考价值。
· <!-- vertex shader -->
· <scriptid="3d-vertex-shader"type="x-shader/x-vertex">
· attribute vec4 a_position;
· uniform mat4 matrix;
· void main(){
· // Multiply the position by the matrix.
· gl_Position= matrix* a_position;
· }
· </script>
和
· <!-- fragment shader -->
· <scriptid="3d-fragment-shader"type="x-shader/x-fragment">
· precision mediump float;
· uniform vec4 color;
· void main(){
· gl_FragColor= color;
· }
· </script>
· const program= webglUtils.createProgramFromScripts(
· gl,['3d-vertex-shader','3d-fragment-shader']);
· const positionLoc= gl.getAttribLocation(program,'a_position');
· const colorLoc= gl.getUniformLocation(program,'color');
· const matrixLoc = gl.getUniformLocation(program,'matrix');
通过buffer关联所需顶点数据:
· const positionBuffer= gl.createBuffer();
· gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
· gl.bufferData(gl.ARRAY_BUFFER,newFloat32Array([
· -0.1, 0.4,
· -0.1,-0.4,
· 0.1,-0.4,
· 0.1,-0.4,
· -0.1, 0.4,
· 0.1, 0.4,
· 0.4,-0.1,
· -0.4,-0.1,
· -0.4, 0.1,
· -0.4, 0.1,
· 0.4,-0.1,
· 0.4, 0.1,
· ]), gl.STATIC_DRAW);
· const numVertices=12;
我们绘制5个相同(这里的相同指每个示例的形状大小一样)实例,那么就需要对应5个矩阵和5个颜色值:
· const numInstances=5;
· const matrices=[
· m4.identity(),
· m4.identity(),
· m4.identity(),
· m4.identity(),
· m4.identity(),
· ];
· const colors =[
· [1,0,0,1,], // red
· [0,1,0,1,], // green
· [0,0,1,1,], // blue
· [1,0,1,1,], // magenta
· [0,1,1,1,], // cyan
· ];
渲染对象首先设置attribute对应的值,循环5次(这里绘制5个实例),每次设置不同的变换矩阵和颜色。
· function render(time){
· time *=0.001;// seconds
· gl.useProgram(program);
· // setup the position attribute
· gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
· gl.enableVertexAttribArray(positionLoc);
· gl.vertexAttribPointer(
· positionLoc, // location
· 2, // size (num values to pull from buffer per iteration)
· gl.FLOAT, // type of data in buffer
· false, // normalize
· 0, // stride (0 = compute from size and type above)
· 0, // offset in buffer
· );
· matrices.forEach((mat, ndx)=>{
· m4.translation(-0.5+ ndx *0.25,0,0, mat);
· m4.zRotate(mat, time *(0.1+0.1* ndx), mat);
· const color = colors[ndx];
· gl.uniform4fv(colorLoc, color);
· gl.uniformMatrix4fv(matrixLoc,false, mat);
· gl.drawArrays(
· gl.TRIANGLES,
· 0, // offset
· numVertices, // num vertices per instance
· );
· });
· requestAnimationFrame(render);
· }
· requestAnimationFrame(render);
这里我们注意到,在调用矩阵计算的数学库时,在每个方法传入已创建的矩阵对象,这样为了将计算结果保存下来以便后续使用。在大部分场景下我们不这样使用,而是直接用数学库新建一个矩阵。
这些工作完成后我们会得到5个形状大小一致、颜色和旋转角度不同的实例:
也打开以下网址可以查看效果:
http://39.106.0.97:8090/lesson/13Misc/07InstancedDrawing-01.html
· const canvas = document.getElementById('canvas');
· const gl = canvas.getContext('webgl');
· if(!gl){
· return;
· }
· const ext = gl.getExtension('ANGLE_instanced_arrays');
· if(!ext){
· return alert('need ANGLE_instanced_arrays');
· }
接下来,改变shader,用attribute代替uniform来传递变换矩阵和颜色。
· <!-- vertex shader -->
· <scriptid="3d-vertex-shader"type="x-shader/x-vertex">
· attribute vec4 a_position;
· uniform mat4 matrix;
· attribute vec4 color;
· attribute mat4 matrix;
· varying vec4 v_color;
· void main(){
· // Multiply the position by the matrix.
· gl_Position = matrix * a_position;
· // Pass the vertex color to the fragment shader.
· v_color = color;
· }
· </script>
和
· <!-- fragment shader -->
· <scriptid="3d-fragment-shader"type="x-shader/x-fragment">
· precision mediump float;
· uniform vec4 color;
· // Passed in from the vertex shader.
· varying vec4 v_color;
· void main(){
· gl_FragColor = color;
· gl_FragColor = v_color;
· }
· </script>
因为attribute类型只能在顶点着色器中运行,所以我们要通过varying来将颜色值传递到片元着色器。
下一步关联attribute:
· const program = webglUtils.createProgramFromScripts(
· gl,['3d-vertex-shader','3d-fragment-shader']);
· const positionLoc = gl.getAttribLocation(program,'a_position');
· const colorLoc = gl.getUniformLocation(program,'color');
· const matrixLoc = gl.getUniformLocation(program,'matrix');
· const colorLoc = gl.getAttribLocation(program,'color');
· const matrixLoc = gl.getAttribLocation(program,'matrix');
现在我们需要一个buffer对象保存所有变换矩阵,随后会通过attribute使用。
· // setup matrixes, one per instance
· const numInstances =5;
· // make a typed array with one view per matrix
· const matrixData =newFloat32Array(numInstances *16);
· const matrices =[
· m4.identity(),
· m4.identity(),
· m4.identity(),
· m4.identity(),
· m4.identity(),
· ];
· const matrices =[];
· for(let i =0; i < numInstances;++i){
· const byteOffsetToMatrix = i *16*4;
· const numFloatsForView =16;
· matrices.push(newFloat32Array(
· matrixData.buffer,
· byteOffsetToMatrix,
· numFloatsForView));
· }
通过这种方式,我们可以对所有转换矩阵数据做映射,我们可以通过matrices[ndx]单独使用每个转换矩阵。同时需要在GPU中为这些数据创建buffer。我们仅需要分配这些buffer:
· const matrixBuffer = gl.createBuffer();
· gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);
· // just allocate the buffer
· gl.bufferData(gl.ARRAY_BUFFER, matrixData.byteLength, gl.DYNAMIC_DRAW);
注意到,我们最后一个参数传递gl.DYNAMIC_DRAW,这是告诉WebGL我们要经常改变这个值。下一步,我们需要为颜色创建一个buffer,这个值不需要改变,至少这个例子中不需要改变,只需要加载这个数据即可。
· const colors =[
· [1,0,0,1,], // red
· [0,1,0,1,], // green
· [0,0,1,1,], // blue
· [1,0,1,1,], // magenta
· [0,1,1,1,], // cyan
· ];
· // setup colors, one per instance
· const colorBuffer = gl.createBuffer();
· gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
· gl.bufferData(gl.ARRAY_BUFFER,
· newFloat32Array([
· 1,0,0,1, // red
· 0,1,0,1, // green
· 0,0,1,1, // blue
· 1,0,1,1, // magenta
· 0,1,1,1, // cyan
· ]),
· gl.STATIC_DRAW);
在渲染时,之前做法是针对每个实例对象设置转换矩阵和颜色对应的uniform,循环渲染,在渲染前计算出每个转换矩阵。
· // update all the matrices
· matrices.forEach((mat, ndx)=>{
· m4.translation(-0.5+ ndx *0.25,0,0, mat);
· m4.zRotate(mat, time *(0.1+0.1* ndx), mat);
· const color = colors[ndx];
· gl.uniform4fv(colorLoc, color);
· gl.uniformMatrix4fv(matrixLoc,false, mat);
· gl.drawArrays(
· gl.TRIANGLES,
· 0, // offset
· numVertices, // num vertices per instance
· );
· });
因为matrices对象关联matrixData对象的,当所有matrix计算完成,转换矩阵数据就会加载到GPU中。
· // upload the new matrix data
· gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);
· gl.bufferSubData(gl.ARRAY_BUFFER,0, matrixData);
现在我们需要关联转换矩阵和颜色的attribute。转换矩阵的attribute是mat4类型,一个mat4实际上使用4个attribute量。
· const bytesPerMatrix =4*16;
· for(let i =0; i <4;++i){
· const loc = matrixLoc + i;
· gl.enableVertexAttribArray(loc);
· // note the stride and offset
· const offset = i *16; // 4 floats per row, 4 bytes per float
· gl.vertexAttribPointer(
· loc, // location
· 4, // size (num values to pull from buffer per iteration)
· gl.FLOAT, // type of data in buffer
· false, // normalize
· bytesPerMatrix, // stride, num bytes to advance to get to next set of values
· offset, // offset in buffer
· );
· // this line says this attribute only changes for each 1 instance
· ext.vertexAttribDivisorANGLE(loc,1);
· }
实例化中最重要的是调用ext.vertexAttribDivisorANGLE接口。他的作用是告知该实例以后渲染的都使用这个值。即第一个实例渲染时使用第一个转换矩阵的attribute值,第二实例渲染时使用第二个转换矩阵的attribute值,以此类推。
同样的,关联颜色attribute:
· // set attribute for color
· gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
· gl.enableVertexAttribArray(colorLoc);
· gl.vertexAttribPointer(colorLoc,4, gl.FLOAT,false,0,0);
· // this line says this attribute only changes for each 1 instance
· ext.vertexAttribDivisorANGLE(colorLoc,1);
需要记住的是,如果我们需要在其它地方使用这两个attribute的话就要设置divisor,此外需要重置它们到默认值。
最后,我们可以通过一个绘制命令绘制所有实例。
· ext.drawArraysInstancedANGLE(
· gl.TRIANGLES,
· 0, // offset
· numVertices, // num vertices per instance
· numInstances, // num instances
· );
在第一个示例中每个图形需要调用三次WebGL命令,5个图形就是15次。在这个例子我们仅仅调用2次命令绘制所有图形:一次是加载matrices,一次是绘制。在开始绘制前需要对转换矩阵和颜色进行一些操作。我们可以通过vertex array object将这些操作从绘制过程移到初始化的过程。
最后强调一点,也是我一直习惯的做法,虽然这不是这篇文章要介绍的内容。上面的代码没有考虑canvas的因素,没有使用projection matrix 或者 view matrix来进行从模型坐标到裁剪坐标转换,如果你需要这样的转换就要在javascript中计算。这样的话就增加了JavaScript的计算量,更高效的做法就是用uniform传入shader来计算。
· <!-- vertex shader -->
· <scriptid="3d-vertex-shader"type="x-shader/x-vertex">
· attribute vec4 a_position;
· attribute vec4 color;
· attribute mat4 matrix;
· uniform mat4 projection;
· uniform mat4 view;
· varying vec4 v_color;
· void main(){
· // Multiply the position by the matrix.
· gl_Position = matrix * a_position;
· gl_Position = projection * view * matrix * a_position;
· // Pass the vertex color to the fragment shader.
· v_color = color;
· }
· </script>
在初始化时关联
· const positionLoc = gl.getAttribLocation(program,'a_position');
· const colorLoc = gl.getAttribLocation(program,'color');
· const matrixLoc = gl.getAttribLocation(program,'matrix');
· const projectionLoc = gl.getUniformLocation(program,'projection');
· const viewLoc = gl.getUniformLocation(program,'view');
渲染时在合适的地方传值
· gl.useProgram(program);
· // set the view and projection matrices since
· // they are shared by all instances
· const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
· gl.uniformMatrix4fv(projectionLoc,false,
· m4.orthographic(-aspect, aspect,-1,1,-1,1));
· gl.uniformMatrix4fv(viewLoc,false, m4.zRotation(time *.1));
以上是关于WebGL-实例化绘制的主要内容,如果未能解决你的问题,请参考以下文章