On the minimal example below (don't forget to adapt the URL of three.min.js) then open the html file in a window. You should see a (non-regular) tetrahedron. When moving the mouse over the canvas you should see the number of intersection of the ray from the camera to the mouse with all the objects of the scene object, tested with this line in the code:
raycaster.intersectObjects(scene.children,false);
Since apart from the ligths, there is only the tetrahedron, it says mostly 0 or 2 because it counts the number of faces that have been intersected by the infinite ray and because I have chosen a double sided material:
var material = new THREE.MeshLambertMaterial( { color: 0xd8f8b0, side: THREE.DoubleSide } );
Now click the checkbox. Another tetrahedron is created on the fly, its Geometry being a clone of the Geometry of the first Mesh.
geom2 = geom.clone();
I offset the new geom by adding 1 to all the coordinates of its vertices. However, the raycaster answers 0 for most rays intersecting the new object. Is there a bug or did I forget or misunderstand something?
If the geometry is not a clone (change clone=true; to clone=false; on the top of min.js) then it works.
Three.js version : r86
Minimal example
the html file:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="three.min.js"></script>
</head>
<body>
<div style="text-align: center;">
<canvas id="ze-canvas" width="800" height="600"></canvas>
<p>
<input type="checkbox" id="filBox"> click me
<p>
<span id="info-text"></span>
<p>
<script src="min.js"></script>
<script>visualiseur("ze-canvas","info-text","filBox");</script>
</div>
</body>
</html>
the file min.js
var visualiseur = function(canvas_name,info_name,box_name) {
var clone = true;
var canvas = document.getElementById(canvas_name);
var cbox = document.getElementById(box_name);
var textInfo = document.getElementById(info_name);
cbox.checked = false;
var camera = new THREE.PerspectiveCamera( 33, canvas.width / canvas.height, 0.1, 1000 );
camera.position.set(-2,4,8);
camera.lookAt(new THREE.Vector3(0,0,0));
var scene = new THREE.Scene();
scene.add( new THREE.AmbientLight( 0xffffff, .3) );
var light1 = new THREE.PointLight( 0xCCffff, .7, 0, 2 );
var light2 = new THREE.PointLight( 0xffffCC, .7, 0, 2 );
light1.position.set( 50, -50, 20 );
light2.position.set( -50, 150, 60 );
scene.add( light1 );
scene.add( light2 );
var material = new THREE.MeshLambertMaterial( { color: 0xd8f8b0, side: THREE.DoubleSide } );
var makeGeom = function(geom) {
geom.vertices.push(new THREE.Vector3(0,0,0));
geom.vertices.push(new THREE.Vector3(0,0,1));
geom.vertices.push(new THREE.Vector3(0,1,0));
geom.vertices.push(new THREE.Vector3(1,0,0));
geom.faces.push(new THREE.Face3(0,1,2));
geom.faces.push(new THREE.Face3(1,3,2));
geom.faces.push(new THREE.Face3(0,2,3));
geom.faces.push(new THREE.Face3(0,3,1));
geom.computeFlatVertexNormals();
}
var geom = new THREE.Geometry();
makeGeom(geom);
var mesh = new THREE.Mesh(geom,material);
scene.add(mesh);
var renderer = new THREE.WebGLRenderer({ canvas : canvas, antialias: true});
var render = function() {
renderer.render( scene, camera );
}
function getMousePos(evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
var raycaster = new THREE.Raycaster();
canvas.onmousemove = function(e) {
render();
var p=getMousePos(e);
p.x = p.x/canvas.width*2 - 1;
p.y = -p.y/canvas.height*2 + 1;
raycaster.setFromCamera( new THREE.Vector2(p.x,p.y), camera);
var intersects = raycaster.intersectObjects(scene.children,false);
textInfo.innerHTML=intersects.length+" intersections";
}
var done=false;
cbox.onclick = function(e) {
if(done) return;
done = true;
var geom2;
if(clone) {
geom2 = geom.clone();
}
else {
geom2 = new THREE.Geometry();
makeGeom(geom2);
}
geom2.vertices.forEach(function(v) {
v.x += 1;
v.y += 1;
v.z += 1;
});
geom2.verticesNeedUpdate=true;
geom2.computeFlatVertexNormals();
scene.add(new THREE.Mesh(geom2,material));
render();
}
render();
}
This was a tricky one but I found the solution.
In short: add
geom2.computeBoundingSphere();
just after changing the vertices.
Long version: Here's how I found the solution.
I started looking at the source code of Geometry.js and looking at every member function or variable in Geometry that I might have overlooked.
I finally noticed this bounding box and bounding sphere things, which I had never heard of in THREE.js before. Looking back at the Geometry section of the documentation of THREE.js, they are mentioned but without any explanation on what they are used for.
One would naturally think then they are used to accelerate the rendering on the graphics card by first computing the intersection of a ray with the box/sphere (this is fast I suppose) and if there is none we can skip the whole object.
This turns out to be a false assumption: on my minimal example, the second tetrahedron does show up even though its bounding sphere is wrong.
Then it got even more strange. I had the script log the bounding boxes and spheres of the geometries when I click the box, once just before the cloning and once just after the next rendering pass.
They never get a bounding box.
The first geometry has bounding sphere before and after.
The second has a bounding sphere before rendering only if clone = true (so no bounding sphere when created).
After rendering, both objects have a bounding sphere.
Conclusion : the bounding sphere is used by the Raycaster but not the rendered. (this is a surprise to me)
By inspecting the bounding sphere centers, I realized that the bounding sphere of the second geometry was wrong when it is cloned and the vertices moved, and is not updated by render().
When an object is created and render() called, then its bounding sphere is created and is correct. However, if you change the vertices, and even if you set the verticesNeedUpdate flag to true, the bounding sphere does not get updated, you have to call manually computeBoundingSphere().
It is all the more puzzling that the bounding sphere is secretly created when you call render() but not the bounding box.
Let me sum up what I understood of this all:
the bounding sphere is used by the Raycaster but not the renderer
I ignore if the bounding box is used by either (I have not spent time testing that)
if the bounding sphere does not exist, it will get created when calling render(). If it exists it is not updated by calling render() even when the flag verticesNeedUpdate is set to true.
the bounding box does not get created by render()
Is this the designed behaviour or is this a bug?
Related
I am using the following code to create this 3D transparent cube.
// Create the cube itself
const cubeGeom = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( {color: 0x00ff00, opacity:0.4, transparent:true});
const cube = new THREE.Mesh( cubeGeom, material );
// Also add a wireframe to the cube to better see the depth
const _wireframe = new THREE.EdgesGeometry( cubeGeom ); // or WireframeGeometry( geometry )
const wireframe = new THREE.LineSegments( _wireframe);
// Rotate it a little for a better vantage point
cube.rotation.set(0.2, -0.2, -0.1)
wireframe.rotation.set(0.2, -0.2, -0.1)
// add to scene
scene.add( cube )
scene.add( wireframe );
As can been seen, the cube appears as a single volume that is transparent. Instead, I would want to create a hollow cube with 6 transparent faces. Think of a cube made out of 6 transparent and colored window-panes. See this example: my desired result would be example 1 for each of the 6 faces, but now it is like example 2.
Update
I tried to create individual 'window panes'. However the behavior is not as I would expect.
I create individual panes like so:
geometry = new THREE.PlaneGeometry( 1, 1 );
material = new THREE.MeshBasicMaterial( {color: 0x00ff00, side: THREE.DoubleSide, transparent:true, opacity:0.2});
planeX = new THREE.Mesh( geometry, material);
planeY = new THREE.Mesh( geometry, material);
planeZ = new THREE.Mesh( geometry, material);
And then I add all three planes to wireframe.
Then I rotate them a little, so they intersect at different orientations.
const RAD_TO_DEG = Math.PI * 2 / 360;
planeX.rotation.y = RAD_TO_DEG * 90
planeY.rotation.x = RAD_TO_DEG * 90
Now I can see the effect of 'stacking' the panes on top of each other, however it is not as it should be.
I would instead expect something like this based on real physics (made with terrible paint-skills). That is, the color depends on the number of overlapping panes.
EDIT
When transparent panes overlap from the viewing direciton, transparancy appears to work perfectly. However, when the panes intersect it breaks.
Here I have copied the snipped provided by #Anye and added one.rotation.y = Math.PI * 0.5 and commented out two.position.set(0.5, 0.5, 0.5); so that the panes intersect.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var cube = new THREE.Group();
one = new Pane();
two = new Pane();
one.rotation.y = Math.PI * 0.5
one.position.z = 0.2;
// two.position.set(0.5, 0.5, 0.5);
cube.add(one);
cube.add(two);
cube.rotation.set(Math.PI / 4, Math.PI / 4, Math.PI / 4);
scene.add(cube);
function Pane() {
let geometry = new THREE.PlaneGeometry(1, 1);
let material = new THREE.MeshBasicMaterial({color:0x00ff00, transparent: true, opacity: 0.4});
let mesh = new THREE.Mesh(geometry, material);
return mesh;
}
camera.position.z = 2;
var animate = function () {
requestAnimationFrame( animate );
renderer.render(scene, camera);
};
animate();
body {
margin: 0;
overflow: hidden;
}
canvas {
width: 640px;
height: 360px;
}
<html>
<head>
<title>Demo</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
</body>
</html>
EDIT
The snipped looks pretty good; it clearly shows a different color where the panes overlap. However, it does not show this everywhere. See this image. The left is what the snippet generates, the right is what it should look like. Only the portion of overlap that is in front of the intersection shows the discoloration, while the section behind the intersection should, but does not show discoloration.
You might want to take a look at CSG, Constructive Solid Geometry. With CSG, you can create a hole in your original cube using a boolean. To start, you could take a look at this quick tutorial. Below are some examples of what you can do with CSG.
var cube = new CSG.cube();
var sphere = CSG.sphere({radius: 1.3, stacks: 16});
var geometry = cube.subtract(sphere);
=>
CSG, though, has some limitations, since it isn't made specifically for three.js. A cheap alternative would be to create six individual translucent panes, and format them to create a cube. Then you could group them:
var group = new THREE.Group();
group.add(pane1);
group.add(pane2);
group.add(pane3);
group.add(pane4);
group.add(pane5);
group.add(pane6);
Update
Something may be wrong with your code, which is why it isn't shading accordingly for you. See this minimal example, which shows how the panes shade appropriately based on overlaps.
Update 2
I updated the snippet so the 2 panes aren't touching at all... I am still able to see the shading. Maybe if you were to try to reproduce this example?
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var cube = new THREE.Group();
one = new Pane();
two = new Pane();
one.rotation.y = Math.PI * 0.5;
one.position.z = 0.2;
cube.add(one);
cube.add(two);
cube.rotation.set(Math.PI / 4, Math.PI / 4, Math.PI / 4);
scene.add(cube);
function Pane() {
let geometry = new THREE.PlaneGeometry(1, 1);
let material = new THREE.MeshBasicMaterial({color:0x00ff00, transparent: true, opacity: 0.4});
material.depthWrite = false
let mesh = new THREE.Mesh(geometry, material);
return mesh;
}
camera.position.z = 2;
var animate = function () {
requestAnimationFrame( animate );
renderer.render(scene, camera);
};
animate();
body {
margin: 0;
overflow: hidden;
}
canvas {
width: 640px;
height: 360px;
}
<html>
<head>
<title>Demo</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
</body>
</html>
Update 3
Below is a screenshot of what I see in your snippet... Seems to be working fine...
You're experiencing one of my first head-scratchers:
ShaderMaterial transparency
As the answer to that question states, the three.js transparency system performs order-dependent transparency. Normally, it will take whichever object is closest to the camera (by mesh position), but because all of your planes are centered at the same point, there is no winner, so you get some strange transparency effects.
If you move the plane meshes out to form the actual sides of the box, then you should see the effect you're looking for. But that won't be the end of strange transparency effects, And you would need to implement your own Order-Independent Transparency (or find an extension library that does it for you) to achieve more physically-accurate transparency effects.
A rotated object (cylinder in this case) cuts off objects (a triangle made by lines in this case) even though the renderOrder of the second object is higher. See this jsfiddle demo for the effect.
The triangle should be rendered completely on top of the cylinder but is cut off where the outside of the cylinder intersects with it. It's easier to understand what's happening when a texture is used, but jsfiddle is bad at using external images.
var mesh, renderer, scene, camera, controls;
init();
animate();
function init() {
renderer = new THREE.WebGLRenderer({
antialias: true,
preserveDrawingBuffer: true
});
renderer.setClearColor(0x24132E, 1);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 10000);
camera.position.set(0, 0, 7);
camera.lookAt(scene.position)
scene.add(camera);
var geometry = new THREE.CylinderGeometry(1, 1, 100, 32, 1, true);
var material = new THREE.MeshBasicMaterial({
color: 0x0000ff
});
material.side = THREE.DoubleSide;
mesh = new THREE.Mesh(geometry, material);
mesh.rotation.x = Math.PI / 2;
scene.add(mesh);
var c = 3, // Side length of the triangle
a = c / 2,
b = Math.sqrt(c * c - a * a),
yOffset = -b / 3; // The vertical offset (if 0, triangle is on x axis)
// Draw the red triangle
var geo = new THREE.Geometry();
geo.vertices.push(
new THREE.Vector3(0, b + yOffset, 0),
new THREE.Vector3(-a, 0 + yOffset, 0),
new THREE.Vector3(a, 0 + yOffset, 0),
new THREE.Vector3(0, b + yOffset, 0)
);
var lineMaterial = new THREE.LineBasicMaterial({
color: 0xff0000,
linewidth: 5,
linejoin: "miter"
});
plane = new THREE.Line(geo, lineMaterial);
// Place it on top of the cylinder
plane.renderOrder = 2; // This should override any clipping, right?
scene.add(plane);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
Am I doing something wrong or is this a bug?
for the effect that you want use a second scene and render it onto the first one
function init(){
.....
renderer.autoClear = false;
scene.add(tube);
overlayScene.add(triangle);
.....
}
function render() {
renderer.clear();
renderer.render(scene, camera);
renderer.clearDepth();
renderer.render(overlayScene, camera);
}
renderOrder does not mean what you think it means, look at the implementation in WebGLRenderer
objects are sorted by the order, if it meant what you anticipated from it, there would always be some fixed rendering order and colliding objects would be seen through each other, renderOrder is AFAIK used when you have issues with order of transparent/ not opaque objects
I worte a little plugin for three.js for flares for my game. Three.js built-in flares plugin is slow and I preferred not to run another rendering pass which was cutting framerate in half. Here's how I got flares visible on top of objects which were actually in front of them.
Material parameters:
{
side: THREE.FrontSide,
blending: THREE.AdditiveBlending,
transparent: true,
map: flareMap,
depthWrite: false,
polygonOffset: true,
polygonOffsetFactor: -200
}
depthWrite - set to false
polygonOffset - set to true
polygonOffsetFactor - give negative number to get object in front of others. Give it some really high value to be really on top of everything i.e. -10000
Ignore other params, they are needed for my flares
I have an instance of THREE.PlaneBufferGeometry that I apply an image texture to like this:
var camera, scene, renderer;
var geometry, material, mesh, light, floor;
scene = new THREE.Scene();
THREE.ImageUtils.loadTexture( "someImage.png", undefined, handleLoaded, handleError );
function handleLoaded(texture) {
var geometry = new THREE.PlaneBufferGeometry(
texture.image.naturalWidth,
texture.image.naturalHeight,
1,
1
);
var material = new THREE.MeshBasicMaterial({
map: texture,
overdraw: true
});
floor = new THREE.Mesh( geometry, material );
floor.material.side = THREE.DoubleSide;
scene.add( floor );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, texture.image.naturalHeight * A_BUNCH );
camera.position.z = texture.image.naturalWidth * 0.5;
camera.position.y = SOME_INT;
camera.lookAt(floor.position);
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth,window.innerHeight);
appendToDom();
animate();
}
function handleError() {
console.log(arguments);
}
function appendToDom() {
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene,camera);
}
Here's the code pen: http://codepen.io/anon/pen/qELxvj?editors=001
( Note: ThreeJs "pollutes" the global scope, to use a harsh term, and then decorates THREE using a decorator pattern--relying on scripts loading in the correct order without using a module loader system. So, for brevity's sake, I simply copy-pasted the source code of a few required decorators into the code pen to ensure they load in the right order. You'll have to scroll down several thousand lines to the bottom of the code pen to play with the code that instantiates the plane, paints it and moves the camera. )
In the code pen, I simply lay the plane flat against the x-y axis, looking straight up the z-axis, as it were. Then, I slowly pan the camera down along the y-axis, continuously pointing it at the plane.
As you can see in the code pen, as the camera moves along the y-axis in the negative direction, the texture on the plane appears to develop a kink in it around West Texas.
Why? How can I prevent this from happening?
I've seen similar behaviour, not in three.js, not in a browser with webGL but with directX and vvvv; still, i think you'll just have to set widthSegments/heightSegments of your PlaneBufferGeometry to a higher level (>4) and you're set!
I'm new to threejs just doing a basic cube with a texture to the backside. I have words on colour sides to the texture. However the words come out mirrored like. How can I get them to come out correctly.
You can negatively scale your cube to undo the mirror effect, like this:
cube.scale.x = -1;
There are two things you can do:
Reverse or rotate your UV coordinates on each face on the cube until you get the desired result. This is easy since the UV coordinates of a cube are usually 0.0 and 1.0.
Use an image package to rotate the textures as you want them.
I think I had the same problem as you with regards to texturing a cube.
As I understand it all surfaces come out correct orientation except the backside. The way i got around this was to place the textures on the cube per face and then alter the UV mapping of the back face.
This solved the problem of the back face being oriented incorrectly and also as a result of UV mapping I am now able to put textures on irregular faces to like pyramids etc.
Here is the solution by changing the UV of the backface. Just replace the loaded texture with a local texture cut and paste into notepad and save as html file and your good to go.
<html>
<head>
</head> <body> <script src="js/three.min.js"></script> <script> var
scene, camera, renderer; var geometry, material; var modarray=[];
var material=[]; var rotation=0; init(); animate(); function init()
{
renderer = new THREE.WebGLRenderer();
//renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize( window.innerWidth, 100 );
document.body.appendChild( renderer.domElement );
/////////// // Camera// ///////////
camera = new THREE.OrthographicCamera( window.innerWidth / - 2,
window.innerWidth / 2, 100 / 2, 100 / - 2, - 500, 1000 );
camera.position.z = 2000; camera.position.y = 0; camera.position.x = 0; scene= new THREE.Scene();
geometry = new THREE.BoxGeometry( 50, 50, 50 ); geometry2 = new THREE.BoxGeometry( 50, 50, 50 );
/////////////////////////////// // Store Materials for blocks//
/////////////////////////////// var bricks; material[0] = new
THREE.MeshPhongMaterial( { map:
THREE.ImageUtils.loadTexture('10.png') } );
var basex=-455; //////////////////////////////////////////////////
// Vector array to hold where UV will be placed //
////////////////////////////////////////////////// bricks = [new
THREE.Vector2(1, 0), new THREE.Vector2(1, 1), new
THREE.Vector2(0, 1), new THREE.Vector3(0, 0)];
///////////////////////////////////////////////////// // choose what
face this eccects from vertex array // // in this case backside
// // choose the orientation of the triangles //
/////////////////////////////////////////////////////
geometry.faceVertexUvs[0][10] = [ bricks[0], bricks[1], bricks[3]];
geometry.faceVertexUvs[0][11] = [ bricks[1], bricks[2], bricks[3]];
modarray[0] = new THREE.Mesh( geometry, material[0]); modarray[1] = new THREE.Mesh( geometry2, material[0]);
modarray[0].position.x=basex; modarray[0].position.z=1000;
modarray[0].position.y=0;
scene.add(modarray[0]);
modarray[1].position.x=basex+65; modarray[1].position.z=1000;
modarray[1].position.y=0;
scene.add(modarray[0]); scene.add(modarray[1]);
////////// // LIGHT// ////////// var light2 = new
THREE.AmbientLight(0xffffff); light2.position.set(0,100,2000);
scene.add(light2);
}
//////////////////// // Animation Loop // ///////////////////
function animate() {
requestAnimationFrame( animate ); var flag=0;
for(n=0; n<2; n++) {
modarray[n].rotation.x=rotation;
} rotation+=0.03;
renderer.render( scene, camera );
}
</script> <p>The cube on the left is with UV mapping to correct the
back surface.
The cube on the right is without the UV mapping.</p> </body>
</html>
I want to make it such that I can move around in the following manner in three.js by moving the perspectivecamera:
"hit a key or button to move the camera forward into the direction im looking at"
"hit a key or button to move the camera downward below the direction im looking at"
"hit a key or button to pitch the camera such that the "fov" is of a different value
"hit keys or buttons to pitch the camera such that i am rotating as if pivoted at the place the camera is to be able to see whats left and right of me
The following is my current code. Based on what I am seeing, it appears that PerspectiveCamera from the docs does not appear to have any methods like "setFov" or anything like that, simply "camera.fov = " does not appear to have any effect like the meshes after the camera has been initialized. So how would I properly be able to do the above?:
<!DOCTYPE html>
<html>
<head>
<title>Example 01.03 - Materials and light</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/jquery-1.9.0.js"></script>
<script type="text/javascript" src="../libs/stats.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 () {
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.setClearColorHex(0xEEEEEE, 1.0);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
// create the ground plane
var planeGeometry = new THREE.PlaneGeometry(60,20,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=15
plane.position.y=0
plane.position.z=0
// add the plane to the scene
scene.add(plane);
// create a cube
var cubeGeometry = new THREE.CubeGeometry(4,4,4);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
// position the cube
cube.position.x=-4;
cube.position.y=3;
cube.position.z=0;
// add the cube to the scene
scene.add(cube);
var sphereGeometry = new THREE.SphereGeometry(4,20,20);
var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
var sphere = new THREE.Mesh(sphereGeometry,sphereMaterial);
// position the sphere
sphere.position.x=20;
sphere.position.y=0;
sphere.position.z=2;
sphere.castShadow=true;
// add the sphere to the scene
scene.add(sphere);
// position and point the camera to the center of the scene
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
// 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
$("#WebGL-output").append(renderer.domElement);
// call the render function
var step=0;
render();
function render() {
stats.update();
// rotate the cube around its axes
cube.rotation.x += 0.02;
cube.rotation.y += 0.02;
cube.rotation.z += 0.02;
// bounce the sphere up and down
step+=0.04;
sphere.position.x = 20+( 10*(Math.cos(step)));
sphere.position.y = 2 +( 10*Math.abs(Math.sin(step)));
// render using requestAnimationFrame
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';
$("#Stats-output").append( stats.domElement );
return stats;
}
});
</script>
</body>
</html>
I believe you simply need to understand the components of the camera's matrix to realize what you want. The three.js "camera" is a 4x4 matrix of the classic linear algebra type.
4x4 matrix = [a b c d
e f g h
i j k l
n m o p]
The 3x3 inner matrix is literally the orientation of your camera:
camera orientation = [a b c
e f g
i j k]
Meaning the vector { a, b, c } is the direction vector pointing to the camera's right, from the camera's pov. The vector { e, f, g } is the direction vector pointing up, from the camera's point of view. And the vector { i, j, k } is the director vector pointing in the direction the camera is facing.
Together these 3 vectors compose the orientation of the camera.
They can be further envisioned by holding your left hand in front of you like this:
Each finger is pointing in the positive direction of the camera's pov, and they likewise equal the rotation factors necessary to orient the camera.
And the other, non-rotation components { n, m, o } is the x, y, z position of the camera.
To move in the direction of the camera, add the vector { i, j, k } to { n, m, o }.
To move to the right, from the camera's pov, add the vector { a, b, c } to { n, m, o }.
To move up, from the camera's pov, add the vector { e, f, g } to { n, m, o }.
And, of course, to move in the opposite directions add the negatives of those orientation vectors.