前言
书接上回,这次会着重聊一聊上一篇文章中最后的例子中所用到的 WebGL 着色器,至于示例代码就不再复制了,可以通过上面的链接去回顾一下。
着色器
着色器就是让开发者自己编写一段程序代码(GLSL),用来替代固定渲染管线来处理图像的渲染。
在 WebGL 中,着色器分为两种:顶点着色器 和 片元着色器。
想要创建一个着色器还是有点麻烦的,需要使用 GLSL 编写顶点着色器源码,然后使用 WebGL 上下文对象 WebGLRenderingContext 也就是 canvas.getContext('webgl')
,去创建一个顶点着色器类型的实例,创建完实例后还需要进行源码绑定,最后还需要编译着色器才行。
实际上有更多其他类型着色器,例如:几何着色器和计算着色器,但是这几类着色器并不包含在标准规范中,因此 WebGL 不直接支持这些着色器,但是可以通过扩展机制,在特定的设备和浏览器中使用,当然前提是需要确保设备和浏览器支持所需的功能。
顶点着色器
初步可以理解为:坐标位置、大小等
顶点着色器是用于对三维模型的顶点进行处理和变换的程序,它在图形渲染管线的早期阶段运行,主要负责的任务是将输入的顶点坐标和其他属性(如法向量、纹理坐标等)转换成屏幕坐标,并进行一些其他的操作,如光照计算、变形、筛选等,还可以根据需要对顶点属性进行插值计算,将结果传递给下一个阶段的图形渲染管线。
片元着色器
初步可以理解为:颜色、皮肤等
片元着色器是一个计算和处理每个像素的小程序,也被称为像素着色器。它接收由顶点着色器传递的数据,如顶点的颜色、法线或纹理坐标等。片元着色器根据给定的光照模型、纹理等信息,计算每个像素的最终颜色。它可以根据各种算法和技术,实现逼真的光照效果、纹理映射、阴影和特殊效果等。
创建顶点着色器
获取上下文的方法在本中后续的例子中也会省略掉!
在 WebGL 上下文对象中,可以使用 createShader 方法来创建着色器,该方法接收一个参数 type
,也就是着色器类型,参数 type
为 ctx.VERTEX_SHADER
和 ctx.FRAGMENT_SHADER
中的一个(ctx 指的是 WebGL 上下文对象),其中 ctx.VERTEX_SHADER
代表顶点着色器类型,ctx.FRAGMENT_SHADER
代表片元着色器类型。
javascript
// 获取上下文的方法在本中后续的例子中也会省略掉!
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('webgl')
// 创建着色器
const vertexShader = ctx.createShader(ctx.VERTEX_SHADER) // 顶点着色器
const fragmentShader = ctx.createShader(ctx.FRAGMENT_SHADER) // 片元着色器
指定着色器源码
光创建了着色器实例还不够的,还需对着色器实例设置其程序代码。
需要使用 shaderSource 方法来设置着色器的 GLSL 程序代码,该方法接收两个入参 shader, source
,其中 shader
是着色器实例,source
是 GLSL 程序代码字符串。
这里稍微介绍一下 GLSL ,GLSL(OpenGL Shading Language)是用于编写着色器代码的语言,它类似于C语言。
GLSL 是一种强类型语言,支持的数据类型有 int、float、bool以及矢量类型(vec2、vec3、vec4);矢量类型表示具有多个分量的值,比如 vec3
表示具有3个浮点分量的矢量。
GLSL 提供了一些内置的特殊变量和函数,用于访问着色器程序的输入和输出,以及执行常见的数学和图形操作。例如,gl_Position
表示顶点着色器的输出位置、gl_PointSize
表示绘制点的大小、gl_FragColor
表示片元着色器的输出颜色。
csharp
// 顶点着色器
const vertexShaderSource = `
void main () {
// 要绘制点的坐标
// 这里的入参分别代表 x, y, z, w
// 其中 w 代表的是齐次坐标
// 下面的参数最终可以理解成 x/w, y/w, z/w
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
// 点的大小
gl_PointSize = 10.0;
}
`
// 片元着色器
const fragmentShaderSource = `
void main () {
// 要绘制的颜色信息
// 这里的入参分别代表 r, g, b, a
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
// 指定着色器的代码
ctx.shaderSource(vertexShader, vertexShaderSource) // 设置顶点着色器源码
ctx.shaderSource(fragmentShader, fragmentShaderSource) // 设置片元着色器源码
编译着色器
接着需要使用 compileShader 方法来对着色器进行编译操作,使其成为 二进制数据,才可以被 WebGLProgram(着色器程序对象)
对象所使用。
compileShader 方法接受一个参数 shader,即着色器对象。
scss
// 编译着色器
ctx.compileShader(vertexShader)
ctx.compileShader(fragmentShader)
着色器程序对象
还没完,目前着色器仍然无法使用,还需要创建着色器程序对象(WebGLProgram),着色器程序对象由两个着色器组成,分别是顶点着色器和片元着色器。
创建一个着色器程序对象需要使用 createProgram 方法,然后还需要使用 attachShader 方法为其 附加/绑定 上着色器,之后还需要使用 linkProgram 方法将它们连接成一个可用的程序。
createProgram 方法无需入参。
attachShader 方法接收两个参数 program(色器程序对象)、shader(着色器对象)。
linkProgram 方法接收一个参数 program ,即着色器程序对象。
scss
// 创建程序对象
const program = ctx.createProgram()
// 绑定着色器
ctx.attachShader(program, vertexShader)
ctx.attachShader(program, fragmentShader)
// 将顶点着色器和片元着色器连接成一个可执行的程序
ctx.linkProgram(program)
绘制
目前我们拥有一个可用的着色器程序对象,那么是否可以直接进行绘制了呢?答案是 不可以。
在绘制之前,我们需要使用 useProgram 方法将定义好的着色器程序对象添加到当前的渲染状态中,然后才可以执行 drawArrays 方法进行最终的绘制了。
useProgram 方法接收一个参数 program ,即着色器程序对象。
drawArrays 方法接收三个参数,分别是:mode、first、count;
- mode 用来指定绘制图元的方式,可能值如下:
- POINTS(点)
- LINE_STRIP(线条)
- LINE_LOOP(线圆)
- LINES(线段)
- TRIANGLE_STRIP(三角带)
- TRIANGLE_FAN(三角扇)
- TRIANGLES(三角形)
- first 指定从那个点开始绘制;
- count 指定绘制需要使用到多少个点;
scss
// 将指定的程序对象设置为当前绘制的程序,可以理解为注册的意思?
ctx.useProgram(program)
// 执行绘制
// 入参分别是 要绘制的基本图元类型、从哪个顶点开始绘制、要绘制的顶点数量
ctx.drawArrays(ctx.POINTS, 0, 1)
总结
到这里我们已经初步的了解到了着色器,知道了如何去创建着色器并且将着色器绘制到当前的画布中。当然整个过程还是挺复杂的,要用到的 API 也还挺多的,不过相信还是难不倒 jym 。