Animating a line in Three.js with lineTo and curveTo - three.js

I'm a three.js newbie and I'm coming from Processing/p5.js (so I'm a bit spoiled on animating loops). I'm trying to create something like this simple stretching pill shape:
Stretching pill shape
This is what I cobbled together from some things I found online. I just have the 'O' shape. I'm trying to get the variable oHeight to be the variable that causes the fluxing back and forth with a Math.sin.
Do I need to update the path? Or the bufferGeometry? Or the THREE.path?
Sorry this code is so messy. Just starting out!
var camera, scene, renderer;
var curve;
var path;
var oHeight = 0;
var delta = 0;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 600;
scene = new THREE.Scene();
path = new THREE.Path();
path.lineTo( 0, 0 );
path.quadraticCurveTo( 0, 20, 20, 20 );
path.lineTo( 40, 20 );
path.quadraticCurveTo( 60,20, 60,0);
path.lineTo(60,-40-oHeight);
path.quadraticCurveTo( 60,-60-oHeight, 40,-60-oHeight);
path.lineTo(20,-60-oHeight);
path.quadraticCurveTo(0,-60,0,-40-oHeight);
path.lineTo(0,0);
var points = path.getPoints();
var geometry = new THREE.BufferGeometry().setFromPoints( points );
var material = new THREE.LineBasicMaterial( { color: 0xffffff } );
curve = new THREE.Line( geometry, material );
scene.add( curve );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
delta +=.1;
oHeight = Math.sin(delta)*20;
line.needUpdate = true; //is this where I went wrong?
renderer.render( scene, camera );
}

There is no object line in your code and there is no mystic reference from the object geometry to the oHeight. The THREE.Path is only a temporary object, which contains the information, that is needed to create the BufferGeoemtry. Note, at the end the vertices of the geometry are stored in an array buffer on the GPU.
You have to create the path in the animate function and to set it to the BufferGeoemtry. So the geometry is recreated in every frame:
function animate() {
requestAnimationFrame( animate );
delta +=.1;
oHeight = Math.sin(delta)*20;
path = new THREE.Path();
path.lineTo( 0, 0 );
path.quadraticCurveTo( 0, 20, 20, 20 );
path.lineTo( 40, 20 );
path.quadraticCurveTo( 60,20, 60,0);
path.lineTo(60,-40-oHeight);
path.quadraticCurveTo( 60,-60-oHeight, 40,-60-oHeight);
path.lineTo(20,-60-oHeight);
path.quadraticCurveTo(0,-60-oHeight,0,-40-oHeight);
path.lineTo(0,0);
geometry.dispose();
geometry.setFromPoints( path.getPoints() );
renderer.render( scene, camera );
}
var camera, scene, renderer;
var curve;
var path;
var oHeight = 0;
var delta = 0;
var geometry;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 600;
scene = new THREE.Scene();
geometry = new THREE.BufferGeometry();
var material = new THREE.LineBasicMaterial( { color: 0xffffff } );
curve = new THREE.Line( geometry, material );
scene.add( curve );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
window.onresize = resize;
}
function animate() {
requestAnimationFrame( animate );
delta +=.1;
oHeight = Math.sin(delta)*20;
path = new THREE.Path();
path.lineTo( 0, 0 );
path.quadraticCurveTo( 0, 20, 20, 20 );
path.lineTo( 40, 20 );
path.quadraticCurveTo( 60,20, 60,0);
path.lineTo(60,-40-oHeight);
path.quadraticCurveTo( 60,-60-oHeight, 40,-60-oHeight);
path.lineTo(20,-60-oHeight);
path.quadraticCurveTo(0,-60-oHeight,0,-40-oHeight);
path.lineTo(0,0);
geometry.dispose();
geometry.setFromPoints( path.getPoints() );
renderer.render( scene, camera );
}
function resize() {
var aspect = window.innerWidth / window.innerHeight;
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = aspect;
camera.updateProjectionMatrix();
}
<script src="https://threejs.org/build/three.min.js"></script>

Related

Improve Three.js performance with SSR (Node,js next,js)

