WebGL Canvas
创建 canvas 刷底色
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
overflow: hidden; /* 防止不同浏览器长宽大小导致出现滚动条 */
}
/* 不应该使用 css 控制 canvas 大小,会使内部元素显示出现问题
#canvas {
width: 100%;
height: 100vh;
}
*/
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
const canvas = document.getElementById('canvas')
// 使用 window.innerHeight/Width 设置 canvas 大小
canvas.height = window.innerHeight
canvas.width = window.innerWidth
const gl = canvas.getContext('webgl') // 创建 gl 上下文
gl.clearColor(0.5, 0.5, 0.5, 1.0) // 指定将要用来清空绘图区的颜色
gl.clear(gl.COLOR_BUFFER_BIT) // 使用之前指定的颜色,清空绘图区
</script>
</body>
</html>
webgl 中的颜色配置
const canvas = document.getElementById('canvas')
const gl = canvas.getContext('webgl')
gl.clearColor(r, g, b, a); // clearColor 中 r、g、b 的定义域是[0,1] css 里的是 [0, 255]
// css 转 canvas
function css2gl(cssStr) {
const reg = RegExp(/\((.*)\)/)
const rgbaStr = reg.exec(cssStr)[1]
const rgb = rgbaStr.split(',').map(m => parseInt(m))
const r = rgb[0] / 255
const g = rgb[1] / 255
const b = rgb[2] / 255
const a = rgb[3] || 1
return {
r,
g,
b,
a,
}
}
const color = css2gl('rgb(255,0,125)')
gl.clearColor(color.r, color.g, color.b, color.a)
// 还可以使用 three.js 的 Color 对象进行转换
// 使用 import 需要加上 <script type="module">
import { Color } from "https://unpkg.com/three/build/three.module.js";
const color = new Color(1, 0, 0);
// 建立色相偏移动画
!(function ani() {
color.offsetHSL(0.005, 0, 0);
// 将提供的 h、s 和 l 值添加到当前颜色的 h、s 和 l 值上
// 色调(Hue)、饱和度(Saturation)、亮度(Lightness)
gl.clearColor(color.r, color.g, color.b, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
requestAnimationFrame(ani); // 循环渲染接口,调用频率跟显示器刷新率一致,运行在主线程,主线程繁忙会受到影响
})();
坐标系
canvas 2d 画布和 webgl 画布使用的坐标系都是二维直角坐标系
2d
- canvas 2d 坐标系的原点在左上角
- canvas 2d 坐标系的y 轴方向是朝下的
- canvas 2d 坐标系的坐标基底有两个分量,分别是一个像素的宽和一个像素的高,即1个单位的宽便是1个像素的宽,1个单位的高便是一个像素的高
webgl
- webgl坐标系的坐标原点在画布中心
- webgl坐标系的y 轴方向是朝上的
- webgl坐标基底中的两个分量分别是半个canvas的宽和canvas的高,即1个单位的宽便是半个个canvas的宽,1个单位的高便是半个canvas的高
webgl 最简单的图形
2d
//canvas画布
const canvas=document.getElementById('canvas');
//二维画笔
const ctx=canvas.getContext('2d');
//设置画笔的颜色
ctx.fillStyle = 'green'
//用画笔画一个矩形
ctx.fillRect(20, 10, 150, 100)
webgl
由于浏览器 2d 是用 js 线程渲染,而 3d 是用 GUI 线程渲染的,使用 GLSL ES 语言,写法有所不同
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
overflow: hidden;
}
</style>
</head>
<body>
<!-- 不推荐使用 <canvas id="canvas"></canvas> vscode 无法直接判断类型 -->
<script id="vertexShader" type="x-shader/x-vertex">
//顶点着色器
// vec4() 是一个4维矢量对象
// 将 vec4() 赋值给顶点点位gl_Position 的时候,其中的前三个参数是x、y、z,第4个参数默认1.0
void main() {
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
gl_PointSize = 100.0;
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
//片元着色器
// 将vec4() 赋值给片元颜色gl_FragColor 的时候,其中的参数是r,g,b,a
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>
<script>
// 直接使用 createElement,vscode 可以判断类型
const canvas = document.createElement('canvas')
document.querySelector('body').appendChild(canvas)
// 设置大小
canvas.height = window.innerHeight
canvas.width = window.innerWidth
// 获取上下文
const gl = canvas.getContext('webgl')
// 获取着色器
const vsSource = document.getElementById('vertexShader').innerText
const fsSource = document.getElementById('fragmentShader').innerText
initShaders(gl, vsSource, fsSource)
// 设置背景
gl.clearColor(0, 0, 0, 1)
gl.clear(gl.COLOR_BUFFER_BIT)
// 绘制方块
console.log(gl.POINTS) // 0
// https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/drawArrays
gl.drawArrays(gl.POINTS, 0, 1)
function initShaders(gl, vsSource, fsSource) {
//创建程序对象
const program = gl.createProgram()
//建立着色对象
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource)
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource)
//把顶点着色对象装进程序对象中
gl.attachShader(program, vertexShader)
//把片元着色对象装进程序对象中
gl.attachShader(program, fragmentShader)
//连接webgl上下文对象和程序对象
gl.linkProgram(program)
//启动程序对象
gl.useProgram(program)
//将程序对象挂到上下文对象上
gl.program = program
return true
}
function loadShader(gl, type, source) {
//根据着色类型,建立着色器对象
const shader = gl.createShader(type)
//将着色器源文件传入着色器对象中
gl.shaderSource(shader, source)
//编译着色器对象
gl.compileShader(shader)
//返回着色器对象
return shader
}
</script>
</body>
</html>