webGL基础-着色器
不会3D的厨师不是一个好司机,这个时代不卷就只能去开滴滴。 在元宇宙概念的背景下,上级领导也要求做点技术储备,在学习了一些常用的Web3D框架(babylonjs,threejs)后, 好奇于它们的底层原理,所以去了解下webGL的知识,本文主要介绍WebGl中着色器的原理。
webGL
webGL其实是一个很底层的图像引擎,主要提供的能力就是接收输入的图形的顶点坐标,程序在GPU中帮你画出图形(光珊化)和上色后输出到浏览器,在GPU运行的程序则是一种OpenGL着色语言(GLGS),一个webGL程序大概是如下步骤、其中最关键的还是着色器部分。
下面的一些代码基本都会省略api调用部分
着色器
简单点的说着色器就干两件事:
- 根据顶点坐标绘制图像
- 给图形的每个像素点上色
在webGL
中分别对应两种着色器,顶点着色器(vertext shader) 和 片元着色器(fragment shader) ,两种是成对出现的组成一段着色程序在GPU上运行。
缓冲区
缓冲是发送到GPU的一些二进制数据序列,通常情况下缓冲数据包括位置,法向量,纹理坐标,顶点颜色值等。 也可以存储任何数据。
如果我们要画一个三角形,那我们在缓冲区写入三个顶点的数据,数据类型是一个Float32Aarray
就是一个一维数组:
1 |
|
在gl中的坐标是一种叫裁切空间的坐标,取值是(-1,1)
最后渲染后会换算成屏幕空间坐标,假如我们的画布是400x300, 那(-0.5,0.5)
的坐标就是(100,225)
。
1 | (-1,1) (1,1) |
这是配置顶点数据的,下面我们继续看下画一个三角形顶点着色器和片元着色器的代码:
画一个三角形
1 |
|
attributes
属性用来指明怎么从缓冲中获取所需数据并将它提供给顶点着色器,前面我们已经在缓冲区创建了一个顶点数组,两个可以理解为等值,通过webGL api绑定后,在顶点着色器中就可以获取到,再赋值给gl_Position
片元着色器 是在顶点着色器运行完后给图形的每个片元(也可以理解为每个像素)上色,上面代码中我们是设置了固定的颜色,gl中颜色值是vec4(r,g,b,a)
,每个通道的取值范围是0-1,rgb分别代表红绿蓝,如果红色则是vec4(1, 0, 0 ,1)
。
最后调用gl.drawArrays(type, offset, vCount)
来执行程序,顶点着色器的运行次数是一个确切的数字,例如这个三角形的例子,我们是设置了三个顶点,所以vCount
这个值就是3
,每次从属性值中获取2
个,这个2
个也是通过api指定的,为了方便理解,代码没有写出来,假设我们是一个三维的图形(x,y,z),那么就是指定每次获取3个。
如果画个矩形,那就是:
1 | var positions = new Float32Array([ |
给片元着色器传值
前面片元着色器颜色是固定,但在实际程序中,我们可能会给每个三角形设置不同颜色,或者贴图的时候需要传纹理坐标,那么如何传值呢?同样我们需要创建一个buffer,使用关键字varying
将数据通过顶点着色器再传到片元着色器,如下代码:
1 | //顶点颜色 |
从代码上看我们还是用attribute
从缓冲区取数据,然后通过varying
关键字定义了v_color
,在顶点着色器中赋值,最后片元着色器也是通过varying
取到颜色值。我们给三角形三个顶点设置了不同颜色,每个片元的值是由顶点的线性插值得到,所以看到的是下面的渐变的颜色。
执行顺序
根据下图,我们再来梳理下着色器的执行流程,首先在缓冲区中获得数据,顶点着色器根据坐标输出图形(三角形),这一步可以叫做图元装配,确定形状后,我们就知道图形中存在多少片元(像素),每个像素都会调用一次片元着色器进行上色。
顶点索引
我们知道再复杂的模型的是由三角形的组成的,前面我们画一个平面是使用了两个三角6个顶点来画的,但其实两个三角中其中其实是有两个顶点是重复的,所以我们可以使用gl.drawElemnt
来代替gl.drawArrays
,前者支持通过索引来指定顶点,这样可以减少我们顶点传输量,一个平面可能就只减少了两个点,但如果图形复杂就减少很多内存了。
1 |
|
总结
本篇到这里就结束了,其实只是讲了一点点基础概念,实际要发开3d还需要非常多的储备知识、比如还要了解光照原理,摄像机(透视矩阵)原理等等,其实对数学知识的要求还是很高的、当然webGL也就了解下就好,实际开发可以使用three.js,babylonjs等框架。
参考
mdn Getting started with webGL
Raw webGL 101 — Part 1: Getting Started
《webGL 编程指南》