I'm trying to find best solution to improve my website with three.js performance.
I would like to ask someone who have tried three.js with Server Side Rendering and if the
performance improve.
My three.js project include 5 glb files in the scene. Each file is about 1MB.
So far I improved.
Speed Index First
Meaningful Paint
by using webpack
** minify and include all javascript into one file.
** use next generation image wep for all textures.
** able gzip to my sever
Also, to improve loading speed instead of load all 5 glb models at the beginning,
load it 1 or two first, then load rest of models when user needed.
To reduce fan sound( the CPU cause)
I did animate render only user interact
window.addEventListener("wheel", function(e) {
animate();
}, true);
var scene, renderer, camera, model, mixer;
function init() {
var container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 1, 2, - 3 );
camera.lookAt( 0, 1, 0 );
clock = new THREE.Clock();
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xa0a0a0 );
scene.fog = new THREE.Fog( 0xa0a0a0, 10, 50 );
var hemiLight = new THREE.HemisphereLight( 0xffffff, 'rgb(100,100,100)' );
hemiLight.position.set( 0, 20, 0 );
scene.add( hemiLight );
var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2000, 2000 ), new THREE.MeshPhongMaterial( { color: 0x999999, depthWrite: false } ) );
mesh.rotation.x = - Math.PI / 2;
mesh.receiveShadow = true;
scene.add( mesh );
var grid = new THREE.GridHelper( 2000, 20, 0x000000, 0x000000 );
grid.material.opacity = 0.2;
grid.material.transparent = true;
scene.add( grid );
var textload = new THREE.TextureLoader();
var text = textload.load( require('./material/text.jpg') );
//text.jpg = 80kb;
text.flipY = false;
var textloadbbump = new THREE.TextureLoader();
var textbump = textloadbbump.load( require('./material/textbump.jpg') );
//textbump.jpg = 50kb;
textbump.flipY = false;
var loader = new GLTFLoader();
//character.glb is 1.1MB
loader.load( require('./material/character.glb'), function ( gltf ) {
model = gltf.scene;
model.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material.roughness = 1;
if(child.name=='body'){
child.material.map = text;
child.material.bumpMap = textbump;
child.material.bumpScale = 0.001;
}
}});
scene.add( model );
mixer = new THREE.AnimationMixer( model );
mixer.clipAction( gltf.animations[ 0 ] ).play();
});
renderer = new THREE.WebGLRenderer( {
// antialias: true
powerPreference: 'low-power',
//precision: 'lowp'
} );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = false;
container.appendChild( renderer.domElement );
}
window.onresize = function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
};
init();
window.addEventListener("wheel", function(e) {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}, true);

Three.js setHSL() sets unexpected color

setHSL(h,s,l) - set the value of this color on the supplied HSL values. The values range from 0 to 1.
why am I getting cyan when I try material.color.setHSL( 0.5, 1, 0.5 )? I expect to have yellow instead as color picker shows
let camera, scene, renderer, material;
init();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 1;
scene = new THREE.Scene();
const geometry = new THREE.PlaneGeometry();
material = new THREE.MeshBasicMaterial( { color: 0xffffff } );
material.color.setHSL( 0.5, 1, 0.5 );
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
renderer.render( scene, camera );
}
body {
margin: 0;
}
<script src="//cdn.rawgit.com/mrdoob/three.js/master/build/three.min.js"></script>
Color picker goes from 0 to 360.
setHSL goes from 0 to 1.
if you want hue 50 out of 360 you set 50/360 which is 0.1388888888888889.
setHSL( 0.1388888888888889, 1, 0.5 ) is yellow.
let camera, scene, renderer, material;
init();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 1;
scene = new THREE.Scene();
const geometry = new THREE.PlaneGeometry();
material = new THREE.MeshBasicMaterial( { color: 0xffffff } );
material.color.setHSL( 0.1388888888888889, 1, 0.5 );
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
renderer.render( scene, camera );
}
body {
margin: 0;
}
<script src="//cdn.rawgit.com/mrdoob/three.js/master/build/three.min.js"></script>

three.js Mesh not displaying rectangle depending on orientation

I have created a MWE that creates a single rectangle spinning. However, the rectangle disappears based on its orientation, and the material (which claims to be dotted lines) does not work and instead the rectangle is drawn in solid white.
I suspect the disappearance is due to rectangles only being visible facing the camera. Is there a simple two-sided rectangle parameter?
Why isn't the rectangle being drawn as a dashed outline?
var container, stats;
var camera, scene, renderer;
var group;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
function init( ) {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 0, 150, 500 );
scene = new THREE.Scene();
var lineDash = new THREE.LineDashedMaterial( { color: 0xffaa00, dashSize: 3, gapSize: 1, linewidth: 2 } );
var wall = new THREE.Geometry();
var h = 200;
wall.vertices.push(new THREE.Vector3(0, 0, 0));
wall.vertices.push(new THREE.Vector3(200, 0, 0));
wall.vertices.push(new THREE.Vector3(200, 0, h));
wall.vertices.push(new THREE.Vector3(0, 0, h));
wall.faces.push( new THREE.Face3( 0, 1, 2 ) );
wall.faces.push( new THREE.Face3( 0, 2, 3 ) );
var wallObj = new THREE.Mesh(wall, lineDash );
wallObj.position.x = 0;
wallObj.position.y = 200;
wallObj.rotation.x = Math.PI/2;
group = new THREE.Group();
group.add(wallObj);
scene.add( group );
renderer = new THREE.CanvasRenderer();
renderer.setClearColor( 0xf0f0f0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
group.rotation.y += .05;
renderer.render( scene, camera );
}
init( );
animate();
To make a material double-sided, set
material.side = THREE.DoubleSide.
LineDashedMaterial requires line distances to be computed.
geometry.computeLineDistances().
WebGLRenderer is preferable to CanvasRenderer.
three.js r.75

