魔方
<!DOCTYPE html>
<html>
<head>
<title>Example 04.05 - Mesh face material</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<script type="text/javascript" src="../libs/TrackballControls.js"></script>
<script type="text/javascript" src="./libs/MagicCubeFace.js"></script>
<style>
body {
/* set margin to 0 and overflow to hidden, to go fullscreen */
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<!-- Div which will hold the Output -->
<div id="WebGL-output">
</div>
<!-- Javascript code that runs our Three.js examples -->
<script type="text/javascript">
// once everything is loaded, we run our Three.js stuff.
function init() {
var clock = new THREE.Clock();
var stats = initStats();
// create a scene, that will hold all our elements such as objects, cameras and lights.
var scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render and set the size
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = false;
// create the ground plane
var planeGeometry = new THREE.PlaneGeometry(60, 40, 1, 1);
var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
// rotate and position the plane
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 0;
plane.position.y = -7;
plane.position.z = 0;
// add the plane to the scene
scene.add(plane);
// position and point the camera to the center of the scene
camera.position.x = -40;
camera.position.y = 40;
camera.position.z = 40;
camera.lookAt(scene.position);
var trackballControls = new THREE.TrackballControls(camera);
trackballControls.rotateSpeed = 1.0;
trackballControls.zoomSpeed = 1.0;
trackballControls.panSpeed = 1.0;
trackballControls.staticMoving = true;
document.addEventListener('mousedown', onDocumentMouseDown, false);
// add subtle ambient lighting
// var ambientLight = new THREE.AmbientLight(0x0c0c0c);
// scene.add(ambientLight);
// add spotlight for the shadows
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
// add the output of the renderer to the html element
document.getElementById("WebGL-output").appendChild(renderer.domElement);
var group = new THREE.Mesh();
var cubes = [];
initGroups(3);
for (var x = 0; x < 3; x++) {
for (var y = 0; y < 3; y++) {
for (var z = 0; z < 3; z++) {
var cubeGeom = new THREE.BoxGeometry(2, 2, 2);
var cube = new THREE.Mesh(cubeGeom, createFaceMaterial() );
cube.position.set(x * 3 - 3, y * 3 - 3, z * 3 - 3);
initCubeFaces(cube, x, y, z);
cubes.push(cube);
group.add(cube);
}
}
}
console.log(groupsLog);
// group.rotation.y = 0.5 * Math.PI;
// call the render function
scene.add(group);
var step = 0;
var controls = new function () {
this.rotationSpeed = 0.02;
this.numberOfObjects = scene.children.length;
};
var gui = new dat.GUI();
gui.add(controls, 'rotationSpeed', 0, 0.5);
render();
function render() {
stats.update();
// group.rotation.y = step += controls.rotationSpeed;
// group.rotation.x = step;
// group.rotation.z = step;
var delta = clock.getDelta();
trackballControls.update(delta);
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
function createFaceMaterial() {
// add all the rubik cube elements
var mats = [];
var color = "#009e60";
//后前 上下 右左
mats.push(new THREE.MeshBasicMaterial({color: 0x009e60})); //green
mats.push(new THREE.MeshBasicMaterial({color: 0x0051ba})); //blue, green opposite blue
mats.push(new THREE.MeshBasicMaterial({color: 0xffd500}));
mats.push(new THREE.MeshBasicMaterial({color: 0xffffff}));
mats.push(new THREE.MeshBasicMaterial({color: 0xC41E3A}));
mats.push(new THREE.MeshBasicMaterial({color: 0xff5800}));
mats.forEach(obj => {
// obj.side = THREE.BackSide;
obj.side = THREE.FrontSide;
obj.transparent = true;
// obj.opacity = 0.5;
});
var faceMaterial = new THREE.MeshFaceMaterial(mats);
return faceMaterial;
}
function onDocumentMouseDown(event) {
var vector = new THREE.Vector3(( event.clientX / window.innerWidth ) * 2 - 1, -( event.clientY / window.innerHeight ) * 2 + 1, 0.5);
vector = vector.unproject(camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects(cubes);
if (intersects.length > 0) {
var selectedObj = intersects[0];
console.log("faceIndex: " + selectedObj.faceIndex);
// console.log(selectedObj.object.name);
// console.log(selectedObj);
selectedObj.object.material.materials.forEach( m => m.opacity = 0.8);
}
}
}
window.onload = init;
</script>
</body>
</html>
./libs/MagicCubeFace.js
var colorToIntMap = {yellow:0, white:1, blue:2, green:3, orange:4, red:5};
var intToColorArr = ["yellow", "white", "blue", "green", "orange", "red"];
//上黄下白 前蓝后绿 左橙右红,
var groups = [[], [], []];
var groupsLog = [[], [], []];
var axis = {
X:0,
Y:1,
Z:2
};
var opTypeEnum = {
CLOCKWISE: 0,
COUNTERCLOCKWISE: 1,
CLOCKWISE_DOUBLE: 2
};
var lvl = 2;
function initGroups(level) {
lvl = level;
for (var i = 0; i < level; i++) {
groups[0].push([]);
groups[1].push([]);
groups[2].push([]);
groupsLog[0].push([]);
groupsLog[1].push([]);
groupsLog[2].push([]);
}
}
var MagicCubeFace = function(originFaceDir, originLocation) {
var that = {
color: intToColorArr[originFaceDir],
faceDir: originFaceDir,
location: originLocation,
getOriginFaceDir: function () {
return originFaceDir;
},
getOriginFaceLoc: function () {
return originFaceLoc;
},
setFaceDirAndLocation: function(dir, location) { //还没有燃尽返回true
that.faceDir = dir;
that.location = location;
}
};
return that;
}
var CubeFaces = function (colorAndCodeArr) {
//从Three.js立方体的面索引找颜色编号, 颜色编号{yellow:0, white:1, blue:2, green:3, orange:4, red:5}
var faceIndexToIntArr = [3, 3, 2, 2, 0, 0, 1, 1, 5, 5, 4, 4];
var that = {};
colorAndCodeArr.forEach(obj => {
var colorIndex = intToColorArr[obj.color];
that[colorIndex] = MagicCubeFace(colorIndex, obj.code);
});
that.getCubeFaceByFaceIndex = function (threejsFaceIndex) {
var colorIndex = faceIndexToIntArr[threejsFaceIndex];
return that[colorIndex];
};
return that;
}
//axis is int type, x axis is 0, y axis is 1, z axis is 2
function rotateLayer(axis, index, operationType) {
rotateArr(groups[axis][index], lvl, operationType);
groups[2][0][index],groups[2][1][index],groups[2][2][index]
}
function rotateArr(arr, colNum, operationType) {
var ret = [];
var rowNum = Math.ceil(arr.length/colNum);
for (let i = 0; i < arr.length; i++) {
let row = ~~(i / colNum), col = i % colNum;
switch (operationType) {
case 0://opTypeEnum.CLOCKWISE:
ret[col * rowNum + rowNum - row - 1] = arr[i];
break;
case 1://opTypeEnum.COUNTERCLOCKWISE:
ret[(colNum - 1 - col) * rowNum + row] = arr[i];
break;
case 2://opTypeEnum.CLOCKWISE_DOUBLE:
let row2 = rowNum - 1 - row;
let col2 = colNum - 1 - col;
ret[row2 * colNum + col2] = arr[i];
break;
}
}
return ret;
}
function prettyLog(arr, colNum) {
var ret = [], sub = [], count = 1;
for (let i = 0; i < arr.length; i++, count++) {
sub.push(arr[i]);
if (count >= colNum) {
count = 0;
ret.push(sub);
console.log(sub);
sub = [];
}
}
return ret;
}
// var arr = []; for (let i = 0; i < 16; i++) { arr[i] = i;}
// prettyLog(rotateArr(arr, 8, 1), 8);
function initCubeFaces(cube, x, y, z) {
groups[axis.X][x][lvl * y + z] = cube;
groups[axis.Y][y][lvl * x + z] = cube;
groups[axis.Z][z][lvl * y + x] = cube;
cube.name = x + ", " + y + ", " + z;
groupsLog[axis.X][x][lvl * y + z] = cube.name;
groupsLog[axis.Y][y][lvl * x + z] = cube.name;
groupsLog[axis.Z][z][lvl * y + x] = cube.name;
cube.lx = x, cube.ly = y, cube.lz = z;
if (x == 0 && y == 0 && z == 0) {
cube.faceObj = CubeFaces([ {color:"blue", code:6}, {color:"orange", code:8}, {color:"white", code:0} ]);
}
if (x == 0 && y == 0 && z == 1) {
cube.faceObj = CubeFaces([ {color:"blue", code:7}, {color:"white", code:1} ]);
}
if (x == 0 && y == 0 && z == 2) {
cube.faceObj = CubeFaces([ {color:"blue", code:8}, {color:"white", code:2}, {color:"red", code:6} ]);
}
if (x == 0 && y == 1 && z == 0) {
cube.faceObj = CubeFaces([ {color:"blue", code:3}, {color:"orange", code:5}]);
}
if (x == 0 && y == 1 && z == 1) {
cube.faceObj = CubeFaces([ {color:"blue", code:4} ]);
}
if (x == 0 && y == 1 && z == 2) {
cube.faceObj = CubeFaces([ {color:"blue", code:5}, {color:"red", code:3} ]);
}
if (x == 0 && y == 2 && z == 0) {
cube.faceObj = CubeFaces([ {color:"blue", code:0}, {color:"orange", code:2}, {color:"yellow", code:6} ]);
}
if (x == 0 && y == 2 && z == 1) {
cube.faceObj = CubeFaces([ {color:"blue", code:1}, {color:"yellow", code:7} ]);
}
if (x == 0 && y == 2 && z == 2) {
cube.faceObj = CubeFaces([ {color:"blue", code:2}, {color:"yellow", code:8}, {color:"red", code:0} ]);
}
if (x == 1 && y == 0 && z == 0) {
cube.faceObj = CubeFaces([ {color:"orange", code:7}, {color:"white", code:3} ]);
}
if (x == 1 && y == 0 && z == 1) {
cube.faceObj = CubeFaces([ {color:"white", code:4} ]);
}
if (x == 1 && y == 0 && z == 2) {
cube.faceObj = CubeFaces([ {color:"white", code:5}, {color:"red", code:7} ]);
}
if (x == 1 && y == 1 && z == 0) {
cube.faceObj = CubeFaces([ {color:"orange", code:4} ]);
}
// if (x == 1 && y == 1 && z == 1) {
// cube.faceObj = null;
// }
if (x == 1 && y == 1 && z == 2) {
cube.faceObj = CubeFaces([ {color:"red", code:4} ]);
}
if (x == 1 && y == 2 && z == 0) {
cube.faceObj = CubeFaces([ {color:"orange", code:1}, {color:"yellow", code:3} ]);
}
if (x == 1 && y == 2 && z == 1) {
cube.faceObj = CubeFaces([ {color:"yellow", code:4} ]);
}
if (x == 1 && y == 2 && z == 2) {
cube.faceObj = CubeFaces([ {color:"yellow", code:5}, {color:"red", code:1} ]);
}
if (x == 2 && y == 0 && z == 0) {
cube.faceObj = CubeFaces([ {color:"green", code:8}, {color:"orange", code:6}, {color:"white", code:6} ]);
}
if (x == 2 && y == 0 && z == 1) {
cube.faceObj = CubeFaces([ {color:"green", code:7}, {color:"white", code:7} ]);
}
if (x == 2 && y == 0 && z == 2) {
cube.faceObj = CubeFaces([ {color:"green", code:6}, {color:"white", code:8}, {color:"red", code:8} ]);
}
if (x == 2 && y == 1 && z == 0) {
cube.faceObj = CubeFaces([ {color:"green", code:5}, {color:"orange", code:3}]);
}
if (x == 2 && y == 1 && z == 1) {
cube.faceObj = CubeFaces([ {color:"green", code:4} ]);
}
if (x == 2 && y == 1 && z == 2) {
cube.faceObj = CubeFaces([ {color:"green", code:3}, {color:"red", code:5} ]);
}
if (x == 2 && y == 2 && z == 0) {
cube.faceObj = CubeFaces([ {color:"green", code:2}, {color:"orange", code:0}, {color:"yellow", code:0} ]);
}
if (x == 2 && y == 2 && z == 1) {
cube.faceObj = CubeFaces([ {color:"green", code:1}, {color:"yellow", code:1} ]);
}
if (x == 2 && y == 2 && z == 2) {
cube.faceObj = CubeFaces([ {color:"green", code:0}, {color:"yellow", code:2}, {color:"red", code:2} ]);
}
}
自定义几何体
对于自定义几何体,根据顶点构造三角面时,要特别注意,凡是镜头camera 能够看到的三角面,顶点的序列必须是顺时针的,看不见的三角面的顶点序列必须是逆顺时针的,否则构造的几何体会很奇怪,并且不能产生投影
<!DOCTYPE html>
<html>
<head>
<title>Example 02.05 - Custom geometry</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<script type="text/javascript" src="../libs/TrackballControls.js"></script>
<style>
body {
/* set margin to 0 and overflow to hidden, to go fullscreen */
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<!-- Div which will hold the Output -->
<div id="WebGL-output">
</div>
<!-- Javascript code that runs our Three.js examples -->
<script type="text/javascript">
// once everything is loaded, we run our Three.js stuff.
function init() {
var clock = new THREE.Clock();
var stats = initStats();
// create a scene, that will hold all our elements such as objects, cameras and lights.
var scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render and set the size
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
// create the ground plane
var planeGeometry = new THREE.PlaneGeometry(60, 40, 1, 1);
var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
// rotate and position the plane
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 0;
plane.position.y = 0;
plane.position.z = 0;
// add the plane to the scene
scene.add(plane);
// position and point the camera to the center of the scene
camera.position.x = -20;
camera.position.y = 35;
camera.position.z = 20;
// camera.position.x = -30;
// camera.position.y = 40;
// camera.position.z = -30;
camera.lookAt(new THREE.Vector3(0, 0, 0));
var trackballControls = new THREE.TrackballControls(camera);
trackballControls.rotateSpeed = 1.0;
trackballControls.zoomSpeed = 1.0;
trackballControls.panSpeed = 1.0;
// trackballControls.noZoom=false;
// trackballControls.noPan=false;
trackballControls.staticMoving = true;
// trackballControls.dynamicDampingFactor=0.3;
// add subtle ambient lighting
// var ambientLight = new THREE.AmbientLight(0x494949);
// scene.add(ambientLight);
// add spotlight for the shadows
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, 10);
spotLight.castShadow = true;
scene.add(spotLight);
// add the output of the renderer to the html element
document.getElementById("WebGL-output").appendChild(renderer.domElement);
// call the render function
var step = 0;
var vertices = [
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(1, -1, 1),
new THREE.Vector3(1, -1, -1),
new THREE.Vector3(-1, 0, -1),
new THREE.Vector3(-1, 0, 1)
];
var faces = [
new THREE.Face3(1, 3, 5),
new THREE.Face3(0, 2, 4),
new THREE.Face3(2, 1, 5),
new THREE.Face3(1, 0, 3),
new THREE.Face3(3, 4, 5),
new THREE.Face3(0, 1, 2),
new THREE.Face3(0, 4, 3),
new THREE.Face3(2, 5, 4)
];
var geom = new THREE.Geometry();
geom.vertices = vertices;
geom.faces = faces;
geom.computeFaceNormals();
var materials = [
// new THREE.MeshLambertMaterial({opacity: 0.6, color: 0x44ff44, transparent: true}),
new THREE.MeshNormalMaterial({color: 0x000000, transparent: true, opacity: 0.8}),
new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true})
];
var mesh = THREE.SceneUtils.createMultiMaterialObject(geom, materials);
mesh.children.forEach(function (e) {
e.castShadow = true
});
// mesh.children[0].translateX(0.5);
// mesh.children[0].translateZ(0.5);
scene.add(mesh);
function addControl(x, y, z) {
var controls = {x:x, y:y, z:z};
return controls;
}
var controlPoints = [];
var half = 2.5;
controlPoints.push(addControl(-half, -half, -half));
controlPoints.push(addControl(half, -half, -half));
controlPoints.push(addControl(half, -half, half));
controlPoints.push(addControl(-half, half, -half));
controlPoints.push(addControl(-half, half, half));
controlPoints.push(addControl(half, half, half));
var gui = new dat.GUI();
gui.add(new function () {
this.clone = function () {
var clonedGeometry = mesh.children[0].geometry.clone();
var materials = [
new THREE.MeshLambertMaterial({opacity: 0.6, color: 0xff44ff, transparent: true}),
new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true})
];
var mesh2 = THREE.SceneUtils.createMultiMaterialObject(clonedGeometry, materials);
mesh2.children.forEach(function (e) {
e.castShadow = true
});
mesh2.translateX(5);
mesh2.translateZ(5);
mesh2.name = "clone";
scene.remove(scene.getChildByName("clone"));
scene.add(mesh2);
}
}, 'clone');
for (var i = 0; i < controlPoints.length; i++) {
f1 = gui.addFolder('Vertices ' + (i + 1));
f1.add(controlPoints[i], 'x', -10, 10);
f1.add(controlPoints[i], 'y', -10, 10);
f1.add(controlPoints[i], 'z', -10, 10);
}
render();
function render() {
stats.update();
var vertices = [];
for (var i = 0; i < controlPoints.length; i++) {
vertices.push(new THREE.Vector3(controlPoints[i].x, controlPoints[i].y, controlPoints[i].z));
}
mesh.children.forEach(function (e) {
e.geometry.vertices = vertices;
e.geometry.verticesNeedUpdate = true;
e.geometry.computeFaceNormals();
});
mesh.position.y = 10;
// mesh.rotation.x += 0.02;
mesh.rotation.y += 0.01;
// mesh.rotation.z += 0.02;
var delta = clock.getDelta();
trackballControls.update(delta);
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}
window.onload = init
</script>
</body>
http://xuewen.cnki.net/CJFD-ZOXE200609015.html
https://github.com/mrdoob/three.js/blob/master/src/geometries/DodecahedronGeometry.js
https://github.com/mrdoob/three.js/blob/master/src/geometries/PolyhedronGeometry.js