webGL编程指南实战教程
创始人
2024-05-29 02:03:36
0

学习路线:

  1. 如果你是在校大学生,有足够的时间去学习:前端>数学(几何+线性代数)>图形学>webgl>shader >threejs>three.js源码
  2. 如果你是工作中使用,需要快速出产成品:前端>threeJs。

教程主要分为四大部分:

  1. webgl容器(坐标系)
  2. webgl渲染管线
  3. webgl关键名词
  4. 案例实战

一、webgl容器(坐标系)

1、canvas坐标系

在2D绘图环境中的坐标系统,默认情况下是与窗口坐标系统相同,它以canvas的左上角为坐标原点,沿x轴向右为正值,沿y轴项下为正值。其中canvas坐标的单位都是"px";
在这里插入图片描述

2、webgl坐标系

webgl使用的是正交右手坐标系,且每个方向都有可使用的值的区间,超出该矩形区间的图像不会绘制:

  • x轴最左边为-1,最右边为1;
  • y轴最下边为-1,最上边为1;
  • z轴朝向你的方向最大值为1,远离你的方向最大值为-1;

注:这些值与canvase的尺寸无关,无论canvas的长宽比是多少,webgl的区间值都是一致的
在这里插入图片描述

3、webgl与屏幕坐标的关系

在2D绘图环境中的坐标系统,默认情况下是与窗口坐标系统相同,它以canvas的左上角为坐标原点,沿x轴向右为正值,沿y轴向下为正值。其中canvasd坐标的单位都是"px"。
在这里插入图片描述

二、webgl渲染管线

渲染管线就像一条流水线,由一系列具有特定功能的数字电路单元组成,下一个功能单元处理上一个功能单元生成的数据,逐级处理数据。

定点着色器和片元着色器是可编程的功能单元,拥有更大自主性,还有光栅器、深度测试等补课表承德功能单元。CPU会通过webgl api和GPU通讯,传递着色器程序和数据,GPU执行的着色器程序
在这里插入图片描述

webgl渲染管线其实就是一个流水线,一个方便面为例子:

  1. 生成一个方便面由顶点构成的坐标集合,这个集合可以勾勒方便面的雏形,保存在顶点缓冲区中,待用。
  2. 拿到顶点缓冲区中的顶点数据,使用uniform传给顶点着色器。
  3. 收集数据之后,使用图元装配,将方便面的雏形构建成型。
  4. 然后使用光栅器,将方便面雏形切割成一个个三维的小方块,类似像素化。
  5. 然后使用片元着色器,给像素化后的方便面雏形上色、上纹理。
  6. 然后使用归属测试、模板测试、深度测试 做图形测试。
  7. 融合、抖动,存入颜色缓存区,供用户展现调用。

三、webgl关键名词

1、顶点着色器

顶点着色器是GPU渲染管线上一个可以执行着色器语言的功能单元,具体执行的就是顶点着色器程序,webgl定点着色器程序在javascript中一字符串的形式存在,通过编译处理后传递给顶点着色器执行。定点着色器主要作用就是执行点点着色器程序对定点进行变换计算,比如点点位置坐标执行进行旋转、平移等矩阵变换,变换后新的顶点坐标然后赋值给内置变量gl_Position,作为顶点着色器的输出,图元装配和光栅化环节的输入;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、图元装配

顶点变换后的操作是图元装配,硬件上具体是怎么回事不用考虑,从程序的角度来看,就是绘制函数drawArray()drawElement()第一个参数绘制模式mode控制定点如何装配为图元,gl.LINES的定义的是把两个定点装配成一个线条图元,gl.TRIANGLES定义的是三个顶点装配为一个三角面图元,gl.POINTS定义的是一个点域图元。
在这里插入图片描述

3、光栅化

就是将图元分解成片元
在这里插入图片描述

4、片元着色器

片元着色器和顶点着色器一样是GPU渲染管线上一个可以执行着色器程序的功能单元,顶点着色器处理的是逐顶点处理顶点数据,片元着色器是逐片元处理片源数据。通过给内置变量gl.FragColor赋值可以给每一个片元进行着色,值可以是一个确定的RGBA值,可以是一个和片元位置相关的值,也可以是炒制后的顶点颜色。除了给片元进行着色之外,通过关键字discard还可以实现那些偏远可以被丢弃,被丢弃的片元不会出现在帧缓冲区,自然不会显示在canvas画布上。
在这里插入图片描述

