Im developing an web-app that is supposed to create a 3d model of a gravel pile
from points measured with a laser instrument with three.js. The only problem is the
creating a hull that supposed to be the surface of the pile that includes all points. I already made a pointswarm model with the points and tried to make a hull around it with ConvexBufferGeometry but not all points get included in the hull when using ConvexBufferGeometry. Can someone please point me in the right direction?
You can try to combine Three.js with Delaunator library for triangulation of your points to make a surface:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, 1, 1, 1000);
camera.position.setScalar(150);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
var canvas = renderer.domElement;
document.body.appendChild(canvas);
var controls = new THREE.OrbitControls(camera, canvas);
var light = new THREE.DirectionalLight(0xffffff, 1.5);
light.position.setScalar(100);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
var size = { x: 200, y: 200 };
var pointsCount = 1000;
var points3d = [];
for (let i = 0; i < pointsCount; i++) {
let x = THREE.Math.randFloatSpread(size.x);
let z = THREE.Math.randFloatSpread(size.y);
let y = noise.perlin2(x / size.x * 5, z / size.y * 5) * 50;
points3d.push(new THREE.Vector3(x, y, z));
}
var geom = new THREE.BufferGeometry().setFromPoints(points3d);
var cloud = new THREE.Points(
geom,
new THREE.PointsMaterial({ color: 0x99ccff, size: 2 })
);
scene.add(cloud);
// triangulate by [x, z]
var indexDelaunay = Delaunator.from(
points3d.map(v => {
return [v.x, v.z];
})
);
var meshIndex = []; // delaunay index => three.js index
for (let i = 0; i < indexDelaunay.triangles.length; i++){
meshIndex.push(indexDelaunay.triangles[i]);
}
geom.setIndex(meshIndex); // add three.js index to the existing geometry
geom.computeVertexNormals();
var mesh = new THREE.Mesh(
geom, // re-use the existing geometry
new THREE.MeshLambertMaterial({ color: "purple", wireframe: true })
);
scene.add(mesh);
var gui = new dat.GUI();
gui.add(mesh.material, "wireframe");
render();
function resize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render() {
if (resize(renderer)) {
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
html, body {
height: 100%;
margin: 0;
overflow: hidden;
font-family: Verdana;
}
canvas {
width: 100%;
height: 100%;
display; block;
}
#info{
position: absolute;
margin-left: 10px;
}
a{
color: yellow;
text-decoration: none;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.115.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115.0/examples/js/controls/OrbitControls.js"></script>
<!-- https://github.com/mapbox/delaunator -->
<script src="https://unpkg.com/delaunator#3.0.2/delaunator.js"></script>
<script src="https://josephg.github.io/noisejs/perlin.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.3/dat.gui.min.js"></script>
<div id="info">
Delaunator<br>(triangulation)
</div>
Related
I want to create a grid of geometries (ROWS * COLUMNS) and wanna rotate each individual mesh around it's own center.
I generate the grid through two for loops, translate the single elements to there correct position and push them into an array to use them again in the animation function. In the animation function I iterate again over all the elements and rotate every single one.
the problem is that even though I address each element individually and I have translated each element to the right place, each element still revolves around the center of the page and not its own center (see the screenshot)
Here is my current attempt:
const main = () => {
const ROWS = 4;
const COLUMNS = 4;
const ITEMS = [];
const canvas = document.querySelector('#canvas');
const renderer = new THREE.WebGLRenderer({canvas, antialias: true});
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(1, window.innerWidth/window.innerHeight, 0.1, 1000);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor('#e5e5e5');
camera.position.z = 500;
// GEOMETRY
for (let x = 0; x < COLUMNS; x++) {
for (let y = 0; y < ROWS; y++) {
const geometry = new THREE.BoxGeometry(1, 1, 0.5);
const edges = new THREE.EdgesGeometry(geometry);
const edgesMaterial = new THREE.LineBasicMaterial({ color: 0x1940EB });
const edgesMesh = new THREE.LineSegments(edges, edgesMaterial);
ITEMS.push(edgesMesh);
edges.translate(x*1.5, y*1.5, 0);
scene.add(edgesMesh);
}
}
scene.position.set(-COLUMNS/2, -ROWS/2, 0);
const animation = () => {
requestAnimationFrame(animation);
ITEMS.map(item => {
item.rotation.x += 0.01;
item.rotation.y += 0.01;
})
camera.updateMatrixWorld();
renderer.render(scene, camera);
}
const onWindowResize = () => {
const w = window.innerWidth;
const h = window.innerHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
}
animation();
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
};
window.addEventListener('load', main, false);
body {
padding: 0;
margin: 0;
height: 100vh;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.js"></script>
<canvas id="canvas"></canvas>
Can someone explain to me why the elements individually addressed in the animation function still do not revolve around themselves?
Thanks for your help
Because the code is moving the geometry itself so that it's center is no longer in the middle of the box.
change
edges.translate(x*1.5, y*1.5, 0);
to
edgesMesh.position.set(x*1.5, y*1.5, 0);
This will move the Object3D in the scenegraph instead of the vertices of the geometry data.
const main = () => {
const ROWS = 4;
const COLUMNS = 4;
const ITEMS = [];
const canvas = document.querySelector('#canvas');
const renderer = new THREE.WebGLRenderer({canvas, antialias: true});
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(1, window.innerWidth/window.innerHeight, 0.1, 1000);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor('#e5e5e5');
camera.position.z = 500;
// GEOMETRY
for (let x = 0; x < COLUMNS; x++) {
for (let y = 0; y < ROWS; y++) {
const geometry = new THREE.BoxGeometry(1, 1, 0.5);
const edges = new THREE.EdgesGeometry(geometry);
const edgesMaterial = new THREE.LineBasicMaterial({ color: 0x1940EB });
const edgesMesh = new THREE.LineSegments(edges, edgesMaterial);
ITEMS.push(edgesMesh);
edgesMesh.position.set(x*1.5, y*1.5, 0);
scene.add(edgesMesh);
}
}
scene.position.set(-COLUMNS/2, -ROWS/2, 0);
const animation = () => {
requestAnimationFrame(animation);
ITEMS.map(item => {
item.rotation.x += 0.01;
item.rotation.y += 0.01;
})
camera.updateMatrixWorld();
renderer.render(scene, camera);
}
const onWindowResize = () => {
const w = window.innerWidth;
const h = window.innerHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
}
animation();
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
};
window.addEventListener('load', main, false);
body {
padding: 0;
margin: 0;
height: 100vh;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.js"></script>
<canvas id="canvas"></canvas>
You might find this article useful
I will like to put each of the spheres in different canvas so that I could manipulate their positions easily with CSS.
Then hover in each h2 to show display them one by one
I have been advised to refactor the code with ES6 Classes for constructing every canvas with the sphere inside.
how can I acchive this?
https://jsfiddle.net/zhampu/q5j42pu9/9/
const v = new THREE.Vector3()
const sceneElements = []
const spheres = []
// Renderer
const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector('#c'),
antialias: true,
alpha: true
})
renderer.setClearColor(0x000000, 0)
renderer.setPixelRatio(window.devicePixelRatio)
// renderer.setSize(window.innerWidth, window.innerHeight)
// Scene & Camera
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000)
// const camera = new THREE.PerspectiveCamera(70, 2, 1, 1000);
camera.position.z = 10
// Data Diagram
document.querySelectorAll('[data-diagram]').forEach((elem) => {
const sceneTexture = elem.dataset.diagram
const eachTexture = new THREE.TextureLoader().load(sceneTexture)
sceneElements.push(eachTexture)
return sceneElements
})
function makeBlob(sceneElements) {
for (var i = 0; i < sceneElements.length; i++) {
const sphere_geometry = new THREE.SphereBufferGeometry(1, 32, 16).toNonIndexed()
let material = new THREE.MeshBasicMaterial({
map: sceneElements[i]
})
const sphere = new THREE.Mesh(sphere_geometry, material)
const positionOfPiece = document.querySelectorAll('[data-diagram]')
sphere.position.x = 3 * i -3
// sphere.position.y = positionOfPiece.top
scene.add(sphere)
spheres.push(sphere)
}
}
makeBlob(sceneElements)
function animate() {
requestAnimationFrame(animate)
let time = performance.now() * 0.0005
resizeCanvasToDisplaySize()
for (let i = 0; i < spheres.length; i++) {
var sphere = spheres[i]
let k = 1
var positionAttribute = sphere.geometry.getAttribute('position')
for (let j = 0; j < positionAttribute.count; j++) {
v.fromBufferAttribute(positionAttribute, j)
v.normalize().multiplyScalar(1 + 0.3 * noise.perlin3(v.x * k + time, v.y * k, v.z * k))
positionAttribute.setXYZ(j, v.x, v.y, v.z)
}
positionAttribute.needsUpdate = true
sphere.rotation.y += 0.003
}
renderer.render(scene, camera)
}
animate()
function resizeCanvasToDisplaySize() {
const canvas = renderer.domElement
const width = canvas.clientWidth
const height = canvas.clientHeight
if (canvas.width !== width || canvas.height !== height) {
// you must pass false here or three.js sadly fights the browser
renderer.setSize(width, height, false)
camera.aspect = width / height
camera.updateProjectionMatrix()
// set render target sizes here
}
}
#c {
position: absolute;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
display: block;
z-index: -1;
}
*[data-diagram] {
display: inline-block;
width: 5em;
height: 3em;
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.115/build/three.js"></script>
<script src="https://www.fariskassim.com/stage/rebel9/teaf/blob/v4/js/perlin.js"></script>
<canvas id="c"></canvas>
<a class="pieces-list" href="" target="_self">
<h2>Publications</h2>
<span data-diagram="https://i.picsum.photos/id/1002/600/300.jpg"></span>
</a>
<a class="pieces-list" href="" target="_self">
<h2>Fashion</h2>
<span data-diagram="https://i.picsum.photos/id/1002/200/300.jpg"></span>
</a>
I have a three js Mesh on Click of a button the mesh need to be focus based on the button
for example on click of view top camera need to focus from the top of the mesh
in three.js is there any inbuilt method to focus a mesh or how to calculate top left front of a mesh?
var scene, camera, renderer, controls;
var ROTATE = true;
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, 450 / 450, 0.1, 1000);
renderer = new THREE.WebGLRenderer();
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.rotateUp(Math.PI * 0.1);
// controls.autoRotate = ROTATE; //true:自動回転する,false:自動回転しない
controls.autoRotateSpeed = 4.0; //自動回転する時の速度
renderer.setSize(465, 465);
renderer.shadowMapEnabled = true;
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 150;
camera.lookAt(scene.position);
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(100, 100, -100);
scene.add(spotLight);
var light = new THREE.DirectionalLight(0xffffff, 1.5);
light.position.set(100, 100, 100).normalize();
scene.add(light);
var light2 = new THREE.DirectionalLight(0xffffff);
light2.position.set(-100, -100, -100).normalize();
scene.add(light2);
document.body.appendChild(renderer.domElement);
var parser = new vox.Parser();
// yuusha.vox
parser.parse("https://rawcdn.githack.com/siouxcitizen/3DModel/b2d06e45f6b64c3e342b724a4ec5a0c427a4ce0a/vox/hiyoko.vox").then(function(voxelData) {
var builder = new vox.MeshBuilder(voxelData, {
voxelSize: 7.0,
vertexColor: true,
optimizeFaces: false,
});
var v0 = builder.createMesh();
v0.position.y -= 50;
scene.add(v0);
});
render();
}
function render() {
controls.update();
requestAnimationFrame(render);
renderer.render(scene, camera);
}
init();
function topView(){
alert("topView view")
}
function sideView(){
alert("sideview view")
}
function frontiew(){
alert("front view")
}
* {
margin: 0;
padding: 0;
border: 0;
overflow: hidden;
}
body {
background: #000;
font: 30px sans-serif;
}
button{
padding:5px 10px
}
<!-- three.min.js r87 -->
<button onClick={topView()}>top view</button>
<button onClick={frontiew()}>front view</button>
<button onClick={sideView()}>side view</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
<!-- vox.js v1.0.1 -->
<script src="https://cdn.jsdelivr.net/gh/daishihmr/vox.js#1.0.1/build/vox.min.js"></script>
<!-- Old OrbitControls.js -->
<script src="https://codepen.io/siouxcitizen/pen/XWWXZZN.js"></script>
the above is demo code.
I try to forcefully update camera position it won't work
You can get axis aligned bbox with new THREE.Box3().setFromObject(mesh)
and next - fit camera to this box.
Here is my code (typescript) to do it (works well with PerspetiveCamera and OrbitControls).
fitCameraTo(boundingBox: THREE.Box3) {
const camera = this._camera;
const objPosition = boundingBox.getCenter(new THREE.Vector3());
const objSize = boundingBox.getSize(new THREE.Vector3());
boundingBox.min.y = 0;
boundingBox.max.y = 0;
const boundingSphere = boundingBox.getBoundingSphere(new THREE.Sphere());
let dim = boundingSphere.radius * 2;
if (dim < camera.near) {
dim = camera.near;
}
const direction = THREE.Object3D.DefaultUp.clone(); // view direction
// object angular size
const fov = THREE.Math.degToRad(camera.fov);
let distance = dim / (2.0 * Math.tan(fov / 2.0));
if (camera.aspect <= 1) {
distance = distance / camera.aspect;
}
if (distance < camera.near) {
distance = objSize.y;
}
if (distance < camera.near) {
distance = camera.near;
}
camera.position.copy(objPosition.clone().add(direction.multiplyScalar(distance)));
if (this.this._orbitControls) {
this._orbitControls.target.copy(objPosition);
this._orbitControls.rotateLeft(Math.PI);
} else {
camera.lookAt(objPosition);
}
camera.updateProjectionMatrix();
}
i m following this guide for the light Light in Threejs
and i already add some light in my scene.
Now i m try to add a light on the character of my game, but still dont work.
i use the same code of the guide changing just the position.set
const color = 0xFFFFFF;
const intensity = 1;
const light2 = new THREE.SpotLight(color, intensity,0,Math.PI/3);
light2.position.set(100,-5000,1000);
light2.target = lightTarget;
light2.castShadow = true;
const helper2 = new THREE.SpotLightHelper(light2);
and after i add in this way to my character
self.flame.add( helper2 );
self.flame.add(lightTarget);
self.flame.add(light2);
I added a helper too, but if I use just the helper in the scene , so if comment
self.flame.add(light2)
I see the position of the light in perfect way, when add the light the helper disappear (in other light don't happened) and the light go as her want.
Someone can help me?
The helpers have to be parented to the scene (or at least the SpotLightHelper does). You may or may not want to parent the target to the scene.
You also need to call helper.update for each helper
'use strict';
/* global THREE, dat */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 45;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 100;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 10, 20);
const controls = new THREE.OrbitControls(camera, canvas);
controls.target.set(0, 5, 0);
controls.update();
const scene = new THREE.Scene();
scene.background = new THREE.Color('black');
{
const planeSize = 40;
const loader = new THREE.TextureLoader();
const texture = loader.load('https://threejsfundamentals.org/threejs/resources/images/checker.png');
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.magFilter = THREE.NearestFilter;
const repeats = planeSize / 2;
texture.repeat.set(repeats, repeats);
const planeGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
const planeMat = new THREE.MeshPhongMaterial({
map: texture,
side: THREE.DoubleSide,
});
const mesh = new THREE.Mesh(planeGeo, planeMat);
mesh.rotation.x = Math.PI * -.5;
scene.add(mesh);
}
const cubes = [];
let parent = scene;
{
const cubeSize = 1;
const cubeGeo = new THREE.BoxBufferGeometry(cubeSize, cubeSize, cubeSize);
const cubeMat = new THREE.MeshPhongMaterial({
color: '#8AC',
emissive: '#333',
});
for (let i = 0; i < 6; ++i) {
const mesh = new THREE.Mesh(cubeGeo, cubeMat);
mesh.position.set(1, 0, 0);
parent.add(mesh);
cubes.push(mesh);
parent = mesh;
}
}
cubes[0].position.set(-3, 7, 0);
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.SpotLight(color, intensity);
light.position.set(0, 0, 0);
light.target.position.set(0, -1, 0);
parent.add(light);
parent.add(light.target);
//scene.add(light.target);
const helper = new THREE.SpotLightHelper(light);
scene.add(helper);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
for (const cube of cubes) {
cube.rotation.z = Math.sin(time) * .4;
}
light.angle = THREE.Math.lerp(
THREE.Math.degToRad(20),
THREE.Math.degToRad(80),
Math.sin(time * 0.77) * 0.5 + 0.5);
helper.update();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
html, body {
margin: 0;
height: 100%;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/js/controls/OrbitControls.js"></script>
I have a simple scene in Three with some planes. Right now on click the planes move to random positions.
After a click, I'd like instead to move the planes into a new grid perpendicular to the camera, such that the projected grid's x and y axes are parallel with the screen's x and y axes.
Here's the scene:
// generate a scene object
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
// generate a camera
var aspectRatio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.01, 10000);
// position the camera
camera.position.set(51.389, -451.056, 839.455);
var rotObjectMatrix = new THREE.Matrix4();
var q = {_x: 0.0184, _y: -0.2122, _z: 0.9770, _w: -0.0081};
rotObjectMatrix.makeRotationFromQuaternion(q);
camera.quaternion.setFromRotationMatrix(rotObjectMatrix);
camera.up.set(0.00806, -0.91008, -0.41432);
// generate a renderer
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio); // <3 retina
renderer.setSize(window.innerWidth, window.innerHeight); // canvas size
document.body.appendChild(renderer.domElement);
// generate controls
var controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.target.set(19.053, -111.316, 93.996);
// generate some lights
var ambientLight = new THREE.AmbientLight(0xeeeeee);
scene.add(ambientLight);
// render loop
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
controls.update();
};
// draw a grid
var grid = new THREE.GridHelper(2000, 20, 0x000000, 0x000000);
grid.material.opacity = 0.2;
grid.material.transparent = true;
grid.rotation.x = -Math.PI;
scene.add(grid);
// draw some
planes = [];
for (var i=0; i<2**10; i++) {
var geometry = new THREE.PlaneGeometry( 20, 20, 32 );
var material = new THREE.MeshBasicMaterial(
{color: 0xff0000, side: THREE.DoubleSide} );
var plane = new THREE.Mesh( geometry, material );
var x = ((i % 2**5) * 40) - (2**5 * 40)/2;
var z = (Math.floor(i/2**5) * 40) - (2**5 * 40)/2;
plane.position.set(x, 0, z);
scene.add(plane);
planes.push(plane);
}
// transition the planes on body click
document.querySelector('body').addEventListener('click', function() {
planes.forEach(function(plane) {
// placeholder
plane.position.set(
Math.random() * 500 - (Math.random() * 500)/2,
0,
Math.random() * 500 - (Math.random() * 500)/2,
)
})
})
render();
html, body { width: 100%; height: 100%; background: #000; }
body { margin: 0; overflow: hidden; }
canvas { width: 100%; height: 100%; }
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/TrackballControls.js'></script>
Using the following comes close, but doesn't tilt the planes such that they're perpendicular to the camera:
planes.forEach(function(plane) {
// close to projection discussed above...
plane.position.set(
plane.position.x,
plane.position.z,
0,
)
})
Does anyone know how to achieve the projection described above? Any suggestions others can offer would be greatly appreciated!
This is a bit of a shortcut, but it'll help you avoid a lot of math calculations: Just group your planes into a Group and then use Group.lookAt(camera.position) to point them all in unison towards the camera.
// generate a scene object
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
// generate a camera
var aspectRatio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.01, 10000);
// position the camera
camera.position.set(51.389, -451.056, 839.455);
var rotObjectMatrix = new THREE.Matrix4();
var q = {_x: 0.0184, _y: -0.2122, _z: 0.9770, _w: -0.0081};
rotObjectMatrix.makeRotationFromQuaternion(q);
camera.quaternion.setFromRotationMatrix(rotObjectMatrix);
// Not sure why you're changing camera's up axis, but this will
// need to be duplicated on planeGroup;
camera.up.set(0.00806, -0.91008, -0.41432);
// generate a renderer
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio); // <3 retina
renderer.setSize(window.innerWidth, window.innerHeight); // canvas size
document.body.appendChild(renderer.domElement);
// generate controls
var controls = new THREE.TrackballControls(camera, renderer.domElement);
// If you change the pos where the camera is looking at,
// you'll need to place planeGroup at this position.
controls.target.set(19.053, -111.316, 93.996);
// generate some lights
var ambientLight = new THREE.AmbientLight(0xeeeeee);
scene.add(ambientLight);
// render loop
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
controls.update();
};
// draw a grid
var grid = new THREE.GridHelper(2000, 20, 0x000000, 0x000000);
grid.material.opacity = 0.2;
grid.material.transparent = true;
grid.rotation.x = -Math.PI;
scene.add(grid);
// draw some
planes = [];
var planeGroup = new THREE.Group();
for (var i=0; i<2**10; i++) {
var geometry = new THREE.PlaneGeometry( 20, 20, 32 );
var material = new THREE.MeshBasicMaterial(
{color: 0xff0000, side: THREE.DoubleSide} );
var plane = new THREE.Mesh( geometry, material );
var x = ((i % 2**5) * 40) - (2**5 * 40)/2;
var y = (Math.floor(i/2**5) * 40) - (2**5 * 40)/2;
// Setting x,y instead of x,z
plane.position.set(x, y, 0);
planeGroup.add(plane);
planes.push(plane);
}
scene.add(planeGroup);
// Copying camera.up and controls.target from above
planeGroup.up.copy(camera.up);
planeGroup.position.copy(controls.target);
planeGroup.lookAt(camera.position);
// transition the planes on body click
document.querySelector('body').addEventListener('click', function() {
planes.forEach(function(plane) {
// placeholder
plane.position.set(
Math.random() * 500 - (Math.random() * 500)/2,
0,
Math.random() * 500 - (Math.random() * 500)/2,
)
})
})
render();
html, body { width: 100%; height: 100%; background: #000; }
body { margin: 0; overflow: hidden; }
canvas { width: 100%; height: 100%; }
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/TrackballControls.js'></script>
Then, if you want to randomize the plane's positions, you can still do that, but all you have to worry about afterward is to return them to their original x,y,0 positions, and they'll automatically line up "looking at" the camera.
EDIT: I had to copy the changes you made to camera.up and controls.target for this to work. If you change these 2 attributes, you'll need to copy them to planeGroup for this approach to work.
Here's the doc on the .lookAt() method