# 摄像机、投影、3D旋转、缩放

## 简述

3D效果分两种，一种是伪3D骨架，一种是3D实体.

#### 3D骨架：是通过大量的计算将3D世界中所有点投影 到二维平面中。

3D实体:通过摄像机向投影面发射射线与世界中的物体交汇，把与物体交汇点的颜色渲染 到投影面** （光线追踪的基础） 。**本系列的所有演示都是3D骨架，非3D实体。本文将穿插图片、公式、代码、演示，让读者深刻理解3D的基本概念极其思想。

## 投影分析

#### h / Math.abs(vidiconZ - z1)

x = x1 * h / Math.abs(vidiconZ - z);

y=  y1 * h / Math.abs(vidiconZ - z);

```&lt;canvas id="myCanvas" width="700" height="500" style="border: 1px solid #c3c3c3;"&gt;
Your browser does not support the canvas element.
&lt;/canvas&gt;
&lt;script type="text/javascript"&gt;
var c = document.getElementById("myCanvas");
var cxt = c.getContext("2d");
cxt.lineWidth = 3;
//&#x6B63;&#x65B9;&#x4F53;8&#x4E2A;&#x9876;&#x70B9;
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;
//&#x6444;&#x50CF;&#x673A;&#x5230;&#x663E;&#x793A;&#x5C4F;&#x7684;&#x8DDD;&#x79BB;
var distance = 500;
//&#x6444;&#x50CF;&#x673A;&#x4F4D;&#x7F6E;
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();
}
&lt;/script&gt;
&lt;div id="show"&gt;
&lt;/div&gt;
&lt;input type="button" onclick="drawCube();" value="&#x5F00;&#x59CB;&#x753B;&#x7ACB;&#x65B9;&#x4F53;"
style="width: 135px" /&gt;```

## 演示

Your browser does not support the canvas element. 当然我们可以重构一下，将8个点都放到Array中。

```&lt;script type="text/javascript"&gt;
var c = document.getElementById("myCanvas");
var cxt = c.getContext("2d");
cxt.lineWidth = 3;
//&#x6B63;&#x65B9;&#x4F53;8&#x4E2A;&#x9876;&#x70B9;
var Point = new Array();
Point[0] = { x: 100, y: 100, z: 100 };
Point[1] = { x: 100, y: 100, z: -100 };
Point[2] = { x: -100, y: 100, z: -100 };
Point[3] = { x: -100, y: 100, z: 100 };
Point[4] = { x: 100, y: -100, z: 100 };
Point[5] = { x: 100, y: -100, z: -100 };
Point[6] = { x: -100, y: -100, z: -100 };
Point[7] = { x: -100, y: -100, z: 100 };
var startX = 250;
var startY = 250;
//&#x6444;&#x50CF;&#x673A;&#x5230;&#x663E;&#x793A;&#x5C4F;&#x7684;&#x8DDD;&#x79BB;
var distance = 500;
//&#x6444;&#x50CF;&#x673A;&#x4F4D;&#x7F6E;
var eyePosition = { x: 0, y: 0, z: 700 };
function changeDistance() {
for (var i = 0; i &lt; Point.length; i++) {
Point[i].x = Point[i].x * distance / Math.abs(eyePosition.z - Point[i].z);
Point[i].y = Point[i].y * distance / Math.abs(eyePosition.z - Point[i].z);
}
}
var drawCube = function () {
changeDistance();
cxt.beginPath();
cxt.moveTo(startX + Point[0].x, startY - Point[0].y);
cxt.lineTo(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[0].x, startY - Point[0].y);
cxt.moveTo(startX + Point[4].x, startY - Point[4].y);
cxt.lineTo(startX + Point[5].x, startY - Point[5].y);
cxt.lineTo(startX + Point[6].x, startY - Point[6].y);
cxt.lineTo(startX + Point[7].x, startY - Point[7].y);
cxt.lineTo(startX + Point[4].x, startY - Point[4].y);
cxt.moveTo(startX + Point[1].x, startY - Point[1].y);
cxt.lineTo(startX + Point[5].x, startY - Point[5].y);
cxt.moveTo(startX + Point[0].x, startY - Point[0].y);
cxt.lineTo(startX + Point[4].x, startY - Point[4].y);
cxt.moveTo(startX + Point[2].x, startY - Point[2].y);
cxt.lineTo(startX + Point[6].x, startY - Point[6].y);
cxt.moveTo(startX + Point[3].x, startY - Point[3].y);
cxt.lineTo(startX + Point[7].x, startY - Point[7].y);
cxt.stroke();
}
&lt;/script&gt;```