片元着色器的功能可以简单理解成,给顶点着色器着色。

四、鼠标动态绘制点

实现思路:

  1. 绘制单个点
  2. 鼠标事件监听点击事件
  3. 将点推送到数组中
  4. 绘制数组中所有点

1、创建一个空白画布



  1. gl.clearColor(red,green.blue,alpha)

    执行绘图区域背景色。

    • red,green.blue设置都是从0.0到1.0
    • alpha指定透明度,值是从0.0到1.0

    在我们css颜色系统中,设置都是从0到255,webgl的色值是从0-1,这是因为继承自openGL,越大颜色越是亮。一旦指定了背景色之后,颜色就会驻留在webgl系统中,在下次调用gl.clearColor()之前不会改变。

  2. gl.clear(buffer)

    将指定缓冲区设定为预定的值。如果清空的是颜色缓冲区,那么将使用gl.clearColor()指定的值(作为预定值)

    • bugger
      • gl.COLOR_BUFFER_BIT:指定颜色缓存-clearColor(red,green,blue,alpha)
      • gl.DEPTH_BUFFER_BIT:指定深度缓冲区-clearDepth(depth)
      • gl.STENCLL_BUFFER_BIT:指定模板缓冲区-clearStencil(s)

2、画一个点


  • gl.drawArrays(mode,first,count)

    可以用于绘制各种图形。实际是执行着色器,按照mode参数指定的方式绘制图形。

    • mode:指定绘制方式,可接收一下常量符号:gl.POINTS、gl.LINEs、gl.LINE_STRIP、gl.LINE_LOOP、gl.TRIANGLES、gl.TRIANGLE_STRIP、gl.TRIANGLE_FAN。
    • first:指定从哪个顶点开始绘制(整型)
    • count:指定绘制需要用到多少个顶点(整型)
  • getWebGLContext

    获取webgl实例(具体见源码)

  • initShaders

    初始化着色器(具体见源码)

  • VSHADER_SOURCE

    gl_Position:设置位置

    gl_PointSize:设置尺寸

  • FSHADER_SOURCE

    gl_FragColor:设置颜色

在这里插入图片描述

webgl遵守右手坐标系,所以我们看到修改gl_Position = vec4(0.0, 0.0, 0.0, 1.0);中的坐标值,对应不同的效果,具体如下:

  1. vec4(1.0, 0.0, 0.0, 1.0):移动到最右边
  2. vec4(0.0, 1.0, 0.0, 1.0):移动到最上边
  3. vec4(0.0, 0.0, 1.0, 1.0):移动到最前边(靠近屏幕前的你)

3、使用attribute传值gl_Position


如上,我们使用attribute作为一个将js中变量传入GLSL着色器语法的媒介。

  • getAttribLocation(program,name)

    获取由name参数指定的attribute变量的存储地址。返回值如果等于-1,则表示变量不存在。正常>=0;

  • vertexAttrib3f(location,v0,v1,v2)

    将数据(v0,v1,v2)传给由location指定的attribute变量;

给attribute变量赋值的方法,除了vertexAttrib3f,还有:vertexAttrib1fvertexAttrib2fvertexAttrib4f,用法都一样。以上这些都是浮点类型的入参。还有四个整型的入参,名字和上面的四个类似:vertexAttrib1ivertexAttrib2ivertexAttrib3ivertexAttrib4i

4、使用attribute传值gl_PointSize


代码逻辑和gl_Position的传值类似,归纳如下:

在着色器中定义变量,并在main中传入赋值,然后在js代码逻辑中用getAttribLocation获取拿到变量地址,然后使用vertexAttrib1f给变量地址塞入值;

5、动态绘制一个点

到目前位置,绘制的点事js代码中写死的,我们这里改成根据鼠标在画布上点击,点击在哪儿,就在哪儿画上点。代码逻辑如下:

  • 监听canvas上的鼠标点击事件
  • 点击时获取鼠标坐标,并转化为webgl坐标系
  • 清空画布,遍历点集合,并一一绘制