Three.js material isn't applied correctly to hexagonal faces

I have a mesh, created in blender and exported to .obj. The mesh looks valid and has UV map applied and exported into the same .obj as well. For some reason, when I try to apply a texture material, or even basic material to the mesh, only half of the hexagon is actually painted.
This is a mesh
This is the code
var container;
var camera, scene, renderer;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.5, 3000000 );
camera.position.set( 2000, 750, 2000 );
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.userPan = false;
controls.userPanSpeed = 0.0;
controls.maxDistance = 5000.0;
controls.maxPolarAngle = Math.PI * 0.495;
controls.center.set( 0, 1, 0 );
var light = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 );
light.position.set( - 1, 1, - 1 );
scene.add( light );
waterNormals = new THREE.ImageUtils.loadTexture( 'textures/waternormals.jpg' );
waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping;
water = new THREE.Water( renderer, camera, scene, {
textureWidth: 512,
textureHeight: 512,
waterNormals: waterNormals,
alpha: 1.0,
sunDirection: light.position.clone().normalize(),
sunColor: 0xffffff,
waterColor: 0x001e0f,
distortionScale: 50.0,
} );
var loader = new THREE.OBJMTLLoader();
loader.load( "models/world.obj", "models/world.mtl", function(object)
{
console.log(object.children[0].children[1].geometry);
var mesh = new THREE.Mesh(
object.children[0].children[1].geometry,
new THREE.MeshBasicMaterial
);
scene.add(mesh);
});
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
controls.update();
renderer.render( scene, camera );
}
And this is how it looks:
When I split the hexagons into 2 quads it works perfectly, thing is, I need faces to stay hexagons for picking, the faces I want to be selected are hexagons.

Three.js ray object intersection

friends.
Here is my code. It should find intersection of ray and cube. But it does not work and makes me mad. This code is quite simple, but it is hard for me to find the error. Please, help.
jsFiddle
<script>
var container;
var camera, controls, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 1000;
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( 'change', render );
// world
scene = new THREE.Scene();
var testObject_G = new THREE.CubeGeometry(100, 100, 100);
var testObject_M = new THREE.MeshBasicMaterial({ color: 0xBBBBBB });
var testObject_Mesh = new THREE.Mesh(testObject_G, testObject_M);
testObject_Mesh.position.x = 300;
scene.add(testObject_Mesh);
scene2 = new THREE.Object3D();
// rays
var direction = new THREE.Vector3(1, 0, 0);
direction.normalize();
var startPoint = new THREE.Vector3(0, 0, 0);
var ray = new THREE.Raycaster(startPoint, direction);
var rayIntersects = ray.intersectObjects(scene.children, true);
if (rayIntersects[0]) {
console.log(rayIntersects[0]);
var geometry = new THREE.CubeGeometry(10, 10, 10);
var material = new THREE.MeshBasicMaterial({color: 0xff0000});
var cube = new THREE.Mesh(geometry, material);
cube.position = rayIntersects[0].point;
scene2.add(cube);
}
var ray_G = new THREE.Geometry();
ray_G.vertices.push(new THREE.Vector3(0, 0, 0));
ray_G.vertices.push(direction.multiplyScalar(1000));
var ray_M = new THREE.LineBasicMaterial({ color: 0x000000 });
var ray_Mesh = new THREE.Line(ray_G, ray_M);
scene2.add(ray_Mesh);
scene.add(scene2);
// renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0xffffff, 1);
container = document.getElementById( 'container' );
container.appendChild( renderer.domElement );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
controls.handleResize();
render();
}
function animate() {
requestAnimationFrame( animate );
controls.update();
}
function render() {
renderer.render( scene, camera );
}
</script>
</body>
Thank you very much for replies.
During the render loop, three.js updates each object's transform matrix for you, based on your specified object.position, object.rotation/quaternion, and object.scale.
Since you are calling Raycaster.intersectObjects() before the first render call, you have to update the object matrices yourself prior to raycasting.
scene.updateMatrixWorld();
fiddle: http://jsfiddle.net/mzRtJ/5/
three.js r.64

Resources