```&lt;script language="javascript" type="text/javascript" src="lib/uglifyjs-parser.js"&gt;&lt;/script&gt;
&lt;script language="javascript" type="text/javascript" src="src/jscex.js"&gt;&lt;/script&gt;
&lt;script language="javascript" type="text/javascript" src="src/jscex.builderBase.js"&gt;&lt;/script&gt;
&lt;script language="javascript" type="text/javascript" src="src/jscex.async.js"&gt;&lt;/script&gt;
&lt;!--[if IE]&gt;
&lt;script language="javascript" type="text/javascript" src="lib/json2.js"&gt;&lt;/script&gt;
&lt;script language="javascript"&gt;
Jscex.config.codeGenerator = function (code) { return "false || " + code; }
&lt;/script&gt;
&lt;![endif]--&gt;
&lt;canvas id="myCanvas" width="700" height="500" style="border: 1px solid #c3c3c3;"&gt;
Your browser does not support the canvas element.
&lt;/canvas&gt;
&lt;script type="text/javascript"&gt;
var c = document.getElementById("myCanvas");
var cxt = c.getContext("2d");
cxt.lineWidth = 3;
var Point = new Array();
var startX = 250;
var startY = 250;
var distance = 500;
var eyePosition = { x: 0, y: 0, z: 700 };
function init() {
Point[0] = { x: 100, y: 100, z: 100 };
Point[1] = { x: 100, y: 100, z: -100 };
Point[2] = { x: -100, y: 100, z: -100 };
Point[3] = { x: -100, y: 100, z: 100 };
Point[4] = { x: 100, y: -100, z: 100 };
Point[5] = { x: 100, y: -100, z: -100 };
Point[6] = { x: -100, y: -100, z: -100 };
Point[7] = { x: -100, y: -100, z: 100 };
}
function changeDistance() {
for (var i = 0; i &lt; Point.length; i++) {
Point[i].x = Point[i].x * distance / Math.abs(eyePosition.z - Point[i].z);
Point[i].y = Point[i].y * distance / Math.abs(eyePosition.z - Point[i].z);
}
}
var drawCube = function (increment) {
cxt.clearRect(0, 0, 1200, 1200);
init();
distance += increment;
changeDistance();
cxt.beginPath();
cxt.moveTo(startX + Point[0].x, startY - Point[0].y);
cxt.lineTo(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[0].x, startY - Point[0].y);
cxt.moveTo(startX + Point[4].x, startY - Point[4].y);
cxt.lineTo(startX + Point[5].x, startY - Point[5].y);
cxt.lineTo(startX + Point[6].x, startY - Point[6].y);
cxt.lineTo(startX + Point[7].x, startY - Point[7].y);
cxt.lineTo(startX + Point[4].x, startY - Point[4].y);
cxt.moveTo(startX + Point[1].x, startY - Point[1].y);
cxt.lineTo(startX + Point[5].x, startY - Point[5].y);
cxt.moveTo(startX + Point[0].x, startY - Point[0].y);
cxt.lineTo(startX + Point[4].x, startY - Point[4].y);
cxt.moveTo(startX + Point[2].x, startY - Point[2].y);
cxt.lineTo(startX + Point[6].x, startY - Point[6].y);
cxt.moveTo(startX + Point[3].x, startY - Point[3].y);
cxt.lineTo(startX + Point[7].x, startY - Point[7].y);
cxt.stroke();
}
var reduceDrawCubeAsync = eval(Jscex.compile("async", function () {
//&#x5F53;&#x6444;&#x50CF;&#x673A;&#x5230;&#x663E;&#x793A;&#x5C4F;&#x7684;&#x8DDD;&#x79BB;&#x5927;&#x4E8E;750&#xFF0C;&#x9000;&#x51FA;&#x5FAA;&#x73AF;&#xB7;
while (distance &lt; 750) {
drawCube(10);
\$await(Jscex.Async.sleep(100));
}
}));
var magnifyDrawCubeAsync = eval(Jscex.compile("async", function () {
//&#x5F53;&#x6444;&#x50CF;&#x673A;&#x5230;&#x663E;&#x793A;&#x5C4F;&#x7684;&#x8DDD;&#x79BB;&#x5C0F;&#x4E8E;150&#xFF0C;&#x9000;&#x51FA;&#x5FAA;&#x73AF;&#xB7;
while (distance &gt; 150) {
drawCube(-10);
\$await(Jscex.Async.sleep(100));
}
}));
var executeAsync = eval(Jscex.compile("async", function () {
\$await(reduceDrawCubeAsync());
\$await(magnifyDrawCubeAsync());
}));
&lt;/script&gt;
&lt;div id="show"&gt;
&lt;/div&gt;
&lt;input type="button" onclick="executeAsync().start();" value="&#x5F00;&#x59CB;&#x79FB;&#x52A8;&#x663E;&#x793A;&#x5C4F;" style="width: 135px" /&gt;```

