从WebGL到Babylonjs
一、关于WebGL
- 前世今生 OpenGL => OpenGL ES => WebGL
- 本质:通过js代码去调用OpenGL的一系列Api
二、WebGL程序的构成
1、一个简单的webgl程序
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');
const vertexShaderSource = `#version 300 es
in vec4 position;
void main() {
gl_Position = position;
}`;
const fragmentShaderSource = `#version 300 es
precision highp float;
out vec4 outColor;
void main() {
outColor = vec4(0, 1, 0.5, 1);
}`
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
const prg = gl.createProgram();
gl.attachShader(prg, vertexShader);
gl.attachShader(prg, fragmentShader);
gl.linkProgram(prg);
const triangleVAO = gl.createVertexArray();
gl.bindVertexArray(triangleVAO);
const vertexPositions = new Float32Array([0, 0.7, 0.5, -0.7, -0.5, -0.7]);
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexPositions, gl.STATIC_DRAW);
const positionLoc = gl.getAttribLocation(prg, 'position')
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
gl.useProgram(prg);
gl.drawArrays(gl.TRIANGLES, 0, 3);
最终输出
可以看到、为了画一个三角形花了四十行代码,看起来很多,但是大部分是webgl相关的操作(上面以gl.
开头的代码)
写起来是非常麻烦和不易理解的
如果对webgl的具体执行流程感兴趣可以访问这个在线网站
[https://webgl2fundamentals.org/webgl/lessons/resources/webgl-state-diagram.html?exampleId=samplers#no-help]
2、为了更好的理解,我们可以把WebGL程序分成两个部分
2.1 js代码
- 数据的准备(模型加载、纹理图像加载)
- 向GPU传输数据
2.2 着色器代码
- 顶点着色器(VertexShader)
负责处理顶点数据,顶点位置变换
- 片元着色器(FragmentShader)
负责一个着色函数,让每一个像素点都经过这个函数处理,最终变成一个新的像素值
可以把他们理解为两个函数,分别对顶点和像素数据进行一些操作
三、WebGL(OpenGL)的渲染管线(Render PipeLine)
-
完整流程
白色部分大多是GPU自动处理的,而绿色部分是可由开发人员自由控制的,我们对GPU的操作也大都集中在这两个部分。
上面的完整渲染管线看起来非常复杂,当经过简化之后大概就是下面这张图,看起来非常简洁明了
通俗的解释一下:
-
模型数据进入显存之后,首先会经过顶点着色器,一般在这里处理模型顶点位置的变换
-
接下来经过片元着色器,在这里根据灯光、材质、贴图等数据计算出最终的颜色
-
最后输出到屏幕
四、Babylonjs
4.1 和WebGL的区别
- WebGL:一系列的API
- Babylonjs:对WebGL更高一级的抽象
比如在Babylon中新建一个立方体,看似只有一行代码, 但是在背后它可能就自动帮你完成了顶点数据准备,着色器创建、编译,数据绑定、数据传输...
等工作
4.2 几个主要的概念
- Engine:负责与WebGL的直接交互
- Scene:一个大的场景树,管理着所有Mesh、Light、Camera等
- Camera:相机,决定着观察者的视角,本质就是一个矩阵
- Light:灯光,点亮场景,可以理解为片元着色器里面的一个输入参数
- Mesh:网格,一个可渲染对象,包含了单次渲染的必要数据
- Material:片元着色器里面的一群输入参数
- Geometry:存储着顶点的位置数据等
4.3 与webgl的对应关系
在渲染时,可以理解为每个mesh都执行了一遍render方法
在渲染当前mesh的时候,把当前mesh相对应的顶点、贴图数据、program等设置为激活状态,然后调用一次drawCall指令,GPU就会安装预定好的渲染管线结合mesh的这些数据开始绘制
当场景中的mesh遍历完之后,一个完整的render就结束了
我们知道babylonjs是对webgl一层抽象,但是不管你再怎么花里胡哨抽象,在最终落实到渲染的时候还是得转换成一个标准的webgl程序,最终通过渲染管线把数据转换成像素输出到屏幕。
落实到webgl的渲染管线上,他们的大致对应关系将如下图所示
- Mesh上挂载的Geometry和Texture将会送入到显存中,其中顶点数据将会送给接下来的顶点着色器使用,Texture将会给到片元着色器使用;
- Camera将会成为MVP矩阵的一部分,决定着顶点的输出位置
- Light、Material等将会决定着片元着色器的输出颜色
五、学习资源推荐
(注:部分网站需要🪜)
1、WebGL
- WebGL2理论基础(推荐) https://webgl2fundamentals.org/webgl/lessons/zh_cn/
- WebGL程序运行状态可视化(推荐) https://webgl2fundamentals.org/webgl/lessons/resources/webgl-state-diagram.html?exampleId=triangle#no-help
- WebGL规范(官方、很全) https://registry.khronos.org/webgl/specs/latest/2.0/
- 图形学基础概念 https://math.hws.edu/eck/cs424/graphicsbook-1.3/
2、着色器学习
- 基础概念(推荐) https://thebookofshaders.com/
- 在线运行网站 https://glslsandbox.com/
3、Babylonjs
- 文档 https://doc.babylonjs.com/journey/theFirstStep
- 实验场(在线运行代码)https://playground.babylonjs.com/
- 沙盒(快速查看模型)https://playground.babylonjs.com/