这里有个canvas坐标转换为webgl的公式,看代码可能比较抽象,下面上一张图:

在这里插入图片描述

最终绘制效果如下:

请添加图片描述

6、改变点的颜色

类似使用attributegl_position传值,颜色的传值使用uniform来传值,具体传值逻辑如下:

  • 片元着色器中定义uniform颜色变量u_FragColor
  • js逻辑中拿到u_FragColor的地址
  • 在绘制每个点的时候,使用gl.uniform4f(u_FragColor, ...point, 0.0, 1.0);传值,设置点的颜色

整体代码如下:


修改片段着色器,定义uniform变量,用于接收js传入的值:

var FSHADER_SOURCE ='precision mediump float;\n' +//精度限定词来指定变量的范围(最大值和最小值)和精度,这里为中精度。'uniform vec4 u_FragColor;\n' +'void main() {\n' +'  gl_FragColor = u_FragColor;\n' + // Set the point color'}\n';

拿到uniform变量u_FragColor的值:

const u_FragColor = gl.getUniformLocation(gl.program, "u_FragColor");

设置颜色:

gl.uniform4f(u_FragColor, ...point, 0.0, 1.0);

最终效果如下:

请添加图片描述

在js代码中向着色器传值时,attribute用于向顶点着色器传值,uniform用于向片元着色器传值;

  • uniform4f(location,v0,v1,v2)

    和vertexAttrib类似,uniform4f也有总计4个同类方法,分别是:uniform1funiform2funiform3funiform4f

五、绘制和变换三角形

1、绘制多个点

不管三维模型的形状多么复杂,其基本组成部分都是三角形,只不过复杂的模型有更多的三角形构成而已。通过创建更细小和更大量的三角形,就可以创建更复杂和更逼真的三维模型。前面我们绘制多个点的时候,每鼠标点击一次,就把坐标存储在g_points中,最后对g_points进行遍历,并使用gl.drawArrays()绘制。这种方式只能绘制一个点,对于复杂图形如果也是这么遍历一一绘制,效率会很差。

webGL提供了一种很方便的机制,即缓冲区对象(buffer object),他可以一次性的想着色器传入多个顶点的数据。缓冲区对象是webGl系统中的一块内存区域,我们可以一次性的想缓冲区对象中填充大量的顶点数据,然后将这些数据保存在其中,共顶点着色器使用。

使用缓冲区对象向顶点着色器传入多个顶点的数据,需要遵循一下五个步骤。处理其他对象,如纹理对象、帧缓冲区对象时的步骤也比较类似,

创建的五个步骤具体如下:

  1. 创建缓冲区对象(gl.creaeBuffer())
  2. 绑定缓冲区对象(gl.bindBuffer())
  3. 将数据写入缓冲区对象(gl.bufferData())
  4. 将缓冲区对象分配给一个attribute变量(gl.vertexAttribPointer())
  5. 开启attribute变量(gl.enableVertexAttribArray())

图示如下:
在这里插入图片描述

  • gl.createBuffer()

    创建缓冲区对象

  • gl.deleteBuffer(buffer)

    删除参数buffer表示的缓冲区对象;buffer是待删除的缓冲区对象。

  • gl.bindBuffer(target,buffer)

    绑定缓冲区。创建完成后就是绑定缓冲区到指定的目标,这个目标表示缓冲区对象的用途(在这里,就是向定点着色器提供传给attribute变量的数据),这样webgl才能处理其中的内容。

    • target:参数可以是一下中的一个:
      • gl.ARRAY_BUFFER 表示缓冲区对象中包含了顶点的数据
      • gl.ELEMENT 表示缓冲区对象中包含了顶点的索引值
  • gl.bufferData(target,data,usage)

    向缓冲区写入数据。

    • target:gl.ARRAY_BUFFERgl.ELEMENT_ARRAY_BUFFER
    • data:写入缓冲区对象的数据(类型化数组)
    • usage:
      • gl.STATIC_DRAW:只会向缓冲区对象中写入一次数据,但需要绘制很多次
      • gl.STREAM_DRAW:只会相缓冲区对象中写入一次数据,然后绘制若干次
      • gl.DYNAMIC_DRAW:回想缓冲区对象中多次写入数据,并绘制很多次
  • 类型化数据

    为了优化性能,webgl为每种基本数据类型引入了一种特殊的数组(JavaScript 类型化数组)。浏览器事先知道数组中的数据类型,所以处理起来也更加有效率。

  • gl.vertexAttribPointer()

    将缓冲区对象分配给attribute;

  • gl.enableVertexAttribArray()

    开启attribute变量。为了顶点着色器能够访问缓冲区内的数据,我们需要开启attribute变量。开启后缓冲区对象和attribute变量之间的链接就真正建立起来了。也可以使用gl.disableVertexAttribArray()来关闭分配。开启attribute变量后,即不能用gl.vertexAttrib[1234]f()向他传数据了,除非显示的关闭改attribute变量,你无法同时使用这两个函数。

