前言
我准备开始学习 WebGL 这个底层的 3D 代码,这里顺便给大家介绍一下李伟老师,他之前是某课吧的讲师(我报名了他的 3D 课程),后来倒闭了之后,课程不再更新,但是老师很良心,把课程在小破站进行更新,目前 WebGL 部分是更新完了的,大家如果需要的话也可以去学习一下。我博客里面会记录我学习的成果,内容肯定没有老师的详细,大家可以去老师那边仔细学习。
老师主页
博客:https://www.yxyy.name/blog/
代码:https://github.com/buglas/webgl-lesson
视频:https://space.bilibili.com/1028978784
掘金:https://juejin.cn/user/87572001856951/posts
入门 webgl
刷底色
新建画布
在 html 中新建一个 canvas 画布
1 | <canvas id="canvas"></canvas> |
获取画布
在 js 中获取 canvas 画布
1 | const canvas = document.getElementById("canvas"); |
获取 webgl 上下文
使用 canvas 获取 webgl 绘图上下文
1 | const gl = canvas.getContext("webgl"); |
指定颜色
指定将要用来清空绘图区的颜色,这里的四个参数分别是 rgba,但是前面的三个的范围是将我们常规理解的 0255 变成了 01
1 | gl.clearColor(1, 1, 0, 1); |
使用颜色
使用之前指定的颜色,清空绘图区
1 | gl.clear(gl.COLOR_BUFFER_BIT); |
完整代码
1 |
|
效果
使用 CSS 的 rgba 刷底色
我们已经知道了 webgl 中的颜色和 css 中 rgba 的关系,那么我们就可以使用 css 中的 rgba 颜色进行颜色转化为 webgl 中的颜色,接下来让我们一起来实现一下代码。
1 | //css颜色 |
此时的颜色,依然是我们的黄色
多姿多彩的画布
引入 Color 对象
1 | import { Color } from "https://unpkg.com/three/build/three.module.js"; |
我这是通过 CDN 引入的,这种方法不适用于 nodejs,因为 nodejs 无法直接通过网络路径请求资源。这里注意一个细节,如果我们要在 js 中直接引入 CDN 资源,需要给 script 标签加上一个 type=”module”
实例化 Color 对象
1 | const color = new Color(1, 0, 0); |
建立色相偏移动画
1 | !(function ani() { |
此时的效果如下
webgl 坐标系
canvas 2d 画布的坐标系
canvas 2d 坐标系的原点在左上角。
canvas 2d 坐标系的 y 轴方向是朝下的。
canvas 2d 坐标系的坐标基底有两个分量,分别是一个像素的宽和一个像素的高,即 1 个单位的宽便是 1 个像素的宽,1 个单位的高便是一个像素的高。
如下图,下图两个方块表示两个像素:
webgl 的坐标系
webgl 坐标系的坐标原点在画布中心。
webgl 坐标系的 y 轴方向是朝上的。
webgl 坐标基底中的两个分量分别是半个 canvas 的宽和 canvas 的高,即 1 个单位的宽便是半个个 canvas 的宽,1 个单位的高便是半个 canvas 的高。
如下图:
webgl 坐标系与 canvas 2d 坐标系的转化
接下来,我们根据上面的两张图来讲解一下 webgl 坐标系与 canvas 2d 坐标系的转化
- 得到 canvas 的原点坐标
1 | const rect = canvas.getBoundingClientRect(); |
此时原点坐标(a,b)就是
1 | a = rect.left; |
- P 点的 CSS 坐标
此时 P 点在 CSS 的坐标可以设置为(x,y),此时 P 点的 CSS 坐标可以表示为
1 | x = window.clientX; |
- P 点对应的 canvas 坐标
这时候,我们可以得到 P 点在 canvas 的坐标系上的坐标是
1 | canvasX=x-a=window.clientX-rect.left; |
- P 点对应的 webgl 坐标
然后,我们根据 canvas 和 webgl 的映射关系,我们将 canvas 的坐标系中心点平移到原点,并将坐标系旋转(此时的 canvas 坐标系与 webgl 坐标系原点位置和 xy 轴方向完全一致),那么此时的 P 点坐标为
1 | transX = canvasX - width / 2; |
然后,因为坐标系的长度有比例的关系的,webgl 坐标系的坐标区间为-1.0 到 1.0,因此我们再对比例出手,得到了下面这个公式
1 | webglX = (canvasX - width / 2) / (width / 2) =(window.clientX-rect.left -width/2) / (width /2) ; |
webgl 最简单的图形-画一个点
webgl 绘图步骤
1.在 html 中建立 canvas 画布
1 | <canvas id="canvas"></canvas> |
2.在 js 中获取 canvas 画布
1 | const canvas = document.getElementById("canvas"); |
3.使用 canvas 获取 webgl 绘图上下文
1 | const gl = canvas.getContext("webgl"); |
4.在 script 中建立顶点着色器和片元着色器,glsl es
1 | //顶点着色器 |
5.在 js 中获取顶点着色器和片元着色器的文本
1 | const vsSource = document.getElementById("vertexShader").innerText; |
6.初始化着色器
1 | initShaders(gl, vsSource, fsSource); |
7.指定将要用来清空绘图区的颜色
1 | gl.clearColor(0, 0, 0, 1); |
8.使用之前指定的颜色,清空绘图区
1 | gl.clear(gl.COLOR_BUFFER_BIT); |
9.绘制顶点
这里的三个参数,第一个是类型,第二个是从哪个点开始,第三个是绘制第几个点
1 | gl.drawArrays(gl.POINTS, 0, 1); |
完整代码
1 |
|
目录:jsm/Utils.js
1 | function initShaders(gl, vsSource, fsSource) { |
着色器
着色器的概念
webgl 绘图需要两种着色器:
- 顶点着色器(Vertex shader):描述顶点的特征,如位置、颜色等。
- 片元着色器(Fragment shader):进行逐片元处理,如光照。
看了这两个名词的解释,我想很多初学者会是懵的。
我给大家翻译翻译:
补间动画大家知道不?顶点着色器里的顶点就是补间动画里的关键帧,片元着色器里的片元就是关键帧之间以某种算法算出的插值。当然,咱们webgl里的片元是像素的意思。
再给大家举一个更简单、更贴切的例子:
两点决定一条直线大家知道不?顶点着色器里的顶点就是决定这一条直线的两个点,片元着色器里的片元就是把直线画到画布上后,这两个点之间构成直线的每个像素。
关于概念咱们就说到这,接下来咱们说着色器语言。
着色器语言
webgl 的着色器语言是GLSL ES语言
- 顶点着色程序,要写在
type=“x-shader/x-vertex”
的script中。1
2
3
4
5
6<script id="vertexShader" type="x-shader/x-vertex">
void main() {
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
gl_PointSize = 100.0;
}
</script> - 片元着色程序,要写在
type=“x-shader/x-fragment”
的script中。void main() {…… } 是主体函数。1
2
3
4
5<script id="fragmentShader" type="x-shader/x-fragment">
void main() {
gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
}
</script>
在顶点着色器中,gl_Position 是顶点的位置,gl_PointSize 是顶点的尺寸,这种名称都是固定的,不能写成别的。
在片元着色器中,gl_FragColor 是片元的颜色。
vec4() 是一个4维矢量对象。
将vec4() 赋值给顶点点位gl_Position 的时候,其中的前三个参数是x、y、z,第4个参数默认1.0,其含义我们后面会详解;
将vec4() 赋值给片元颜色gl_FragColor 的时候,其中的参数是r,g,b,a。
至于GLSL ES语言的其它知识,咱们会在后面另开一篇详解,这里先以入门为主。
在第6步中,我们使用了一个自定义的方法initShaders() ,这是用于初始化着色器的,接下来咱们详细说一下。
着色器初始化
初始化着色器的步骤:
建立程序对象
1
const shaderProgram = gl.createProgram();
建立顶点着色器对象和片元着色器对象
二者可以分工合作,把js信号解析为计算机语言(GLSL ES),然后让计算机(浏览器的webgl 渲染引擎)识别显示。1
2const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);将顶点着色器对象和片元着色器对象装进程序对象中
1
2gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);连接webgl 上下文对象和程序对象
1
gl.linkProgram(shaderProgram);
启动程序对象
1
gl.useProgram(program);
上面第二步中的建立着色对象方法loadShader(),是一个自定义的方法,其参数是(webgl上下文对象,着色器类型,着色器源文件),gl.VERTEX_SHADER 是顶点着色器类型,gl.FRAGMENT_SHADER是片元着色器类型。
1 | function loadShader(gl, type, source) { |
gl.createShader(type) :根据着色器类型建立着色器对象的方法。
gl.shaderSource(shader, source):将着色器源文件传入着色器对象中,这里的着色器源文件就是我们之前在script 里用GLSL ES写的着色程序。
gl.compileShader(shader):编译着色器对象。
结语
本篇文章就先到这里,更多内容敬请期待,债见~