上节文章中,跟大家分享了一下如何入门 webgl
并在画布上绘制一个点。
诚然,我们看到的流程是十分繁琐的。
这里想说明的一点是,webgl
基础很类似于 JavaScript
基础。
在学习 JavaScript
的时候如果直接上手 jquery、vue、react、angular……
想必之后的天花板会非常非常低。
因为学习曲线决定了上手难度,而基础决定了天花板高度。
学习 webgl
可能在前期会比较麻烦,也会有很多难以理解的知识点。像:光线、3d坐标、矩阵变换、矩阵处理(乘积、点积、归一化等操作)、向量、精度、纹理、渲染管线……
但是之后也会慢慢接触到一些类库,也会接触到一些3d
的框架。这些框架和类库会极大的提升开发效率,并降低开发难度。
但是还是那句话,无论如何,基础都是重中之重。
好了,闲言少叙吧,进入今天的分享环节。
webgl
的渲染依赖底层 GPU
的渲染能力。所以 webgl
渲染流程和 GPU
内部的渲染管线是相符的。
渲染管线的作用是将3D模型转换为2维图像。
在早期,渲染管线是不可编程的,叫做固定渲染管线,工作的细节流程已经固定,修改的话需要调整一些参数。
现代的 GPU
所包含的渲染管线为可编程渲染管线,可以通过编程 GLSL,着色器语言
来控制一些渲染阶段的细节。
(心理承受弱的同学可跳过此小节)
webgl
的渲染过程分为以下几项:
这里一系列的名词可能会吓到很多同学,千万别被名词吓到哟,接下来的过程中会详细说明。也希望通过本文可以让大家理解基本的渲染流程。附图解一张,助大家理解。
顶点着色器的作用是通过计算获得最终的顶点坐标, 如:
A --> () => {…………} ==> A1
:坐标 A
经过一系列的计算,最终获取坐标 A1
B --> () => {…………} ==> B1
:坐标 B
经过一系列的计算,最终获取坐标 B1
顶点着色器计算出来的坐标将会渲染到最终的显示画布上。
此外,顶点着色器还会计算如下内容:颜色、纹理坐标、顶点尺寸……
在顶点着色器阶段通常会涉及到三个类型的变量。
像 attribute
这个变量之前我们就用到过,用来设置了顶点的位置和大小。 回顾一下
// 顶点着色器
const VERTEX_SHADER = '' +
'void main(){' +
' gl_Position = vec4(0.0,0.0,0.0,1.0);' +
' gl_PointSize = 15.0;' +
'}' +
''
// 顶点着色器
const FRAGMENT_SHADER = '' +
'void main() {' +
' gl_FragColor = vec4(1.0,0.0,0.0,1.0);' +
'}' +
''
其他的两个变量暂时没有用到,接下来的内容里会用到这两种类型的变量。敬请期待
什么是图元?
描述各种图形元素的函数叫做图元,描述几何元素的称为几何图元(点,线段或多边形)。点和线是最简单的几何图元
经过顶点着色器计算之后的坐标会被组装成组合图元。
接下来通过一组图解来看看渲染器如何进行图元装配和光栅化
图元就是一个点、一条线段、或者是一个多边形。
什么是图元装配呢?
简单理解就是说将我们设置的顶点、颜色、纹理等内容组装称为一个可渲染的多边形的过程。
注意:
如何组装取决于 gl.drawArrays(type, count)
中的 type
类型,本文最后有详细的内容。
在之前的文章里,我们就使用 gl.POINTS
来绘制了一个点。
片元:二维图象上每个点都包含了颜色、深度和纹理数据。这样一个点称为片元
光栅化可以简单理解为以下内容:
通过图元装配生成的多边形,计算像素并填充,剔除不可见的部分,剪裁掉不在可视范围内的部分。最终生成可见的带有颜色数据的图形并绘制。
webgl
中,我们也可以设定物体的背面不可见,那么在渲染过程中,就会将不可见的部分剔除,不参与绘制。节省渲染开销。接收光栅化阶段生成的片元,在光栅化阶段中,已经计算出每个片元的颜色信息,这一阶段会将片元做逐片元挑选的操作,处理过的片元会继续向后面的阶段传递。
通过模板测试和深度测试来确定片元是否要显示,测试过程中会丢弃掉部分无用的片元内容,然后生成可绘制的二维图像绘制并显示。
z
轴的值做测试,值比较小的片元内容会覆盖值比较大的。(类似于近处的物体会遮挡远处物体)。首先我们来看下上篇文章中绘制一个点的代码。绘制一个点
// 顶点着色器
const VERTEX_SHADER = '' +
'void main(){' +
' gl_Position = vec4(0.0,0.0,0.0,1.0);' +
' gl_PointSize = 15.0;' +
'}' +
''
// 片元着色器
const FRAGMENT_SHADER = '' +
'void main() {' +
' gl_FragColor = vec4(1.0,0.0,0.0,1.0);' +
'}' +
''
// 创建顶点和片元着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// 将顶点着色器代码添加到顶点着色器中
gl.shaderSource(vertexShader, VERTEX_SHADER);
// 将片元着色器代码添加到片元着色器中
gl.shaderSource(fragmentShader, FRAGMENT_SHADER);
// 添加完成后,需要编译着色器
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
// 创建程序对象
const program = gl.createProgram();
// 将着色器添加到程序对象中
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 关联程序对象
gl.linkProgram(program);
// 使用程序对象
gl.useProgram(program);
// 绘制一个点
gl.drawArrays(gl.POINTS, 0, 1);
分析:
首先创建了两个变量 VERTEX_SHADER 顶点着色器
和 FRAGMENT_SHADER 片元着色器
。这两个内容会通过下面的代码添加到顶点着色器和片元着色器中。
之后通过 gl.createShader()
创建了两个着色器。传入不同的参数即可创建不同的着色器。创建并关联着色器的步骤比较复杂。当你熟悉这个过程之后,可以将这个过程的代码封装起来作为备用。
接下来,通过 将着色器代码添加到着色器中、编辑着色器、创建程序对象、将着色器添加到程序对象中、关联程序对象、使用程序对象
这一系列的步骤初始化着色器和程序对象。
当所有的前置工作准备就绪之后,就可以调用 gl.drawArrays()
方法来绘制想要的图形。
type
代表要绘制的图形形状,值有以下几种:gl.POINTS: 要绘制一系列的点
gl.LINES: 要绘制了一系列未连接直线段(单独行)
gl.LINE_STRIP: 要绘制一系列连接的线段
**gl.LINE_LOOP **: 要绘制一系列连接的线段。它还连接第一个和最后一个顶点,以形成一个环
gl.TRIANGLES: 一系列单独的三角形
gl.TRIANGLE_STRIP: 绘制一个三角带
gl.TRIANGLE_FAN: 绘制一个扇形(三角扇)
first
代表从哪个点开始count
代表需要使用几个点gl.drawArrays(gl.POINTS, 0, 1)// 绘制一个点,从第1个点开始,每次使用一个点。
其他的形状可以自己尝试…………会有意想不到的效果。
本节内容只介绍了着色器的一部分,相对于整体的着色器知识还相差很多,但是这些知识入门是绰绰有余了,更深层次的内容只有接触的多了才能掌握的比较熟练,现在做分享反而有画蛇添足的感觉。
好了,今天的分享就到这里了,Bye~