VBO
VBO(Vertex Buffer Object,顶点缓冲对象)是在运行图形应用程序的系统内存(通常由 CPU
管理)中创建的,但最终会被上传到图形处理单元(GPU
)的内存中进行存储和使用。即CPU管理
创建在内存
中,之后通过如OpenGL将其上传到GPU的显存
中存储
vertexAttribPointer 和 enableVertexAttribArray含义
vertexAttribPointer
语法
void gl.vertexAttribPointer(index, size, type, normalized, stride, offset);
其主要作用是告诉 WebGL 如何从缓冲区中获取顶点数据,并将其与顶点着色器中的特定属性关联起来
index | 指定要修改的顶点属性的索引 |
size | 指定每个顶点属性的组成数量,必须是1,2,3或4。 |
type | 指定数组中每个元素的数据类型 |
normalized | 当转换为浮点数时是否应该将整数数值归一化到特定的范围 |
stride | 一个GLsizei,以字节为单位指定连续顶点属性开始之间的偏移量(即数组中一行长度)。不能大于255。如果stride为0,则假定该属性是紧密打包的,即不交错属性,每个属性在一个单独的块中,下一个顶点的属性紧跟当前顶点之后。注意一个字节是8位,如Int16就是一个元素占两个字节,一般一个vbo只传一种类型如position,所以一般都是0 |
offset | GLintptr (en-US)指定顶点属性数组中第一部分的字节偏移量。必须是类型的字节长度的倍数 一般一个vbo只传一种类型如position,所以一般都是0 |
enableVertexAttribArray
执行gl.enableVertexArray(location)
后,顶点着色器才能访问到缓冲区的数据,函数实际上处理的对象是缓冲区。
只使用VBO渲染案例(参考可以看下)
VBO使用流程
1.创建
1.创建VBO[createBuffer],
绑定VBO[bindBuffer],
上传VBO数据[bindData],
gl.enableVertexAttribArray
2.创建EBO[createBuffer],
绑定EBO[bindBuffer],
上传数据[bindData]
2.渲染的时候使用
1.绑定VBO[bindBuffer]
2.gl.vertexAttribPointer
3.绑定EBO[bindBuffer]
4.draw
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Basic Draw Rectangle</title>
<script id="shader-vs" type="x-shader/x-vertex">
// precision lowp float;
attribute vec3 position;
attribute vec3 color;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
varying vec4 vcolor;
void main(void){
vcolor = vec4(color,1.0);
gl_Position = uPMatrix * uMVMatrix * vec4(position, 1.0);
}
</script>
<script id="shader-fs" type="x-shader/x-fragment">
precision lowp float;
varying vec4 vcolor;
void main(void) {
gl_FragColor = vcolor;
}
</script>
</head>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/3.4.2/gl-matrix-min.js"></script>
<script>
var gl;
var mat4 = glMatrix.mat4;
var canvas = document.getElementById('canvas');
var pMatrix = mat4.create();
var mvMatrix = mat4.create();
var glProgram = null;
var ext = null;
function getGLContext() {
var glContextNames = ['webgl', 'experimental-webgl'];
for (var i = 0; i < glContextNames.length; i++) {
try {
gl = canvas.getContext(glContextNames[i]);
} catch (e) { }
if (gl) {
gl.clearColor(74 / 255, 115 / 255, 94 / 255, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.viewport(0, 0, canvas.width, canvas.height);
// ext = gl.getExtension("OES_vertex_array_object");
break;
}
}
}
function initShaders() {
//get shader source
var vs_source = document.getElementById('shader-vs').innerHTML,
fs_source = document.getElementById('shader-fs').innerHTML;
//compile shaders
vertexShader = makeShader(vs_source, gl.VERTEX_SHADER);
fragmentShader = makeShader(fs_source, gl.FRAGMENT_SHADER);
//create program
glProgram = gl.createProgram();
//attach and link shaders to the program
gl.attachShader(glProgram, vertexShader);
gl.attachShader(glProgram, fragmentShader);
gl.linkProgram(glProgram);
if (!gl.getProgramParameter(glProgram, gl.LINK_STATUS)) {
alert("Unable to initialize the shader program.");
}
//use program
gl.useProgram(glProgram);
}
function makeShader(src, type) {
//compile the vertex shader
var shader = gl.createShader(type);
gl.shaderSource(shader, src);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error("Error compiling shader: " + gl.getShaderInfoLog(shader));
}
return shader;
}
function toRadian(angle) {
return angle * Math.PI / 180;
}
function getAllUniforms() {
glProgram.pMatrixUniform = gl.getUniformLocation(glProgram, 'uPMatrix');
glProgram.mvMatrixUniform = gl.getUniformLocation(glProgram, 'uMVMatrix');
}
function setAllUniforms() {
gl.uniformMatrix4fv(glProgram.pMatrixUniform, false, pMatrix);
gl.uniformMatrix4fv(glProgram.mvMatrixUniform, false, mvMatrix);
}
function updateProjectionMatrix() {
var near = 10;
var far = 400;
var widthHeightRatio = canvas.width / canvas.height;
var fovy = 40;
// mat4.ortho(pMatrix, -canvas.width / 2, canvas.width / 2, canvas.height / 2, -canvas.height / 2, near, far);
mat4.perspective(pMatrix, toRadian(fovy), widthHeightRatio, near, far);
}
var rotate = 0;
function updateModelViewMatrix() {
mat4.identity(mvMatrix, mvMatrix);
mat4.translate(mvMatrix, mvMatrix, [0, 0, -200]);
mat4.rotate(mvMatrix, mvMatrix, toRadian(rotate++), [0, 1, 0]);
}
var position_buffer_mesh1 = null;
var color_buffer_mesh1 = null;
var index_buffer_mesh1 = null;
function setupBuffer1() {
// 1.创建VBO,并bind顶点属性数据
var position = [
-50, -30, 0,
-5, -30, 0,
-5, 30, 0,
-50, 30, 0
];
var color = [
1, 0, 0,
0, 1, 0,
0, 0, 1,
1, 0, 0
];
position_buffer_mesh1 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, position_buffer_mesh1);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(position), gl.STATIC_DRAW);
color_buffer_mesh1 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer_mesh1);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(color), gl.STATIC_DRAW);
// 2.复创建EBO
var index = [
0, 1, 2,
0, 2, 3
];
index_buffer_mesh1 = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer_mesh1);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(index), gl.STATIC_DRAW);
// 3.开启顶点属性
let aVertexPosition = gl.getAttribLocation(glProgram, 'position');
let aVertexColor = gl.getAttribLocation(glProgram, 'color');
gl.enableVertexAttribArray(aVertexPosition);
gl.enableVertexAttribArray(aVertexColor);
}
var position_buffer_mesh2 = null;
var color_buffer_mesh2 = null;
var index_buffer_mesh2 = null;
function setupBuffer2() {
// 1.创建VBO,并bind顶点属性数据
var position = [
5, -30, 0,
50, -30, 0,
50, 30, 0,
5, 30, 0
];
var color = [
1, 0, 0,
0, 1, 0,
0, 0, 1,
1, 0, 0
];
position_buffer_mesh2 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, position_buffer_mesh2);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(position), gl.STATIC_DRAW);
color_buffer_mesh2 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer_mesh2);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(color), gl.STATIC_DRAW);
// 2.复创建EBO
var index = [
0, 1, 2,
0, 2, 3
];
index_buffer_mesh2 = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer_mesh2);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(index), gl.STATIC_DRAW);
// 3.开启顶点属性
let aVertexPosition = gl.getAttribLocation(glProgram, 'position');
let aVertexColor = gl.getAttribLocation(glProgram, 'color');
gl.enableVertexAttribArray(aVertexPosition);
gl.enableVertexAttribArray(aVertexColor);
}
function draw(n) {
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0);
}
function animate() {
requestAnimationFrame(animate);
// update uniform
updateModelViewMatrix();
setAllUniforms();
// 这个案例只使用了一个material
let aVertexPosition = gl.getAttribLocation(glProgram, 'position');
let aVertexColor = gl.getAttribLocation(glProgram, 'color');
gl.clear(gl.COLOR_BUFFER_BIT);
// draw first one
gl.bindBuffer(gl.ARRAY_BUFFER, position_buffer_mesh1);
gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer_mesh1);
gl.vertexAttribPointer(aVertexColor, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer_mesh1);
draw(6);
// draw second one
gl.bindBuffer(gl.ARRAY_BUFFER, position_buffer_mesh2);
gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer_mesh2);
gl.vertexAttribPointer(aVertexColor, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer_mesh2);
draw(6);
}
window.onload = function () {
getGLContext();
initShaders();
getAllUniforms();
updateProjectionMatrix();
setupBuffer1();
setupBuffer2();
animate();
}
</script>
</body>
</html>
使用VAO渲染案例(参考可以看下)
VAO使用流程
1.创建
1.创建VBO[createBuffer],绑定VBO[bindBuffer],上传VBO数据[bindData]
2.创建EBO[createBuffer],绑定EBO[bindBuffer],上传数据[bindData]
3.创建VAO[createVertexArrayOES],
绑定VAO[bindVertexArrayOES],
enableVertexAttribArray,
绑定VBO[bindBuffer],
vertexAttribPointer,
绑定EBO[bindBuffer],
取消绑定VAO[bindVertexArrayOES(null)]
2.渲染的时候使用
1.bindVertexArrayOES(vao)
2.draw
这里注意VAO使用的时候需要绑定VBO,而且EBO,VBO/EBO可以之前就创建绑定上传数据,使用VAO的时候再绑定他们就可以
代码
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Basic Draw Rectangle</title>
<script id="shader-vs" type="x-shader/x-vertex">
// precision lowp float;
attribute vec3 position;
attribute vec3 color;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
varying vec4 vcolor;
void main(void){
vcolor = vec4(color,1.0);
gl_Position = uPMatrix * uMVMatrix * vec4(position, 1.0);
}
</script>
<script id="shader-fs" type="x-shader/x-fragment">
precision lowp float;
varying vec4 vcolor;
void main(void) {
gl_FragColor = vcolor;
}
</script>
</head>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/3.4.2/gl-matrix-min.js"></script>
<script>
var gl;
var mat4 = glMatrix.mat4;
var canvas = document.getElementById('canvas');
var pMatrix = mat4.create();
var mvMatrix = mat4.create();
var glProgram = null;
var ext = null;
function getGLContext() {
var glContextNames = ['webgl', 'experimental-webgl'];
for (var i = 0; i < glContextNames.length; i++) {
try {
gl = canvas.getContext(glContextNames[i]);
} catch (e) { }
if (gl) {
gl.clearColor(74 / 255, 115 / 255, 94 / 255, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.viewport(0, 0, canvas.width, canvas.height);
ext = gl.getExtension("OES_vertex_array_object");
break;
}
}
}
function initShaders() {
//get shader source
var vs_source = document.getElementById('shader-vs').innerHTML,
fs_source = document.getElementById('shader-fs').innerHTML;
//compile shaders
vertexShader = makeShader(vs_source, gl.VERTEX_SHADER);
fragmentShader = makeShader(fs_source, gl.FRAGMENT_SHADER);
//create program
glProgram = gl.createProgram();
//attach and link shaders to the program
gl.attachShader(glProgram, vertexShader);
gl.attachShader(glProgram, fragmentShader);
gl.linkProgram(glProgram);
if (!gl.getProgramParameter(glProgram, gl.LINK_STATUS)) {
alert("Unable to initialize the shader program.");
}
//use program
gl.useProgram(glProgram);
}
function makeShader(src, type) {
//compile the vertex shader
var shader = gl.createShader(type);
gl.shaderSource(shader, src);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error("Error compiling shader: " + gl.getShaderInfoLog(shader));
}
return shader;
}
function toRadian(angle) {
return angle * Math.PI / 180;
}
function getAllUniforms() {
glProgram.pMatrixUniform = gl.getUniformLocation(glProgram, 'uPMatrix');
glProgram.mvMatrixUniform = gl.getUniformLocation(glProgram, 'uMVMatrix');
}
function setAllUniforms() {
gl.uniformMatrix4fv(glProgram.pMatrixUniform, false, pMatrix);
gl.uniformMatrix4fv(glProgram.mvMatrixUniform, false, mvMatrix);
}
function updateProjectionMatrix() {
var near = 10;
var far = 400;
var widthHeightRatio = canvas.width / canvas.height;
var fovy = 40;
// mat4.ortho(pMatrix, -canvas.width / 2, canvas.width / 2, canvas.height / 2, -canvas.height / 2, near, far);
mat4.perspective(pMatrix, toRadian(fovy), widthHeightRatio, near, far);
}
var rotate = 0;
function updateModelViewMatrix() {
mat4.identity(mvMatrix, mvMatrix);
mat4.translate(mvMatrix, mvMatrix, [0, 0, -200]);
mat4.rotate(mvMatrix, mvMatrix, toRadian(rotate++), [0, 1, 0]);
}
var position_buffer_mesh1 = null;
var color_buffer_mesh1 = null;
var index_buffer_mesh1 = null;
var vao1 = null;
function setupBuffer1() {
// 1.创建VBO,并bind顶点属性数据
var position = [
-50, -30, 0,
-5, -30, 0,
-5, 30, 0,
-50, 30, 0
];
var color = [
1, 0, 0,
0, 1, 0,
0, 0, 1,
1, 0, 0
];
position_buffer_mesh1 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, position_buffer_mesh1);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(position), gl.STATIC_DRAW);
color_buffer_mesh1 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer_mesh1);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(color), gl.STATIC_DRAW);
// 2.复创建EBO
var index = [
0, 1, 2,
0, 2, 3
];
index_buffer_mesh1 = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer_mesh1);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(index), gl.STATIC_DRAW);
// 3.开启顶点属性 并 创建和绑定VAO对象
vao1 = ext.createVertexArrayOES();
ext.bindVertexArrayOES(vao1);
let aVertexPosition = gl.getAttribLocation(glProgram, 'position');
let aVertexColor = gl.getAttribLocation(glProgram, 'color');
gl.enableVertexAttribArray(aVertexPosition);
gl.enableVertexAttribArray(aVertexColor);
gl.bindBuffer(gl.ARRAY_BUFFER, position_buffer_mesh1);
gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer_mesh1);
gl.vertexAttribPointer(aVertexColor, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer_mesh1);
ext.bindVertexArrayOES(null);
}
var position_buffer_mesh2 = null;
var color_buffer_mesh2 = null;
var index_buffer_mesh2 = null;
var vao2 = null;
function setupBuffer2() {
// 1.创建VBO,并bind顶点属性数据
var position = [
5, -30, 0,
50, -30, 0,
50, 30, 0,
5, 30, 0
];
var color = [
1, 0, 0,
0, 1, 0,
0, 0, 1,
1, 0, 0
];
position_buffer_mesh2 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, position_buffer_mesh2);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(position), gl.STATIC_DRAW);
color_buffer_mesh2 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer_mesh2);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(color), gl.STATIC_DRAW);
// 2.复创建EBO
var index = [
0, 1, 2,
0, 2, 3
];
index_buffer_mesh2 = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer_mesh2);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(index), gl.STATIC_DRAW);
// 3.开启顶点属性 并 创建和绑定VAO对象
vao2 = ext.createVertexArrayOES();
ext.bindVertexArrayOES(vao2);
let aVertexPosition = gl.getAttribLocation(glProgram, 'position');
let aVertexColor = gl.getAttribLocation(glProgram, 'color');
gl.enableVertexAttribArray(aVertexPosition);
gl.enableVertexAttribArray(aVertexColor);
gl.bindBuffer(gl.ARRAY_BUFFER, position_buffer_mesh2);
gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer_mesh2);
gl.vertexAttribPointer(aVertexColor, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer_mesh2);
ext.bindVertexArrayOES(null);
}
function draw(n) {
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0);
}
function animate() {
requestAnimationFrame(animate);
// update uniform
updateModelViewMatrix();
setAllUniforms();
gl.clear(gl.COLOR_BUFFER_BIT);
// draw first one
ext.bindVertexArrayOES(vao1);
draw(6);
// // draw second one
ext.bindVertexArrayOES(vao2);
draw(6);
}
window.onload = function () {
getGLContext();
initShaders();
getAllUniforms();
updateProjectionMatrix();
setupBuffer1();
setupBuffer2();
animate();
}
</script>
</body>
</html>