I want to turn an image into a Point Cloud and offset the z-position based on the color value. So far I have:
Loaded an image, and upon load
Built a geometry and stored image colour within the geometry
Create a PointsMaterial and build a THREE.Points
Add to scene
However, the result looks nothing like the input image. I am missing something but unsure of what.
What am I missing with regards to displaying a point cloud version of the image?
Example: https://img2pointcloud.glitch.me/
<html>
<head>
<script src="https://threejs.org/build/three.js"></script>
</head>
<body style="margin:0">
<script>
var container, renderer, points;
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
1,
10000
);
camera.position.set(0, 0, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var imgSrc =
"https://cdn.glitch.com/c3afecd9-365c-424f-a08e-90fce02a151a%2Fimg.jpeg?v=1588102020636";
var img = new Image();
var width = 1920 / 4;
var height = 1080 / 4;
img.crossOrigin = "anonymous";
img.src = imgSrc;
img.onload = function() {
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var imageData = ctx.getImageData(0, 0, width, height);
var data = imageData.data;
document.body.appendChild(canvas);
createPointCloud(imageData);
};
function createPointCloud(imageData) {
var geometry = new THREE.BufferGeometry();
var positions = [];
for (var x = 0; x < height; x++) {
for (var z = 0; z < width; z++) {
positions.push(x, z, x);
}
}
var color = new THREE.Color();
var colors = [];
for (let i = 0; i < imageData.data.length; i += 4) {
const r = imageData.data[i + 0];
const g = imageData.data[i + 1];
const b = imageData.data[i + 2];
const a = imageData.data[i + 3];
color.setRGB(r, g, b);
colors.push(color.r, color.b, color.c);
}
geometry.setAttribute(
"position",
new THREE.Float32BufferAttribute(positions, 3)
);
geometry.setAttribute(
"color",
new THREE.Float32BufferAttribute(colors, 3)
);
geometry.computeBoundingSphere();
var material = new THREE.PointsMaterial({
size: 0.1,
vertexColors: true
});
points = new THREE.Points(geometry, material);
scene.add(points);
animate();
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
</script>
</body>
</html>
[1]: https://i.stack.imgur.com/9etFB.png
[2]: https://i.stack.imgur.com/HKM7m.png
Another approach is to modify shaders of THREE.PointsMaterial():
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(-75, 0, 1);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
new THREE.TextureLoader().load("https://cdn.glitch.com/c3afecd9-365c-424f-a08e-90fce02a151a%2Fimg.jpeg?v=1588102020636", tex => {
let img = tex.image;
console.log(img.width, img.height);
let g = new THREE.PlaneBufferGeometry(Math.floor(img.width / 4), Math.floor(img.height / 4), img.width, img.height);
let m = new THREE.PointsMaterial({
map: tex,
size: 0.1
});
m.onBeforeCompile = shader => {
shader.vertexShader = `
varying vec2 vUv;
${shader.vertexShader}
`;
shader.vertexShader = shader.vertexShader.replace(
`#include <color_vertex>`,
`
vUv = uv;
#include <color_vertex>`
);
shader.fragmentShader = `
varying vec2 vUv;
${shader.fragmentShader}
`;
shader.fragmentShader = shader.fragmentShader.replace(
`#include <map_particle_fragment>`,
`vec4 mapTexel = texture2D( map, vUv );
diffuseColor = mapTexel;
`
);
console.log(shader.vertexShader);
};
let p = new THREE.Points(g, m);
scene.add(p);
});
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>
There's a big typo in my code where it for some reason says colors.push(color.r, color.b, color.c); and not colors.push(color.r, color.g, color.b); (notice the rbc vs rgb).
Sorted now, thanks for the comment, made me see it.
Related
I'm trying to keep a texture centered when it's shown in a different sized box.
I've seen this answer
Three.js: Make image texture fit object without distorting or repeating
But it's not quite doing it for me.
this.texture = new THREE.Texture(this.image)
const vec = new THREE.Vector3()
new THREE.Box3().setFromObject( this.rounded ).getSize(vec)
const imageAspect = this.image.width/this.image.height
const boxAspect = vec.x/vec.y
this.texture.wrapT = THREE.RepeatWrapping;
this.texture.offset.y = 0.5 * ( 1 - boxAspect/imageAspect )
//texture.wrapT = THREE.RepeatWrapping; texture.repeat.x = geometryAspectRatio / imageAspectRatio; texture.offset.x = 0.5 * ( 1 - texture.repeat.x )
this.texture.needsUpdate = true
this.rounded.material = new THREE.MeshPhongMaterial( { map: this.texture, side: THREE.DoubleSide } )
In this aspect the values are
Image: {width:399 height:275}
Texture: {width:1, height: 0.75}
In this aspect the values are
Image: {width:399 height:275}
Texture: {width:2, height: 1}
How do I fix it so the graphic is always central, maintains the aspect and is not distorted?
I hope I got you correctly, here is an option of how you can center it:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
var planeGeom = new THREE.PlaneBufferGeometry(16, 9);
var planeMat = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/758px-Canestra_di_frutta_(Caravaggio).jpg", tex => {
//console.log(tex);
//console.log(tex.image.width, tex.image.height);
let imgRatio = tex.image.width / tex.image.height;
let planeRatio = planeGeom.parameters.width / planeGeom.parameters.height;
//console.log(imgRatio, planeRatio);
tex.wrapS = THREE.RepeatWrapping; // THREE.ClampToEdgeWrapping;
tex.repeat.x = planeRatio / imgRatio;
tex.offset.x = -0.5 * ((planeRatio / imgRatio) - 1);
})
});
var plane = new THREE.Mesh(planeGeom, planeMat);
scene.add(plane);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
})
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
Addition: how to re-compute UVs for ShapeBufferGeometry
var box = new THREE.Box3().setFromObject(mesh); // mesh with ShapeBufferGeometry
var size = new THREE.Vector3();
box.getSize(size);
var vec3 = new THREE.Vector3(); // temp vector
var attPos = mesh.geometry.attributes.position;
var attUv = mesh.geometry.attributes.uv;
for (let i = 0; i < attPos.count; i++){
vec3.fromBufferAttribute(attPos, i);
attUv.setXY(i,
(vec3.x - box.min.x) / size.x,
(vec3.y - box.min.y) / size.y
);
}
attUv.needsUpdate = true; // just in case
I'm trying to add a THREE.js scene into a Mapbox GL visualization following this example. I've added a sphere and a ground plane and a DirectionalLight. Now I'm trying to get the light to cast a shadow on the ground plane. Adding a DirectionalLightHelper and a CameraHelper for the light's shadow camera, everything looks pretty reasonable to me:
I'd expect to see a shadow for the sphere on the plane.
Full code here, but here are the highlights:
class SpriteCustomLayer {
type = 'custom';
renderingMode = '3d';
constructor(id) {
this.id = id;
this.gui = new dat.GUI();
THREE.Object3D.DefaultUp.set(0, 0, 1);
}
async onAdd(map, gl) {
this.camera = new THREE.Camera();
const centerLngLat = map.getCenter();
this.center = MercatorCoordinate.fromLngLat(centerLngLat, 0);
const {x, y, z} = this.center;
this.cameraTransform = new THREE.Matrix4()
.makeTranslation(x, y, z)
.scale(new THREE.Vector3(1, -1, 1));
this.map = map;
this.scene = this.makeScene();
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true,
});
this.renderer.shadowMap.enabled = true;
this.renderer.autoClear = false;
}
makeScene() {
const scene = new THREE.Scene();
scene.add(new THREE.AmbientLight(0xffffff, 0.25));
const s = this.center.meterInMercatorCoordinateUnits();
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0.000002360847837325531, 0.000004566603480958114, 0.00000725142167844218);
light.target.position.set(0, 0, 0);
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
light.shadow.camera.left = -0.000002383416166278454 * 2;
light.shadow.camera.right = 0.000002383416166278454 * 2;
light.shadow.camera.bottom = -0.000002383416166278454 * 2;
light.shadow.camera.top = 0.000002383416166278454 * 2;
light.shadow.camera.near = 0.0000012388642793465356;
light.shadow.camera.far *= s;
scene.add(light);
this.light = light;
{
const planeSize = 500;
const loader = new THREE.TextureLoader();
const texture = loader.load('/checker.png');
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.magFilter = THREE.NearestFilter;
const repeats = 10;
texture.repeat.set(repeats, repeats);
const planeGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
const planeMat = new THREE.MeshPhongMaterial({
map: texture,
side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeGeo, planeMat);
plane.scale.setScalar(s);
plane.receiveShadow = true;
scene.add(plane);
}
{
const sphereRadius = 5e-7;
const sphereGeo = new THREE.SphereBufferGeometry(sphereRadius, 32, 32);
const sphereMat = new THREE.MeshPhongMaterial({color: '#CA8'});
const mesh = new THREE.Mesh(sphereGeo, sphereMat);
mesh.position.set(0, 0, 5e-6);
mesh.castShadow = true;
mesh.receiveShadow = false;
sphereMat.side = THREE.DoubleSide;
scene.add(mesh);
}
return scene;
}
render(gl, matrix) {
this.camera.projectionMatrix = new THREE.Matrix4()
.fromArray(matrix)
.multiply(this.cameraTransform);
this.renderer.state.reset();
this.renderer.render(this.scene, this.camera);
this.map.triggerRepaint();
}
}
Mapbox GL JS uses a coordinate system where the entire world is in [0, 1] so the coordinates are pretty tiny. It also uses x/y for lat/lng and z for up, which is different than usual Three.js coordinates.
How can I get the shadow to appear? I'm using Three.js r109 and Mapbox GL JS 1.4.0. I've tried replacing the PlaneBufferGeometry with a thin BoxGeometry to no avail.
EDIT
Forget everything I said in my old answer.
The example below scales things WAY down and the shadow remains.
The kicker was here:
shadowLight.shadow.camera.near *= (scaleDown) ? 0.1 : 10;
shadowLight.shadow.camera.far *= (scaleDown) ? 0.1 : 10;
shadowLight.shadow.camera.updateProjectionMatrix(); // <========= !!!!!
I was updating the scale, but wasn't updating the near/far of the shadow camera. Then, once I was, I was forgetting to update that camera's projection matrix. With all the pieces back together, it seems to be working well.
Try adding a call to update the shadow-casting light's camera's projection matrix after you configure the values.
If it still doesn't work, maybe you can use my example to figure out what's going on in your code.
If MY example doesn't work for you, then it might be your hardware doesn't support the level of precision you need.
// just some random colors to show it's actually rendering
const colors = [
0xff0000, // 1e+1
0x00ff00, // 1e+0
0x0000ff, // 1e-1
0xffff00, // 1e-2
0xff00ff, // 1e-3
0x00ffff, // 1e-4
0xabcdef, // 1e-5
0xfedcba, // 1e-6
0x883300, // 1e-7
0x008833, // 1e-8
0x330088, // 1e-9
0x338800 // 1e-10
];
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.shadowMap.enabled = true; // turn on shadow mapping
renderer.setClearColor(0xcccccc);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(28, 1, 1, 1000)
camera.position.set(25, 10, 15);
camera.lookAt(0, 0, 0);
const camLight = new THREE.PointLight(0xffffff, 1);
camera.add(camLight);
const floor = new THREE.Mesh(
new THREE.PlaneBufferGeometry(50, 50),
new THREE.MeshPhongMaterial({
color: "gray"
})
);
floor.receiveShadow = true;
floor.rotation.set(Math.PI / -2, 0, 0);
floor.position.set(0, -1, 0);
const sphere = new THREE.Mesh(
new THREE.SphereBufferGeometry(2, 16, 32),
new THREE.MeshPhongMaterial({
color: colors[0]
})
);
sphere.castShadow = true;
sphere.position.set(0, 1, 0);
const shadowLight = new THREE.PointLight(0xffffff, 1);
shadowLight.castShadow = true;
shadowLight.position.set(-10, 10, 5);
const group = new THREE.Group();
group.add(floor);
group.add(sphere);
group.add(shadowLight);
group.add(camera);
scene.add(group);
function render() {
renderer.render(scene, camera);
}
function resize() {
const W = window.innerWidth;
const H = window.innerHeight;
renderer.setSize(W, H);
camera.aspect = W / H;
camera.updateProjectionMatrix();
}
window.onresize = resize;
resize();
render();
let scaler = 10;
let scaleLevel = 10;
let scaleLevelOutput = document.getElementById("scaleLevel");
let scaleDown = true;
let colorIndex = 0;
setInterval(() => {
colorIndex += (scaleDown) ? 1 : -1;
scaleLevel *= (scaleDown) ? 0.1 : 10;
shadowLight.shadow.camera.near *= (scaleDown) ? 0.1 : 10;
shadowLight.shadow.camera.far *= (scaleDown) ? 0.1 : 10;
shadowLight.shadow.camera.updateProjectionMatrix();
if (scaleLevel < 1e-9 && scaleDown) {
scaleDown = false;
}
if (scaleLevel >= 10 && !scaleDown) {
scaleDown = true;
}
scaleLevelOutput.innerText = `SCALE LEVEL: ${scaleLevel.toExponential()}`;
group.scale.set(scaleLevel, scaleLevel, scaleLevel);
sphere.material.color.setHex(colors[colorIndex]);
sphere.material.needsUpdate = true;
render();
}, 1000);
body {
margin: 0;
overflow: hidden;
}
#scaleLevel {
font-family: monospace;
font-size: 2em;
position: absolute;
top: 0;
left: 0;
font-weight: bold;
margin: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.min.js"></script>
<div id="scaleLevel">SCALE LEVEL: 1e+1</div>
Hi i am having a problem maybe you can help me.
I have a camera that is going down a tube following a path. and a camera that rotates around that tube always pointing toward the next point in the tube. However, the camera sometimes can be below or beside the tube like a roller coaster. Like this
I have the position of point a and the position of the camera which is point b. I am always looking at point a+1
var bpoints = this.cameraPathpoints;
var apoints = this.pathPoints;
this.camera.position.copy(bpoints[i]);
this.camera.lookAt(apoints[i+1]);
The camera is always looking at the point correctly however i want that the camera rotates in its z axis so that it is always normal to the tube. I tried making some calculations so that the camera rotates in its z axis so that the camera always faces normal to the tube, however my calculations work only on certain positions. Maybe there is a simpler way to do this. Thank you very much for any help.
var angleRadians = Math.atan2(cpv[this.cameraPos].pos.y - centePoints[this.cameraPos].pos.y, cpv[this.cameraPos].pos.x - centePoints[this.cameraPos].pos.x);
if(angleRadians > 0 && angleRadians > Math.PI/2){
console.log("+90",(Math.PI/2) - angleRadians);
angleRadians = (Math.PI/2) - angleRadians;
this.camera.rotateZ(angleRadians);
console.log("rotated ", angleRadians * 180/Math.PI);
}
else if(angleRadians > 0 && angleRadians < Math.PI/2 && anglesum >
Math.PI/2){
console.log("-90",(Math.PI/2) - angleRadians);
angleRadians = (Math.PI/2) - angleRadians;
this.camera.rotateZ(-angleRadians);
console.log("rotated ", -angleRadians * 180/Math.PI);
}
else if(angleRadians > 0 && angleRadians < Math.PI/2){
console.log("-90",(Math.PI/2) + angleRadians);
angleRadians = -(Math.PI/2) - (angleRadians/Math.PI/2);
this.camera.rotateZ(angleRadians);
console.log("rotated ", angleRadians * 180/Math.PI);
}
else if(angleRadians < 0 && angleRadians < -Math.PI/2){
console.log("--90");
angleRadians = (Math.PI/2) + angleRadians;
this.camera.rotateZ(-angleRadians);
console.log("rotated ",-angleRadians * 180/Math.PI);
}else if(angleRadians < 0 && angleRadians > -Math.PI/2){
console.log("+-90");
angleRadians = (Math.PI/2) - angleRadians;
this.camera.rotateZ(-angleRadians);
console.log("rotated ", -angleRadians * 180/Math.PI);
}
Rather than doing math, make the camera a child of some other THREE.Object3D and use lookAt with that object. Set the camera's position and rotation relative to that object.
Below the object is called the mount. It goes down the path (center of the tube). The camera is a child of mount. The tube has a 1 unit radius so setting the camera.position.y to 1.5 makes it outside the tube. lookAt makes non-camera objects look down positive Z but the camera looks down negative Z so we rotate the camera 180 degrees.
Example:
'use strict';
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas: canvas});
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xAAAAAA);
const fov = 40;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.y = 1.5; // 2 units above the mount
camera.rotation.y = Math.PI; // the mount will lootAt positiveZ
const mount = new THREE.Object3D();
mount.add(camera);
scene.add(mount);
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(1, -2, -4);
scene.add(light);
}
const curve = new THREE.Curves.GrannyKnot();
const tubularSegments = 200;
const radius = 1;
const radialSegments = 6;
const closed = true;
const tube = new THREE.TubeBufferGeometry(
curve, tubularSegments, radius, radialSegments, closed);
const texture = new THREE.DataTexture(new Uint8Array([128, 255, 255, 128]),
2, 2, THREE.LuminanceFormat);
texture.needsUpdate = true;
texture.magFilter = THREE.NearestFilter;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 100, 4 );
const material = new THREE.MeshPhongMaterial({
map: texture,
color: '#8CF',
flatShading: true,
});
const mesh = new THREE.Mesh(tube, material);
scene.add(mesh);
const target = new THREE.Vector3();
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();
}
const t = time * 0.1 % 1;
curve.getPointAt(t, mount.position);
curve.getPointAt((t + 0.01) % 1, target);
mount.lookAt(target);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/js/CurveExtras.js"></script>
You can easily orient the camera relative to the mount to say look more toward the path or way by setting camera.rotation.x. If you want to rotate around the mount either change the mount's up property or add another object between the mount and the camera and set its Z rotation.
'use strict';
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas: canvas});
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xAAAAAA);
const fov = 40;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.y = 1.5; // 2 units above the mount
camera.rotation.y = Math.PI; // the mount will lootAt positiveZ
const mount = new THREE.Object3D();
const subMount = new THREE.Object3D();
subMount.rotation.z = Math.PI * .5;
subMount.add(camera);
mount.add(subMount);
scene.add(mount);
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(1, -2, -4);
scene.add(light);
}
const curve = new THREE.Curves.GrannyKnot();
const tubularSegments = 200;
const radius = 1;
const radialSegments = 6;
const closed = true;
const tube = new THREE.TubeBufferGeometry(
curve, tubularSegments, radius, radialSegments, closed);
const texture = new THREE.DataTexture(new Uint8Array([128, 255, 255, 128]),
2, 2, THREE.LuminanceFormat);
texture.needsUpdate = true;
texture.magFilter = THREE.NearestFilter;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 100, 4 );
const material = new THREE.MeshPhongMaterial({
map: texture,
color: '#8CF',
flatShading: true,
});
const mesh = new THREE.Mesh(tube, material);
scene.add(mesh);
const target = new THREE.Vector3();
const target2 = new THREE.Vector3();
const mountToTarget = new THREE.Vector3();
const targetToTarget2 = new THREE.Vector3();
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();
}
const t = time * 0.1 % 1;
curve.getPointAt(t, mount.position);
curve.getPointAt((t + 0.01) % 1, target);
// set mount up to be perpenticular to the
// curve
curve.getPointAt((t + 0.02) % 1, target2);
mountToTarget.subVectors(mount.position, target).normalize();
targetToTarget2.subVectors(target2, target).normalize();
mount.up.crossVectors(mountToTarget, targetToTarget2);
mount.lookAt(target);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/js/CurveExtras.js"></script>
I'm trying to set the camera to be 3 units away from a collection of points I would like this to be relative to the group of points since the points will change later on.
So far I can retrieve x,y,z coordinates from the database and are returned using djangos {{coord_x}} I will have to return the correct length, (I could do this on the python side - len()) for now the database query is limited to 20 rows. These points are brought into three.js using a for loop.
How do I set a camera relative to the objects? Do I need to calculate a bounding box?
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.001, 100000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// allow resizing of the window
window.addEventListener('resize', function()
{
var width = window.innerWidth;
var height = window.innerHeight;
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
});
//Controls
controls = new THREE.OrbitControls(camera, renderer.domElement)
//create the shape
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({color: 0x007654, wireframe: false});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
var numpoints = 20;
var dots = []; //If you want to use for other task
for (var i = 0 ; i < numpoints ; i++) {
var x = "{{coord_x}}";
var y = "{{coord_y}}";
var z = "{{coord_z}}";
// var x = Math.random() * (0 - 1) + 1
// var y = Math.random() * (0 - 1) + 1
// var z = Math.random() * (0 - 1) + 1
var dotGeometry = new THREE.Geometry();
dots.push(dotGeometry);
dotGeometry.vertices.push(new THREE.Vector3(x, y, z));
var dotMaterial = new THREE.PointsMaterial( { size: 3, sizeAttenuation: false, color: 0xFF0000 });
var dot = new THREE.Points( dotGeometry, dotMaterial);
scene.add(dot);
}
camera.position.z = 30
//game logic, allow rotation
var update = function()
{
//cube.rotation.x += 0.00;
//cube.rotation.y += 0.0025;
//dot.rotation.x += 0.00;
//dot.rotation.y += 0.005;
};
// draw scene
var render = function()
{
renderer.render(scene, camera);
};
// run game loop (update, render, repeat)
var GameLoop = function()
{
requestAnimationFrame(GameLoop);
update();
render();
};
GameLoop();
</script>
That's how you can work with THREE.Sphere() object to set the position of your camera:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var geom = new THREE.Geometry();
for (let i = 0; i < 100; i++) {
geom.vertices.push(
new THREE.Vector3(
Math.random() - 0.5,
Math.random() - 0.5,
Math.random() - 0.5
).multiplyScalar(10)
);
}
var points = new THREE.Points(geom, new THREE.PointsMaterial({
size: 0.25,
color: "aqua"
}));
scene.add(points);
var sphere = new THREE.Sphere().setFromPoints(geom.vertices);
console.log(sphere);
camera.position.copy(sphere.center);
camera.position.z += sphere.radius / Math.sin(THREE.Math.degToRad(camera.fov / 2));
render();
function render() {
requestAnimationFrame(render);
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>
// THREEJS RELATED VARIABLES
var scene, camera, renderer, container, controls, raycaster, sphere_radius, HEIGHT, WIDTH;
//INIT THREE JS, SCREEN AND MOUSE EVENTS
function createScene() {
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
scene = new THREE.Scene();
aspectRatio = WIDTH / HEIGHT;
camera = new THREE.PerspectiveCamera(60, WIDTH / HEIGHT, 1, 10000);
camera.position.x = 0;
camera.position.z = 1500;
camera.position.y = 0;
// ORBIT CAMERA
controls = new THREE.OrbitControls( camera );
controls.update();
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(WIDTH, HEIGHT);
container = document.getElementById('world');
container.appendChild(renderer.domElement);
}
function randomSpherePoint(x0,y0,z0,radius){
var u = Math.random();
var v = Math.random();
var theta = 2 * Math.PI * u;
var phi = Math.acos(2 * v - 1);
var x = x0 + (radius * Math.sin(phi) * Math.cos(theta));
var y = y0 + (radius * Math.sin(phi) * Math.sin(theta));
var z = z0 + (radius * Math.cos(phi));
return [x,y,z];
}
Island = function(){
this.mesh = new THREE.Object3D();
// number of cubes
this.nLands = 30;
this.lands = [];
for(var i = 0; i < this.nLands; i++){
var c = new Land();
this.lands.push(c);
var stepAngle = Math.PI*2 * Math.random();
var a = stepAngle*i;
var h = sphere_radius;
var randPos = randomSpherePoint(0,0,0,sphere_radius);
c.mesh.position.y = randPos[1];
c.mesh.position.x = randPos[0];
c.mesh.position.z = randPos[2];
this.mesh.add(c.mesh);
}
}
Land = function(){
this.mesh = new THREE.Object3D();
this.mesh.name = "land";
var geom = new THREE.CylinderGeometry( 2, 2, 50, 64 );
var mat = new THREE.MeshPhongMaterial({color:0x59332e});
var m = new THREE.Mesh(geom.clone(), mat);
m.position.x = 0;
m.position.y = Math.random()*2;
m.position.z = Math.random()*10;
this.mesh.add(m);
}
Sea = function(){
// radius top, radius bottom, height, number of segments on the radius, number of segments vertically
sphere_radius = 300;
var geom = new THREE.SphereGeometry(sphere_radius,sphere_radius,32,32);
var mat = new THREE.MeshNormalMaterial({
transparent:true,
opacity:.5,
flatShading:true,
});
this.mesh = new THREE.Mesh(geom, mat);
this.mesh.name = "sea";
}
// 3D Models
var sea;
function createSea(){
sea = new Sea();
sea.mesh.position.y = 0;
scene.add(sea.mesh);
}
function createIsland(){
island = new Island();
island.mesh.position.y = 0;
scene.add(island.mesh);
}
function loop(){
renderer.render(scene, camera);
requestAnimationFrame(loop);
}
function init(event){
createScene();
createSea();
createIsland();
loop();
}
window.addEventListener('load', init, false);
function handleWindowResize() {
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
}
window.addEventListener('resize', handleWindowResize, false);
html, body {
overflow: hidden;
margin:0;
padding:0;
}
<div id="world"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r94/js/controls/OrbitControls.js"></script>
Related codepen:
https://codepen.io/farisk/pen/zLymrz
On three.js, i have a bunch of cylinders added to random positions on the exterior of sphere.
The issue im having now is that they're all facing straight up to the z-axis.
Im thinking that what i should do is to make the cylinder lookAt the angle of where the cylinder intersects the sphere. But im not sure how to achieve this. Any help is appreciated.
Heres a image that describes the situation.