I've been playing with the interactive cubes and decided to add a click function, which leads to a specific link. Now I would like to add unique text on each block which is rendered as a material. For right now, I am using a single material, so I am thinking I need to create an array for all my possible materials, however whenever I reference that array in the parameters for building the mesh of my objects, I get a setHex() is undefined returned to me in the console. How can I attach a unique material to each cube?
<html lang="en">
<head>
<title>three.js webgl - interactive cubes</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
div#stats{
display:none;
}
p{
position:absolute;
top:500px;
left:500px;
}
</style>
</head>
<body>
<div><p>Hello!</p></div>
<script src="../build/three.min.js"></script>
<script src="js/renderers/Projector.js"></script>
<script src='../build/threex.dynamictexture.js'></script>
<script src="js/libs/stats.min.js"></script>
<script>
var container, stats;
var camera, scene, raycaster, renderer;
var mouse = new THREE.Vector2(), INTERSECTED;
var radius = 100, theta = 0;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
var info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
// info.innerHTML = 'three.js webgl - interactive cubes';
container.appendChild( info );
camera = new THREE.PerspectiveCamera( 100, window.innerWidth / window.innerHeight, 1, 10000 );
scene = new THREE.Scene();
var light = new THREE.DirectionalLight( 0x6699CC, 0.5 );
light.position.set( 1, 1, 1 ).normalize();
scene.add( light );
raycaster = new THREE.Raycaster();
projector = new THREE.Projector();
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0xf0f0f0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false;
container.appendChild(renderer.domElement);
var dynamicTexture = new THREEx.DynamicTexture(512,512);
var dynamicTexture2 = new THREEx.DynamicTexture(512,512);
dynamicTexture.context.font = "bold"+(0.2*512)+ "px Courier";
dynamicTexture.texture.anisotropy = renderer.getMaxAnisotropy();
dynamicTexture2.context.font = "bold"+(0.2*512)+ "px Courier";
dynamicTexture2.texture.anisotropy = renderer.getMaxAnisotropy();
dynamicTexture2.clear('blue');
dynamicTexture.drawTextCooked({
text: 'alltheletters',
lineHeight: 0.2,
})
dynamicTexture.clear('gray');
dynamicTexture.drawTextCooked({
text: 'wow text text text',
lineHeight: 0.2,
})
// var materialArray = [];
// materialArray.push(new THREE.MeshBasicMaterial({map: dynamicTexture2 }))
scene.add(object);
for ( var i = 0; i < 5; i ++ ) {
var object = new THREE.Mesh( geometry, material );
object.position.x = Math.random() * 800 - 400;
object.position.y = Math.random() * 800 - 400;
object.position.z = Math.random() * 800 - 400;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
object.scale.x = Math.random() + 0.5;
object.scale.y = Math.random() + 0.5;
object.scale.z = Math.random() + 0.5;
scene.add( object );
var geometry = new THREE.BoxGeometry( 62, 62, 62 );
var material = new THREE.MeshBasicMaterial({
map: dynamicTexture.texture,
color:Math.random() * 0xffffff,
name: "box1",
})
switch (i) {
case 0:
object.userData = {
URL: "http://google.com"
};
break;
case 1:
object.userData = {
URL: "http://yahoo.com"
};
break;
case 2:
object.userData = {
URL: "http://msn.com"
};
break;
case 3:
object.userData = {
URL: "http://engadget.com"
};
break;
case 4:
object.userData = {
URL: "http://stackoverflow.com"
};
break;
}
}
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
document.addEventListener('mousedown', onDocumentMouseDown,false);
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function onDocumentMouseDown(event) {
event.preventDefault();
var vector = new THREE.Vector3((event.clientX / window.innerWidth) * 2 -
1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5);
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position)
.normalize());
var intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
window.open(intersects[0].object.userData.URL);
}
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
theta += 0.1;
camera.position.x = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.y = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.z = radius * Math.cos( THREE.Math.degToRad( theta ) );
camera.lookAt( scene.position );
camera.updateMatrixWorld();
// find intersections
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children ), material;
// for(var p =0; p < intersects.length; p++){
// }
if (intersects.length > 0) {
if (INTERSECTED != intersects[0].object) {
if (INTERSECTED){
material = INTERSECTED.material;
if(material.emissive){
material.emissive.setHex(INTERSECTED.currentHex);
}
else{
material.color.setHex(INTERSECTED.currentHex);
}
}
INTERSECTED = intersects[0].object;
material = INTERSECTED.material;
if(material.emissive){
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
material.emissive.setHex(0xff0000);
}
else{
INTERSECTED.currentHex = material.color.getHex();
material.color.setHex(0xff0000);
}
console.log(INTERSECTED.position);
}
}
else {
if (INTERSECTED){
material = INTERSECTED.material;
if(material.emissive){
material.emissive.setHex(INTERSECTED.currentHex);
}
else
{
material.color.setHex(INTERSECTED.currentHex);
}
}
INTERSECTED = null;
}
renderer.render( scene, camera );
}
</script>
</body>
</html>
I don't think the problem is the array, its the object in the array. Try this:
var materialArray = [];
materialArray.push(new THREE.MeshBasicMaterial({
map: dynamicTexture2.texture,
color:Math.random() * 0xffffff,
name: "box1",
}));
//...
var object = new THREE.Mesh( geometry, materialArray[0] );
If you want to create multiple cubes with different materials, you'll need to create multiple objects, instead of reusing object. You can wrap all the object creation/positioning code in a function, and have the unique material be a parameter.
Related
I have a simple three.js graphics, and I tried to use the answers in this and this question to make the created plot zoomable by the mouse wheel. By using the mouse wheel I would like to zoom in to the graphics or to zoom out.
Here is the complete code: pastebin link
However, when turning the mouse wheel nothing happens, and I do not get an error message. Maybe I am missing something?
DEMO
var container, camera, scene, renderer, colors;
var selected = 0;
var selectedObject;
var objects = [];
// DOM element...
container = document.createElement('div');
document.body.appendChild(container);
// Camera...
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(0, 0, 150);
// Scene...
scene = new THREE.Scene();
scene.add(camera);
// Renderer...
renderer = new THREE.WebGLRenderer({
clearAlpha: 1
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
document.body.appendChild(renderer.domElement);
// Scatter plot...
scatterPlot = new THREE.Object3D();
scene.add(scatterPlot);
// Plot some random points...
circle = new THREE.CircleGeometry(1, 20);
colors = [];
var max = 50;
var min = -50;
for (var i = 0; i < 10; i++) {
var object = new THREE.Mesh( circle.clone(), new THREE.MeshBasicMaterial( { color: new THREE.Color('black'), opacity: 0.5 } ) );
object.position.x = Math.random() * (max - min) + min;
object.position.y = Math.random() * (max - min) + min;
object.position.z = 0;
scene.add( object );
objects.push( object );
}
animate();
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2();
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
document.addEventListener( 'mousewheel', onDocumentMouseWheel, false );
function onDocumentMouseDown( event ) {
event.preventDefault();
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 ) {
//intersects[ 0 ].object.material.color.set('red');
//intersects[ 0 ].object.geometry.scale(1.1,1.1,1.1);
if (selected === 0) {
selected = 1;
selectedObject = intersects[ 0 ].object;
selectedObject.material.color.set('red');
console.log(selectedObject.position.x);
} else {
selected = 0;
var geometry = new THREE.Geometry();
geometry.vertices.push(intersects[ 0 ].object.position);
geometry.vertices.push(selectedObject.position);
var line = new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: 0x0000ff }));
scene.add(line);
selectedObject.material.color.set('black');
}
}
}
function onWindowResize() {
camera.left = window.innerWidth / - 2;
camera.right = window.innerWidth / 2;
camera.top = window.innerHeight / 2;
camera.bottom = window.innerHeight / - 2;
camera.aspect = window.innerWidth / window.innerHeight;
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseWheel( event ) {
var fovMAX = 160;
var fovMIN = 1;
camera.fov -= event.wheelDeltaY * 0.05;
camera.fov = Math.max( Math.min( camera.fov, fovMAX ), fovMIN );
camera.projectionMatrix = new THREE.Matrix4().makePerspective(camera.fov, window.innerWidth / window.innerHeight, camera.near, camera.far);
}
body { margin: 0; }
canvas { width: 100%; height: 100% }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/54/three.js"></script>
In your code add event listener
document.addEventListener( 'mousewheel', onDocumentMouseWheel, false );
document.addEventListener( 'mousewheel', (event) => {
camera.position.z +=event.deltaY/500;
});
The mousewheel event is supported only in Webkit browsers.
For cross-browser compaitibility use the wheel event.
I have a PlaneGeometry and I want to modify the z position of the vertice hovered but I don't know how to retrieve it.
//THREE.WebGLRenderer 69
// Generating plane
var geometryPlane = new THREE.PlaneGeometry( 100, 100, 20, 10 );
for (var vertIndex = 0; vertIndex < geometryPlane.vertices.length; vertIndex++) {
geometryPlane.vertices[vertIndex].z += Math.random();
}
geometryPlane.dynamic = true;
geometryPlane.computeFaceNormals();
geometryPlane.normalsNeedUpdate = true;
var materialPlane = new THREE.MeshLambertMaterial( {
color: 0xffff00,
side: THREE.DoubleSide,
shading: THREE.FlatShading,
overdraw: 0.5,
vertexColors: THREE.FaceColors
} );
plane = new THREE.Mesh( geometryPlane, materialPlane );
plane.geometry.colorsNeedUpdate = true;
// Mouse event
container[0].addEventListener( 'mousemove', onMouseMove, false );
function onMouseMove( event ) {
var mouseX = ( event.clientX / window.innerWidth ) * 2 - 1;
var mouseY = -( event.clientY / window.innerHeight ) * 2 + 1;
var vector = new THREE.Vector3( mouseX, mouseY, camera.near );
vector.unproject( camera );
raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
if ( intersects.length > 0 ) {
// Change the z position of the selected vertice
var selectedVertice = ???
selectedVertice.position.z +=5;
}
}
If the question concerns one vertex and not the whole object maybe you can :
1- retrieve the intersection face in intersects[0].face
2- this face may contain 3 vertices (it is probably a THREE.Face3) : find the nearest vertex V with the intersection point intersects[0].point.
3- change V.z
I don't know if it works : it is an idea... ;)
You can modify the Z position of the intersected object like this
intersects[0].object.position.z+=10;
You can refer to the code snippet below for demo/code.
var container, stats;
var camera, scene, raycaster, renderer;
var mouse = new THREE.Vector2(),
INTERSECTED;
var radius = 100,
theta = 0;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
var info = document.createElement('div');
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = 'three.js webgl - interactive cubes';
container.appendChild(info);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000);
scene = new THREE.Scene();
var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 1, 1).normalize();
scene.add(light);
var geometry = new THREE.PlaneGeometry (50, 50, 50);
for (var i = 0; i < 500; i++) {
var object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
color: Math.random() * 0xffffff
}));
object.position.x = Math.random() * 800 - 400;
object.position.y = Math.random() * 800 - 400;
object.position.z = Math.random() * 800 - 400;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
object.scale.x = Math.random() + 0.5;
object.scale.y = Math.random() + 0.5;
object.scale.z = Math.random() + 0.5;
scene.add(object);
}
raycaster = new THREE.Raycaster();
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xf0f0f0);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.sortObjects = false;
container.appendChild(renderer.domElement);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild(stats.domElement);
document.addEventListener('mousemove', onDocumentMouseMove, false);
//
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onDocumentMouseMove(event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
//
function animate() {
requestAnimationFrame(animate);
render();
stats.update();
}
function render() {
// find intersections
var vector = new THREE.Vector3(mouse.x, mouse.y, 1).unproject(camera);
raycaster.set(camera.position, vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
if (INTERSECTED != intersects[0].object) {
console.log(intersects[0].point);
console.log(intersects[0].object.position);
if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
/*******************************************************************/
/// You can change the Z position like the way done below
intersects[0].object.position.z+=10;
/********************************************************************/
INTERSECTED = intersects[0].object;
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex(0xff0000);
}
} else {
if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
INTERSECTED = null;
}
renderer.render(scene, camera);
}
<script src="http://threejs.org/examples/js/libs/stats.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/three.js/r69/three.min.js"></script>
Every CSS3D sprite in my scene has a reflection, witch appears when I rotate the camera. Is there a way around this? The Reflection isn't visible on every Browser/OS - on FF on Mac it isn't visible, on Linux it is. Another problem is, even if it's not visible, it is clickable. You can use Firebug to indicate the position of the reflection by holding the mouse on the a-element in HTML-inspector.
Example:
<!DOCTYPE html>
<html>
<head>
<title>test11a</title>
<style>canvas { width: 100%; height: 100% } body {margin: 0px;overflow: hidden;}</style>
</head>
<body>
<script src="../build/three.min.js"></script>
<script src="js/renderers/CSS3DRenderer.js"></script>
<script>
var camera, scene, renderer;
var mesh;
var infoIcon;
var lon = 0;
var lat = 0;
var phi = 0;
var theta = 0;
init();
animate();
function init()
{
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
//camera.position.z = 400;
camera.target = new THREE.Vector3( 0, 0, 0 );
scene = new THREE.Scene();
htmlScene = new THREE.Scene();
var geometry = new THREE.SphereGeometry( 100, 60, 40 );
var texture = THREE.ImageUtils.loadTexture( 'textures/disturb.jpg' );
texture.anisotropy = renderer.getMaxAnisotropy();
var material = new THREE.MeshBasicMaterial( { map: texture } );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
camera.position.z = 400;
window.addEventListener( 'resize', onWindowResize, false );
//place infoIcon
/*------------*/
var imageInfoLink = document.createElement( 'a' );
imageInfoLink.setAttribute("href", "http://www.google.de");
var imageInfo = document.createElement( 'img' );
imageInfo.src = 'textures/sprite0.png';
imageInfoLink.appendChild(imageInfo);
//imageInfo.style.cursor = 'pointer';
infoIcon = new THREE.CSS3DSprite( imageInfoLink );
//infoIcon.position.x = 50;
//infoIcon.position.y = 2500;
//infoIcon.scale.x = 0.1;
//infoIcon.scale.y = 0.1;
infoIcon.position.z = -500;
htmlScene.add( infoIcon );
/*------------*/
htmlRenderer = new THREE.CSS3DRenderer();
htmlRenderer.setSize( window.innerWidth, window.innerHeight );
htmlRenderer.domElement.style.position = 'absolute';
htmlRenderer.domElement.style.top = 0;
htmlRenderer.domElement.style.zIndex = 10;
document.body.appendChild( htmlRenderer.domElement );
}
function onWindowResize()
{
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate()
{
requestAnimationFrame( animate );
rotate();
lon += 0.5;
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.Math.degToRad( 90 - lat );
theta = THREE.Math.degToRad( lon );
camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );
camera.target.y = 500 * Math.cos( phi );
camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta );
camera.lookAt( camera.target );
htmlRenderer.render( htmlScene, camera );
}
function rotate()
{
//mesh.rotation.x += 0.005;
mesh.rotation.y += 0.01;
renderer.render( scene, camera );
}
</script>
</body>
</html>
Never mind, I'm using THREE.Sprite instead
i am using threejs with my colladaLoader, and my model displays fine except for the fact that the ambient light or something related makes it dark sometimes.. the light comes and goes. with the following code, how can i disable this behavior and make is lighting constant and bright?
CODE:
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
var container, stats;
var camera, scene, renderer, objects;
var particleLight, pointLight;
var skin;
function load_model(el,model_url,type) {
window.loader = new THREE.ColladaLoader();
window.loader.options.convertUpAxis = true;
window.imageReplace = [{"name":"stock.jpg","new_image":"colors/generated_color.png"}];
window.loader.load( model_url, imageReplace, function ( collada ) {
//console.log(collada);
dae = collada.scene;
skin = collada.skins[ 0 ];
dae.scale.x = dae.scale.y = dae.scale.z = 0.040;
dae.updateMatrix();
//setMaterial(dae, new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( "/media/images/stock.jpg") } ));
window.init(el);
window.animate();
} );
window.init = function(el) {
container = document.createElement( 'div' );
el.append( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.set( 2, 2, 3 );
scene = new THREE.Scene();
// Add the COLLADA
scene.add( dae );
particleLight = new THREE.Mesh( new THREE.SphereGeometry( 4, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) );
scene.add( particleLight );
// Lights
// scene.add( new THREE.AmbientLight( 0xFFFFFF ) );
var directionalLight = new THREE.DirectionalLight(/*Math.random() * 0xffffff*/0xeeeeee );
directionalLight.position.x = Math.random() - 0.5;
directionalLight.position.y = Math.random() - 0.5;
directionalLight.position.z = Math.random() - 0.5;
directionalLight.position.normalize();
scene.add( directionalLight );
// pointLight = new THREE.PointLight( 0xffffff, 4 );
// pointLight.position = particleLight.position;
// scene.add( pointLight );
renderer = new THREE.WebGLRenderer();
if ( type =='edit')
renderer.setSize( window.innerWidth/2, window.innerHeight/2 );
else
renderer.setSize( window.innerWidth/3, window.innerHeight/3 );
container.appendChild( renderer.domElement );
// stats = new Stats();
// stats.domElement.style.position = 'absolute';
// stats.domElement.style.top = '0px';
// container.appendChild( stats.domElement );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
if ( type =='edit')
renderer.setSize( window.innerWidth/2, window.innerHeight/2 );
else
renderer.setSize( window.innerWidth/3, window.innerHeight/3 );
}
//
var t = 0;
var clock = new THREE.Clock();
window.animate = function() {
var delta = clock.getDelta();
requestAnimationFrame( animate );
if ( t > 1 ) t = 0;
if ( skin ) {
// guess this can be done smarter...
// (Indeed, there are way more frames than needed and interpolation is not used at all
// could be something like - one morph per each skinning pose keyframe, or even less,
// animation could be resampled, morphing interpolation handles sparse keyframes quite well.
// Simple animation cycles like this look ok with 10-15 frames instead of 100 ;)
for ( var i = 0; i < skin.morphTargetInfluences.length; i++ ) {
skin.morphTargetInfluences[ i ] = 0;
}
skin.morphTargetInfluences[ Math.floor( t * 30 ) ] = 1;
t += delta;
}
window.render();
//stats.update();
}
window.render = function() {
var timer = Date.now() * 0.0005;
camera.position.x = Math.cos( timer ) * 10;
camera.position.y = 2;
camera.position.z = Math.sin( timer ) * 10;
camera.lookAt( scene.position );
particleLight.position.x = Math.sin( timer * 4 ) * 3009;
particleLight.position.y = Math.cos( timer * 5 ) * 4000;
particleLight.position.z = Math.cos( timer * 4 ) * 3009;
renderer.render( scene, camera );
}
}
Your main (?) light position is defined as random like this:
directionalLight.position.x = Math.random() - 0.5;
directionalLight.position.y = Math.random() - 0.5;
directionalLight.position.z = Math.random() - 0.5;
Just use hardcoded, constant position there.
You can use ambient light too, that doesn't need position at all. You can uncomment that from your code, if you want.
Essentially, what I am saying is I want to create a tag or label that appears on top of/on the surface of an object so that the tag always faces the camera when the user clicks the object even when the object is rotated.
How do I go about doing so?
I was told to use Orthogonal camera (but I'm not sure how?) and CSS for the label (see previous post: How can I make my text labels face the camera at all times? Perhaps using sprites?)
The label in CSS and html is below. However, I want to do this for several objects as well, so I guess I can make a list of all the tags I want for each cube in this case.
CSS:
label {
vertical-align: middle;
display: table-cell;
background-color : #99FFCC;
border: 1px solid #008000;
width: 150px;
}
HTML:
<div id="Cube1">
<label>Cube 1</label>
</div>
Previous Code:
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js canvas - interactive - cubes</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<script src="js/three.min.js"></script>
<script src="js/stats.min.js"></script>
<script>
var container, stats;
var camera, scene, projector, renderer;
var projector, mouse = { x: 0, y: 0 }, INTERSECTED;
var particleMaterial;
var currentLabel = null;
var objects = [];
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
var info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = 'three.js - clickable objects';
container.appendChild( info );
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 0, 300, 500 );
scene = new THREE.Scene();
var geometry = new THREE.CubeGeometry( 100, 100, 100 );
for ( var i = 0; i < 10; i ++ ) {
var object = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, opacity: 0.5 } ) );
object.position.x = Math.random() * 800 - 400;
object.position.y = Math.random() * 800 - 400;
object.position.z = Math.random() * 800 - 400;
object.scale.x = Math.random() * 2 + 1;
object.scale.y = Math.random() * 2 + 1;
object.scale.z = Math.random() * 2 + 1;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
object.label = "Object " + i;
scene.add( object );
objects.push( object );
}
var PI2 = Math.PI * 2;
particleMaterial = new THREE.ParticleCanvasMaterial( {
color: 0x000000,
program: function ( context ) {
context.beginPath();
context.arc( 0, 0, 1, 0, PI2, true );
context.closePath();
context.fill();
}
} );
projector = new THREE.Projector();
renderer = new THREE.CanvasRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseDown( event ) {
event.preventDefault();
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
projector.unprojectVector( vector, camera );
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 ) {
if ( intersects[ 0 ].object != INTERSECTED )
{
// restore previous intersection object (if it exists) to its original color
if ( INTERSECTED ) {
INTERSECTED.material.color.setHex( INTERSECTED.currentHex ); }
// store reference to closest object as current intersection object
INTERSECTED = intersects[ 0 ].object;
// store color of closest object (for later restoration)
INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
// set a new color for closest object
INTERSECTED.material.color.setHex( 0xffff00 );
var canvas1 = document.createElement('canvas');
var context1 = canvas1.getContext('2d');
context1.font = "Bold 40px Arial";
context1.fillStyle = "rgba(255,0,0,0.95)";
context1.fillText(INTERSECTED.label, 0, 50);
// canvas contents will be used for a texture
var texture1 = new THREE.Texture(canvas1)
texture1.needsUpdate = true;
var material1 = new THREE.MeshBasicMaterial( {map: texture1, side:THREE.DoubleSide } );
material1.transparent = true;
var mesh1 = new THREE.Mesh(
new THREE.PlaneGeometry(canvas1.width, canvas1.height),
material1
);
mesh1.position = intersects[0].point;
if (currentLabel)
scene.remove(currentLabel);
scene.add( mesh1 );
currentLabel = mesh1;
}
else // there are no intersections
{
// restore previous intersection object (if it exists) to its original color
if ( INTERSECTED ) {
console.log("hello");
INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
}
// remove previous intersection object reference
// by setting current intersection object to "nothing"
INTERSECTED = null;
mesh1 = null;
mesh1.position = intersects[0].point;
scene.add( mesh1 );
}
//var particle = new THREE.Particle( particleMaterial );
//particle.position = intersects[ 0 ].point;
//particle.scale.x = particle.scale.y = 8;
//scene.add( particle );
}
/*
// Parse all the faces
for ( var i in intersects ) {
intersects[ i ].face.material[ 0 ].color.setHex( Math.random() * 0xffffff | 0x80000000 );
}
*/
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
var radius = 600;
var theta = 0;
function render() {
theta += 0.1;
camera.position.x = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.y = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.z = radius * Math.cos( THREE.Math.degToRad( theta ) );
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
</script>
</body>
Please let me know if I'm being unclear.
I'm not especially familiar with Three.js, but here's the usual steps:
Choose a point on the surface of your object you want to align the label to.
Use projector.projectVector to get an on-screen point from the in-world point.
(You may need to scale the result from NDC (-1 to 1) to canvas (0 to canvas.width) coordinates here; I'm not sure.)
Use the X and Y to set CSS absolute positioning for your label.
Here's code I wrote to do the same thing in my Cubes project (not using Three.js, but the principles are the same). It is slightly more complex because what it does is position the element so that it is next to an object represented by a set of points (which are provided to the callback passed to pointGenerator). It also tries to do sensible things when the object is out of view of the camera.
Feel free to reuse this code and adapt it to your liking.
// Position an overlay HTML element adjacent to the provided set of points.
function positionByWorld(element, keepInBounds, pointGenerator) {
var canvasStyle = window.getComputedStyle(theCanvas,null);
var canvasWidth = parseInt(canvasStyle.width, 10);
var canvasHeight = parseInt(canvasStyle.height, 10);
var elemStyle = window.getComputedStyle(element, null);
var elemWidth = parseInt(elemStyle.width, 10);
var elemHeight = parseInt(elemStyle.height, 10);
var slx = Infinity;
var sly = Infinity;
var shx = -Infinity;
var shy = -Infinity;
var toScreenPoint = vec4.create();
pointGenerator(function (x, y, z, w) {
toScreenPoint[0] = x;
toScreenPoint[1] = y;
toScreenPoint[2] = z;
toScreenPoint[3] = w;
renderer.transformPoint(toScreenPoint);
toScreenPoint[0] /= toScreenPoint[3];
toScreenPoint[1] /= toScreenPoint[3];
toScreenPoint[2] /= toScreenPoint[3];
if (toScreenPoint[3] > 0) {
slx = Math.min(slx, toScreenPoint[0]);
shx = Math.max(shx, toScreenPoint[0]);
sly = Math.min(sly, toScreenPoint[1]);
shy = Math.max(shy, toScreenPoint[1]);
}
});
if (shx > -1 && shy > -1 && slx < 1 && sly < 1 /* visible */) {
// convert to screen
slx = (slx + 1) / 2 * canvasWidth;
//shx = (shx + 1) / 2 * canvasWidth;
//sly = (sly + 1) / 2 * canvasHeight;
shy = (shy + 1) / 2 * canvasHeight;
if (keepInBounds) {
slx = Math.max(0, Math.min(canvasWidth - elemWidth, slx));
shy = Math.max(0, Math.min(canvasHeight - elemHeight, shy));
}
element.style.left = slx + "px";
element.style.bottom = shy + "px";
} else {
element.style.left = canvasWidth + "px";
}
}
—Permalink on GitHub