I am new to ThreeJS and still exploring the library.
I have a very basic example of drawing a white dot, with a simple translation force.
let dotGeometry = new THREE.Geometry();
let dotMaterial = new THREE.PointsMaterial({
size: 10,
color: 0xffffff,
});
dotGeometry.vertices.push(new THREE.Vector3(1, 0, -1));
let dot = new THREE.Points(dotGeometry, dotMaterial);
scene.add(dot);
function animate() {
requestAnimationFrame(animate);
dot.position.x += 0.01;
renderer.render(scene, camera);
}
animate();
I was wondering why the translation is pretty laggy even with a single point rendered on screen.
Is there a better way to achieve smoother transformations, am I doing something wrong?
Many thanks in advance.
Andrea
Actually you should always honor the time delta of a single animation step when transforming objects. In this way, a value like 0.1 gets a better semantic. It's 0.1 world units per second. It also makes the animation more smooth and independent of the framerate. Try this code:
const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 5;
const scene = new THREE.Scene();
const clock = new THREE.Clock();
const dotGeometry = new THREE.Geometry();
const dotMaterial = new THREE.PointsMaterial({
size: 10,
color: 0xffffff,
});
dotGeometry.vertices.push(new THREE.Vector3(1, 0, -1));
const dot = new THREE.Points(dotGeometry, dotMaterial);
scene.add(dot);
const 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);
const delta = clock.getDelta();
dot.position.x += 0.1 * delta;
renderer.render(scene, camera);
}
animate();
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.123/build/three.js"></script>
Related
I'm trying to smooth the normals of a mesh starting from a non indexed BufferGeometry.
This question has been answered before however the Three.js api has changed substantially since and I can't get it to work on r130
From what I've understood, I need to first merge the vertices to get an indexed BufferGeometry then re-compute the normals, but it doesn't seem to work.
Here is a minimal example using the defaulf cube :
// Scene
const scene = new THREE.Scene();
// Geometry
const boxGeometry = new THREE.BoxGeometry(.7,.7,.7);
// Materials
const shadedMaterial = new THREE.MeshStandardMaterial();
shadedMaterial.metalness = 0.4;
shadedMaterial.roughness = 0.4;
shadedMaterial.color = new THREE.Color(0xffffff);
// Mesh
const smoothBoxGeometry=BufferGeometryUtils
.mergeVertices(new THREE.BufferGeometry().copy(boxGeometry))
smoothBoxGeometry.computeVertexNormals();
smoothBoxGeometry.computeBoundingBox();
smoothBoxGeometry.normalizeNormals();
const box = new THREE.Mesh(smoothBoxGeometry, shadedMaterial);
scene.add(box);
flat shaded cube instead of expected smooth shaded cube
What am I missing ?
Try it like so:
let camera, scene, renderer;
let mesh;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 4;
scene = new THREE.Scene();
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
hemiLight.position.set(0, 20, 0);
scene.add(hemiLight);
const dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(-3, 10, -10);
scene.add(dirLight);
let geometry = new THREE.BoxGeometry();
geometry.deleteAttribute('normal');
geometry.deleteAttribute('uv');
geometry = THREE.BufferGeometryUtils.mergeVertices(geometry);
geometry.computeVertexNormals();
const material = new THREE.MeshStandardMaterial();
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);
}
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.130.1/build/three.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.130.1/examples/js/utils/BufferGeometryUtils.js"></script>
BufferGeometryUtils.mergeVertices() can only perform the merge if vertex data are identical. To ensure this, it is necessary to remove the existing normal and uv attribute.
I want to create a cylinder with slanted bottom shape in three.js.
I don't see any direct way to create such geometry in three.js. I tried to see if I can cut the cylinder by a slanted plane, but could not find such operation in the documentation. Can anybody suggest a way to create this?
You can operate with vertices as you want.
Just a simple concept:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.setScalar(10);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
var cylGeom = new THREE.CylinderBufferGeometry(1, 1, 5, 16);
var vertices = cylGeom.attributes.position;
// change upper vertices
var v3 = new THREE.Vector3(); // temp vector
for (let i = 0; i < vertices.count; i++) {
v3.fromBufferAttribute(vertices, i); // set the temp vector
v3.y = v3.y > 0 ? (v3.x * 0.5) + 2.5 : v3.y; // change position by condition and equation
vertices.setY(i, v3.y); // set Y-component of a vertex
}
var cylMat = new THREE.MeshLambertMaterial({
color: "aqua"
});
var cyl = new THREE.Mesh(cylGeom, cylMat);
scene.add(cyl);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera)
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
I'm new in three.js, and my first feature was to create a box geometry which can increased from only one side.
Problem : When you increase width or height of an object the two sides automatically increased.
jsFiddle Example
So i lost 1 hour, to find the good algorythm :
geometry = new THREE.BoxGeometry(strength, 200, 200);
material = new THREE.MeshBasicMaterial({
color: 0xff0000
});
mesh = new THREE.Mesh(geometry, material);
mesh.applyMatrix( new THREE.Matrix4().makeTranslation( - strength + strength / 2, 0, 0 ) );
Someone can explain me: - strength + strength / 2 (If i increase the strength by 1 the translation is only -0.5 not -1 ?)
What is the name of this sort of algorythm, where i can find good ressources to learn this purpose (beginner)?
Also, you can shift the geometry with .translate() method, thus you won't have a container object in the scene graph:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(2, 3, 5);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
scene.add(new THREE.GridHelper(10, 10));
var boxGeom = new THREE.BoxGeometry();
boxGeom.translate(0.5, 0.5, 0); // pivot point is shifted
var box = new THREE.Mesh(boxGeom, new THREE.MeshNormalMaterial());
scene.add(box);
var clock = new THREE.Clock();
var delta = 0;
var time = 0;
render();
function render() {
requestAnimationFrame(render);
delta = clock.getDelta();
time += delta;
box.scale.set(2.5 + Math.sin(time) * 2, 1.5, 1.5);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
Another option:
var container = new THREE.Object3D()
var boxMesh = new THREE.Mesh(new THREE.BoxGeometry(1,1,1));
boxMesh.position.set(0.5,0.5,0.5)
container.add(boxMesh)
scene.add(container)
container.scale.set(strength,200,200);
https://en.wikipedia.org/wiki/Scene_graph
I'm using ArrowHelpers to model and display forces on an object. The length of the vector represents the magnitude of the force.
But: modifying the length of my arrowhelper does not seem to have any visible effect. When viewing the scene, all of my ArrowHelpers appear to have the length that was assigned to them during construction. The direction of the vector is properly updated. What am I doing wrong? Here's my code:
// construction:
ARROW_GRAVITY = new THREE.ArrowHelper( dir, origin, length, 0xDD3377 );
SCENE.add( ARROW_GRAVITY );
// updating scene:
ARROW_GRAVITY.position.copy(BULLET.position);
var dirGrav = new THREE.Vector3( 0, -1, 0 );
dirGrav.normalize(); // not necessary in this case, I know
ARROW_GRAVITY.setDirection(dirGrav);
var gravForce = PROJ_MASS * 9.8;
ARROW_GRAVITY.length = gravForce;
ARROW_GRAVITY.updateMatrix();
console.log(ARROW_GRAVITY.length); // works as expected (returns gravForce)
See the documentation for how to change the length of ArrowHelper.
arrowHelper.setLength( length );
Note: there are additional optional arguments.
three.js r.93
In addition to WestLangley's answer, .setLength() with parameters works as expected:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(2, 3, 5).setLength(4);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x101000);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var dir = new THREE.Vector3(0, 1, 0);
var arrow = new THREE.ArrowHelper(dir, scene.position, 2);
scene.add(arrow);
scene.add(new THREE.GridHelper(4, 4));
var clock = new THREE.Clock();
var time = 0;
render();
function render() {
requestAnimationFrame(render);
time += clock.getDelta();
arrow.setLength(Math.cos(time) * 0.5 + 1.5, 0.5, 0.1);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/93/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
First image is what I want. (I even want the opposite diagonal too, but I will derive that accordingly.) Second image is a normal GridHelper.
Would this be possible by adding another grid helper but rotated along the y-axis by 45 degrees?
I cannot seem to find anything in the official docs, so I am assuming that there is an alternate approach. Any pointers on how this could be done?
My code for the GridHelper:
grid = new THREE.GridHelper(80, 15, 0x000000, 0x000000);
grid.position.y = -0.2;
scene.add(grid);
I am thinking that a possible solution may lie in EdgesGeometry. I am still unsure about how to proceed with this, since:
EdgesGeometry( geometry, thresholdAngle )
Where geometry is any geometry object.
Does the GridHelper count as a geometry object? Here are the official docs.
EDIT:
Here is a visual example of my current situation: codepen example.
Using of THREE.PlaneBufferGeometry() and a material with wireframe: true will give you the desired result.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 50, 25);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var gridGeometry = new THREE.PlaneBufferGeometry(80, 80, 15, 15);
gridGeometry.rotateY(Math.PI);
gridGeometry.rotateX(-Math.PI * .5);
var gridMaterial = new THREE.MeshBasicMaterial({
color: "black",
wireframe: true
});
var gridWithDiagonals = new THREE.Mesh(gridGeometry, gridMaterial);
scene.add(gridWithDiagonals);
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>