从画一个点入手学习 WebGL

WebGL 简介

webgl 是一个可以结合 h5 和 js 在网页上绘制渲染 3D/2D 图形的 js api,gl 即 Graphics Library,图形库的意思。webgl 基于 OpenGL ES 2.0,OpenGL ES 是专为嵌入式系统设计的图形 api,是 OpenGL 的子集。

绘制点

webgl 的这些 api 都是在 <canvas> 元素中使用的,关于 <canvas> 的一些细节知识,可参见《canvas 实现卫星绕月动画》,本文不再赘述。要使用 webgl 画一个点,我们可以新建一个 html 文件,在里面放上一个 <canvas>,然后使用 js 去获取 canvas 元素,再通过 getContext 传入 'webgl' 得到 gl,即 WebGLRenderingContext 接口 —— 正是它提供了基于 OpenGL ES 2.0 的绘图上下文,我们才能在 <canvas> 中绘图:

<body>
  <canvas id="canvas"></canvas>
  <script>
    const canvas = document.getElementById('canvas')
    const gl = canvas.getContext('webgl')
  </script>
</body>

重置画布颜色

如果现在打开浏览器查看代码的运行效果,看到的会是一片空白。如果想看到 canvas 画布,之前我们都是直接给 <canvas> 设置 css 样式 background-color 来实现,有了 gl, 我们就可以使用它的 clearColor()clear() 方法来重置画布的颜色:

gl.clearColor(0.5, 0, 0, 1)
gl.clear(gl.COLOR_BUFFER_BIT)
  • gl.clearColor() 用于设置清空颜色缓冲时的颜色值,传入的 4 个参数分别代表 r(红)g(绿)b(蓝)和 a(透明度),注意它们的取值区间为 0 ~ 1,而不是在 css 中涉及颜色时 rgb 常用的 0 ~ 255;
  • gl.clear() 方法使用预设值来清空缓冲,传入 gl.COLOR_BUFFER_BIT 表示清空颜色缓冲区。

效果如下:
从画一个点入手学习 WebGL

着色器(shaders)

使用 webgl 绘制任何图形,都需要使用被称为“着色器”的玩意来实现。开发者可以自己编写一段着色器程序,编译后让着色器去执行。它属于 GPU 渲染管线的一个功能单元,而渲染管线可以理解为类似工厂流水线,只不过处理的是图形的渲染。

着色器可以分为顶点着色器和片元着色器:

顶点着色器(vertex shader)

顶点着色器用于输入顶点数据(顶点也就是二/三维空间中的一个个点,或者说坐标),然后进行平移、旋转等变换计算,最后将变换后的顶点坐标赋值给用于存储当前顶点位置的全局内置变量 gl_Position,作为顶点着色器的输出,传递给渲染管线的下一个功能单元。

// 顶点着色器源码
const vsSource = `
  void main() {
    // 点的坐标
    gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
    // 点的大小
    gl_PointSize = 24.0;
  }
`

// 创建顶点着色器
const vShader = gl.createShader(gl.VERTEX_SHADER)

// 给着色器指定源码
gl.shaderSource(vShader, vsSource)

vsSource 就是我们自己编写的色器程序,其在 js 中以字符串的形式存在,使用的是着色器语言:

着色器语言(GLSL,OpenGL Shading Language)

  • GLSL 通过 main 函数作为程序的入口,void 表示该函数没有返回值,语句末尾的分号不可省略(这些让我想到了 Dart);
  • vec4() 是矢量的构造函数,另外还有 vec2()vec3()。此处传入的 4 个参数代表的是点的 x、y、z 坐标以及齐次坐标 w,函数返回值为具有 4 个浮点数元素的矢量。注意,GLSL 是一种强类型语言,其默认基础数据类型有:
    • int:整型;
    • float:单精度浮点数;
    • boolean:布尔值

所以在传值时,都是传诸如 0.0,而不是 0

  • GLSL 中的注释和 js 是一样的,单行注释使用 //,多行注释使用 /**/

gl.createShader() 用于创建着色器对象,传入的参数为 gl.VERTEX_SHADERgl.FRAGMENT_SHADER,前者表示创建顶点着色器,后者表示创建片元着色器。有了着色器源码也有了着色器,就需要使用 gl.shaderSource() 将它们关联到一起。

片元着色器(fragment shader )

片元可以理解为一个个像素,类比顶点着色器,片元着色器的作用就是计算出一个个像素的颜色信息,然后赋值给内置的gl_FragColor 变量 。

// 片元着色器源码
const fsSource = `
  void main() {
    // 点的颜色
    gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
  }
`

// 创建片元着色器
const fShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fShader, fsSource)
  • 此处给内置变量 gl_FragColor 赋值时也用到了 vec4(),但是传入的 4 个参数代表的是颜色值的 rgba。

创建着色器程序

有了着色器,并且也指定好了着色器源码,就可以创建我们的项目或者说着色器程序对象了。在此之前,需要使用 gl.compileShader() 传入对应的着色器,将着色器编译成二进制数据,方可被着色器程序对象使用。

// 编译着色器
gl.compileShader(vShader)
gl.compileShader(fShader)

// 创建程序对象
const program = gl.createProgram()

// 关联程序对象和着色器
gl.attachShader(program, vShader)
gl.attachShader(program, fShader)

// 连接程序对象
gl.linkProgram(program)

// 使用程序对象
gl.useProgram(program)

// 执行绘制
gl.drawArrays(gl.POINTS, 0, 1)
  • 通过 gl.attachShader() 分别传入顶点着色器和片元着色器,以给着色器程序对象分配 webgl 运行必须的着色器;
  • 使用 gl.linkProgram() 传入程序对象 program,告诉 webgl 与 program 进行连接,去完成一些准备 GPU 代码的过程;
  • gl.useProgram() 则是告诉 webgl 在绘制时要使用哪个程序对象;
  • 最后使用 gl.drawArrays() 用于从向量数组中执行绘制。第 1 个参数用于在被称为图元装配(也就是如何将独立的顶点装配成几何图形)的过程中指定绘制图元的方式,gl.POINTS 表示要绘制的是一系列点;0 表示指定从第 1 个点开始绘制,第 1 个点的下标就是 01 表示绘制时需要使用到 1 个点。

效果演示

webgl 三维坐标系

在使用 canvas 绘制二维图形时,我们知道 canvas 本身的默认坐标空间是以画布左上角为原点,x 轴方向朝右,y 轴方向朝下的。但从上方的效果演示可以看到,当我们输入点的坐标的 x、y 和 z 都为 0.0 时,点是相对 canvas 画布居中显示的。这是因为 webgl 的三维坐标系的原点位于画布的中心点,x 轴方向朝右,y 轴方向朝上,z 轴方向则是朝向屏幕外(正交右手坐标系):

从画一个点入手学习 WebGL

x、y 和 z 轴的取值区间都为 [-1, 1],与 canvas 的尺寸无关。即 x 轴最右边为 1,最左边为 -1;y 轴最上面为 1,最下边为 -1;z 轴则是朝屏幕外最远值为 1,朝屏幕里远值为 -1。

从画一个点入手学习 WebGL
从画一个点入手学习 WebGL

原文链接:https://juejin.cn/post/7332735079163346979 作者:亦黑迷失

(0)
上一篇 2024年2月9日 下午4:06
下一篇 2024年2月9日 下午4:17

相关推荐

发表回复

登录后才能评论