HTML
导入代码模板:
/*! Reticulum - v1.0.12 - 2015-08-17
* https://gqpbj.github.io/examples/basic.html
*
* Copyright (c) 2015 Godfrey Q;
* Licensed under the MIT license */
var Reticulum = (function() {
var INTERSECTED = null;
var collisionList = [];
var raycaster;
var vector;
var clock;
var reticle = {};
var fuse = {};
var frustum;
var cameraViewProjectionMatrix;
//Settings from user
var settings = {
camera: null, //Required
proximity: false
};
//Vibrate
var vibrate = navigator.vibrate ? navigator.vibrate.bind(navigator) : function() {};
//Fuse
fuse.initiate = function(options) {
var parameters = options || {};
this.visible = parameters.visible !== false; //default to true;
this.duration = parameters.duration || 2.5;
this.vibratePattern = parameters.vibrate || 100;
this.color = parameters.color || 0x000000;
this.innerRadius = parameters.innerRadius || reticle.innerRadiusTo;
this.outerRadius = parameters.outerRadius || reticle.outerRadiusTo;
this.phiSegments = 3;
this.thetaSegments = 32;
this.thetaStart = Math.PI / 2;
//var geometry = new THREE.CircleGeometry( reticle.outerRadiusTo, 32, Math.PI/2, 0 );
var geometry = new THREE.RingGeometry(this.innerRadius, this.outerRadius, this.thetaSegments, this.phiSegments, this.thetaStart, 0);
//Make Mesh
this.mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
color: this.color,
side: THREE.BackSide,
fog: false
}));
//Set mesh visibility
this.mesh.visible = this.visible;
//Change position and rotation of fuse
this.mesh.position.z = 0.0001; // Keep in front of reticle
this.mesh.rotation.y = 180 * (Math.PI / 180); //Make it clockwise
//Add to reticle
reticle.mesh.add(this.mesh);
//geometry.dispose();
};
fuse.out = function() {
this.active = false;
this.mesh.visible = false;
this.update(0);
}
fuse.over = function() {
this.active = true;
this.update(0);
this.mesh.visible = this.visible;
}
fuse.update = function(elapsed) {
if (!this.active) return;
//--RING
var gazedTime = elapsed / this.duration;
var thetaLength = gazedTime * (Math.PI * 2);
var vertices = this.mesh.geometry.vertices;
var radius = this.innerRadius;
var radiusStep = ((this.outerRadius - this.innerRadius) / this.phiSegments);
var count = 0;
for (var i = 0; i < this.phiSegments + 1; i++) {
for (var o = 0; o < this.thetaSegments + 1; o++) {
var vertex = vertices[count];
var segment = this.thetaStart + o / this.thetaSegments * thetaLength;
vertex.x = radius * Math.cos(segment);
vertex.y = radius * Math.sin(segment);
count++;
}
radius += radiusStep;
}
this.mesh.geometry.verticesNeedUpdate = true;
//Disable fuse if reached 100%
if (gazedTime >= 1) {
this.active = false;
}
//--RING EOF
}
//Reticle
reticle.initiate = function(options) {
var parameters = options || {};
this.active = true;
this.visible = parameters.visible !== false; //default to true;
this.far = parameters.far || settings.camera.far - 10.0;
this.color = parameters.color || 0xcc0000;
this.innerRadius = parameters.innerRadius || 0.0001;
this.outerRadius = parameters.outerRadius || 0.003;
this.worldPosition = new THREE.Vector3();
this.ignoreInvisible = parameters.ignoreInvisible !== false; //default to true;
//Hover
this.innerRadiusTo = parameters.hover.innerRadius || 0.02;
this.outerRadiusTo = parameters.hover.outerRadius || 0.024;
this.colorTo = parameters.hover.color || this.color;
this.vibratePattern = parameters.hover.vibrate || 50;
this.hit = false;
//Animation options
this.speed = parameters.hover.speed || 5;
this.moveSpeed = 0;
//Geometry
var geometry = new THREE.RingGeometry(this.innerRadius, this.outerRadius, 32, 3, 0, Math.PI * 2);
var geometryScale = new THREE.RingGeometry(this.innerRadiusTo, this.outerRadiusTo, 32, 3, 0, Math.PI * 2);
//Add Morph Targets for scale animation
geometry.morphTargets.push({
name: "target1",
vertices: geometryScale.vertices
});
//Make Mesh
this.mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
color: this.color,
morphTargets: true,
fog: false
}));
this.mesh.visible = this.visible;
//set depth and scale
this.setDepthAndScale();
//Add to camera
settings.camera.add(this.mesh);
};
//Sets the depth and scale of the reticle - reduces eyestrain and depth issues
reticle.setDepthAndScale = function(depth) {
var crosshair = this.mesh;
var z = Math.abs(depth || this.far); //Default to user far setting
var cameraZ = settings.camera.position.z;
//Force reticle to appear the same size - scale
//https://answers.unity3d.com/questions/419342/make-gameobject-size-always-be-the-same.html
var scale = Math.abs(cameraZ - z) - Math.abs(cameraZ);
//Set Depth
crosshair.position.x = 0;
crosshair.position.y = 0;
crosshair.position.z = THREE.Math.clampBottom(z, settings.camera.near + 0.1) * -1;
//Set Scale
crosshair.scale.set(scale, scale, scale);
};
reticle.update = function(delta) {
//If not active
if (!this.active) return;
var accel = delta * this.speed;
if (this.hit) {
this.moveSpeed += accel;
this.moveSpeed = Math.min(this.moveSpeed, 1);
} else {
this.moveSpeed -= accel;
this.moveSpeed = Math.max(this.moveSpeed, 0);
}
//Morph
this.mesh.morphTargetInfluences[0] = this.moveSpeed;
};
var initiate = function(camera, options) {
//Update Settings:
if (options) {
settings.camera = camera; //required
settings.proximity = options.proximity || settings.proximity;
options.reticle = options.reticle || {};
options.fuse = options.fuse || {};
}
//Raycaster Setup
raycaster = new THREE.Raycaster();
vector = new THREE.Vector2(0, 0);
//Proximity Setup
if (settings.proximity) {
frustum = new THREE.Frustum();
cameraViewProjectionMatrix = new THREE.Matrix4();
}
//Clock Setup
clock = new THREE.Clock(true);
//Initiate Reticle
reticle.initiate(options.reticle);
//Initiate Fuse
fuse.initiate(options.fuse);
};
var proximity = function() {
var camera = settings.camera;
var showReticle = false;
//Use frustum to see if any targetable object is visible
//https://stackoverflow.com/questions/17624021/determine-if-a-mesh-is-visible-on-the-viewport-according-to-current-camera
camera.updateMatrixWorld();
camera.matrixWorldInverse.getInverse(camera.matrixWorld);
cameraViewProjectionMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
frustum.setFromMatrix(cameraViewProjectionMatrix);
for (var i = 0, l = collisionList.length; i < l; i++) {
var newObj = collisionList[i];
if (!newObj.gazeable) {
continue;
}
if (reticle.ignoreInvisible && !newObj.visible) {
continue;
}
if (frustum.intersectsObject(newObj)) {
showReticle = true;
break;
}
}
reticle.mesh.visible = showReticle;
};
var detectHit = function() {
try {
raycaster.setFromCamera(vector, settings.camera);
} catch (e) {
//Assumes PerspectiveCamera for now...
//Support for Three.js < rev70
raycaster.ray.origin.copy(settings.camera.position);
raycaster.ray.direction.set(vector.x, vector.y, 0.5).unproject(settings.camera).sub(settings.camera.position).normalize();
}
//
var intersects = raycaster.intersectObjects(collisionList);
var intersectsCount = intersects.length;
//Detect
if (intersectsCount) {
var newObj;
//Check if what we are hitting can be used
for (var i = 0, l = intersectsCount; i < l; i++) {
newObj = intersects[i].object;
//If new object is not gazeable skip it.
if (!newObj.gazeable) {
continue;
}
//If new object is invisible skip it.
if (reticle.ignoreInvisible && !newObj.visible) {
continue;
}
//No issues let use this one
break;
}
//Is it a new object?
if (INTERSECTED != newObj) {
//If old INTERSECTED i.e. not null reset and gazeout
if (INTERSECTED) {
gazeOut(INTERSECTED);
};
//Updated INTERSECTED with new object
INTERSECTED = newObj;
//Is the object gazeable?
//if (INTERSECTED.gazeable) {
//Yes
gazeOver(INTERSECTED);
//}
} else {
//Ok it looks like we are in love
gazeLong(INTERSECTED);
}
} else {
//Is the object gazeable?
//if (INTERSECTED.gazeable) {
if (INTERSECTED) {
//GAZE OUT
gazeOut(INTERSECTED);
}
//}
INTERSECTED = null;
}
};
var setColor = function(threeObject, color) {
threeObject.material.color.setHex(color);
};
var gazeOut = function(threeObject) {
threeObject.hitTime = 0;
//if(threeObject.fuse) {
fuse.out();
//}
//Change Color
setColor(reticle.mesh, reticle.color);
//
reticle.hit = false;
reticle.setDepthAndScale();
if (threeObject.ongazeout != undefined) {
threeObject.ongazeout();
}
};
var gazeOver = function(threeObject) {
//if(threeObject.fuse) {
fuse.over();
//}
threeObject.hitTime = clock.getElapsedTime();
//Change Color
setColor(reticle.mesh, reticle.colorTo);
//Vibrate
vibrate(reticle.vibratePattern);
//Does object have an action assigned to it?
if (threeObject.ongazeover != undefined) {
threeObject.ongazeover();
}
};
var gazeLong = function(threeObject) {
var distance;
var elapsed = clock.getElapsedTime();
var gazeTime = elapsed - threeObject.hitTime;
//There has to be a better way...
//Keep updating distance while user is focused on target
if (reticle.active) {
reticle.worldPosition.setFromMatrixPosition(threeObject.matrixWorld);
distance = settings.camera.position.distanceTo(reticle.worldPosition);
distance -= threeObject.geometry.boundingSphere.radius;
reticle.hit = true;
reticle.setDepthAndScale(distance);
}
//Fuse
if (gazeTime >= fuse.duration && !fuse.active) {
//Vibrate
vibrate(fuse.vibratePattern);
//Does object have an action assigned to it?
if (threeObject.ongazelong != undefined) {
threeObject.ongazelong();
}
//Reset the clock
threeObject.hitTime = elapsed;
} else {
fuse.update(gazeTime);
}
};
return {
addCollider: function(threeObject, options) {
var parameters = options || {};
threeObject.gazeable = true;
//threeObject.fuse = true;
/*threeObject.fuse = parameters.fuse !== false, //default to true;
threeObject.reticle = {
hover: {
vibrate: parameters.hover.vibrate || null,
color: parameters.hover.color || null
}
}*/
collisionList.push(threeObject);
},
removeCollider: function(threeObject) {
var index = collisionList.indexOf(threeObject);
threeObject.gazeable = false;
if (index > -1) {
collisionList.splice(index, 1);
}
},
update: function() {
var delta = clock.getDelta(); //
detectHit();
//Proximity
if (settings.proximity) {
proximity();
}
//Animation
reticle.update(delta);
},
init: function(camera, options) {
var c = camera || null;
var o = options || {};
if (!c instanceof THREE.Camera) {
console.error("ERROR: Camera was not correctly defined. Unable to initiate Reticulum.");
return;
}
initiate(c, o);
}
};
})();
// Declaration of Variable Independence
var width = window.innerWidth;
var height = window.innerHeight;
var boo = 0;
var scene, camera, renderer, controls;
var directionalLight
var skyGeo, skyMat, sky, skyTex, skyOpa;
var wall_back, wall_left, wall_right, wall_floor, wall_top,
wall_front, wall_material,
wall_geometry;
var title, titleGeo, titleMat, myString;
var titleBoo = 1;
var gridHelper_back, gridHelper_left, gridHelper_right,
gridHelper_floor, gridHelper_top, gridHelper_front, gridColor;
var storyFrame = 0;
var advanceable = 1;
var titleGeo, titleMat, title;
var cubeGeo, cubeMat, cube, cube2, cube3, cube3Mat;
var cube2opa = 1;
var hNew = 0.13,
sNew = 0.89,
lNew = 0.50;
var skyze = 1; // sky + size = skyze
var mouseX = 0,
mouseY = 0;
var wall_width = 800,
wall_height = 1000;
var grid_size = wall_width,
grid_step = wall_width / 10;
wall_width = 800;
// main
skyOpa = 1;
console.log("begin")
init();
animate();
// functions
function init() {
// create scene, camera, renderer,
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 5100);
camera2 = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2600);
camera2.position.z = 400;
renderer = new THREE.WebGLRenderer({
alpha: true
});
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
skyGeo = new THREE.SphereGeometry(5000, 10, 10);
THREE.ImageUtils.crossOrigin = "";
skyTex = THREE.ImageUtils.loadTexture('/uploads/160301/s3.jpg');
skyMat = new THREE.MeshBasicMaterial({
map: skyTex,
//color:0x16a085
})
sky = new THREE.Mesh(skyGeo, skyMat);
scene.add(sky);
sky.material.side = THREE.DoubleSide;
cubeGeo = new THREE.BoxGeometry(10, 10, 5)
cubeMat = new THREE.MeshPhongMaterial({
color: 0xff0000,
shading: THREE.FlatShading
})
cube = new THREE.Mesh(cubeGeo, cubeMat);
scene.add(cube);
cube.position.z = -50;
makeGrids();
makeWalls();
Reticulum.init(camera, {
proximity: false,
reticle: {
visible: true,
far: 1000, //Defines the reticle's resting point when no object has been targeted
color: 0x000000,
innerRadius: 0.0001,
outerRadius: 0.006,
ignoreInvisible: true,
hover: {
color: 0x000000,
innerRadius: 0.02,
outerRadius: 0.024,
speed: 3,
vibrate: 50 //Set to 0 or [] to disable
}
},
fuse: {
visible: true,
duration: 2.5,
color: 0x0,
innerRadius: 0.045,
outerRadius: 0.06,
vibrate: 100 //Set to 0 or [] to disable
}
});
Reticulum.addCollider(cube);
cube.ongazeover = function() {
// have the object react when user looks at it
this.material = new THREE.MeshPhongMaterial({
color: 0xffcc00
});
}
cube.ongazeout = function() { // have the object react when user looks away from it
this.material = new THREE.MeshPhongMaterial({
color: 0xcc0000
});
}
cube.ongazelong = function() {
// have the object react when user looks at it for a specific time
this.material = new THREE.MeshPhongMaterial({
color: 0x00cc,
transparent: true,
opacity: cube2opa
});
advanceScene();
}
scene.add(camera);
lightsOn();
checkMobile();
}
function animate() {
//camera.rotation.y +=0.01;
Reticulum.update()
requestAnimationFrame(animate);
moveThings();
if (boo) {
controls.update();
}
if (!boo) {
camera.position.x += (mouseX - camera.position.x) * 0.01;
camera.position.y += (-mouseY - camera.position.y) * 0.05;
}
if (boo) {
renderer.autoClear = false;
renderer.setViewport(width / 2, 0, width / 2, height);
renderer.render(scene, camera);
renderer.setViewport(0, 0, width / 2, height);
}
renderer.render(scene, camera);
}
//
//more FUNctions below
/
function moveThings() {
if (!advanceable) {
cube.rotation.y += 0.1;
}
if (storyFrame == 1) {
var posStep = 10;
var opaStep = 0.001;
wall_material.opacity -= opaStep;
wall_back.position.z -= posStep;
wall_left.position.x -= posStep;
wall_right.position.x += posStep;
wall_front.position.z += posStep;
wall_top.position.y += posStep;
wall_floor.position.y -= posStep;
gridHelper_back.position.z -= posStep;
gridHelper_left.position.x -= posStep;
gridHelper_right.position.x += posStep;
gridHelper_front.position.z += posStep;
gridHelper_top.position.y += posStep;
gridHelper_floor.position.y -= posStep;
/*
Won't let me change opacity of grids, so this is a hack.
wall color HSL:0.47 0.76 0.36
grid color HSL: 0.13 0.89 0.50*/
var hStep = (0.47 - 0.13) / 100;
var sStep = (0.76 - 0.89) / 100;
var lStep = (0.36 - 0.50) / 100;
hNew += hStep;
sNew += sStep;
lNew += lStep;
gridColor.setHSL(hNew, sNew, lNew);
gridHelper_back.setColors(gridColor, gridColor);
gridHelper_left.setColors(gridColor, gridColor);
gridHelper_right.setColors(gridColor, gridColor);
gridHelper_top.setColors(gridColor, gridColor);
gridHelper_floor.setColors(gridColor, gridColor);
gridHelper_front.setColors(gridColor, gridColor);
// make sure the cube is oriented correctly before making it stop spinning
if (wall_back.position.z < -2500 && Math.abs(cube.rotation.y % Math.PI) < 0.01) {
advanceable = 1;
}
if (hNew > 0.47)
scene.remove(gridHelper_back, gridHelper_front, gridHelper_left, gridHelper_right, gridHelper_top, gridHelper_floor);
}
// the universe collapses
if (storyFrame == 2) {
scene.remove(wall_back, wall_front, wall_left, wall_right, wall_top, wall_floor);
wall_geometry.dispose();
wall_material.dispose();
var scaleFactor = 0.99;
skyze = skyze * scaleFactor;
sky.scale.set(skyze, skyze, 1);
if (skyze < 0.005 && Math.abs(cube.rotation.y % Math.PI) < 0.01) {
scene.remove(sky);
skyMat.dispose();
skyTex.dispose();
skyGeo.dispose();
advanceable = 1;
}
}
if (storyFrame == 3) {
if (titleBoo) {
myString = "BRAINS and SPACE";
writeTitle();
titleBoo = 0;
advanceable = 1;
}
}
}
function advanceScene() {
if (advanceable) {
storyFrame++;
advanceable = 0;
} else {}
console.log(storyFrame);
}
function writeTitle(thisString) {
titleGeo = new THREE.TextGeometry(myString, {
size: 10,
height: 10
})
titleMat = new THREE.MeshBasicMaterial({
color: 0xff
});
title = new THREE.Mesh(titleGeo, titleMat);
scene.add(title);
title.position.z = -100;
title.position.x = -100;
title.position.y = 50;
Reticulum.addCollider(title);
title.ongazeover = function() {
// have the object react when user looks at it
this.material = new THREE.MeshBasicMaterial({
color: 0xffcc00
});
}
title.ongazeout = function() { // have the object react when user looks away from it
this.material = new THREE.MeshBasicMaterial({
color: 0xcc0000
});
}
title.ongazelong = function() {
// have the object react when user looks at it for a specific time
this.material = new THREE.MeshBasicMaterial({
color: 0x00cc
});
}
}
function makeWalls() {
wall_geometry = new THREE.PlaneGeometry(wall_width * 2, wall_width * 2, 1);
wall_material = new THREE.MeshBasicMaterial({
transparent: true,
color: 0x16a085
});
wall_material.side = THREE.DoubleSide;
// breathe life into objects
wall_back = new THREE.Mesh(wall_geometry, wall_material);
wall_left = new THREE.Mesh(wall_geometry, wall_material);
wall_right = new THREE.Mesh(wall_geometry, wall_material);
wall_floor = new THREE.Mesh(wall_geometry, wall_material);
wall_top = new THREE.Mesh(wall_geometry, wall_material);
wall_front = new THREE.Mesh(wall_geometry, wall_material);
scene.add(wall_back);
scene.add(wall_left);
scene.add(wall_right);
scene.add(wall_floor);
scene.add(wall_top);
scene.add(wall_front);
wall_left.rotation.y = Math.PI / 2;
wall_left.position.x -= wall_width;
// wall_left.position.z = wall_width/2 - 10;
wall_right.rotation.y = -Math.PI / 2;
wall_right.position.x += wall_width;
//wall_right.position.z = wall_width/2 - 10;
wall_floor.rotation.x = -Math.PI / 2;
wall_floor.position.y = -wall_width;
//wall_floor.position.z = wall_width/2 - 10 - 10 -10 -10 -10 -10 -10;
wall_top.rotation.x = Math.PI / 2;
wall_top.position.y = wall_width;
//wall_top.position.z = wall_width/2 - 10 - 10 -10 -10 -10 -10 -10;
wall_front.position.z = wall_width;
wall_front.rotation.x = Math.PI;
wall_back.position.z -= wall_width + 1;
}
function makeGrids() {
gridColor = new THREE.Color(0xf1c40f);
gridHelper_back = new THREE.GridHelper(grid_size, grid_step);
gridHelper_back.position.z = -grid_size;
gridHelper_back.rotation.x = Math.PI / 2;
gridHelper_back.setColors(gridColor, gridColor);
scene.add(gridHelper_back);
gridHelper_left = new THREE.GridHelper(grid_size, grid_step);
gridHelper_left.setColors(gridColor, gridColor);
gridHelper_left.rotation.z = Math.PI / 2;
gridHelper_left.position.x -= wall_width - 1;
scene.add(gridHelper_left);
gridHelper_right = new THREE.GridHelper(grid_size, grid_step);
gridHelper_right.setColors(gridColor, gridColor);
gridHelper_right.rotation.z = -Math.PI / 2;
gridHelper_right.position.x += wall_width - 1;
scene.add(gridHelper_right);
gridHelper_floor = new THREE.GridHelper(grid_size, grid_step);
gridHelper_floor.setColors(gridColor, gridColor);
gridHelper_floor.position.y -= wall_width - 1;
scene.add(gridHelper_floor);
gridHelper_top = new THREE.GridHelper(grid_size, grid_step);
gridHelper_top.setColors(gridColor, gridColor);
gridHelper_top.position.y = wall_width - 1;
scene.add(gridHelper_top);
gridHelper_front = new THREE.GridHelper(grid_size, grid_step);
gridHelper_front.position.z = wall_width - 1;
gridHelper_front.rotation.x = Math.PI / 2;
gridHelper_front.setColors(gridColor, gridColor);
scene.add(gridHelper_front);
}
function lightsOn() {
directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(0, 0, 0);
scene.add(directionalLight);
directionalLight.target = cube;
lightHelper = new THREE.DirectionalLightHelper(directionalLight, 100);
directionalLight.castShadow = true;
directionalLight.shadowMapWidth = 2048;
directionalLight.shadowMapHeight = 2048;
var d = 50;
directionalLight.shadowCameraLeft = -d;
directionalLight.shadowCameraRight = d;
directionalLight.shadowCameraTop = d;
directionalLight.shadowCameraBottom = -d;
directionalLight.shadowCameraFar = 3500;
directionalLight.shadowBias = -0.0001;
directionalLight.shadowDarkness = 0.35;
//directionalLight.shadowCameraVisible = true;
cube.castShadow = true;
cube.receiveShadow = true;
}
// controls
function checkMobile() {
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
controls = new THREE.DeviceOrientationControls(camera, true);
boo = 1;
}
}
document.addEventListener('mousemove', onDocumentMouseMove, false);
function onDocumentMouseMove(e) {
mouseX = e.clientX - width / 2;
mouseY = e.clientY - height / 2;
}
//document.addEventListener('click',advanceScene);