WebGL绘制一个模型的步骤:
1、获取模型的顶点坐标
2、图元装配(即画相应的三角形面片)
3、光栅化(生成片元,绘制每个三角片上的像素点,染色、纹理映射都在此步)
顶点坐标处理
现实中最常见的三维模型,都是通过3D建模软件导出的文件,文件内容通常包括,模型的顶点格式,模型的顶点坐标,以及定点之间的映射关系。通常要经过以下过程,才能将模型文件所描述的三维模型绘制到二维的屏幕上。
相关坐标系介绍:
- 本地坐标系:描述模型顶点的坐标系,一般都在模型的做下端,位置不是固定不变的。
- 世界坐标系:描述每个模型在三维场景中位置的坐标系。
- 视图坐标系:描述视点(可理解为眼睛或相机)的位置、上方向(正着看还是斜着看)、被观察者的位置。
- 裁剪坐标:描述投影盒子的坐标系,例如:如果是正方形投影盒子,则需要确定6个面的位置。
WebGL中给出的绘制基本几何的api为:
WebGL通过gl.drawArrays()函数可以绘制一些基本图形,接收的参数如下 :
- 点 (gl . POINTS)
- 线段 (gl . LINES)
- 线条 (gl . LINE_STRIP)
- 回路 (gl . LINE_LOOP)
- 三角形 (gl . TRIANGLES)
- 三角带(gl . TRIANGLE_STRIP)
- 三角扇(gl . TRIANGLE_FAN)
图元装配
一个复杂的模型,都是由一个个三角形画出来的。图元装配就是由顶点生成一个个的图元(即三角形)。也可以理解成是将一个三维模型的表面拆成一个个三角片,方便gpu进行光栅化。图元装配需要利用顶点着色器。
顶点着色器工作流程:有多少个顶点,顶点着色器就执行多少次。
光栅化
图元生成完毕之后,我们需要给模型“上色”,也可以理解为给每个图元进行像素点装填上色。完成这部分工作的,是运行在GPU的“片元着色器”来完成。模型的质地(颜色、漫反射贴图等)、灯光等由片元着色器来计算。
片元着色器工作流程:有多少个像素点,片元着色器就执行多少次。
图元装配之前获取顶点所用到的矩阵:
1 . 基本变换矩阵(平移、伸缩、旋转)
(平移变换)
1
0
0
t
x
0
1
0
t
y
0
0
1
t
z
0
0
0
1
\begin{matrix} 1 &0 &0 & t_x \\ 0 & 1 & 0 & t_y\\ 0 & 0&1& t_z\\ 0 & 0 & 0 & 1 \end{matrix} \tag{平移变换}
100001000010txtytz1(平移变换)
(绕z轴旋转变换) c o s θ − s i n θ 0 0 s i n θ c o s θ 0 0 0 0 1 0 0 0 0 1 \begin{matrix} cos\theta &-sin\theta &0 & 0\\ sin\theta&cos\theta & 0 & 0\\ 0 & 0&1& 0\\ 0 & 0 & 0 & 1 \end{matrix} \tag{绕z轴旋转变换} cosθsinθ00−sinθcosθ0000100001(绕z轴旋转变换)
(伸缩变换) s x 0 0 0 0 s y 0 0 0 0 s z 0 0 0 0 1 \begin{matrix} s_x&0&0 & 0\\ 0&s_y & 0 & 0\\ 0 & 0&s_z& 0\\ 0 & 0 & 0 & 1 \end{matrix} \tag{伸缩变换} sx0000sy0000sz00001(伸缩变换)
2 . 视图矩阵(view matrix)
为了确定观察者的状态,需要知道视点和观察目标点,可以确定视线,但是最后要把观察的景象绘制到屏幕上,还需要确定上方向.
- **视点 : **观察者在三维空间的位置 . (eyeX , eyeY , eyeZ)
- **观察目标点 : **被观察目标的位置. (centerX , centerY , centerZ)
- **上方向 : **最终绘制在屏幕上的影像中的上方向.( upX , upY , upZ)
这三个矢量确定了最终的视图矩阵 .
var e, fx, fy, fz, rlf, sx, sy, sz, rls, ux, uy, uz; fx = centerX - eyeX; fy = centerY - eyeY; fz = centerZ - eyeZ; //Normalize f. rlf = 1 / Math.sqrt(fx*fx + fy*fy + fz*fz); fx *= rlf; fy *= rlf; fz *= rlf; // Calculate cross product of f and up. sx = fy * upZ - fz * upY; sy = fz * upX - fx * upZ; sz = fx * upY - fy * upX; // Normalize s. rls = 1 / Math.sqrt(sx*sx + sy*sy + sz*sz); sx *= rls; sy *= rls; sz *= rls; // Calculate cross product of s and f. ux = sy * fz - sz * fy; uy = sz * fx - sx * fz; uz = sx * fy - sy * fx;
视图矩阵
(视图矩阵) s x s y s z s x ∗ e y e X + u x ∗ e y e Y − f x ∗ e y e Z u x u y u z s y ∗ e y e X + u y ∗ e y e Y − f y ∗ e y e Z − f x − f y − f z s z ∗ e y e X + u z ∗ e y e Y − f z ∗ e y e Z 0 0 0 1 \begin{matrix} sx & sy & sz & sx*eyeX+ux*eyeY-fx*eyeZ \\ ux & uy & uz & sy*eyeX+uy*eyeY-fy*eyeZ\\ -fx & -fy & -fz & sz*eyeX+uz*eyeY-fz*eyeZ\\ 0 & 0 & 0 & 1 \end{matrix} \tag{视图矩阵} sxux−fx0syuy−fy0szuz−fz0sx∗eyeX+ux∗eyeY−fx∗eyeZsy∗eyeX+uy∗eyeY−fy∗eyeZsz∗eyeX+uz∗eyeY−fz∗eyeZ1(视图矩阵)
3 . 投影矩阵(可视空间)
3.1 长方体可视空间
- 长方体可视空间由**正射投影(orthographic projection)**产生.
- 一个长方体的确定只要确定6个面的位置,即 : left , right , bottom , top , near , far
正射投影矩阵
(正射投影矩阵) 2 / ( r i g h t − l e f t ) 0 0 − ( r i g h t + l e f t ) / ( r i g h t − l e f t ) 0 2 / ( t o p − b o t t o m ) 0 − ( t o p + b o t t o m ) / ( t o p − b o t t o m ) 0 0 − 2 / ( f a r − n e a r ) − ( f a r + n e a r ) / ( f a r − n e a r ) 0 0 0 1 \begin{matrix} 2 / (right - left) & 0 & 0 & -(right + left)/(right - left)\\ 0 & 2 / (top - bottom) & 0 & -(top + bottom)/(top - bottom)\\ 0 & 0 &-2 / (far - near) & -(far + near)/(far - near) \\ 0 & 0 & 0 & 1 \end{matrix} \tag{正射投影矩阵} 2/(right−left)00002/(top−bottom)0000−2/(far−near)0−(right+left)/(right−left)−(top+bottom)/(top−bottom)−(far+near)/(far−near)1(正射投影矩阵)
3.2 四棱锥/金字塔可视空间
-
四棱锥/金字塔可视空间由透视投影(perspective)产生.
-
四棱锥可视空间是由以视点为顶点的四棱锥构成 , 确定四棱锥可视空间的参数有fov(可视顶面和底面的夹角) , aspect(远/近裁切面的宽高比) , near(近裁切面位置) , far(远裁切面位置) .
-
注意 : fov必须大于0 ,near必须小鱼far .
透视投影矩阵
(正射投影矩阵) M a t h . c o s ( f o v / 2 ) / ( a s p e c t ∗ s i n ( f o v / 2 ) ) 0 0 0 0 M a t h . c o s ( f o v / 2 ) / M a t h . s i n ( f o v / 2 ) 0 0 0 0 − ( f a r + n e a r ) / ( f a r − n e a r ) − 2 ∗ n e a r ∗ f a r / ( f a r − n e a r ) 0 0 − 1 0 \begin{matrix} Math.cos(fov/2) /(aspect * sin(fov/2)) & 0 & 0 & 0\\ 0 & Math.cos(fov/2) /Math.sin(fov/2) & 0 &0\\ 0 & 0 & -(far + near) / (far - near) & -2 * near * far / (far - near)\\ 0 & 0 & -1& 0 \end{matrix} \tag{正射投影矩阵} Math.cos(fov/2)/(aspect∗sin(fov/2))0000Math.cos(fov/2)/Math.sin(fov/2)0000−(far+near)/(far−near)−100−2∗near∗far/(far−near)0(正射投影矩阵)
4 . 模型视图投影矩阵
顶点在观察体里面的坐标 = <投影矩阵> * <视图矩阵> * <模型变换矩阵> * 世界坐标中的顶点坐标 。
注意:为了避免着色器在处理每一个顶点时都要计算矩阵乘法,所以应该将矩阵相乘的结果算完再传给着色器 .
5. 视口变换
(1) 将规范观察体的内容转变到屏幕坐标系 , 只需将x , y坐标按照比例进行变换 , z坐标只要留给可见性检测和表面绘制时使用 .
(2) 在WebGL中通过gl.enable(parameter)函数来实现.parameter可以选择gl.DEPTH_TEST(深度检测,也可以理解成消除隐藏面), gl.BLEND (混合), gl.POLYGON_OFFSET_FILL(多边形位移.)