3、webgl 基本概念 + 绘制线段 + 绘制三角形
基本概念
缓冲区对象
作用: 对于图形,可能需要多个点组成,这样就需要将一个图形中所有的顶点全部传入顶点着色器,然后才能把图形画出来, webgl 就提供了一种比较方便的机制——缓冲区对象,它可以一次性的向着色器传入多个顶点的数据。
缓冲区对象是 webgl 系统中的一块区域,可以一次性的向缓冲区对象中填入大量顶点数据,然后将这些数据保存在其中,供顶点着色器使用。
gl.bufferData()
绑定缓冲区数据
第一个参数 绑定的缓冲区
第二个参数 传入的数据(要求是强类型语言需要你用类型化数组转换一下
第三个参数 是绘制的方式,一般为 gl.STATIC_DRAW 表示不会频繁改变缓冲区中的数据(webgl会根据这个参数做一些优化处理)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([first.x, first.y,second.x, second.y,third.x, third.y]), gl.STATIC_DRAW)
gl.vertexAttribPointer()
为变量分配数据
第一个参数 是为哪个变量分配数据
第二个参数 是这个变量拥有数据的分量个数
第三个参数 是数据类型
第四个参数 是 如果是非浮点型数据是否将数据进行归一化,如果设置为 true 数值类型字节大小在 -128 到 127 之间的数(BYTE)回转传承 -1 到 1 之间,数值类型字节大小在 0 到 255 之间的数(UNISIGNED_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, 0, 0);// 启用这个位置数据
gl.enableVertexAttribArray(a_position);
// 绘制三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);
绘制三角形
根据随机颜色绘制三角形
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>webgl-随机颜色三角形绘制</title>
</head>
<body><canvas width="500" height="500" id="myCanvas"></canvas><script type="vertex" id="vertex">attribute vec2 a_position;attribute vec4 a_color;uniform vec2 screenSize;varying vec4 v_color;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 = 10.0;v_color = a_color;}</script><script type="fragment" id="fragment">precision mediump float;varying vec4 v_color;void main() {gl_FragColor = v_color;}</script><script>let myCanvas = document.getElementById("myCanvas");let gl = myCanvas.getContext("webgl");// IE8 之前的浏览器不兼容if(!gl) {alert("浏览器不支持 webgl!")}// 创建着色器function createShader(gl, type, source) {const shader = gl.createShader(type);gl.shaderSource(shader, source);gl.compileShader(shader);let isSuccess = gl.getShaderParameter(shader, gl.COMPILE_STATUS);return isSuccess ? shader : console.log(gl.getShaderInfoLog(shader))}// 获取着色器中的文本内容字符串function getInnerText(id) {return document.getElementById(id).innerText;}const vertexStr = getInnerText("vertex");const fragmentStr = getInnerText("fragment");const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexStr)const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentStr);// 创建程序function createProgram(gl, vertexShader, fragmentShader) {let program = gl.createProgram();gl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);gl.linkProgram(program);let isSuccess = gl.getProgramParameter(program, gl.LINK_STATUS);return isSuccess ? program : console.log(gl.getProgramInfoLog(program));}let program = createProgram(gl, vertexShader, fragmentShader);gl.useProgram(program);// 获取顶点着色器中变量let a_position = gl.getAttribLocation(program, 'a_position');let screenSize = gl.getUniformLocation(program, 'screenSize');let a_color = gl.getAttribLocation(program, 'a_color');// 获取画布的宽高,因为顶点着色器中已经gl.uniform2f(screenSize, myCanvas.width, myCanvas.height);let {r, g, b, a} = randomColor();gl.vertexAttrib4f(a_color, r, g, b, a);let positionBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([50, 50,50, 100,100, 100]), gl.STATIC_DRAW);gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 0, 0);gl.enableVertexAttribArray(a_position);gl.drawArrays(gl.TRIANGLES, 0, 3);function randomColor() {let r = Math.random(), g = Math.random(), b = Math.random(), a = 0.5 + Math.random() * 0.5;return {r, g, b, a}}</script>
</body>
</html>
根据鼠标点击的连续三个位置绘制三角形
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>webgl-鼠标点击三个点进行三角形绘制</title>
</head>
<body><canvas width="500" height="500" id="myCanvas"></canvas><script type="vertex" id="vertex">attribute vec2 a_position;attribute vec4 a_color;uniform vec2 screenSize;varying vec4 v_color;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 = 10.0;v_color = a_color;}</script><script type="fragment" id="fragment">precision mediump float;varying vec4 v_color;void main() {gl_FragColor = v_color;}</script><script>let myCanvas = document.getElementById("myCanvas");let gl = myCanvas.getContext("webgl");// IE8 之前的浏览器不兼容if(!gl) {alert("浏览器不支持 webgl!")}// 创建着色器function createShader(gl, type, source) {const shader = gl.createShader(type);gl.shaderSource(shader, source);gl.compileShader(shader);let isSuccess = gl.getShaderParameter(shader, gl.COMPILE_STATUS);return isSuccess ? shader : console.log(gl.getShaderInfoLog(shader))}// 获取着色器中的文本内容字符串function getInnerText(id) {return document.getElementById(id).innerText;}const vertexStr = getInnerText("vertex");const fragmentStr = getInnerText("fragment");const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexStr)const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentStr);// 创建程序function createProgram(gl, vertexShader, fragmentShader) {let program = gl.createProgram();gl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);gl.linkProgram(program);let isSuccess = gl.getProgramParameter(program, gl.LINK_STATUS);return isSuccess ? program : console.log(gl.getProgramInfoLog(program));}let program = createProgram(gl, vertexShader, fragmentShader);gl.useProgram(program);// 获取顶点着色器中变量let a_position = gl.getAttribLocation(program, 'a_position');let screenSize = gl.getUniformLocation(program, 'screenSize');let a_color = gl.getAttribLocation(program, 'a_color');// 获取画布的宽高,因为顶点着色器中已经gl.uniform2f(screenSize, myCanvas.width, myCanvas.height);// let {r, g, b, a} = randomColor();// gl.vertexAttrib4f(a_color, r, g, b, a);let positionBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);function bindEvent() {let points = [];myCanvas.onmousedown = e => {let color = randomColor();let x = e.offsetX, y = e.offsetY;points.push({x,y,color})if(points.length > 0 && points.length % 3 === 0) {for(let i = 0; i < points.length; i+=3) {let first = points[i], second = points[i + 1], third = points[i + 2], {r, g, b, a} = points[i + 2].color;gl.vertexAttrib4f(a_color, r, g, b, a);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([first.x, first.y,second.x, second.y,third.x, third.y]), gl.STATIC_DRAW);gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 0, 0);gl.enableVertexAttribArray(a_position);gl.drawArrays(gl.TRIANGLES, 0, 3);}}}}bindEvent()function randomColor() {let r = Math.random(), g = Math.random(), b = Math.random(), a = 0.5 + Math.random() * 0.5;return {r, g, b, a}}</script>
</body>
</html>
绘制线段
根据鼠标点击连续位置进行线段绘制
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><canvas width="500" height="500" id="myCanvas"></canvas><script type="vertex" id="vertex">attribute vec2 a_position;attribute vec4 a_color;uniform vec2 screenSize;varying vec4 v_color;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 = 10.0;v_color = a_color;}</script><script type="fragment" id="fragment">precision mediump float;varying vec4 v_color;void main() {gl_FragColor = v_color;}</script><script>let myCanvas = document.getElementById("myCanvas");let gl = myCanvas.getContext("webgl");// IE8 之前的浏览器不兼容if(!gl) {alert("浏览器不支持 webgl!")}// 创建着色器function createShader(gl, type, source) {const shader = gl.createShader(type);gl.shaderSource(shader, source);gl.compileShader(shader);let isSuccess = gl.getShaderParameter(shader, gl.COMPILE_STATUS);return isSuccess ? shader : console.log(gl.getShaderInfoLog(shader))}// 获取着色器中的文本内容字符串function getInnerText(id) {return document.getElementById(id).innerText;}const vertexStr = getInnerText("vertex");const fragmentStr = getInnerText("fragment");const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexStr)const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentStr);// 创建程序function createProgram(gl, vertexShader, fragmentShader) {let program = gl.createProgram();gl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);gl.linkProgram(program);let isSuccess = gl.getProgramParameter(program, gl.LINK_STATUS);return isSuccess ? program : console.log(gl.getProgramInfoLog(program));}let program = createProgram(gl, vertexShader, fragmentShader);gl.useProgram(program);// 获取顶点着色器中变量let a_position = gl.getAttribLocation(program, 'a_position');let screenSize = gl.getUniformLocation(program, 'screenSize');let a_color = gl.getAttribLocation(program, 'a_color');// 获取画布的宽高,因为顶点着色器中已经gl.uniform2f(screenSize, myCanvas.width, myCanvas.height);let positionBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);function bindEvent() {let points = [];myCanvas.onmousedown = e => {let color = randomColor();let x = e.offsetX, y = e.offsetY;points.push({x,y,color})if(points.length > 0 && points.length % 2 === 0) {for(let i = 0; i < points.length; i+=2) {let first = points[i], second = points[i + 1], {r, g, b, a} = points[i + 1].color;gl.vertexAttrib4f(a_color, r, g, b, a);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([first.x, first.y,second.x, second.y,]), gl.STATIC_DRAW);gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 0, 0);// 启用这个位置数据gl.enableVertexAttribArray(a_position);// 绘制三角形gl.drawArrays(gl.LINES, 0, 3);}}}}bindEvent()function randomColor() {let r = Math.random(), g = Math.random(), b = Math.random(), a = 0.5 + Math.random() * 0.5;return {r, g, b, a}}</script>
</body>
</html>
