3D效果分两种,一种是伪3D骨架,一种是3D实体.
3D骨架:是通过大量的计算将3D世界中所有点投影到二维平面中。
3D实体:通过摄像机向投影面发射射线与世界中的物体交汇,把与物体交汇点的颜色渲染到投影面 (光线追踪的基础) 。本系列的所有演示都是3D骨架,非3D实体。本文将穿插图片、公式、代码、演示,让读者深刻理解3D的基本概念极其思想。
对象及概念介绍
对象一:摄像机。
大家都有一个基本常识,在不同的角度观看到的物体是不同的。摄像机对象有自己的空间的坐标(vidiconX,vidiconY,vidiconZ)。
对象二:显示屏
任何三维物体,都会以二维的形式投影在显示屏上,显示屏垂直于摄像机的观测方向,所以摄像机的空间坐标变化,会导致显示屏的坐标系的变换。
对象三:被观察测物体
任何物体都是有无数个点构成,每个点有自己的空间坐标(x,y,z),显示屏介于摄像机和物体之间。
为了降低复杂度,本文将显示屏和被观测物体所处的坐标系公用一套(x,y),所有的旋转都是物体旋转,摄像机不动!
缩放原理:摄像机不动,被观察测物体不动,显示屏离摄像机越近,缩放比例越小,显示屏离摄像机越远,缩放比例越大。
投影分析
我们来看下面这张图:
因为,我们将显示屏和被观测物体共用一个坐标系,所以,我们可以计算出点(x1,y1,z1)投影到显示屏上的点的缩放比例为:
h / Math.abs(vidiconZ - z1)
所以投影后的坐标为:
x = x1 * h / Math.abs(vidiconZ - z);
y= y1 * h / Math.abs(vidiconZ - z);
有了以上这些知识,我们可以轻松的在Canvas里画一个正方体(再次强调,是根据计算的结果画,非人类经验)。
<canvas id="myCanvas" width="700" height="500" style="border: 1px solid #c3c3c3;"> Your browser does not support the canvas element. </canvas> <script type="text/javascript"> var c = document.getElementById("myCanvas"); var cxt = c.getContext("2d"); cxt.lineWidth = 3; //正方体8个顶点 var Point1 = { x: 100, y: 100, z: 100 }; var Point2 = { x: 100, y: 100, z: -100 }; var Point3 = { x: -100, y: 100, z: -100 }; var Point4 = { x: -100, y: 100, z: 100 }; var Point_1 = { x: 100, y: -100, z: 100 }; var Point_2 = { x: 100, y: -100, z: -100 }; var Point_3 = { x: -100, y: -100, z: -100 }; var Point_4 = { x: -100, y: -100, z: 100 }; var startX = 250; var startY = 250; //摄像机到显示屏的距离 var distance = 500; //摄像机位置 var eyePosition = { x: 0, y: 0, z: 700 }; function changeDistance() { Point1.x = Point1.x * distance / Math.abs(eyePosition.z - Point1.z); Point1.y = Point1.y * distance / Math.abs(eyePosition.z - Point1.z); Point2.x = Point2.x * distance / Math.abs(eyePosition.z - Point2.z); Point2.y = Point2.y * distance / Math.abs(eyePosition.z - Point2.z); Point3.x = Point3.x * distance / Math.abs(eyePosition.z - Point3.z); Point3.y = Point3.y * distance / Math.abs(eyePosition.z - Point3.z); Point4.x = Point4.x * distance / Math.abs(eyePosition.z - Point4.z); Point4.y = Point4.y * distance / Math.abs(eyePosition.z - Point4.z); Point_1.x = Point_1.x * distance / Math.abs(eyePosition.z - Point_1.z); Point_1.y = Point_1.y * distance / Math.abs(eyePosition.z - Point_1.z); Point_2.x = Point_2.x * distance / Math.abs(eyePosition.z - Point_2.z); Point_2.y = Point_2.y * distance / Math.abs(eyePosition.z - Point_2.z); Point_3.x = Point_3.x * distance / Math.abs(eyePosition.z - Point_3.z); Point_3.y = Point_3.y * distance / Math.abs(eyePosition.z - Point_3.z); Point_4.x = Point_4.x * distance / Math.abs(eyePosition.z - Point_4.z); Point_4.y = Point_4.y * distance / Math.abs(eyePosition.z - Point_4.z); } var drawCube = function () { changeDistance(); cxt.beginPath(); cxt.moveTo(startX + Point1.x, startY - Point1.y); cxt.lineTo(startX + Point2.x, startY - Point2.y); cxt.lineTo(startX + Point3.x, startY - Point3.y); cxt.lineTo(startX + Point4.x, startY - Point4.y); cxt.lineTo(startX + Point1.x, startY - Point1.y); cxt.moveTo(startX + Point_1.x, startY - Point_1.y); cxt.lineTo(startX + Point_2.x, startY - Point_2.y); cxt.lineTo(startX + Point_3.x, startY - Point_3.y); cxt.lineTo(startX + Point_4.x, startY - Point_4.y); cxt.lineTo(startX + Point_1.x, startY - Point_1.y); cxt.moveTo(startX + Point2.x, startY - Point2.y); cxt.lineTo(startX + Point_2.x, startY - Point_2.y); cxt.moveTo(startX + Point1.x, startY - Point1.y); cxt.lineTo(startX + Point_1.x, startY - Point_1.y); cxt.moveTo(startX + Point3.x, startY - Point3.y); cxt.lineTo(startX + Point_3.x, startY - Point_3.y); cxt.moveTo(startX + Point4.x, startY - Point4.y); cxt.lineTo(startX + Point_4.x, startY - Point_4.y); cxt.stroke(); } </script> <div id="show"> </div> <input type="button" onclick="drawCube();" value="开始画立方体" style="width: 135px" />