webgl介绍
什么是webgl
WebGL是一种3D绘图协议,衍生于OpenGLES2.0,可以结合 HTML5和JavasSript在网页上绘制和渲染二/三维图形
webgl的应用场景
- 数据可视化
- 图形/游戏引擎
- 交互演示、图形渲染
- 地图
- VR
- 物品展示
- 室内设计
- 城市规划
webgl的优势
内嵌在浏览器中,不需要安装任何插件即可运行 只需要一个文本编辑器和浏览器,就可以编写三维图形程序
webgl开源框架
- Three.js: JavaScript 3D WebGL库
- Babylonjs:Web3D图形引擎
- KickJs:Web的开源图形和游戏引擎
- ClayGL:构建可扩展的Web3D应用程序
- PlayCanvas:网络游戏和3D图形引擎
- WebGLStudio.is和Litescene.is:开源Web 3D图形编辑器和创建器
canvas和webgl的区别
canvas是HTML5新增的一个 DOM 元素
用途:显示 二维 和 三维 的图像
绘制:二维图形可以使用(Canvas APl或 WebGL API)三维图形使用 WebGL API
canvas Api提供二维绘图的方式-》canvas.getContext('2d') webgl Api提供三维绘图的方式-》canvas.getContext('webgl')
webgl程序
什么是着色器
着色器就是让开发者自己去编写一段程序,用来代替固定染管线,来处理图像的渲染。
流程介绍:
现在我们来绘制一个点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../lib/index.js"></script>
</head>
<body>
<canvas id="canvas" width="400" height="400">
此浏览器不支持canvas
</canvas>
</body>
</html>
<script>
const ctx = document.getElementById('canvas')
const gl = ctx.getContext('webgl')
// 着色器
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
// 必须要存在 main 函数
void main() {
// 要绘制的点的坐标
gl_Position = vec4(0.0,0.0,0.0,1.0);
// 点的大小
gl_PointSize = 30.0;
}
`; // 顶点着色器
// gl_Position vec4(0.0,0.0,0.0,1.0) x, y, z, w齐次坐标 (x/w, y/w, z/w)
// gl_FragColor vec4(1.0,0.0,0.0,1.0) r, g, b, a
const FRAGMENT_SHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`; // 片元着色器
// 创建着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE) // 指定顶点着色器的源码
gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE) // 指定片元着色器的源码
// 编译着色器
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
// 创建一个程序对象
const program = gl.createProgram();
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
gl.useProgram(program)
// 执行绘制
// 要绘制的图形是什么, 从哪个开始, 使用几个顶点
gl.drawArrays(gl.POINTS, 0, 1);
</script>
可以看得出来有从创建着色器、编译着色器、创建程序对象这一块的代码是一定的,可以进行封装
function initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) {
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE) // 指定顶点着色器的源码
gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE) // 指定片元着色器的源码
// 编译着色器
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
// 创建一个程序对象
const program = gl.createProgram();
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
gl.useProgram(program)
return program;
}
绘制多个点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../lib/index.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
canvas{
margin: 50px auto 0;
display: block;
background: yellow;
}
</style>
</head>
<body>
<canvas id="canvas" width="400" height="400">
此浏览器不支持canvas
</canvas>
</body>
</html>
<script>
const ctx = document.getElementById('canvas')
const gl = ctx.getContext('webgl')
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
uniform vec4 uPosition;
// 只传递顶点数据
attribute vec4 aPosition;
void main() {
gl_Position = aPosition; // vec4(0.0,0.0,0.0,1.0)
gl_PointSize = 10.0;
}
`; // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
precision mediump float;
uniform vec2 uColor;
void main() {
gl_FragColor = vec4(uColor.r, uColor.g, 0.0,1.0); // vec4
}
`; // 片元着色器
const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
const aPosition = gl.getAttribLocation(program, 'aPosition');
const uColor = gl.getUniformLocation(program, 'uColor')
const points = []
ctx.onclick = function(ev) {
// 坐标
const x = ev.clientX
const y = ev.clientY
const domPosition = ev.target.getBoundingClientRect();
const domx = x - domPosition.left
const domy = y - domPosition.top;
//计算点在canvas的坐标
/*
0 200 400
-1 0 1
-200 0 200
-1 0 1
需要先 -200 (当前画布的宽度) 然后再 除以 200
1 0 -1
0 200 400
200 0 -200 / 200
需要先让 200 减这个数,然后再 / 200
* */
const halfWidth = ctx.offsetWidth / 2
const halfHeight = ctx.offsetHeight / 2
const clickX = (domx - halfWidth) / halfWidth
const clickY = (halfHeight - domy) / halfHeight
points.push({
clickX, clickY
})
for (let i = 0; i < points.length; i++) {
gl.vertexAttrib2f(aPosition, points[i].clickX, points[i].clickY)
gl.uniform2f(uColor, points[i].clickX, points[i].clickY)
gl.drawArrays(gl.POINTS, 0, 1);
}
}
</script>
声明attribute变量
attribute 变量只能在顶点着色器中使用,不能在片元着色器中使用
获取attribute变量
const aPosition = gl.getAttribLocation(program, 'aPosition');
给attribute变量赋值
uniform变量
获取uniform变量
const uColor = gl.getUniformLocation(program, 'uColor')
webgl缓冲区
什么是缓冲区对象
缓冲区对象是WebGL系统中的一块内存区域,可以一次性地向缓冲区对象中填充大量的顶点数据,然后将这些数据保在其中,供顶点着色器使用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../lib/index.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
canvas{
margin: 50px auto 0;
display: block;
background: yellow;
}
</style>
</head>
<body>
<canvas id="canvas" width="400" height="400">
此浏览器不支持canvas
</canvas>
</body>
</html>
<script>
const ctx = document.getElementById('canvas')
const gl = ctx.getContext('webgl')
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
// 只传递顶点数据
attribute vec4 aPosition;
void main() {
gl_Position = aPosition; // vec4(0.0,0.0,0.0,1.0)
gl_PointSize = 10.0;
}
`; // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`; // 片元着色器
const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
const aPosition = gl.getAttribLocation(program, 'aPosition');
const points = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
0.0, 0.5,
])
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition)
// gl.vertexAttrib2f(aPosition, 0.0, 0.0)
gl.drawArrays(gl.POINTS, 0, 3);
</script>
创建缓冲区对象WebGLRenderingContext.createBuffer() - Web API | MDN
绑定缓冲区对象WebGLRenderingContext.bindBuffer() - Web API | MDN
将数据写入缓存区对象WebGLRenderingContext.bufferData() - Web API | MDN
将缓存区对象分配给一个attribute变量(WebGLRenderingContext.vertexAttribPointer() - Web API | MDN)