5、webgl基本概念 + 绘制多边形 + 绘制圆 + 绘制圆环
基本概念
如何绘制圆
可以绘制顶点位置一致的连续三角形,当三角形的数量越多,底边越小的时候就画好了圆。
三角形的角度计算方式
let angle = i * Math.PI * 2 / n;
x 方向的位置坐标点
let pointx = radius * Math.cos(angle) + x
y 方向的位置坐标点
let pointy = radius * Math.sin(angle) + y
绘制多边形、圆、圆环
多边形绘制
当三角形的数量较少时候,我们绘制出来的其实是一个多边形

<!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();let indexBuffer = gl.createBuffer();gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); // 绑定索引缓冲区gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);let pointsData = circlePoints(100, 100, 50, 8);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pointsData), gl.STATIC_DRAW);gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 4 * 5, 0);gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, 4 * 5, 4* 2);// 启用这个位置数据gl.enableVertexAttribArray(a_position);gl.enableVertexAttribArray(a_color);gl.drawArrays(gl.TRIANGLE_FAN, 0, 10);// 生成圆的坐标数据, 传入 圆心坐标 x, y, 半径 radius, 三角形 个数 nfunction circlePoints(x, y, radius, n) {let points = [x, y, 1, 0, 0];for(let i = 0; i <= n; i++) {let angle = i * Math.PI * 2 / n;let pointx = radius * Math.cos(angle) + x, pointy = radius * Math.sin(angle) + y;// 坐标点 数据后面的三个随机数,是颜色 r g bpoints.push(pointx, pointy, Math.random(),Math.random(), Math.random() )}return points;}</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 positionBuffer = gl.createBuffer();let indexBuffer = gl.createBuffer();gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); // 绑定索引缓冲区gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);let pointsData = circlePoints(100, 100, 50, 50);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pointsData), gl.STATIC_DRAW);gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 4 * 5, 0);gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, 4 * 5, 4* 2);// 启用这个位置数据gl.enableVertexAttribArray(a_position);gl.enableVertexAttribArray(a_color);gl.drawArrays(gl.TRIANGLE_FAN, 0, 52);// 生成圆的坐标数据, 传入 圆心坐标 x, y, 半径 radius, 三角形 个数 nfunction circlePoints(x, y, radius, n) {let points = [x, y, 1, 0, 0];for(let i = 0; i <= n; i++) {let angle = i * Math.PI * 2 / n;let pointx = radius * Math.cos(angle) + x, pointy = radius * Math.sin(angle) + y;points.push(pointx, pointy, Math.random(),Math.random(), Math.random() )}return points;}</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();let indexBuffer = gl.createBuffer();gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); // 绑定索引缓冲区gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);let pointsData = ciclequePoints(100, 100, 100, 50, 50).points;let indexData = ciclequePoints(100, 100, 100, 50, 50).indices;gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pointsData), gl.STATIC_DRAW);gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexData), gl.STATIC_DRAW);gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 4 * 5, 0);gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, 4 * 5, 4* 2);// 启用这个位置数据gl.enableVertexAttribArray(a_position);gl.enableVertexAttribArray(a_color);gl.drawElements(gl.TRIANGLES, indexData.length, gl.UNSIGNED_SHORT, 0); // 索引绘制// gl.drawArrays(gl.TRIANGLE_FAN, 0, 52);function ciclequePoints(x, y, outerRadius, innerRadius, n) {let points = []; // 点坐标let indices = []; // 索引let color = {r: 0, g: 0,b: 0}for(let i = 0; i <= n; i++) {color = {r: Math.random(),g: Math.random(),b: Math.random(),}let angle = i * Math.PI * 2 / n;let innerX = innerRadius * Math.cos(angle) + x , innerY = innerRadius * Math.sin(angle) + y;let outerX = outerRadius * Math.cos(angle) + x,outerY = outerRadius * Math.sin(angle) + y;points.push(innerX, innerY, color.r, color.g, color.b);points.push(outerX, outerY, color.r, color.g, color.b);let v0 = i * 2, v1 = v0 + 1, v2 = v0 + 2, v3 = v0 + 3;if(i === n) {// 最后一个点 连接第一个点即可v2 = 0;v3 = 1;}indices.push(v0, v1, v2, v2, v1, v3);}return {points,indices}}</script>
</body>
</html>
圆环-使用三角带 + 索引缓冲区进行绘制
三角带进行绘制的好处在于索引只需要存 4个。
<!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();let indexBuffer = gl.createBuffer();gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); // 绑定索引缓冲区gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);let pointsData = ciclequePoints(100, 100, 100, 50, 50).points;let indexData = ciclequePoints(100, 100, 100, 50, 50).indices;gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pointsData), gl.STATIC_DRAW);gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexData), gl.STATIC_DRAW);gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 4 * 5, 0);gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, 4 * 5, 4* 2);// 启用这个位置数据gl.enableVertexAttribArray(a_position);gl.enableVertexAttribArray(a_color);// gl.TRIANGLE_STRIP 三角带绘制gl.drawElements(gl.TRIANGLE_STRIP, indexData.length, gl.UNSIGNED_SHORT, 0); // 索引绘制// gl.drawArrays(gl.TRIANGLE_FAN, 0, 52);function ciclequePoints(x, y, outerRadius, innerRadius, n) {let points = []; // 点坐标let indices = []; // 索引let color = {r: 0, g: 0,b: 0}for(let i = 0; i <= n; i++) {color = {r: Math.random(),g: Math.random(),b: Math.random(),}let angle = i * Math.PI * 2 / n;let innerX = innerRadius * Math.cos(angle) + x , innerY = innerRadius * Math.sin(angle) + y;let outerX = outerRadius * Math.cos(angle) + x,outerY = outerRadius * Math.sin(angle) + y;points.push(innerX, innerY, color.r, color.g, color.b);points.push(outerX, outerY, color.r, color.g, color.b);let v0 = i * 2, v1 = v0 + 1, v2 = v0 + 2, v3 = v0 + 3;if(i === n) {// 最后一个点 连接第一个点即可v2 = 0;v3 = 1;}indices.push(v0, v1, v2, v3); // 利用三角带绘制,每一组只需要四个顶点}return {points,indices}}// 生成圆的坐标数据, 传入 圆心坐标 x, y, 半径 radius, 三角形 个数 n// function circlePoints(x, y, radius, n) {// let points = [x, y, 1, 0, 0];// for(let i = 0; i <= n; i++) {// let angle = i * Math.PI * 2 / n;// let pointx = radius * Math.cos(angle) + x, pointy = radius * Math.sin(angle) + y;// points.push(pointx, pointy, Math.random(),Math.random(), Math.random() )// }// return points;// }</script>
</body>
</html>