下面,我们在上面绘制多个点的案例基础上,改用缓存对象来实现,并且鼠标点击制作坐标收集保存,不做渲染,点击渲染按钮时,一次性对缓冲区中的顶点数据做渲染。

代码如下:



效果如下(先在画笔上点击,光标未显示):
请添加图片描述

2、绘制三角形

绘制三角形的方式,相比于上面第四章,只有两个地方有改动。

  1. 不需要a_PointSize,因为只有在绘制单个点的时候才有效;但是如果不删除,也不会导致报错。
// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +//定义a_Position//'attribute float a_PointSize;\n' +//定义a_PointSize'void main() {\n' +'  gl_Position = a_Position;\n' + // a_Position传给gl_Position//'  gl_PointSize = a_PointSize;\n' +'}\n';
  1. gl.POINTS改成gl.TRIANGLES`。
gl.drawArrays(gl.TRIANGLES, 0, g_points.value.length);

需要注意的是,类型数组中的坐标必须是三的对数倍,如:3、6、9、。。。,因为三角形的顶点是3,只有3的倍数个顶点才能正常绘制三角形。

效果如下:
请添加图片描述

可以看到,顶点如果是3的倍数个,那么就可以正常画出对应数量的三角形。

3、绘制其他图形

上面演示了绘制gl.POINTSgl.TRIANGLES,webgl一共支持7中图形,下面演示同一批点,不同渲染模式的显示结果。
在这里插入图片描述

其他的绘制模式代码,和上面区别就只有一点就是修改mode对应的值。

gl.drawArrays(mode, 0, g_points.value.length);

4、移动

实现移动的基本逻辑是,向顶点着色器中传入一个偏移量,每次渲染的时候对每个顶点坐标加上偏移量

// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +//定义 a_Position'attribute float a_PointSize;\n' +//定义 a_PointSize'attribute float a_Translation;\n' +//定义 偏移量'void main() {\n' +'  gl_Position = vec4(a_Position.x+a_Translation,a_Position.y+a_Translation,a_Position.z+a_Translation,1.0);\n' + '  gl_PointSize = a_PointSize;\n' +'}\n';// 移动
const move = () => {T = T + 0.1;const a_Translation = gl.getAttribLocation(gl.program, "a_Translation");	//取出偏移量地址gl.vertexAttrib1f(a_Translation, T);		//给偏移量赋值draw();
}    

请添加图片描述

webGL基本图形

1、drawArrays

前面讲过,drawArray支持的绘制模型有7种:gl.POINTS、gl.LINEs、gl.LINE_STRIP、gl.LINE_LOOP、gl.TRIANGLES、gl.TRIANGLE_STRIP、gl.TRIANGLE_FAN

drawArrays(mode: number, first: number, count: number): void;

  • gl.POINTS 一系列点,依次绘制
  • gl.LINES 每两个一组绘制线段,若点的数目为奇数,最后一个点会被舍弃
  • gl.LINE_STRIP 所有的点依次相连
  • gl.LINE_LOOP 再线条的基础上,将首尾点相连
  • gl.TRIANGLES 每三个一组绘制三角形,若点的数目无法被三整除,剩余的点会被舍弃
  • gl.TRIANGLE_STRIP 一系列条带状的三角形,每个三角形都存在一条边共享
  • gl.TRIANGLE_FAN 类似于扇形的图形

下面上一张各个模式对应的图形:

在这里插入图片描述

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...