I was reading the tutorial on how to draw lines in three.js documentation and the code used looked like this:
The code is fine and have no problems.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 0; }
</style>
</head>
<body>
<script src="///C:/Users/pc/Desktop/threejs_tutorial/build_threejs.html"></script>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 500 );
camera.position.set( 0, 0, 100 );
camera.lookAt( 0, 0, 0 );
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
//create a blue LineBasicMaterial
const material = new THREE.LineBasicMaterial( { color: 0x0000ff } );
const points = [];
points.push( new THREE.Vector3( - 10, 0, 0 ) );
points.push( new THREE.Vector3( 0, 10, 0 ) );
points.push( new THREE.Vector3( 10, 0, 0 ) );
const geometry = new THREE.BufferGeometry().setFromPoints( points );
const line = new THREE.Line( geometry, material );
scene.add( line );
renderer.render( scene, camera );
</script>
</body>
</html>
But what caught my eye is the const points = [], points.push and Vector3 part of the code.
const points = [];
points.push( new THREE.Vector3( - 10, 0, 0 ) );
points.push( new THREE.Vector3( 0, 10, 0 ) );
points.push( new THREE.Vector3( 10, 0, 0 ) );
I don't understand the function of const points = []; and why was it used.
I don't understand the function of points.push and why was it used.
The Three.Vector3 command looks like it position points in 3D plane. For this case it looks like this:
How do we visualize the Vector3 in three.js and why is the output looked like this?
const points = []; creates an empty Array[MDN] object, that is, an Array with no elements, which has .length of zero.
points.push( new THREE.Vector3( - 10, 0, 0 ) ); adds the new Vector3 to the end of the array. Since the array starts with zero elements, the first call adds a first element; the second adds a second element; etc.
For why the geometry is displayed with line segments, compare Line with Points.
Ahhh yes...this is actually the explanation of the purpose of these snippets of code.
The purpose of
const points = [];
is make the points created by Vector3 "real" because calling Vector3 alone merely states the position of point. Calling the array and putting Vector3 in the array creates a real, specified point because array is an object, so the point specified by the Vector3 in the array becomes an object too.
The purpose of
points.push( new THREE.Vector3( - 10, 0, 0 ) );
points.push( new THREE.Vector3( 0, 10, 0 ) );
and
points.push( new THREE.Vector3( 10, 0, 0 ) );
is to position the desired point using Vector3 within the viewing frustrum of the camera...
Related
I intend to create an app that changes the color of reflected light by using the slider created via three.js GUI.
The HTML code I used to change the color of light at her will is:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>How to put GUI in three.js app?</title>
<style>
body { margin: 0; }
</style>
</head>
<body>
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<!-- MODULE option is more difficult to use in this simple setup:
<script src="https://threejs.org/examples/jsm/libs/lil-gui.module.min.js"></script>
-->
<script src="https://cdn.jsdelivr.net/npm/lil-gui#0.17"></script>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set( 0, 0, 50 );
camera.lookAt( 0, 0, 0 );
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const geometry2 = new THREE.SphereGeometry( 50, 32, 16 );
const texture2 = new THREE.TextureLoader().load( 'https://i.imgur.com/P4PQzYk.jpg' );
const material2 = new THREE.MeshPhongMaterial( { map: texture2 } );
const sphere2 = new THREE.Mesh( geometry2, material2 );
scene.add( sphere2 );
sphere2.position.set(100, 0, -500);
function greenDoge(newGreen)
{
const green = newGreen;
let colorofsun = new THREE.Color ("rgb( 255, green, 255");
const light = new THREE.PointLight( colorofsun , 2, 5000, 2 );
light.position.set( 0, 0, 0 );
if (scene) {
scene.remove(light)
}
scene.add( light );
}
greenDoge(10);
const GUI = lil.GUI;
const gui = new GUI();
const params = {
green: 0
}
const folderDoge = gui.addFolder( 'Change Color' );
folderDoge.add( params, 'green', 0, 255, 1).onChange( greenDoge );
folderDoge.open();
const controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.update();
function animate()
{
sphere2.rotation.x += 0.00;
sphere2.rotation.y += 0.01;
renderer.render( scene, camera );
requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
</script>
</body>
</html>
The HTML code did work, but when I tried to adjust the color of the light, the sphere remains white and the web browser becomes noticeably slower.
I don't know where I messed up. I couldn't find any reason why the sphere can't change color when I slide the slider GUI.
What is the reason why the color didn't change?
How do we also modify the code such that the color can be changed to our will? Any hints will be very welcome.
Thank you!
First of all there are very obvious errors in your code.
You're missing a closing bracket in your new THREE.Color constructor call and also you're trying to use a variable in a string, which will not work.
so this:
let colorofsun = new THREE.Color ("rgb( 255, green, 255");
should more look like this:
let colorofsun = new THREE.Color ("rgb( 255, " + green + ", 255 )");
Fixing this however does not fix your problem as there are many other issues. I think you should start reading more documentation and code samples.
but lets stick to your problem:
in your greenDoge function, you're creating a new instance of PointLight and after that you want to remove it from the scene. this will never work as a new created light cannot be in the scene without adding it first.
You would need to give your light a name to remove it and update your materials to have it added for that to work. You might want to read here.
However adding and removing the light every time the color changes is not necessary at all.
What you could do instead is just adding the light once and update its color like this:
let colorofsun = new THREE.Color ("rgb( 255, 255, 255)");
var light = new THREE.PointLight( colorofsun , 2, 5000, 2 );
light.position.set( 0, 0, 0 );
scene.add( light );
function greenDoge(newGreen)
{
light.color.setRGB(1, newGreen, 1);
}
note that the setRGB function is using values from 0 to 1. so you need to change your slider values accordingly.
folderDoge.add( params, 'green', 0, 1, 0.1).onChange( greenDoge );
You can read more about that on the github page of threejs.
I'm working on a AR project, where an objects detection AI return the vertices of the detected object.
On the scene I have the detected object as the parent mesh with a BufferGeometry and the position attributes updated from the AI output, I need to calculate its transformation matrix when the vertices change and apply those transformation to its children.
How can I calculate the transformation matrix (Translation, Scaling, Rotation) from one "detection (position vertices)" to another.
Here is a simplified illustration of my problem, where the blue plane is the detected object and the red one its child, I need to calculate the blue plane transformation from its previous position apply them to red one so they can move together :
https://jsfiddle.net/uv76tj89/1/
Thanks.
If you know that the child geometry is going to be half as big as the parent geometry, just apply child.scale.set(0.5, 0.5, 0.5); and then assign the exact same vertex positions as the parent on update with:
parentGeometry.getAttribute('position').array = parentPositions[posIndex];
parentGeometry.getAttribute('position').needsUpdate = true;
childGeometry.getAttribute('position').array = parentPositions[posIndex];
childGeometry.getAttribute('position').needsUpdate = true;
the Three.js engine will apply the 1/2 scale and +5 to the z axis to the child vertices, so you don't have to worry about manually making these adjusments. See the demo below.
(Notice I created Float32Arrays within parentPositions[] as an optimization so you don't have to make a new array and a new BufferAttribute each time you update them. It's not a noticeable performance boost with just 4 vertices, but it does help when you have 1000's of vertices).
var scene = new THREE.Scene();
var camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 1, 1000 );
var renderer = new THREE.WebGLRenderer({antialias : true});
renderer.setClearColor(0x444444);
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
camera.position.z = 100;
new THREE.OrbitControls( camera, renderer.domElement );
/* Helpers */
// Grid helper
var width = 100;
var height = 100;
var gridHelper = new THREE.GridHelper(width * 2, 10, 0x999999, 0x000000);
gridHelper.position.y = -height / 2;
scene.add(gridHelper)
// Axes helper
scene.add(new THREE.AxesHelper(50));
/* parent Mesh */
var parentGeometry = new THREE.PlaneBufferGeometry(width, height);
var parentMaterial = new THREE.MeshBasicMaterial( { color: 0x209ad6} );
var parent = new THREE.Mesh( parentGeometry, parentMaterial );
scene.add(parent);
/* Child mesh */
var childGeometry = new THREE.PlaneBufferGeometry(width, height);
var childMaterial = new THREE.MeshBasicMaterial( {color: 0xFF0000} );
var child = new THREE.Mesh( childGeometry, childMaterial );
parent.add(child);
// Apply desired transformations to the child Mesh
child.position.z = 5;
child.scale.set(0.5, 0.5, 0.5);
/* Parent positions */
// Make all position arrays Float32Array
// so we don't have to create a new one each frame.
var parentPositions = [
//Reference point
new Float32Array([
-50, 50, 0,
50, 50, 0,
-50, -50, 0,
50, -50, 0
]),
//Variations
new Float32Array([
-50, 50, 50,
50, 50, 50,
-50, -50, -50,
50, -50, -50
]),
new Float32Array([
-75, 75, -25,
75, 75, -25,
-75, -75, 25,
75, -75, 25
]),
new Float32Array([
0, 75, -25,
75, 0, -25,
-75, 0, 25,
0, -75, 25,
]),
//... random positions
];
var lastTime = 0;
var posIndex = 0;
function render(currentTime) {
requestAnimationFrame( render );
// Update position attributes
// Instead of making a new attribute on each update
if (currentTime >= lastTime + 1000) {
parentGeometry.getAttribute('position').array = parentPositions[posIndex];
parentGeometry.getAttribute('position').needsUpdate = true;
childGeometry.getAttribute('position').array = parentPositions[posIndex];
childGeometry.getAttribute('position').needsUpdate = true;
lastTime = currentTime;
posIndex = posIndex === 3 ? 0 : posIndex + 1;
}
renderer.render( scene, camera );
}
render()
html, body {margin: 0; padding: 0;overflow: hidden;}
<script src="https://cdn.jsdelivr.net/npm/three#0.117.1/build/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r110/examples/js/controls/OrbitControls.js"></script>
I'm not sure why you originally made the child a PlaneGeometry, but I had to change it to match the parent PlaneBufferGeometry.
Edit:
I guess I'm just now starting to understand what your problem is, and you'd need to calculate the vector that's perpendicular to your plane face. You can pick any three out of the four vertices to do this:
You can use this answer to figure out which point the triangle is pointing towards, and then you can make the child point in that direction with child.lookAt(x, y, z);
You can read this article for a little more in-depth explanation on how to get that perpendicular.
Im trying to implement line with thinkness and I found this example
However the example uses:
var points = GeometryUtils.hilbert3D( new THREE.Vector3( 0, 0, 0 ), 20.0, 1, 0, 1, 2, 3, 4, 5, 6, 7 );
I do not want to use this and instead i want to create the line with an Array of Vector3 points.
var geometry = new LineGeometry();
geometry.setPositions( positions );
geometry.setColors( colors );
matLine = new LineMaterial( {
color: 0xffffff,
linewidth: 5, // in pixels
vertexColors: true,
//resolution: // to be set by renderer, eventually
dashed: false
} );
line = new Line2( geometry, matLine );
line.computeLineDistances();
line.scale.set( 1, 1, 1 );
scene.add( line );
Basically, in the example it uses positions, I want to use points instead.
Thanks
It's not possible to pass an array of THREE.Vector3() to THREE.LineGeometry. However, you just have to convert your data to this pattern [ x1, y1, z1, x2, y2, z2, ... ] and the setup should work fine.
let camera, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 10 );
camera.position.set( 0.5, 0.5, 2 );
scene = new THREE.Scene();
const points = [
0, 0, 0,
1, 0, 0,
1, 1, 0,
0, 1, 0
];
const geometry = new THREE.LineGeometry();
geometry.setPositions( points );
const material = new THREE.LineMaterial( { color: 0xffffff, linewidth: 2 } );
material.resolution.set( window.innerWidth, window.innerHeight );
const lines = new THREE.Line2( geometry, material );
scene.add( lines );
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 );
renderer.render( scene, camera );
}
body {
margin: 0;
}
canvas {
display: block;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.115/build/three.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115/examples/js/lines/LineSegments2.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115/examples/js/lines/Line2.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115/examples/js/lines/LineMaterial.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115/examples/js/lines/LineSegmentsGeometry.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115/examples/js/lines/LineGeometry.js"></script>
I want to create group of lines.
First line:
var geometry = new THREE.Geometry();
itemLine = new THREE.Line( geometry, material );
Current geometry:
geometry.vertices[0]
Object { x: -540, y: 50, z: 0 }
After, i create group and set position:
item = new THREE.Group();
item.attach(itemLine);
item.position.set( centerPoint.x, centerPoint.y, 0 );
centerPoint is not (0,0,0)
After create group and attach children i saw in console:
geometry.vertices[0]
Object { x: -540, y: 50, z: 0 }`
Vertices not updated! I want new coords (local and world), with offset position of group.
Geometry.vertices defines the geometry in local space. It does not matter if you transform the 3D object or its ancestors, the vertices will always keep the same values.
You can achieve your desired result by transforming the vertices to world space via the lines world matrix. Here is a complete example code of this workflow.
var geometry = new THREE.Geometry();
geometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) );
geometry.vertices.push( new THREE.Vector3( 1, 0, 0 ) );
var material = new THREE.LineBasicMaterial( { color: 0xff0000 } );
var lines = new THREE.Line( geometry, material );
var group = new THREE.Group();
group.add( lines );
group.position.set( 2, 0, 0 );
group.updateMatrixWorld(); // update world matrices of the hierarchy of 3D objects
scene.add( group );
const vertex = new THREE.Vector3();
vertex.copy( geometry.vertices[ 0 ] ).applyMatrix4( lines.matrixWorld );
console.log( vertex ); // prints {x: 2, y: 0, z: 0}
Demo: https://jsfiddle.net/sy6ur1x7/
three.js R112
Using threejs, I'm trying to draw a line from the camera position to the origin.
I expect that the line will always point to the middle of the screen (assuming that the renedered scene is viewed from the camera origin)
but it does not (the line always points towards the side of the screen).
The code below is a subset of the formal threejs webgl_geometry_extrude_shapes.html example.
I added the function draw_line_from_camera_to_origin(scene, camera)
What am I doing wrong?
Thanks,
Avner
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - geometry - extrude shapes</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: #222;
margin: 0px;
overflow: hidden;
}
a {
color: #f80;
}
</style>
</head>
<body>
<script src="../build/three.js"></script>
<script src="js/controls/TrackballControls.js"></script>
<script>
var container;
var camera, scene, renderer, controls;
init();
animate();
function draw_line_from_camera_to_origin(scene, camera)
{
// Draw line from camera to origin
var pointA2 = new THREE.Vector3( camera.position.x, camera.position.y, camera.position.z );
var pointB2 = new THREE.Vector3( 0, 0, 0 );
var geometry2 = new THREE.Geometry();
geometry2.vertices.push( pointA2 );
geometry2.vertices.push( pointB2 );
var material2 = new THREE.LineBasicMaterial( { color : 'yellow' } );
var line2 = new THREE.Line( geometry2, material2 );
scene.add( line2 );
}
function init() {
var info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.style.color = '#fff';
info.style.link = '#f80';
info.innerHTML = 'three.js webgl - geometry extrude shapes';
document.body.appendChild( info );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x222222 );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 0, 0, 500 );
controls = new THREE.TrackballControls( camera, renderer.domElement );
controls.minDistance = 200;
controls.maxDistance = 500;
scene.add( new THREE.AmbientLight( 0x222222 ) );
var light = new THREE.PointLight( 0xffffff );
light.position.copy( camera.position );
scene.add( light );
//
var closedSpline = new THREE.CatmullRomCurve3( [
new THREE.Vector3( -60, -100, 60 ),
new THREE.Vector3( -60, 20, 60 ),
new THREE.Vector3( -60, 120, 60 ),
new THREE.Vector3( 60, 20, -60 ),
new THREE.Vector3( 60, -100, -60 )
] );
closedSpline.curveType = 'catmullrom';
closedSpline.closed = true;
var extrudeSettings = {
steps : 100,
bevelEnabled : false,
extrudePath : closedSpline
};
var pts = [], count = 3;
for ( var i = 0; i < count; i ++ ) {
var l = 20;
var a = 2 * i / count * Math.PI;
pts.push( new THREE.Vector2 ( Math.cos( a ) * l, Math.sin( a ) * l ) );
}
var shape = new THREE.Shape( pts );
var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
var material = new THREE.MeshLambertMaterial( { color: 0xb00000, wireframe: false } );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
draw_line_from_camera_to_origin(scene, camera);
}
function animate() {
requestAnimationFrame( animate );
controls.update();
renderer.render( scene, camera );
}
</script>
</body>
</html>
You need to update the location of the other point.
When you first create the line you tell it a point in space and it never moves from there. It may seem like its moving around but thats just because it is a very long line.
So you need to make geometry2 a global variable (at the top):
var camera, scene, renderer, controls, geomerty2;
Then remove var when you declare geometry2 in draw_line_from_camera_to_origin
Then your animation loop should be:
function animate() {
geometry2.vertices[0].x = camera.position.x;
geometry2.vertices[0].y = camera.position.y;
geometry2.vertices[0].z = camera.position.z;
geometry2.verticesNeedUpdate = true;
requestAnimationFrame( animate );
controls.update();
renderer.render( scene, camera );
}
Now when you move the camera you see the line move with it then drift back to the camera's position. I'm not sure why it does that, I was actually expecting the line to be invisible (because lines have 0 width, and its pointing directly at the camera).
But anyways, happy coding haha hello world 42