I want to draw a Line3 in a Threejs and here is my code for it:
start = new THREE.Vector3(20, 10, 0);
end = new THREE.Vector3(200, 100, 0);
var line = new THREE.Line3(start, end);
scene.add(line);
The code doesn't give any error but it doesn't draw the line either.
In the same program, I also have a sphere:
var initScene = function () {
window.scene = new THREE.Scene();
window.renderer = new THREE.WebGLRenderer({
alpha: true
});
window.renderer.setClearColor(0x000000, 0);
window.renderer.setSize(window.innerWidth, window.innerHeight);
window.renderer.domElement.style.position = 'fixed';
window.renderer.domElement.style.top = 0;
window.renderer.domElement.style.left = 0;
window.renderer.domElement.style.width = '100%';
window.renderer.domElement.style.height = '100%';
document.body.appendChild(window.renderer.domElement);
var directionalLight = new THREE.DirectionalLight( 0xffffff, 1 );
directionalLight.position.set( 0, 0.5, 1 );
window.scene.add(directionalLight);
window.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
window.camera.position.fromArray([0, 150, 700]);
window.camera.lookAt(new THREE.Vector3(0, 160, 0));
window.addEventListener('resize', function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.render(scene, camera);
}, false);
scene.add(camera);
// set up the sphere vars
var radius = 50,
segments = 16,
rings = 16;
// create a new mesh with
// sphere geometry - we will cover
// the sphereMaterial next!
var sphereMaterial =
new THREE.MeshLambertMaterial(
{
color: 0xCC0000
});
var sphere = new THREE.Mesh(
new THREE.SphereGeometry(
radius,
segments,
rings),
sphereMaterial);
// add the sphere to the scene
scene.add(sphere);
start = new THREE.Vector3(20, 10, 0);
end = new THREE.Vector3(200, 100, 0);
var line = new THREE.Line3(start, end);
scene.add(line);
renderer.render(scene, camera);
};
initScene();
I only see the sphere on the screen. Can you please tell me where I am wrong?
ADLADS (A Day Late and A Dollar Short):
From http://threejs.org/docs/
Line3 is one of many math objects of three.js that can be used to compute geometric stuff. Others are:
Box2 Box3 Color Euler Frustum Line3 Math Matrix3 Matrix4 Plane Quaternion Ray Sphere Spline Triangle Vector2 Vector3 Vector4
Say you had your "line", a Line3 object and "plane", a Plane object. You could check if the line intersected the plane by doing plane.intersectsLine(line). Would give true or false.
NONE OF THESE MATH OBJECTS (including Line3's) are Object3D's, the things to be rendered.
Here's the scoop:
1) Make the scene, the camera, and the renderer.
2) Add Object3D's to the scene.
2a) Object3D's can be Points, Lines, or Meshes.
2b) Points, Lines, and Meshes are made of Geometries and Materials.
2c1) for Points and Lines, the Geometries contain vertices(Vector3's) and faces(Face3's). Do "vertices.push" or "faces.push".
2c2) for Meshes, Geometries can be: Box Circle Cylinder Dodecahedron Extrude Icosahedron Lathe Octahedron Parametric Plane Polyhedron Ring Shape Sphere Tetrahedron Text Torus TorusKnot Tube
3) Do "renderer.render(scene, camera)".
Thanks for the question. It straightened me out.
For the sphere, you create a geometry (SphereGeometry) and pass it as argument to create a mesh (sphere) that has a material, so it can be drawed. Your line is a 2-vertices geometry, you need to create the corresponding mesh :
var lineMesh=new THREE.Line(
line,//the line3 geometry you have yet
new THREE.LineBasicMaterial({color:0x0000ff})//basic blue color as material
);
scene.add(lineMesh);
Related
I have quite a large plane with a set displacement map and scale which I do not want to be changed. I simply want the loaded texture to apply to that mesh without it having to scale up so largely.
Currently, a floor texture doesn't look like a floor as it has been upscaled to suit the large plane.
How would I be able to scale down the texture and multiply it across the plane so it looks more like actual terrain?
const tilesNormalMap = textureLoader.load(
"./textures/Stylized_Stone_Floor_005_normal.jpg"
);
function createGround() {
let disMap = new THREE.TextureLoader().load("./models/Heightmap.png");
disMap.wrapS = disMap.wrapT = THREE.RepeatWrapping;
disMap.repeat.set(4, 2);
const groundMat = new THREE.MeshStandardMaterial({
map: tilesBaseColor,
normalMap: tilesNormalMap,
displacementMap: disMap,
displacementScale: 2
});
const groundGeo = new THREE.PlaneGeometry(300, 300, 800, 800);
let groundMesh = new THREE.Mesh(groundGeo, groundMat);
scene.add(groundMesh);
groundMesh.rotation.x = -Math.PI / 2;
groundMesh.position.y -= 1.5;
I tried using the .repeat method as shown below but i can't figure out how this would be implemented
tilesBaseColor.repeat.set(0.9, 0.9);
tilesBaseColor.offset.set(0.001, 0.001);
a photo of the current ground texture
enter image description here
First of all what you want to achieve does currently not work with three.js since it's only possible to a have a single uv transform for all textures (except for light and ao map). And map has priority in your case so you can't have different repeat settings for the displacement map. Related issue at GitHub: https://github.com/mrdoob/three.js/issues/9457
Currently, a floor texture doesn't look like a floor as it has been upscaled to suit the large plane. How would I be able to scale down the texture and multiply it across the plane so it looks more like actual terrain?
In this case, you have to use repeat values great 1 otherwise you zoom into the texture. Do it like in the following live example:
let camera, scene, renderer;
init().then(render);
async function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 1;
scene = new THREE.Scene();
const loader = new THREE.TextureLoader();
const texture = await loader.loadAsync('https://threejs.org/examples/textures/uv_grid_opengl.jpg');
// repeat settings
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(2, 2);
const geometry = new THREE.PlaneGeometry();
const material = new THREE.MeshBasicMaterial({map: texture});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function render() {
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.148/build/three.min.js"></script>
I am having issues trying to set the default view of a panoramic image inside spherebuffergeometry. I have a mesh that makes use of this geometry and the material is an equirectangle panorama image. During runtime, the default view of the image is somewhere towards the right. I want the initial angle to be at the bottom of the camera. I tried changing the Phi angle and theta angle parameters of spherebuffergeometry. While I am able to move the default horizontal angle by changing Phi angle, the panoramic image looks weird when I change the theta angle.
I took the first two snapshots when phi angle was 0 and 100 respectively and theta angle was 0. I took the last snapshot when phi angle was 100 and theta angle was 1.
Any help would be appreciated.
Thanks
sphereBufferGeometry = new THREE.SphereGeometry(100,100,100,0,Math.PI*2,0,Math.PI); sphereBufferGeometry.scale( 1, 1, -1 );
var material = new THREE.MeshBasicMaterial( {
map: new THREE.TextureLoader().load([image] )
});
mesh = new THREE.Mesh( sphereBufferGeometry, material );
I have tried this as well.
init();
var scene,renderer,camera;
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100);
var light = new THREE.PointLight(0xffffff, 0.8);
var light2 = new THREE.DirectionalLight( 0xffddcc, 1 );
light.position.set( 1, 0.75, 0.5 );
scene.add( light2 );
var sphereBufferGeometry = new THREE.SphereGeometry(100, 100, 100, 0, Math.PI * 2, 0, Math.PI);
sphereBufferGeometry.scale(1, 1, -1);
var material = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('https://threejs.org/examples/textures/2294472375_24a3b8ef46_o.jpg')
});
var mesh = new THREE.Mesh(sphereBufferGeometry, material);
mesh.rotation.x=1.6;
scene.add(camera);
scene.add(light);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({
antialias: true,
preserveDrawingBuffer: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
var orbitControls = new THREE.OrbitControls(camera, renderer.domElement);
camera.position.set(0, 0, 0.001);
orbitControls.update();
var container = document.getElementById('container');
var canvas = renderer.domElement;
canvas.setAttribute("class", "frame");
container.appendChild(canvas);
document.body.appendChild(container);
animate();
}
function animate()
{
requestAnimationFrame(animate);
renderer.render( scene, camera );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div id="container"></div>
. As you can see, I've rotated the mesh so that the default view is looking at the floor.. if we click and drag the mouse upwards such that the window in the view is horizontal and then click and drag left or right, orbit controls don't seem to work properly in that it doesn't give a proper panoramic experience.. I would like to know how can this be fixed
I would like to animate a bezier curve in ThreeJS. The start, end and control points will update. Eventually I will need to have many curves animating at once. What's the most efficient way to do this?
If you run the code snippet below, you'll see that I'm creating the Bezier object, Geometry and Line each time the frame renders. I'm removing the previous line from the scene and then adding the new, updated line. Is there a better way? Perhaps updating only the geometry and not adding the line again?
var camera, scene, renderer, geometry, material, mesh;
init();
animate();
/**
Create the scene, camera, renderer
*/
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 500;
scene.add(camera);
addCurve();
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
/**
Add the initial bezier curve to the scene
*/
function addCurve() {
testPoint = 0;
curve = new THREE.CubicBezierCurve3(
new THREE.Vector3( testPoint, 0, 0 ),
new THREE.Vector3( -5, 150, 0 ),
new THREE.Vector3( 20, 150, 0 ),
new THREE.Vector3( 10, 0, 0 )
);
curveGeometry = new THREE.Geometry();
curveGeometry.vertices = curve.getPoints( 50 );
curveMaterial = new THREE.LineBasicMaterial( { color : 0xff0000 } );
curveLine = new THREE.Line( curveGeometry, curveMaterial );
scene.add(curveLine);
}
/**
On each frame render, remove the old line, create new curve, geometry and add the new line
*/
function updateCurve() {
testPoint ++;
scene.remove(curveLine);
curve = new THREE.CubicBezierCurve3(
new THREE.Vector3( testPoint, 0, 0 ),
new THREE.Vector3( -5, 150, 0 ),
new THREE.Vector3( 20, 150, 0 ),
new THREE.Vector3( 10, 0, 0 )
);
curveGeometry = new THREE.Geometry();
curveGeometry.vertices = curve.getPoints( 50 );
curveLine = new THREE.Line( curveGeometry, curveMaterial );
scene.add(curveLine);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
updateCurve();
renderer.render(scene, camera);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.min.js"></script>
Creating new lines per each frame is very exepnsive opertation.
What is that the best way to create animated curves?
Probably using shaders. But it can take much more time to implement, so if my next suggestions will be enough for you, just them.
Improve curve updating in your code
I tried not to change a lot of your code. Marked edited places with "// EDITED" comment. I added an array cause you said that there will be many curves.
Explanation
As #WestLangley said, try to avoid using new keyword inside animation loop.
CubicBezierCurve3 has v0, v1, v2 and v3 attributes. Those are THREE.Vector3`s that you provide from the beginning. .getPoints() uses them to return you vertices array. So you can simply change them and no new keyword is needed. See this line for more details.
Rather then recreating line, in your case you can simply update geometry. As you have a THREE.Line - your geometry only needs vertices. After changing vertices you should set geometry.verticesNeedUpdate = true or Three.js will ignore your changes.
var camera, scene, renderer, geometry, material, mesh, curves = [];
init();
animate();
/**
Create the scene, camera, renderer
*/
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 500;
scene.add(camera);
addCurve();
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
/**
Add the initial bezier curve to the scene
*/
function addCurve() {
testPoint = 0;
curve = new THREE.CubicBezierCurve3(
new THREE.Vector3( testPoint, 0, 0 ),
new THREE.Vector3( -5, 150, 0 ),
new THREE.Vector3( 20, 150, 0 ),
new THREE.Vector3( 10, 0, 0 )
);
curveGeometry = new THREE.Geometry();
curveGeometry.vertices = curve.getPoints( 50 );
curveMaterial = new THREE.LineBasicMaterial( { color : 0xff0000 } );
curveLine = new THREE.Line( curveGeometry, curveMaterial );
scene.add(curveLine);
// EDITED
curves.push(curveLine); // Add curve to curves array
curveLine.curve = curve; // Link curve object to this curveLine
}
/**
On each frame render, remove the old line, create new curve, geometry and add the new line
*/
function updateCurve() {
testPoint ++;
// EDITED
for (var i = 0, l = curves.length; i < l; i++) {
var curveLine = curves[i];
// Update x value of v0 vector
curveLine.curve.v0.x = testPoint;
// Update vertices
curveLine.geometry.vertices = curveLine.curve.getPoints( 50 );
// Let's three.js know that vertices are changed
curveLine.geometry.verticesNeedUpdate = true;
}
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
updateCurve();
renderer.render(scene, camera);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.min.js"></script>
I created a small scene with 3 spheres and a triangle connecting the 3 centers of the spheres, i.e. the triangle vertex positions are the same variables as the sphere positions.
Now I expected that if i change the position of one of the spheres, the triangle vertex should be moved together with it (since it's the same position object) and therefore still connect the three spheres.
However, if I do this coordinate change AFTER the renderer was called, the triangle is NOT changed. (Though it does change if I move the sphere BEFORE the renderer is called.)
This seems to indicate that the renderer doesnt use the original position objects but a clone of them.
Q: Is there a way to avoid this cloning behaviour (or whatever is the reason for the independent positions) so I can still change two objects with one variable change? Or am I doing something wrong?
The code:
var width = window.innerWidth;
var height = window.innerHeight;
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene;
var camera = new THREE.PerspectiveCamera(30, width / height, 0.1, 10000);
camera.position=new THREE.Vector3(50,50,50);
camera.lookAt(new THREE.Vector3(0,0,0));
scene.add(camera);
var pointLight = new THREE.PointLight(0xffffff);
pointLight.position=camera.position;
scene.add(pointLight);
var sphere=[];
var sphereGeometry = new THREE.SphereGeometry(1,8,8);
var sphereMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000 });
var triGeom = new THREE.Geometry();
for (var i=0; i<3; i++) {
sphere[i] = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere[i].position=new THREE.Vector3(10*i,20+5*(i-1)^2,0);
scene.add(sphere[i]);
triGeom.vertices.push(sphere[i].position);
}
triGeom.faces.push( new THREE.Face3( 0, 1, 2 ) );
triGeom.computeFaceNormals();
var tri= new THREE.Mesh( triGeom, new THREE.MeshLambertMaterial({side:THREE.DoubleSide, color: 0x00ff00}) );
scene.add(tri);
sphere[0].position.x+=10; // this changes both sphere and triangle vertex
renderer.render(scene, camera);
sphere[1].position.x+=10; // this changes only the sphere
renderer.render(scene, camera);
This is probably because of geometry caching feature. You will have to set triGeom.verticesNeedUpdate = true every time you change vertex position.
I'm trying to detect collision with terrain, that is created by modifying plane's verticles heights.
But Raycaster detects collision correctly only in about 10% of all attempts.
You can see one of those intersections, that does not detect properly on following example:
http://cerno.ch/~kamen/threejs/test.html
What am I doing wrong?
EDIT: Here's jsfiddle
var camera, scene, renderer, gump = 0;
var geometry, material, mesh, map, axis, ray;
init();
animate();
function init() {
//Create cam, so we can see what's happening
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.x = 500;
camera.position.y = 300;
//Init scene
scene = new THREE.Scene();
//Load terrain material
material = new THREE.MeshBasicMaterial( { color: 0xff6666, wireframe: false } );
geometry = new THREE.PlaneGeometry(128, 128, 127, 127);
mesh = new THREE.Mesh( geometry, material );
mesh.scale.set(50,50,50);
mesh.rotation.x = -Math.PI/2;
scene.add( mesh );
//Create axis with position and rotation of ray
axis = new THREE.AxisHelper( 100 );
axis.position = new THREE.Vector3(333.2637, 216.6575, -515.6349);
axis.rotation = new THREE.Vector3(1.6621025025, 0.119175114, -2.2270436357);
axis.scale.z = 10;
scene.add(axis);
//Create actual ray, use axis position and rotation
ray = new THREE.Raycaster();
ray.ray.origin.copy(axis.position);
ray.ray.direction.copy(axis.rotation);
//Renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
if (mesh)
{
var intersecionts = ray.intersectObject(mesh);
if (intersecionts.length > 0)
{
//Never actually happens
console.log("OK");
}
//Move axis so you can see, that it clearly goes throught mesh object
axis.translateZ(Math.cos(gump) * 2);
gump += 0.01;
//Focus camera on axis
camera.lookAt(axis.position);
}
renderer.render( scene, camera );
}
http://jsfiddle.net/BAGnd/
EDIT: Adding system specs as requested:
Windows 7 64bit
Chrome 26.0.1410
Threejs 57
Graphics card: GTX 560 Ti
Found my error.
I thought, that rotation vector, used to rotate object, and direction vector, used in ray, are same things.
After I transfer my rotation vector to direction vector, it works just fine.
How to transfer rotation vector to direction vector: How to get Orientation of Camera in THREE.js