介绍JavaScriptWebGL矩阵之后,发现在实现3D效果之前,还有一些概念需要了解,于是查了资料,按照自己的习惯进行整合。来源我的GitHub齐次坐标三维坐标理论上三个分量就够了,但是在查看相关程序的时候发现会有四个分量。这种表示称为齐次坐标。它使用具有一个n+1维向量表示的原始n维向量。例如,向量(x,y,z)的齐次坐标可以表示为(x,y,z,w)。这种表示有助于使用矩阵运算将一组点从一个坐标系转换到另一个坐标系。齐次坐标(x,y,z,w)相当于三维坐标(x/w,y/w,z/w)。有关更多详细信息,请参见此处。SpaceTransformationWeGL没有现成的API可以直接绘制三维效果。需要经过一系列的空间变换,最后显示在一个二维空间(如电脑屏幕)中,视觉上看起来像是三维的效果。让我们看一下几个主要的转换过程。模型空间模型空间是描述三维物体本身的空间,它有自己的坐标系和相应的原点。这里的点坐标可以按照WebGL中可见范围[-1,+1]的约束来描述,也可以不按照这个约束来描述。自定义描述规则需要后期转换。世界空间物体模型创建后,将其放置在特定的环境中,以达到预期的效果。此外,它还可以进行位移、缩放和旋转。这些更改需要一个新的参考坐标系。环境是世界空间。有了世界坐标系,相互独立的物体就有了相对位置的描述。从模型空间转换到世界空间,需要一个模型矩阵。3D模型矩阵类似于JavaScriptWebGL矩阵中引入的2D变换矩阵,主要变化是增加了行列和旋转。constm4={翻译:(x,y,z)=>{返回[1,0,0,0,0,1,0,0,0,0,1,0,x,y,z,1,];},//缩放矩阵缩放:(x,y,z)=>{return[x,0,0,0,0,y,0,0,0,0,z,0,0,0,0,1,];},//旋转矩阵xRotation:(angle)=>{constc=Math.cos(angle);consts=Math.sin(角度);返回[1,0,0,0,0,c,s,0,0,-s,c,0,0,0,0,1,];},yRotation:(angle)=>{constc=Math.cos(angle);consts=Math.sin(角度);返回[c,0,s,0,0,1,0,0,-s,0,c,0,0,0,0,1,];},zRotation:(angle)=>{constc=Math.cos(angle);consts=Math.sin(角度);返回[c,s,0,0,-s,c,0,0,0,0,1,0,0,0,0,1,];},}Viewspace人眼观察一个立方体时,远看近看会有大小差异,左看右看也会不同在绘制三维物体时WebGL,需要根据观察者的位置和方向将物体放置在正确的位置。观察者所在的空间就是视野空间。要从世界空间转换为视图空间,您需要使用视图矩阵。为了描述观察者的状态,需要以下信息:视点:观察者在空间中的位置,从该位置出发沿观察方向的射线即为视线。观测目标点:被观测目标所在的点。视点和观察目标点共同决定了视线的方向。UpDirection:最终绘制在屏幕上的图像的向上方向,因为观察者可以围绕视线旋转,所以需要一个特定的参考方向。以上三种信息共同构成了视图矩阵。WebGL中观察者的默认状态是:视点:位于坐标系(0,0,0)原点观察目标点:视线为z轴负方向,观察者点为(0,0,-1)向上方向:y轴的正方向,(0,1,0)一种生成视图矩阵的方法:functionsetLookAt(eye,target,up){const[eyeX,eyeY,eyeZ]=眼睛;const[targetX,targetY,targetZ]=目标;const[upX,upY,upZ]=up;让fx、fy、fz、sx、sy、sz、ux、uy、uz;fx=targetX-eyeX;fy=targetY-eyeY;fz=targetZ-eyeZ;//单位化constrlf=1/Math.sqrt(fx*fx+fy*fy+fz*fz);fx*=rlf;fy*=rlf;fz*=rlf;//f与上向量的叉积sx=fy*upZ-fz*upY;sy=fz*upX-fx*upZ;sz=fx*upY-fy*upX;//单位化constrls=1/Math.sqrt(sx*sx+sy*sy+sz*sz);sx*=rls;sy*=rls;sz*=rls;//s和f的叉积ux=sy*fz-sz*fy;uy=sz*fx-sx*fz;uz=sx*fy-sy*fx;constm12=sx*-eyeX+sy*-eyeY+sz*-eyeZ;constm13=ux*-eyeX+uy*-eyeY+uz*-eyeZ;constm14=-fx*-eyeX+-fy*-eyeY+-fz*-eyeZ;返回[sx,ux,-fx,0,sy,uy,-fy,0,sz,uz,-fz,0,m12,m13,m14,1,];}这里使用叉积,通过两个向量的叉积,可以生成一个垂直于这两个向量的法向量,构建坐标系,即观察者所在的空间。以下是使用或不使用自定义观察器的3D示例:使用自定义观察器和不使用自定义观察器。裁剪空间基于上面的例子,如果你旋转它,你会发现一些角消失了。这是因为它超出了WebGL的可视范围。在WebGL程序中,顶点着色器将点转换为称为裁剪空间的特殊坐标系。任何延伸到裁剪空间之外的数据都将被裁剪而不被渲染。要从视图空间转换到裁剪空间,需要用到投影矩阵(ProjectionMatrix)。人眼的观看范围是有限的,WebGL同样限制了水平视角、垂直视角和视觉深度,它们共同决定了观看体积(ViewVolume)。有两种常见的视域类型:长方体/盒视域,由正交投影生成。金字塔/金字塔可视区域,由透视投影生成。正交投影可以很容易地比较场景中物体的大小,因为物体的表观大小与其位置无关。这种投影应该用在建筑平面等技术制图相关的场合。透视投影使生成的场景看起来更深更自然。正射投影正射投影的视域由两个矩形面决定,分别称为近裁剪平面和远裁剪平面。近裁剪平面和远裁剪平面之间的空间是可视区域。只有在这个空间中的对象才会被显示。在正交投影中,近裁剪平面和远裁剪平面大小相同。正交投影矩阵的一种实现方式:functionsetOrthographicProjection(config){const[left,right,bottom,top,near,far]=config;if(left===right||bottom===top||near===far){抛出“无效投影”;}constrw=1/(右-左);constrh=1/(顶部-底部);constrd=1/(远-近);常量m0=2*rw;constm5=2*rh;constm10=-2*rd;常量m12=-(右+左)*rw;常量m13=-(顶部+底部)*rh;constm14=-(远+近)*rd;return[m0,0,0,0,0,m5,0,0,0,0,m10,0,m12,m13,m14,1,];}这是一个例子,通过改变每个border可见范围的变化。有关原理的更详细说明,请参见此处。Canvas上显示的是对象在近裁剪平面上的投影。如果裁剪平面的纵横比与Canvas的纵横比不同,图片会按照Canvas的纵横比进行压缩,物体会变形。透视投影透视投影的可视区域与正投影类似。明显的区别是近裁剪平面和远裁剪平面的大小不同。透视投影矩阵的一种实现方式:/***perspectiveprojection*@param{*}configorderfovy,aspect,near,far*fovy-垂直视角,但是空间上下的夹角必须是大于0*aspect-近裁剪平面的纵横比(宽/高)*near-近裁剪平面的位置,必须大于0*far-远裁剪平面的位置,必须大于0*/functionsetPerspectiveProjection(config){let[fovy,aspect,near,far]=config;if(near===far||aspect===0){throw"nullfrustum";}if(near<=0){throw"near<=0";}if(far<=0){throw"far<=0";}fovy=(Math.PI*fovy)/180/2;consts=Math.sin(fovy);if(s===0){throw"nullfrustum";}constrd=1/(远-近);constct=Math.cos(fovy)/s;constm0=ct/aspect;constm5=ct;常量m10=-(远+近)*rd;常量m14=-2*近*远*rd;返回[m0,0,0,0,0,m5,0,0,0,0,m10,-1,0,0,m14,0,];}这是一个模拟街道两边视角的例子。有关原理的更详细解释,请参见此处。参考资料WebGL编程指南网络版WebGL模型视图投影WebGL相机详解一:模型、视图和投影矩阵变换坐标系的意义
