WebGL基础与入门示例

我们都知道最新的HTML5中支持了画布操作,可以在上面绘制各种二三维图形图像,相应的H5标签就是<canvas>

二维图形绘制:Canvas API和WebGL API,三维图形绘制:WebGL API。

一.WebGL定义:webgl是一种3D绘图库及框架协议,衍生自OpenGL ES2.0,可结合H5和js实现在web端进行二三维图形的渲染和交互,未来支持可穿戴设备的沉浸式渲染交互。

二.WebGL用途:主要应用于数据可视化,图形可视化引擎或在线游戏引擎。

三.WebGL优势:

1.内置在任意浏览器中,几乎每台电子产品都都相应的浏览器支持,无需安装任何插件。

2.几乎不需要任何编程环境,可以用vim或者记事本即可快速编写三维图形程序。

3.快速上手入门,快速呈现效果。

四.WebGL呈现形式:webgl的GLSL ES是以字符串形式存在于javascript,所以整体需用一个单引号括起来的。也可以用后缀名为glsl文件将webgl代码单独存放,然后在js中引入进来,具体引入代码如下:

import waterVertexShader from './shaders/vertex.glsl';//顶点着色器
import waterFragmentShader from './shaders/fragment.glsl';//片元着色器

五.WebGL坐标系和绘图区域(0点在整个画布的正中心)

   

六.WebGL绘制流程和不同类型变量的设置流程

七.WebGL缓冲区对象的获取配置流程

   

可以将同一个缓冲器对象不同部分数据设置到着色器变量中。

八.着色器之间传递数据的方式

利用varying类型的变量绘制图形,在顶点着色器和片元着色器中都要声明varying变量,并且一致,在顶点着色器向片元着色器传递数据,并在片元着色器中使用。详见示例2。

varying vec4 vColor;

执行流程

九.3D图形绘制基础

三维图形在屏幕上绘制需要了解以下几个知识点,分别为视点、目标点、上方向,三者的相对关系如下:

任何一个立体图形要被观察到,其实是被人为的投影到观察平面后形成的,观察平面与物理图形之间的关系如下。

具体实现详见下方示例2。

十.WebGL典型示例

0.公共引用的index.js文件

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;
}

1.绘制二维图形示例,如下图形的显示,代码如下:

<canvas id="canvas" width="400" height="400">
此浏览器不支持canvas
</canvas>
<script>
//1.获得canvas对象
const ctx = document.getElementById('canvas')
//2.获得webgl对象,后续使用。
// const gl = ctx.getContext('webgl')
const gl = ctx.getContext('webgl2')
// //获得canvas api对象
// const c = ctx.getContext('2d')
// c.fillStyle = 'red'
// c.fillRect(10,10,100,100)//左上角坐标,宽,高

//3.创建着色器源码
const VERTEX_SHADER_SOURCE = `
// 只传递顶点数据
attribute vec4 aPosition;
// 必须要存在 main 函数
void main() {
gl_Position = aPosition; // vec4(0.0,0.0,0.0,1.0)
gl_PointSize = 10.0;
}
`; // 3.1顶点着色器

const FRAGMENT_SHADER_SOURCE = `
// 必须要存在 main 函数
void main() {
gl_FragColor = vec4(0.0,0.0,1.0,1.0);
}
`; // 3.2片元着色器

//4.获得一个程序对象
const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
//获得顶点着色器中的一个attriute变量
const aPosition = gl.getAttribLocation(program, 'aPosition');

//5.类型化数组
//创建顶点数据
const points = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
0.5, 0.5,
])
//6.创建缓冲区对象
const buffer = gl.createBuffer();
//7.绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);//第一个参数是缓冲区存储的是顶点数据(也可以是顶点的索引值,即ELEMENT_ARRAY_BUFFER),第二个参数是创建的缓冲区对象。
//8.将数据写入缓冲区对象
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
//9.将缓冲区对象分配给一个attribute变量
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
//10.开启attribute变量
gl.enableVertexAttribArray(aPosition)
// gl.vertexAttrib2f(aPosition, 0.0, 0.0)

//11.绘制不同的图形
gl.drawArrays(gl.POINTS, 0, 2);
gl.drawArrays(gl.LINES, 0, 2); // 最少需要有两个点,
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
</script>

以下是实现多缓冲区的代码,其他同上

<script>
const ctx = document.getElementById('canvas')
const gl = ctx.getContext('webgl')

// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute float aPointSize;
void main() {
gl_Position = aPosition;
gl_PointSize = aPointSize;
}
`; // 顶点着色器

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 aPointSize = gl.getAttribLocation(program, 'aPointSize');
const points = new Float32Array([
-0.5, -0.5, 10.0, // 10.0
0.5, -0.5, 20.0, // 20.0
0.0, 0.5, 30.0, // 30.0
])

const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
const BYTES = points.BYTES_PER_ELEMENT;
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, BYTES * 3, 0);//每行3列数据,前2个用作aPosition,每次取2个数据,取数据时,每行位移为0
gl.enableVertexAttribArray(aPosition)
gl.vertexAttribPointer(aPointSize, 1, gl.FLOAT, false, BYTES * 3, BYTES * 2);//每行3列数据,每次取1个数据,取数据时,每行位移为2*BYTES
gl.enableVertexAttribArray(aPointSize)
gl.drawArrays(gl.POINTS, 0, 3);
</script>

2.绘制三维图形示例

在三维空间中,任意一个正方体的构成如下,正方体是由8个顶点和6个面构成的,顶点变化如下图。

   

其他代码类似上面示例1,顶点法主体代码如下:

<script>
const ctx = document.getElementById('canvas')
const gl = ctx.getContext('webgl')
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute vec4 aColor;
varying vec4 vColor;
uniform mat4 mat;
void main() {
gl_Position = mat * aPosition;
vColor = aPosition;//aColor;
}
`; // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
precision lowp float;
varying vec4 vColor;
void main() {
gl_FragColor = vColor;
}
`; // 片元着色器

const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
const aPosition = gl.getAttribLocation(program, 'aPosition');
const aColor = gl.getAttribLocation(program, 'aColor');
const mat = gl.getUniformLocation(program, 'mat');

// 顶点-8个点
const v0 = [1,1,1];
const v1 = [-1,1,1];
const v2 = [-1,-1,1];
const v3 = [1,-1,1];
const v4 = [1,-1,-1];
const v5 = [1,1,-1];
const v6 = [-1,1,-1];
const v7 = [-1,-1,-1];
//正方体每个面的构成顶点组合,三角网格(Mesh)
const points = new Float32Array([
...v0,...v1,...v2, ...v0,...v2, ...v3, // 前
...v0,...v3,...v4, ...v0,...v4, ...v5, // 右
...v0,...v5,...v6, ...v0,...v6, ...v1, // 上面
...v1,...v6,...v7, ...v1,...v7, ...v2, // 左
...v7,...v4,...v3, ...v7,...v3, ...v2, // 底
...v4,...v7,...v6, ...v4,...v6, ...v5, // 后
])
const buffer = gl.createBuffer();
const BYTES = points.BYTES_PER_ELEMENT;
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition)

//每个面的构成颜色,共6个面,每个面是由6个点组成
const colorData = new Float32Array([
1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,
0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,
0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,
])
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, colorData, gl.STATIC_DRAW);
gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aColor)

//视点定义
let eyex = 3;
let eyey = 3;
let eyez = 5;

//观察角定义
let deg = 0;
function draw() {
deg += 0.01;
const rotate = getRotateMatrix(deg);
const vm = getViewMatrix(eyex,eyey,eyez,0.0,0.0,0.0,0.0,0.6,0.0);
const perspective = getPerspective(30, ctx.width / ctx.height, 100, 1);
gl.enable(gl.DEPTH_TEST);
gl.uniformMatrix4fv(mat, false, mixMatrix(mixMatrix(perspective, vm), rotate));
gl.drawArrays(gl.TRIANGLES, 0, points.length / 3);
requestAnimationFrame(draw)
}
draw()
</script>

其他代码类似上面示例1,索引法主体代码如下:

<script>
const ctx = document.getElementById('canvas')
const gl = ctx.getContext('webgl')
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute vec4 aColor;
varying vec4 vColor;
uniform mat4 mat;
void main() {
gl_Position = mat * aPosition;
vColor = aPosition;
}
`; // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
precision lowp float;
varying vec4 vColor;
void main() {
gl_FragColor = vColor;
}
`; // 片元着色器

const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
const aPosition = gl.getAttribLocation(program, 'aPosition');
const aColor = gl.getAttribLocation(program, 'aColor');
const mat = gl.getUniformLocation(program, 'mat');
// 顶点-8个点
const vertices = new Float32Array([
1, 1, 1,
-1, 1, 1,
-1,-1, 1,
1,-1, 1,
1,-1,-1,
1, 1,-1,
-1, 1,-1,
-1,-1,-1,
])

const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition)

//正方体每个面的构成顶点索引组合,每个面6个顶点,三角网格(Mesh)
const indeces = new Uint8Array([
0,1,2,0,2,3,
0,3,4,0,4,5,
0,5,6,0,6,1,
1,6,7,1,7,2,
7,4,3,7,3,2,
4,6,7,4,6,5,
])
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indeces, gl.STATIC_DRAW);
//视点定义
let eyex = 3;
let eyey = 3;
let eyez = 5;
//观察角定义
let deg = 0;
function draw() {
deg += 0.01;
const rotate = getRotateMatrix(deg);
const vm = getViewMatrix(eyex,eyey,eyez,0.0,0.0,0.0,0.0,0.6,0.0);
const perspective = getPerspective(30, ctx.width / ctx.height, 100, 1);
gl.enable(gl.DEPTH_TEST);
gl.uniformMatrix4fv(mat, false, mixMatrix(mixMatrix(perspective, vm), rotate));
gl.drawElements(gl.TRIANGLES, indeces.length, gl.UNSIGNED_BYTE, 0);
requestAnimationFrame(draw)
}
draw()
</script>

十一.WebGL开源引擎有哪些:

1.Threejs:快速实现三维图形的轻量级引擎

2.BabyIon.js:Web3D图形引擎

3.KickJS:web开源图形和游戏引擎

4.ClayGL:可扩展的Web3D应用程序

5.Luma:Uber的3D WebGL可视化库

6.A-Frame:构建VR(虚拟现实)体验的web框架

7.Unity:游戏引擎,轻量级引擎

8.Unreal Engine:游戏引擎,支持大场景大模型

猜你喜欢

转载自blog.csdn.net/hhue2007/article/details/128884356
今日推荐