前言
通过WebGL做了很多项目,感觉有必要录制一套视频教程,所以在这里写一个录制大纲,大家也可以通过章节目录了解下WebGL的基本内容。
视频教程发布地址
Threejs引擎
Threejs是webgl国内应用最广泛的一款三维引擎,实际做项目的时候一般都是通过一款三维引擎来完成,这样要比直接使用原生WebGL要方便的多,所在这里放了一个相关的链接。 Threejs教程大纲链接:https://blog.csdn.net/u014291990/article/details/82778586
学习WebGL的必要性
教程特点 适合没有任何图形学基础、OpenGL基础的入门,甚至没有计算机背景。 麻雀虽小,五脏俱全。 开发、优化三维引擎 这肯定是要学习的,不过本套教程的深度还不够。 对于入门是极好的 使用三维引擎 有了WebGL基础再去学习threejs等三维引擎,更容易深刻理解并使用其API。 如果你使用过threejs、babylonjs、cesiumjs等任何一款WebGL引擎,理解一直很浅显,想深入理解这些引擎的渲染原理,那么学习原生WebGL教程后,对于这些引擎的使用能力将会有质的提升。 对于一个去职场面试的WebGL工程师来说,只会三维引擎和懂原生WebGL的不在一个竞争水平线上。 如果不是开发三维或优化三维引擎,很少有项目需要使用原生WebGL API,但是并不是意味着不需要学习WebGL,学习WebGL的好处就是让你更好的理解的threejs等三维引擎如何使用,尤其需要自己编写着色器代码的场景。 自定义着色器 代码都是相通的 threejs自定义着色器 WebGL编辑着色器 以threejs为例 顶点信息存储 Geometry的顶点如何理解 顶点位置坐标、顶点颜色、顶点纹理坐标 BufferGeometry的结构 .attributes 顶点位置、颜色、法向量等属性 .index : BufferAttribute BufferAttribute 如何表示顶点的数据 材质material 所谓材质就可以理解为着色器代码 uniform数据的传递(光照等信息) 平移旋转缩放 本质上都是矩阵乘法运算 相机对象 本质也是矩阵变换 视图矩阵、投影矩阵 WebGL渲染器的本质 WebGLRenderer.js 如何封装原生WebGL API 自定义着色器 RawShaderMaterial ShaderMaterial WebGL和OpenGL选择 一个运行在浏览器环境中,一个运行在操作系统环境中 根据项目运行环境选择 封装 WebGL threejs、babylonjs、cesiumjs OpenGL Unity3D、虚幻… 学习顺序 先学WebGL还是它的三维引擎 如果急着做项目,先学习threejs等三维引擎,以后再来学习WebGL 建议同时学习 一个方便实用 一个是底层知识 有助于更好的使用引擎 Threejs视频教程 建议基础不太好的购买 基础好的话,直接看官方的API文档就可以
1.第一个WebGL程序(绘制一个点)
基础 认识HTML Javascript脚本语言 普通数组Array 类型化数组 Float32Array Uint8Array … Canvas画布 一个用于图形绘制的html元素对象,规定了一些方法和属性 canvas对象方法.getContext() 返回对象 canvas.getContext(‘2d’) 2D环境 返回对象具有一系列绘制二维图像的方法 canvas.getContext(‘webgl’); 3D环境 返回对象具有一系列绘制三维场景的方法 WebGL API 测试浏览器对WebGL的支持 执行canvas.getContext(‘webgl’);返回的对象具有的方法 通信 脚本语言Javascript可以通过WebGl API来编译着色器代码,并在GPU中执行,初始化GPU状态 例如drawArrays()方法 向GPU下达绘制命令 Javascript语言 和着色器语言之间的桥梁 硬件 WebGL API与GPU硬件相关,控制相关图形处理单元 顶点着色器代码 GPU顶点着色器单元中执行 片元着色器代码 GPU片元着色器单元中执行 逐层抽象 GPU硬件(渲染管线)——显卡驱动——操作系统——浏览器——WebGL API 着色器语言GLSL ES 作用 用于计算机图形编程 执行 JavaScript语言在CPU中运行 着色器语言编译后在GPU中运行 语法 和CPU 中执行的C语言、JavaScript语言有些不同,但是变量、for循环等概念都是一致的 内置变量 顶点着色器 gl_PointSize gl_Position 片元着色器 gl_FragColor
2.WebGL绘制一个矩形
gl.drawArrays() 语法:gl.drawArrays(mode, first, count); model模式 三角形区域 TRIANGLES TRIANGLE_FAN 线条 LINE_LOOP LINE_STRIP LINES 点 POINTS 绘制过程 1 顶点着色器执行main函数中语句gl_Position =apos;,逐顶点获得顶点坐标数据,有四个点意味着赋值执行四次,这个过程就是逐顶点 逐顶点执行 gl_Position =apos; 2 绘制模式model gl.drawArrays() 光栅化——片元数据 片元——简单理解为像素 framgment:翻译为片元或片段 顶点数据获得后,会进行图元装配,起作用的就是drawArrays方法的第一个参数绘制模式mode,然后光栅化得到的就是片元framgment,你可把片元理解成还没有定义颜色的显示器相应位置的像素rgb值,尽管这么理解不完美 3 逐片元执行 gl_FragColor = vec4(1.0,0.0,0.0,1.0); 光栅化后得到片元数据,片元着色器会执行main函数中语句gl_FragColor = vec4(1.0,0.0,0.0,1.0);,每一个片元依次添加颜色,这里定义的是红色,处理过程就是逐片元处理。 4 处理 处理片元后得到的像素数据再进行一些处理,才能显示在屏幕上 帧缓冲区 一系列像素数据都存储在显卡的帧缓冲区中 你可简单理解为一张图片是一帧 扫描显示 显示器会按照一定的频率扫描并显示这些数据 如果帧缓冲区中的数据不停更新,就可以产生动画的效果 处理后获得的像素颜色数据可能还要经过一些环节,为了简化问题,你暂时可以理解为这些片元处理后得到的一系列像素数据都存储在显卡的帧缓冲区中,你可简单理解为一张图片是一帧,显示器会按照一定的频率扫描并显示这些数据, 界面刷新频率60HZ最常见,人的眼睛有视觉暂留效应,只要刷新频率不太低,就不会感觉到卡顿,有些视频单位时间内播放的帧数太少,大家会感到很不流畅。
3.WebGL坐标系(投影)
三角形 投影 坐标系 1.相对坐标 canvas 像素定义宽高度 WebGL坐标 相对大小 canvas画布宽高采用的是像素值定义,以显示器为准,WebGL中顶点坐标的表示方法采用的是相对坐标,相对于canvas而言 2.x轴 WebGL坐标系统,X轴水平向右,也就是canvas画布的width表示的宽度方向,x等于-1表示canvas画布的左边界,x等于1表示canvas画布的右边界,x等于0对应的是画布宽度方向的中间 3.y轴 WebGL坐标系统,Y轴竖直向上,也就是canvas画布的height表示的高度方向,y等于-1表示canvas画布的下边界,y等于1表示canvas画布的上边界,y等于0对应的是画布高度方向的中间 4.z轴 WebGL坐标系统,Z轴垂直canvas画布朝外,Z值-1和1是Z方向的极限值,GPU成像默认的沿着Z轴投影,你也可以抽象出一个概念,人眼睛位于z轴上,沿着z轴方向去观察物体,如果你在其他的书上看到视图坐标系等其它各类坐标系都是抽象出的概念 都是建立在本节课所说的WebGL 坐标系统之上,例如无人机导航中的所说的机体坐标系、地球坐标系都是直接对现实中事物的描述,三维场景中的各类坐标系与无人机中坐标系没什么区别,但是要显示在屏幕上,就要经过一些处理,这里不再详述,后面的教程后为大家引入各类坐标系概念, 正射投影和透射投影概念。 4.WebGL平移变换 齐次坐标 把三维坐标,增加一个元素1,用4x1矩阵表示,nx1矩阵也称为列向量,对应的数据类型就是vec4 n维向量增加一个维度用n+1维向量表示就是齐次坐标 平移矩阵 4x4矩阵矩阵,就是平移矩阵,平移矩阵左乘顶点的齐次坐标,结果仍然是一个齐次坐标,也就是平移后的坐标。 结合律 矩阵的乘法满足结合律,如果多次平移,可以把所有的平移矩阵先进行乘法运算,然后左乘要平移顶点的齐次坐标 着色器代码 数据类型 mat4 4X4矩阵 vec4 attribute vec4 apos 构造函数 vec4 gl_FragColor = vec4(1.0,0.0,0.0,1.0) mat4 mat4 m4 = mat4(1,0,0,0, 0,1,0,0, 0,0,1,0, -0.4,0,0,1)
5.绘制一个立方体(WebGL旋转变换)
旋转变换 一个点的坐标是(x,y,z),经过旋转变换后的坐标为(X,Y,Z,) 绕Z轴旋转γ角度 X=xcosγ-ysinγ,Y=xsinγ+ycosγ 绕X轴旋转α角度 Y=ycosα-zsinα,Z=ysinα+zcosα 绕Y轴旋转β角度 Z=zcosβ-xsinβ,X=zsinβ+xcosβ 着色器语言 函数 radians() 角度值转弧度值 cos() 计算弧度对应的余弦值 sin() 计算弧度对应的正弦值 立方体顶点坐标
6.WebGL顶点索引绘制 gl.drawArrays() 语法:gl.drawArrays(mode, first, count); model模式 三角形区域 TRIANGLES TRIANGLE_FAN 线条 LINE_LOOP LINE_STRIP LINES 点 POINTS gl.drawElements() 语法:drawElements(mode, count, type, offset) 参数 好处 复用顶点数据 立方体顶点坐标
7.varying变量和颜色插值
颜色插值 着色器 变量类型 attribute 顶点数据 顶点坐标 attribute vec4 apos; 顶点颜色 attribute vec4 a_color; varying 插值相关的变量声明 varying vec4 v_color; 顶点着色器 main() 插值计算 v_color = a_color; 声明变量 顶点相关数据 顶点坐标 attribute vec4 apos; 顶点颜色 attribute vec4 a_color; 片元着色器 插值后的数据 varying vec4 v_color; main() gl_FragColor = v_color; precision lowp float; 定义片元着色器中的所有浮点型float数据的精度,在着色器语言中lowp是精度限定字表示低精度。计算机资源有限,设置数据精度是为了提高执行效率。除了片元着色器中的浮点类型float数据,其它所有类型数据浏览器都有默认的精度,因此要设定片元着色器中浮点类型float数据精度 gl.vertexAttribPointer() 语法 vertexAttribPointer(location,size,type,normalized,stride,offset) 参数
8.立方体(每个面一种颜色)
渲染管线 开启深度测试单元 gl.enable(gl.DEPTH_TEST) 片元数据 颜色数据 深度数据 在空间中的位置 是否会被剪裁 超出WebGL坐标系-1~1的片元会被剪裁 遮挡 深度测试开启的情况下,前面的会遮挡后面的 9.WebGL光照渲染立方体 光照模型 漫反射 漫反射光的颜色 = 几何体表面基色 x 光线颜色 x 光线入射角余弦值 (R2,G2,B2) = (R1,G1,B1) x (R0,G0,B0) x cosθ 光的颜色可以使用多种模型来表示,把上面的文字公式使用RGB具体参数来表示形式如下,比物体表面的颜色是纯红色(1,0,0),入射光是纯白色(1,1,1),光线入射角是60度,余弦值就是0.5,代入下面公式,可以得出结果是(0.5,0,0), 结果仍然是红色,这是符合实际生活的,白色太阳光照在常见的红色物体上,反射的颜色是红色,只是太阳光线照射在物体表面的角度不同,反射的光强度不同。入射光垂直物体表面,也就是入射角是0对应的余弦值是1,光线垂直表面受光量最大, 反射光自然最大,和1对应;入射光线平行物体表面,此时的入射角是90度, 物体表面自然没什么光可以反射, 镜面反射 镜面反射光的颜色 = 几何体表面基色 x 光线颜色 x 视线与反射光线的夹角余弦值n 在室外停放着一辆车,你观察车的时候,你会发现车的外表面会在某个局部出现高光,这很好理解,车的外壳是曲面的,曲面上如果某个区域的的光线反射角刚好是你的视线方向,自然会呈现出局部高亮的现象,其他的部位是漫反射为主。 镜面反射的公式仍然可以写成上面的形式,只是角度不再是光线入射角度而是眼睛视线与反射光线的夹角,n角度余弦值的指数,实际编程的时候你可以自由定义,没有绝对完美的模型,都可以进行修正。 环境光照 环境反射光颜色 = 几何体表面基色 x 环境光颜色 复合光照 环境光、漫反射光、镜面反射光 夹角余弦值计算 有基本的数学知识应该都有法线的概念,垂直与面的直线就是面的法线,对于平面而言面上所有位置的法线方向是相同的,对于曲面而言不同的位置法线的方向是变化的。在三维笛卡尔坐标系中,可以使用向量(x,y,z)来表示法向量,根据几何体表面的法向量和光线的方向, 就可以求解出光线入射角的余弦值,法向量的点积计算满足下面的公式,为了方便计算,着色器语言内置了一个方法dot()用来求解两个向量之间的余弦值,已知向量a1(x1,y1,z1)、a2(x2,y2,z2)执行dot(a1,a2)可以求出两个向量a1、a2的余弦值。 光线与表面的夹角 光线的方向 集合表面的法线 着色器内置函数dot() 向量a1(x1,y1,z1)、a2(x2,y2,z2) dot(a1,a2):两个向量夹角的余弦值 立方体顶点法向量 着色器语言 内置函数 dot() 向量a1(x1,y1,z1)、a2(x2,y2,z2) dot(a1,a2):两个向量夹角的余弦值 max() 比较两个值,选择大的 normalize() 归一化,通过缩放xyz分量,把一个长度不为1的向量,长度变为1 变量类型 attribute 传递顶点信息 顶点 位置坐标 attribute vec4 apos 颜色 attribute vec4 a_color 法向量 attribute vec4 a_normal 顶点坐标、颜色、法向量一一对应 只出现在顶点着色器中 varying 主要用于插值计算 插值后数据对应的变量 颜色 varying vec4 v_color 顶点着色器、片元着色器都要同时声明 uniform 传递光照、模型变换矩阵等信息 光线颜色 uniform vec3 u_lightColor 光线方向 uniform vec3 u_lightDirection 顶点或片元着色器中 数据类型 vec4 四维向量 表示顶点位置数据、顶点颜色数据、顶点法线数据 vec3 三维向量 光线方向、光线颜色等数据 构造函数 vec4(vec3,float) 从程序对象program获得变量索引地址 gl.getAttribLocation() attribute声明的顶点数据对应变量 顶点位置 var aposLocation = gl.getAttribLocation(program,‘apos’) gl.getUniformLocation uniform声明的变量 光线颜色 u_lightColor = gl.getUniformLocation(program,‘u_lightColor’) 传递数据 uniform gl.uniform3f(u_lightColor, 1.0, 1.0, 1.0); 给着色器中的变量传入数据 attribute 要通过缓冲区实现 10.立方体旋转动画 旋转矩阵 定义在着色器中 从CPU传递 uniform声明变量的数据传递 .uniform[1234][fi]v gl.uniform3f(u_lightColor, 1.0, 1.0, 1.0); 传递三维向量数据 .uniformMatrix[234]fv() gl.uniformMatrix4fv() 传递4x4矩阵数据 .uniformMatrix2fv 传递2x2矩阵数据 .uniformMatrix3fv 传递3x3矩阵数据 requestAnimationFrame() 周期性调用参数中函数 默认每秒60次,如果两帧之间计算时间长,可能会低于该频率 动画效果 周期性改变立方体旋转矩阵数据,同时调用 gl.drawArrays()方法重新绘制 canvas画布对应的帧缓冲区 颜色缓冲区 深度缓冲区 模板缓冲区 gl.drawArrays每次执行都会更新帧缓冲区中的数据 系统会从帧缓冲区读取颜色数据显示在画布上
11.WebGL绘制多个几何体
12.纹理贴图
纹理映射 顶点数据 顶点位置坐标 attribute vec4 a_Position 四维向量表示 顶点颜色 顶点法向量 顶点纹理坐标 顶点纹理坐标和顶点位置坐标一一对应 表示图片的坐标 attribute vec2 a_TexCoord 顶点着色器中声明一个顶点纹理坐标边梁 二维向量表示 图片数据传递 参考光照方向数据、模型变换矩阵数据 在片元着色器中声明 uniform sampler2D u_Sampler 数据类型 sampler2D 着色器 纹理坐标插值计算 attribute vec2 a_TexCoord; varying vec2 v_TexCoord; v_TexCoord = a_TexCoord; 内置函数texture2D() gl_FragColor = texture2D(u_Sampler,v_TexCoord) 从传递的图片数据中提取像素数据 u_Sampler:图片数据 v_TexCoord:插值后的纹理坐标 图片纹素格式 jpg gl.RGB png gl.RGBA WebGL API gl.createBuffer() 存储顶点数据的缓冲区 gl.createTexture(); 存储图片的纹理缓冲区
13.彩色图转灰度图
亮度 灰度图颜色只有黑白两色,或者说灰度图颜色分量只有光亮度这一个分量,黑色相当于没有光照,白色相当于最大光照强度。那么光的亮度和RGB颜色模型的三原色有什么样的数学关系, 简单的理解,RGB分量越大,灰度图就越接近于白色,具体的计算公式如下,RGB的系数之和为1,这样可以保证计算的结果不会超出WebGL颜色分量默认的最大值1。 亮度 = 0.299 x R + 0.587 x G + 0.114 x B
14.切换着色器程序
顶点片元着色器编译以后会返回一个程序对象program gl.useProgram() gl.useProgram(program); 可以实现着色器的切换
15.WebGL透明度与α融合
渲染管线 片元数据 颜色数据 深度数据 在空间中的位置 是否会被剪裁 超出WebGL坐标系-1~1的片元会被剪裁 遮挡 深度测试开启的情况下,前面的会遮挡后面的 开启融合单元的功能 gl.enable(gl.BLEND); 方法 gl.enable() 参数 gl.enable(gl.BLEND); 开启融合单元的功能 gl.blendFunc() 参数 常见设置方式 gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA); 计算 R3 = R1 x 参数1 + R2 x 参数2 G3 = G1 x 参数1 + G2 x 参数2 B3 = B1 x 参数1 + B2 x 参数2 颜色叠加顺序 本程序中执行绘制函数之前,没有设置深度测试,就不会考虑像素的Z值,绘制的三个三角面的像素会按照顶点的绘制顺序叠加在一起,后绘制的像素覆盖先绘制的像素,你可以把所有顶点的透明度分量更改为1表示不透明,就可以看到他们的叠加关系。
16.深度测试与α融合
渲染管线 深度测试 融合