2、webgl 基本概念 + 绘制点
webGL 概念
webGL(Web图形库)是一种 JavaScript API,用于在任何兼容的 Web 浏览器中呈现交互式 3D 和 2D 图形
着色器语言
顶点着色器: 用来描述顶点特征的程序
// 一个属性值,将会从缓冲区中获取数据
attribute vec4 a_position;
// 所有着色器都有一个 main 方法
void main() {// gl_Position 是一个顶点着色器主要设置的变量gl_Position = a_position;
}
片元着色器: 进行逐片处理过程如光照的程序
void main() {
// 片元着色器中的内置变量gl_FragColor = vec4(0, 0, 0, 1);
}
变量声明
attribute 变量的声明
存储限定符 类型 变量名
attribute vec4 a_Position
着色器的变量声明方式——存储限定符
1、attribute 变量:传输的是与顶点有关的数据
2、uniform 变量: 传输的是对于所有顶点都相关(或与顶点无关)的数据(全局变量)
3、textures 变量:纹理(纹理是一个数据序列,可以在着色程序运行中随意读取其中的数据。大多数情况存放的是图像数据,但是纹理仅仅是数据序列,你也可以随意存放除了颜色数据以外的其它数据。)
4、Varing 可变变量: 可变量是一种顶点着色器给片元着色器传值的方式,依照渲染的图元是 点,线 还是三角形,顶点着色器中设置的可变量会在 片元着色器运行中获取不同的插值。
GLSL ES 数据类型
| 类别 | GLSL ES数据类型 | 描述 |
| 矢量 | vec2、vec3、vec4 | 具有 2/3/4 个浮点数元素的矢量 |
| | ivec2、ivec3、ivec4 | 具有 2/3/4 个整形元素的矢量 |
| | bvec2、bvec3、bvec4 | 具有 2/3/4 个布尔值元素的矢量|
| 矩阵 | mat2 、mat3、mat4 | 22、33、4*4 的浮点数元素的矩阵(分别具有4,9,16 个元素)|
核心内容
WebGL基本图形
只能绘制三种基本图元:点、线段、三角形
理解图元与复杂图形的关系
绘图上下文获取
通过canvas元素的getContext(‘webgl’)获取
需做浏览器兼容性判断
着色器系统
分为顶点着色器(处理顶点信息)和片元着色器(处理颜色)
两种着色器的分工与协作机制
GLSL变量声明
由存储限定符(attribute/uniform等)+数据类型+变量名组成
attribute与uniform的关键区别
坐标系统转换
WebGL采用归一化设备坐标(-1到1),需从canvas坐标转换
掌握坐标转换公式推导
着色器编程
必须赋值gl_Position内置变量,可选gl_PointSize
主函数必须声明返回类型(void)
动态绘制实现
通过鼠标事件获取坐标,使用vertexAttrib3f传值
顶点数据存储与批量绘制技巧
颜色随机生成
片元着色器通过gl_FragColor接收RGBA值(0-1范围)
精度声明precision mediump float的必要性
渲染管线流程
顶点着色器→图元装配→光栅化→片元着色器→帧缓冲区
理解各阶段数据传递过程
webgl 绘制点
绘制一个点
<!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">void main() {gl_Position = vec4(0, 0, 0, 1);gl_PointSize = 10.0;}</script><script type="fragment" id="fragment">void main() {gl_FragColor = vec4(1, 0, 0, 1);}</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);const 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) {const program = gl.createProgram();gl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);gl.linkProgram(program);const isSuccess = gl.getProgramParameter(program, gl.LINK_STATUS);return isSuccess ? program : console.log(gl.getProgramInfoLog(program));}let program = createProgram(gl, vertexShader, fragmentShader);gl.useProgram(program);gl.clearColor(0, 0, 1, 1);gl.clear(gl.COLOR_BUFFER_BIT);gl.drawArrays(gl.POINT, 0, 1)</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);function bindEvent() {let points = [];myCanvas.onmousedown = e => {gl.clearColor(0, 0, 1, 1);gl.clear(gl.COLOR_BUFFER_BIT);let x = e.offsetX, y = e.offsetY;let color = randomColor();points.push({x,y,color})points.forEach(el => {let {r, g, b, a} = el.color;gl.vertexAttrib4f(a_color, r, g, b, a);gl.vertexAttrib3f(a_position, el.x, el.y, 0);gl.drawArrays(gl.POINT, 0, 1)})}}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() {= 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);function drawRandomPoint() {let el = {color: randomColor(),x: Math.random() * myCanvas.width,y: Math.random() * myCanvas.height}console.log(el)gl.clearColor(0, 0, 1, 1);gl.clear(gl.COLOR_BUFFER_BIT);let {r, g, b, a} = el.color;gl.vertexAttrib4f(a_color, r, g, b, a);gl.vertexAttrib3f(a_position, el.x, el.y, 0);gl.drawArrays(gl.POINT, 0, 1)}drawRandomPoint()// requestAnimationFrame(drawRandomPoint)// 绘制随机点setInterval(() => {drawRandomPoint()}, 1000);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>
