WebGL简易教程(十二):包围球与投影
目录
在这种情况下使用包围盒来计算合适的位置有点难度,使用包围球来设置MVP矩阵更加方便。
2. 实现详解
包围球是利用包围盒生成的,所以首先需要定义一个球体对象:
//定义一个球体 function Sphere(cuboid) { this.centerX = cuboid.CenterX(); this.centerY = cuboid.CenterY(); this.centerZ = cuboid.CenterZ(); this.radius = Math.max(Math.max(cuboid.LengthX(), cuboid.LengthY()), cuboid.LengthZ()) / 2.0; } Sphere.prototype = { constructor: Sphere }
这个球体对象的构造函数传入了一个包围盒对象,以包围盒的中心为球体的中心,包围盒长、宽、高的最大值作为包围球的直径。在构造出包围盒之后,利用包围盒参数构造出包围球,将其保存在自定义的Terrain对象中:
var terrain = new Terrain(); //.... terrain.cuboid = new Cuboid(minX, maxX, minY, maxY, minZ, maxZ); terrain.sphere = new Sphere(terrain.cuboid);
接下来就是改进设置MVP矩阵的函数setMVPMatrix()了。如果仍然想像之前那样进行透视投影,几乎可以不用做改动:
//设置MVP矩阵 function setMVPMatrix(gl, canvas, sphere, lightDirection) { //... //投影矩阵 var fovy = 60; var projMatrix = new Matrix4(); projMatrix.setPerspective(fovy, canvas.width / canvas.height, 1, 10000); //计算lookAt()函数初始视点的高度 var angle = fovy / 2 * Math.PI / 180.0; var eyeHight = (sphere.radius * 2 * 1.1) / 2.0 / angle; //视图矩阵 var viewMatrix = new Matrix4(); // View matrix viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0); //... }
之前是通过透视变换的张角和包围盒的Y方向长度来计算合适的视野高度,现在只不过将包围盒的Y方向长度换成包围球的直径。这样的写法兼容性更高,因为包围球的直径是包围盒XYZ三个方向的最大长度。这个时候的初始渲染状态为:
最后实现下特定角度平行光视角下的地形渲染情况。前面说到过这种情况下是需要设置正射投影的,具体设置过程如下://设置MVP矩阵 function setMVPMatrix(gl, canvas, sphere, lightDirection) { //... //模型矩阵 var modelMatrix = new Matrix4(); modelMatrix.scale(curScale, curScale, curScale); modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis modelMatrix.translate(-sphere.centerX, -sphere.centerY, -sphere.centerZ); //视图矩阵 var viewMatrix = new Matrix4(); var r = sphere.radius + 10; viewMatrix.lookAt(lightDirection.elements[0] * r, lightDirection.elements[1] * r, lightDirection.elements[2] * r, 0, 0, 0, 0, 1, 0); //投影矩阵 var projMatrix = new Matrix4();