## 演示

Your browser does not support the canvas element. ## 3D旋转

#### a.绕Z轴方向的旋转，此时，x和y发生变化，x不变。

```//&#x65CB;&#x8F6C;
function rotate(angle) {
for (var i = 0; i &lt; Points.length; i++) {
var tempX = Points[i].x;
Points[i].x = Points[i].x * Math.cos(angle) - Points[i].z * Math.sin(angle);
Points[i].z = Points[i].z * Math.cos(angle) + tempX * Math.sin(angle);
}
}```

```function degToRad(a) {
return (a / (360 / (2 * Math.PI)));
}```

```function randomColor() {
var arrHex = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"]; var strHex = "#";
var index;
for (var i = 0; i &lt; 6; i++) {
index = Math.round(Math.random() * 15);
strHex += arrHex[index];
}
return strHex;
}```

```var currentAngle = 0;
var drawCube2 = function () {
cxt2.clearRect(0, 0, 1200, 1200);
init();
changedistance2();
cxt2.strokeStyle = randomColor();
cxt2.beginPath();
cxt2.moveTo(startX + Points[0].x, startY - Points[0].y);
cxt2.lineTo(startX + Points[1].x, startY - Points[1].y);
cxt2.lineTo(startX + Points[2].x, startY - Points[2].y);
cxt2.lineTo(startX + Points[3].x, startY - Points[3].y);
cxt2.lineTo(startX + Points[0].x, startY - Points[0].y);
cxt2.moveTo(startX + Points[4].x, startY - Points[4].y);
cxt2.lineTo(startX + Points[5].x, startY - Points[5].y);
cxt2.lineTo(startX + Points[6].x, startY - Points[6].y);
cxt2.lineTo(startX + Points[7].x, startY - Points[7].y);
cxt2.lineTo(startX + Points[4].x, startY - Points[4].y);
cxt2.moveTo(startX + Points[1].x, startY - Points[1].y);
cxt2.lineTo(startX + Points[5].x, startY - Points[5].y);
cxt2.moveTo(startX + Points[0].x, startY - Points[0].y);
cxt2.lineTo(startX + Points[4].x, startY - Points[4].y);
cxt2.moveTo(startX + Points[2].x, startY - Points[2].y);
cxt2.lineTo(startX + Points[6].x, startY - Points[6].y);
cxt2.moveTo(startX + Points[3].x, startY - Points[3].y);
cxt2.lineTo(startX + Points[7].x, startY - Points[7].y);
cxt2.stroke();
}
drawCube2()
var rotateAsync = eval(Jscex.compile("async", function () {
while (true) {
currentAngle += 5;
drawCube2();
\$await(Jscex.Async.sleep(100));
}
}));```

