【计算机图形学】结课大作业——三维场景变换(ASCII表)
程序员文章站
2022-03-22 10:12:31
效果 >_<技术栈经典三剑客——HTML / CSS / JavaScriptWebGL快速开发:Three.js思路分析▶ 变量声明var table——列表,存放ASCII码表及其信息var scene, camera, renderer——场景、相机、渲染器var controls——控件var objects = []——objects[]集合是ascii码图形的集合,记录着3D排列方式var target...
效果 >_<
技术栈
- 【前端】HTML / CSS / JavaScript
- 【图形学】WebGL / Three.js
思路
three.js开发一般是比较套路的——init() + animate()
- init()时把所有的场景摆放好
- animate()就是一个递归调用的渲染过程。
如何实现ASCII码图形的静态排列和动态变化?
- 一次遍历初始化128个element元素对象,紧接着使用Three.js将其THREE成3D对象——这样,我们首先得到了128个3D对象(object)
- 这些3D对象的空间位置(position)都是可以设置的,这也是我们得以排列出这些奇妙图形的关键——这些位置信息用一个大小128的列表容器objects[]装起来——一种图形排列就完成了
- 接着我们初始化出许许多多个这样的容器,每个容器对应一种3D排列图形(可以用数学表达式、也可以将元素直接贴到某个大的3D图形上)——table、sphere、broken、shuffle…
- 接下来一个至关重要的问题是,如何实现3D图形排列的变换?这里我们使用Three.js提供的一个渐变类THREE.Tween——轻易实现了非常丝滑的变换效果!
代码结构
▶ 变量声明
var table |
列表,存放ASCII码表及其信息 |
var scene, camera, renderer |
场景、相机、渲染器 |
var controls |
控件 |
var objects = [] |
装有128个3D元素的容器 |
var targets = { table: [], sphere: [], ... } |
许许多多种3D图形排列方式 |
▶ 初始化
initSCRC(); |
初始化场景、相机、渲染器、控件 |
initFirst(); |
初始化128个元素,THREE成3D对象,装进容器;并进行第一次的展示 |
initGraph(); |
初始化各种3D图形排列 |
initButton(); |
初始化按钮,为每个按钮绑定一个事件(一种3D图形排列) |
initOthers(); |
初始化其他杂事 |
▶ 关键函数
init(){...} |
初始化 |
function transform(targets, duration){...} |
图形转换 |
function animate(){...} |
动画递归 |
unction render(){...} |
渲染 |
完整代码
代码可直接运行。结构非常清晰。注释非常非常非常详细。
▽▽▽ ascii.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<title>Ascii&Animate</title>
<link rel="stylesheet" type="text/css" href="acsii.css" />
<script src="loli/three.js"></script>
<script src="loli/CSS3DRenderer.js"></script>
<script src="loli/TrackballControls.js"></script>
<script src="loli/tween.min.js"></script>
<script src="ascii.js"></script>
</head>
<body>
<div id="container"></div>
<div id="menu">
<!-- <button id="auto">AUTO</button> -->
<button id="table">TABLE</button>
<button id="sphere">SPHERE</button>
<button id="helix">HELIX</button>
<button id="grid">GRID</button>
<button id="broken">BROKEN</button>
<button id="shuffle">SHUFFLE</button>
<button id="gather">GATHER</button>
<button id="boom">BOOM</button>
</div>
<script>
init();
animate();
</script>
</body>
</html>
▽▽▽ ascii.css
html,
body {
height: 100%;
}
body {
background-color: #000000;
margin: 0;
font-family: Helvetica, sans-serif;
overflow: hidden;
}
#menu {
position: absolute;
bottom: 20px;
width: 100%;
text-align: center;
}
.element {
width: 120px;
height: 160px;
box-shadow: 0px 0px 12px rgba(0, 255, 255, 0.5);
border: 1px solid rgba(127, 255, 255, 0.25);
text-align: center;
cursor: default;
}
.element:hover {
box-shadow: 0px 0px 12px rgba(0, 255, 255, 0.75);
border: 1px solid rgba(127, 255, 255, 0.75);
}
.element .number {
position: absolute;
top: 20px;
right: 20px;
font-size: 12px;
font-weight: bold;
color: rgba(255, 255, 255, 0.75);
}
.element .symbol {
position: absolute;
top: 45px;
left: 0;
right: 0;
font-size: 50px;
font-weight: bold;
color: rgba(255, 255, 255, 0.75);
text-shadow: 0 0 10px rgba(0, 255, 255, 0.95);
}
.element .details {
position: absolute;
bottom: 15px;
left: 0;
right: 0;
font-size: 12px;
color: rgba(127, 255, 255, 0.75);
}
button {
color: rgba(127, 255, 255, 0.75);
background: transparent;
outline: 1px solid rgba(127, 255, 255, 0.75);
border: 0;
padding: 5px 10px;
cursor: pointer;
}
button:hover {
background-color: rgba(0, 255, 255, 0.5);
}
button:active {
background-color: rgba(0, 255, 255, 0.75);
}
▽▽▽ ascii.js
// ascii要展示的信息————符号、16进制、二进制
var table = [
"NUL", "00", "00000000", 1, 1,
"SOH", "01", "00000001", 2, 1,
"STX", "02", "00000010", 3, 1,
"ETX", "03", "00000011", 4, 1,
"EOT", "04", "00000100", 5, 1,
"ENQ", "05", "00000101", 6, 1,
"ACK", "06", "00000110", 7, 1,
"BEL", "07", "00000111", 8, 1,
"BS", "08", "00001000", 9, 1,
"HT", "09", "00001001", 10, 1,
"LF", "0A", "00001010", 11, 1,
"VT", "0B", "00001011", 12, 1,
"FF", "0C", "00001100", 13, 1,
"CR", "0D", "00001101", 14, 1,
"SO", "0E", "00001110", 15, 1,
"SI", "0F", "00001111", 16, 1,
"DLE", "10", "00010000", 17, 1,
"DCI", "11", "00010001", 18, 1,
"DC2", "12", "00010010", 1, 2,
"DC3", "13", "00010011", 2, 2,
"DC4", "14", "00010100", 3, 2,
"NAK", "15", "00010101", 4, 2,
"SYN", "16", "00010110", 5, 2,
"TB", "17", "00010111", 6, 2,
"CAN", "18", "00011000", 7, 2,
"EM", "19", "00011001", 8, 2,
"SUB", "1A", "00011010", 9, 2,
"ESC", "1B", "00011011", 10, 2,
"FS", "1C", "00011100", 11, 2,
"GS", "1D", "00011101", 12, 2,
"RS", "1E", "00011110", 13, 2,
"US", "1F", "00011111", 14, 2,
"spa", "20", "00100000", 15, 2,
"!", "21", "00100001", 16, 2,
'"', "22", "00100010", 17, 2,
"#", "23", "00100011", 18, 2,
"$", "24", "00100100", 1, 3,
"%", "25", "00100101", 2, 3,
"&", "26", "00100110", 3, 3,
"'", "27", "00100111", 4, 3,
"(", "28", "00101000", 5, 3,
")", "29", "00101001", 6, 3,
"*", "2A", "00101010", 7, 3,
"+", "2B", "00101011", 8, 3,
",", "2C", "00101100", 9, 3,
"-", "2D", "00101101", 10, 3,
".", "2E", "00101110", 11, 3,
"/", "2F", "00101111", 12, 3,
"0", "30", "00110000", 13, 3,
"1", "31", "00110001", 14, 3,
"2", "32", "00110010", 15, 3,
"3", "33", "00110011", 16, 3,
"4", "34", "00110100", 17, 3,
"5", "35", "00110101", 18, 3,
"6", "36", "00110110", 1, 4,
"7", "37", "00110111", 2, 4,
"8", "38", "00111000", 3, 4,
"9", "39", "00111001", 4, 4,
":", "3A", "00111010", 5, 4,
";", "3B", "00111011", 6, 4,
"<", "3C", "00111100", 7, 4,
"=", "3D", "00111101", 8, 4,
">", "3E", "00111110", 9, 4,
"?", "3F", "00111111", 10, 4,
"@", "40", "01000000", 11, 4,
"A", "41", "01000001", 12, 4,
"B", "42", "01000010", 13, 4,
"C", "43", "01000011", 14, 4,
"D", "44", "01000100", 15, 4,
"E", "45", "01000101", 16, 4,
"F", "46", "01000110", 17, 4,
"G", "47", "01000111", 18, 4,
"H", "48", "01001000", 1, 5,
"I", "49", "01001001", 2, 5,
"j", "4A", "01001010", 3, 5,
"K", "4B", "01001011", 4, 5,
"L", "4C", "01001100", 5, 5,
"M", "4D", "01001101", 6, 5,
"N", "4E", "01001110", 7, 5,
"O", "4F", "01001111", 8, 5,
"P", "50", "01010000", 9, 5,
"Q", "51", "01010001", 10, 5,
"R", "52", "01010010", 11, 5,
"S", "53", "01010011", 12, 5,
"T", "54", "01010100", 13, 5,
"U", "55", "01010101", 14, 5,
"V", "56", "01010110", 15, 5,
"W", "57", "01010111", 16, 5,
"X", "58", "01011000", 17, 5,
"Y", "59", "01011001", 18, 5,
"Z", "5A", "01011010", 1, 6,
"[", "5B", "01011011", 2, 6,
"/", "5C", "01011100", 3, 6,
"]", "5D", "01011101", 4, 6,
"^", "5E", "01011110", 5, 6,
"_", "5F", "01011111", 6, 6,
"`", "60", "01100000", 7, 6,
"a", "61", "01100001", 8, 6,
"b", "62", "01100010", 9, 6,
"c", "63", "01100011", 10, 6,
"d", "64", "01100100", 11, 6,
"e", "65", "01100101", 12, 6,
"f", "66", "01100110", 13, 6,
"g", "67", "01100111", 14, 6,
"h", "68", "01101000", 15, 6,
"i", "69", "01101001", 16, 6,
"j", "6A", "01101010", 17, 6,
"k", "6B", "01101011", 18, 6,
"l", "6C", "01101100", 1, 7,
"m", "6D", "01101101", 2, 7,
"n", "6E", "01101110", 3, 7,
"o", "6F", "01101111", 4, 7,
"p", "70", "01110000", 5, 7,
"q", "71", "01110001", 6, 7,
"r", "72", "01110010", 7, 7,
"s", "73", "01110011", 8, 7,
"t", "74", "01110100", 9, 7,
"u", "75", "01110101", 10, 7,
"v", "76", "01110110", 11, 7,
"w", "77", "01110111", 12, 7,
"x", "78", "01111000", 13, 7,
"y", "79", "01111001", 14, 7,
"z", "7A", "01111010", 15, 7,
"{", "7B", "01111011", 16, 7,
"|", "7C", "01111100", 17, 7,
"}", "7D", "01111101", 18, 7,
"~", "7E", "01111110", 1, 8,
"DEL", "7F", "01111111", 2, 8
];
// 场景、相机、渲染器————Three.js老三样
var scene, camera, renderer;
// 控件
var controls;
// 所有元素的集合
var objects = [];
// 每个key都对应一个上面的objects[]
var targets = { table: [], sphere: [], helix: [], grid: [], broken: [], shuffle: [], gather: [], boom: [] };
/**
* 初始化
*/
function init() {
initSCRC(); // 初始化场景(Scene)、相机(Camera)、渲染器(Renderer),以及控件(Controls)
initFirst(); // 初始化128个元素,THREE成3D对象,装进容器;并进行第一次的展示
initGraph(); // 初始化各种3D图形排列
initButton(); // 初始化按钮,为每个按钮绑定一个事件(一种3D图形排列)
initOthers(); // 初始化其他杂事
}
/**
* 初始化场景(Scene)、相机(Camera)、渲染器(Renderer)
* 初始化控件(Controls)
*/
function initSCRC() {
// 初始化场景
scene = new THREE.Scene();
// 初始化相机
camera = new THREE.PerspectiveCamera(40, window.innerWidth, window.innerHeight, 1, 10000);
camera.position.z = 3000;
// 初始化渲染器
renderer = new THREE.CSS3DRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('container').appendChild(renderer.domElement);
// 初始化控件
controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.rotateSpeed = 0.5;
controls.minDistance = 500;
controls.maxDistance = 6000;
controls.staticMoving = true;
controls.addEventListener('change', render);
}
/**
* 初始化128个元素,并进行第一次的展示
* 即根据信息第一次生成所有的元素,并附加上数字、符号、细节...THREE成3D对象后装进Objects集合里
*/
function initFirst() {
for (var i = 0; i < table.length; i += 5) {
//【Step1】新建element元素
var element = document.createElement('div');
element.className = 'element';
element.style.backgroundColor = 'rgba(0,127,127,' + (Math.random() * 0.5 + 0.25) + ')';
//【Step2.1】附加数字
var number = document.createElement('div');
number.className = 'number';
number.textContent = i / 5;
element.appendChild(number);
//【Step2.2】附加符号
var symbol = document.createElement('div');
symbol.className = 'symbol';
symbol.textContent = table[i];
element.appendChild(symbol);
//【Step2.3】附加细节
var details = document.createElement('div');
details.className = 'details';
details.innerHTML = table[i + 1] + '<br>' + table[i + 2];
element.appendChild(details);
//【Step3】Three成3D对象
var object = new THREE.CSS3DObject(element);
object.position.x = Math.random() * 4000 - 2000;
object.position.y = Math.random() * 4000 - 2000;
object.position.z = Math.random() * 4000 - 2000;
//【Step4.1】加到到场景中(展示出来)
scene.add(object);
//【Step4.2】加到元素集合中
objects.push(object);
}
}
/**
* 初始化各种3D图形排列
* 即事先计算并保存在各种图形中元素的位置(position),放在targets字典中,随用随取
*/
function initGraph() {
/**
* 表格(Table)
*/
for (var i = 0; i < objects.length; i++) {
var object = new THREE.Object3D();
object.position.x = (table[5 * i + 3] * 140) - 1330;
object.position.y = 990 - (table[5 * i + 4] * 180);
targets.table.push(object);
}
/**
* 球体(Sphere)
*/
var vector = new THREE.Vector3();
var spherical = new THREE.Spherical();
for (var i = 0; i < objects.length; i++) {
var object = new THREE.Object3D();
var l = objects.length;
var phi = Math.acos(-1 + (2 * i) / l);
var theta = Math.sqrt(l * Math.PI) * phi;
spherical.set(800, phi, theta);
object.position.setFromSpherical(spherical);
vector.copy(object.position).multiplyScalar(2);
object.lookAt(vector);
targets.sphere.push(object);
}
/**
* 螺旋(Helex)
*/
var vector = new THREE.Vector3();
var cylindrical = new THREE.Cylindrical();
for (var i = 0; i < objects.length; i++) {
var object = new THREE.Object3D();
var theta = i * 0.175 + Math.PI;
var y = - (i * 8) + 450;
cylindrical.set(900, theta, y);
object.position.setFromCylindrical(cylindrical);
vector.x = object.position.x * 2;
vector.y = object.position.y;
vector.z = object.position.z * 2;
object.lookAt(vector);
targets.helix.push(object);
}
/**
* 网格(Grid)
*/
for (var i = 0; i < objects.length; i++) {
var object = new THREE.Object3D();
object.position.x = ((i % 5) * 400) - 800;
object.position.y = (- (Math.floor(i / 5) % 5) * 400) + 800;
object.position.z = (Math.floor(i / 25)) * 1000 - 2000;
targets.grid.push(object);
}
/**
* 崩塌(Broken)
*/
for (var i = 0; i < objects.length; i++) {
var object = new THREE.Object3D();
object.position.y = -2500 - i * i;
object.position.z = 0;
targets.broken.push(object);
}
/**
* 混乱(Shuffle)
*/
for (var i = 0; i < objects.length; i++) {
var object = new THREE.Object3D();
object.position.x = Math.random() * 4000 - 2000;
object.position.y = Math.random() * 4000 - 2000;
object.position.z = Math.random() * 4000 - 2000;
targets.shuffle.push(object);
}
/**
* 归一(Gather)
*/
for (var i = 0; i < objects.length; i++) {
var object = new THREE.Object3D();
object.position.z = 1000;
targets.gather.push(object);
}
/**
* 爆炸(Boom)
*/
for (var i = 0; i < objects.length; i++) {
var object = new THREE.Object3D();
// object.position.x =
// object.position.y =
object.position.z = 10000;
targets.boom.push(object);
}
}
/**
* 初始化按钮
* 即为每个按钮绑定事件
*/
function initButton() {
document.getElementById('table').addEventListener('click', function (event) {
transform(targets.table, 2000);
}, false);
document.getElementById('sphere').addEventListener('click', function (event) {
transform(targets.sphere, 2000);
}, false);
document.getElementById('helix').addEventListener('click', function (event) {
transform(targets.helix, 2000);
}, false);
document.getElementById('grid').addEventListener('click', function (event) {
transform(targets.grid, 2000);
}, false);
document.getElementById('broken').addEventListener('click', function (event) {
transform(targets.broken, 2000);
}, false);
document.getElementById('shuffle').addEventListener('click', function (event) {
transform(targets.shuffle, 2000);
}, false);
document.getElementById('gather').addEventListener('click', function (event) {
transform(targets.gather, 2000);
}, false);
document.getElementById('boom').addEventListener('click', function (event) {
transform(targets.boom, 2000);
}, false);
}
/**
* 初始化其他
* 做一些其他的杂事————比如处理窗口缩放事件,自动从First混乱态转换为Table表格
*/
function initOthers() {
window.addEventListener('resize', onWindowResize, false);
transform(targets.table, 2000);
}
/**
* 图形转换
*/
function transform(targets, duration) {
// 使用到了THREE渐变类
TWEEN.removeAll();
for (var i = 0; i < objects.length; i++) {
var object = objects[i]; // 当前3D对象
var target = targets[i]; // 目标3D对象
// 位置
new TWEEN.Tween(object.position)
.to({ x: target.position.x, y: target.position.y, z: target.position.z }, Math.random() * duration + duration)
.easing(TWEEN.Easing.Exponential.InOut)
.start();
// 角度
new TWEEN.Tween(object.rotation)
.to({ x: target.rotation.x, y: target.rotation.y, z: target.rotation.z }, Math.random() * duration + duration)
.easing(TWEEN.Easing.Exponential.InOut)
.start();
}
// 别忘了渲染!!!
new TWEEN.Tween(this)
.to({}, duration * 2)
.onUpdate(render)
.start();
}
/**
* 窗口缩放时的回调函数
*/
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
render();
}
/**
* 动画刷新
*/
function animate() {
requestAnimationFrame(animate);
TWEEN.update();
controls.update();
}
/**
* 渲染
*/
function render() {
renderer.render(scene, camera);
}
E N D END END
本文地址:https://blog.csdn.net/m0_46202073/article/details/110384845
上一篇: 基于注解的全局异常处理
下一篇: Javascript的~(波浪号)用法