这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战
webGL使用指南(3) 绘制三角形
你可能知道。构成三维模型的基本单位是三角形,不敢三维模型的形状多么复杂,其基本组成部分都是三角形,只不过复杂的模型由更多的三角形构成而已。因此,如何绘制三角形对渲染三维模型至关重要。
缓冲区的作用
对于图形,可能需要多个点组成,这样就需要将一个图形中所有的顶点全部传入到顶点着色器,然后才能把图形变化出来,webgl就提供了一种比较方便的机制——缓冲区对象,它可以一次性的向着色器传入多个顶点的数据。
缓冲区对象是webgl系统中的一块区域,可以一次性的向缓冲区对象中填入大量顶点数据,然后将这些数据保存在其中,供顶点着色器使用。
1.创建/删除缓冲区对象
gl.createBuffer();//创建缓冲区对象
gl.deleteBuffer(buffer);//删除参数buffer表示的缓冲区对象
2. 将缓冲区对象绑定到webgl系统中的目标上
gl.bindBuffer(target, buffer);
//允许使用buffer表示的缓冲区对象并且将其绑定到target表示的目标上。
//target参数可以是以下中的一个:
gl.ARRAY_BUFFER 表示缓冲区对象中包含了顶点的数据
gl.ELEMENT_ ARRAY_BUFFER 表示缓冲区对象包含了顶点的索引值
3. 向缓冲区中写入数据
gl.bufferData(target, data, usage);
//开辟储存空间,向绑定在target的缓冲区对象写入数据data
//target gl.ARRAY_BUFFER或gl.ELEMENT_ARRAY_BUFFER
//data 写入缓冲区对象的数据
//usage 表示程序将如何使用储存在缓冲区对象中的数据。
gl.STATIC_DRAW 表示只会向缓冲区对象中写入一次数据,但需要绘制很多次
gl.STREAM_DRAW 表示只会向缓冲区对象写入一次数据,然后绘制若干次
gl.DYNAMIC_DRAW 表示会向缓冲区对象多次写入数据,并绘制多次
4. 将缓冲区对象分配给attribute变量
gl.vertexAttribPointer(location,size,type,normalized,stride,offset);
//将绑定到gl.ARRAY_BUFFER到缓冲区对象分配给由location指定的attribute变量。
//location 指定分配attribute变量的储存位置
size 指定缓冲区中每个顶点的分量个数(1-4)。
type 用以下类型之一来指定数据格式:
gl.NUSIGNED_BYTE 无符号字节,Uint8Array
gl.SHORT 短整型,Int16Array
gl.UNSIGNED_SHORT 无符号短整型,Uint16Array
gl.INT 整型,Int32Array
gl.UNSIGEND_INT 无符号整型,Uint32Array
gl.FLOAT 浮点型,Float32Array
normalized 指定相邻两个顶点之间的字节数,默认为0
offset 指定缓冲区对象中的偏移量。即attribute变量从缓冲区中的何处开始储存,如果是从起始位置开始的,offset设置为0
接下来我们用代码实现一下绘制三角形
<body>
<canvas width="500" height="500" id="oCanvas"></canvas>
<script type="notjs" id="vertex">
attribute vec2 a_position;
attribute vec4 a_color;
uniform vec2 screenSize;
varying vec4 v_color;
attribute float pointsize;
void main () {
float x = a_position.x * 2.0 / screenSize.x - 1.0;
float y = 1.0 - (a_position.y * 2.0 / screenSize.y);
gl_Position = vec4(x, y, 0, 1);
gl_PointSize = pointsize;
v_color = a_color;
}
</script>
<script type="notjs" id="fragment">
precision mediump float;
varying vec4 v_color;
void main () {
gl_FragColor = v_color;
}
</script>
<script>
var oCanvas = document.getElementById('oCanvas');
var gl = oCanvas.getContext('webgl');
if (!gl) {
alert('浏览器不支持webgl');
}
// var str = ""
// 创建着色器函数
function createShader(gl, type, source) {
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.log(gl.getShaderInfoLog(shader));
}
var vetexStr = document.getElementById('vertex').innerText;
var fragmentStr = document.getElementById('fragment').innerText;
var vertexShader = createShader(gl, gl.VERTEX_SHADER, vetexStr);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentStr);
function createProgram(gl, vertexShader, fragmentShader) {
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
var success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program;
}
console.log(gl.getProgramInfoLog(shader));
}
var program = createProgram(gl, vertexShader, fragmentShader);
console.log(program);
gl.useProgram(program);
var a_position = gl.getAttribLocation(program, "a_position");
var pointsize = gl.getAttribLocation(program, "pointsize");
var screenSize = gl.getUniformLocation(program, 'screenSize');
var a_color = gl.getAttribLocation(program, "a_color");
gl.uniform2f(screenSize, oCanvas.width, oCanvas.height);
gl.vertexAttrib4f(a_color, 1, 0, 1, 1);
var positionBuffer = gl.createBuffer();//创建缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);//绑定缓冲区对象
function bindEvent() {
var points = [];
oCanvas.onmousedown = function (e) {
gl.clearColor(0, 1, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
var x = e.offsetX;
var y = e.offsetY;
var color = randomColor();
points.push(x,y,10.0);
if (points.length % 3 == 0) {
// 绑定缓冲区数据
// 第一个参数绑定的缓冲区
// 第二个参数 传入的数据(要求时强类型语言需要你用类型化数组转换一下)
// 第三个参数是绘制的方式一般为gl.STATIC_DRAW 表示不会频繁改变缓冲区中的数据(webgl会根据这个参数做一些优化处理)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.STATIC_DRAW);
// 为变量分配数据
// 第一个参数是为哪个变量分配数据
// 第二个参数是这个变量拥有数据的分量个数
// 第三个参数是数据类型
// 第四个参数是 如果是非浮点型数据是否将数据进行归一化 如果设置为true 数值类型字节大小在-128到127之间的数(BYTE)回转传承-1.0 到1.0之间,数值类型字节大小在0 到255之间的数(UNSIGNED_BYTE)变为0.0 到1.0之间,SHORT也是转换到-1.0 - 1.0之间,如果该值为false 则按照用户输入的数据处理。
// 第四个参数说白了就是只有在第三个参数除了为FLOAT之外的(即为UNSIGNED_BYTE, SHORT, UNSIGNED_SHORT, INT, UNSIGNED_INT)时实用
// 第五个参数为两个顶点之间的字节数,也就是每个顶点的字节数,如果设置为0 则代表两个顶点之间字节数为 顶点数据分量 * 每个元素的字节数
// 第六个参数为当前变量数据是从每个顶点数据的哪一位开始取
gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 4 * 3, 0);
gl.vertexAttribPointer(pointsize, 1, gl.FLOAT, false, 4 * 3, 4 * 2);
// 启用这个位置数据
gl.enableVertexAttribArray(a_position);
gl.enableVertexAttribArray(pointsize);
// 绘制三角形
gl.drawArrays(gl.TRIANGLES, 0, points.length / 3);
}
}
}
function randomColor() {
var r = Math.random();
var g = Math.random();
var b = Math.random();
var a = 0.5 + Math.random() * 0.5;
return {
r,
g,
b,
a
}
}
bindEvent();
</script>
</body>
我们可以看到我们成功使用代码实现绘制三角形,只要在画布任意点三个点就会绘制成三角形