## 演示

Your browser does not support the canvas element.

```for (var i = 0; i &lt; Points4.length; i++) {
var tempY = Points4[i].y;
Points4[i].y = Points4[i].z * Math.sin(angle) - Points4[i].y * Math.cos(angle);
Points4[i].z = tempY * Math.sin(angle) + Points4[i].z * Math.cos(angle);
}```

## 演示

Your browser does not support the canvas element.

```function rotate(angle) {
for (var i = 0; i &lt; Points2.length; i++) {
var tempX = Points2[i].x;
var tempZ = Points2[i].z;
Points2[i].x = Points2[i].x * Math.cos(angle) - Points2[i].z * Math.sin(angle);
Points2[i].z = Points2[i].z * Math.cos(angle) + tempX * Math.sin(angle);
}
for (var i = 0; i &lt; Points2.length; i++) {
var tempY = Points2[i].y;
Points2[i].y = Points2[i].y * Math.cos(angle) - Points2[i].z * Math.sin(angle);
Points2[i].z = tempY * Math.sin(angle) + Points2[i].z * Math.cos(angle);
}
}```

## 演示

Your browser does not support the canvas element.

## 总结

0 条评论

• ### 摄像机、投影、3D旋转、缩放

本文作者：IMWeb 黎腾 原文出处：IMWeb社区 未经同意，禁止转载 简述 3D效果分两种，一种是伪3D骨架，一种是3D实体. 3D骨架：是通过大...

• ### QQ天气H5-前端完整解析

前言： 什么是手Q天气 手Q天气是在手Q 6.0版本以上新增的功能，页面会展现当天的气温情况，已经五天温度折线图以及24小时温度图表等。 并且为了更好的交互效果...

• ### QQ天气H5-前端完整解析

手Q天气是在手Q 6.0版本以上新增的功能，页面会展现当天的气温情况，已经五天温度折线图以及24小时温度图表等。 并且为了更好的交互效果，天气页面会根据8种不同...

• ### 摄像机、投影、3D旋转、缩放

本文作者：IMWeb 黎腾 原文出处：IMWeb社区 未经同意，禁止转载 简述 3D效果分两种，一种是伪3D骨架，一种是3D实体. 3D骨架：是通过大...

• ### 《Go 语言程序设计》读书笔记 (三) 方法

在函数声明时，在其名字之前放上一个变量，即是一个方法。这个附加的参数会将该函数附加到这种类型上，即相当于为这种类型定义了一个独占的方法。

• ### K8S 生态周报| 2019.04.08~2019.04.14

> 「K8S 生态周报」内容主要包含我所接触到的 K8S 生态相关的每周值得推荐的一些信息。欢迎订阅知乎专栏「k8s生态」。

• ### 压力管理续：人比人气死人

今早七点起一早的会，真让人难以为继啊。 昨天的文章收到了好几十条回复，差点看不过来，最有趣的几条是： 东莞曾经是减压圣地，现在没了 『医生的处方白纸黑丝的摆在那...

• ### 机器学习面试中常考的知识点和代码实现（一）

本文是机器学习面试中常考的知识点和代码实现，也是作为一个算法工程师必会的理论基础知识；既然是以面试为主要目的，亦不可以篇概全，请谅解，有问题可提出。

• ### 妹子如何运用R语言数据分析选择心仪的对象？

前提假设 妹子们一生中可以遇到100个追求者，追求者的优秀程度符合正态分布； 每个妹子都具备判断并比较追求者优秀程度的能力； 接受或拒绝一个追求者后永远无法后...