I'm trying to render my geometries without the diagonals. Here is a plane example (attached) to explain:
Left plane is what I get, right plane is what I am looking for. Is this possible anyhow?
if you going to use plane geometry, you can try out the following link...
EdgeHelper
var geometry = new THREE.PlaneGeometry(10, 10, 10,10);
var material = new THREE.MeshLambertMaterial({color:0xff0000,opacity:0.2,transparent:true,overdraw:0.5});
for (var i = 0; i < 1; i ++) { var mesh = new THREE.Mesh(geometry, material);
mesh.position.x = i*20; mesh.position.y = 0;//Math.random()*20-(1*i); mesh.position.z = 0//-59;//Math.random()*20 - 100; mesh.rotation.x = Math.random(); mesh.rotation.y = Math.random(); scene.add(mesh);
check the link..
Related
I'm currently trying to get into THREE.js a bit and I'm obviously following tutorials and examples but
even after all that I can't seem to get shadows working.
shadowMap.enabled is set to true, castShadow is set on the cube beneath the light and receiveShadow is set to true on the floor.
My attempt is visible on https://mroman.ch/shadows/cg.html
import * as THREE from "https://threejs.org/build/three.module.js";
import { OrbitControls } from "https://threejs.org/examples/jsm/controls/OrbitControls.js";
function main() {
const canvas = document.querySelector('#canvas');
const renderer = new THREE.WebGLRenderer({canvas,'antialias':true});
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.BasicShadowMap;
const fov = 75;
const aspect = 1;
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 8*0.2;
camera.position.y = 2*0.2;
const controls = new OrbitControls( camera, renderer.domElement );
controls.update();
const sz = 0.2;
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 2;
const light = new THREE.PointLight(color, intensity);
light.position.set(0, 2*sz, 2*sz);
light.castShadow = true;
light.shadow.mapSize.width = 512;
light.shadow.mapSize.height = 512;
var d = 200;
light.shadow.cameraLeft = -d;
light.shadow.cameraRight = d;
light.shadow.cameraTop = d;
light.shadow.cameraBottom = -d;
light.shadow.camera.far = 1000;
const helper = new THREE.CameraHelper( light.shadow.camera );
scene.add( helper );
scene.add(light);
}
const material = new THREE.MeshPhongMaterial({color: "brown"});
const mat_green = new THREE.MeshPhongMaterial({color:"green"});
const mat_floor = new THREE.MeshPhongMaterial({color:"gray"});
const group = new THREE.Group();
var geometry = new THREE.CylinderGeometry(sz/2,sz/1.5,3*sz,8,8);
var cube = new THREE.Mesh(geometry, material);
cube.position.set(0,sz,0);
group.add(cube);
geometry = new THREE.BoxGeometry(sz,sz,sz);
cube = new THREE.Mesh(geometry, mat_green);
cube.position.set(0.0,3*sz,0);
group.add(cube);
geometry = new THREE.IcosahedronGeometry(sz);
cube = new THREE.Mesh(geometry, mat_green);
cube.position.set(sz,3*sz,0);
group.add(cube);
geometry = new THREE.IcosahedronGeometry(sz);
cube = new THREE.Mesh(geometry, mat_green);
cube.position.set(-sz,3*sz,0);
group.add(cube);
geometry = new THREE.IcosahedronGeometry(sz);
cube = new THREE.Mesh(geometry, mat_green);
cube.position.set(0.0,3*sz,sz);
group.add(cube);
geometry = new THREE.IcosahedronGeometry(sz);
cube = new THREE.Mesh(geometry, mat_green);
cube.position.set(0.0,3*sz,-sz);
group.add(cube);
geometry = new THREE.IcosahedronGeometry(sz);
cube = new THREE.Mesh(geometry, mat_green);
cube.position.set(0.0,4*sz,0.0);
group.add(cube);
const floor = new THREE.Group();
for(var x = -4; x <= 4; x++) {
for(var z = -4; z <= 4; z++) {
geometry = new THREE.BoxGeometry(sz,sz,sz);
cube = new THREE.Mesh(geometry, mat_floor);
cube.position.set(x*sz,-sz,z*sz);
cube.receiveShadow = true;
floor.add(cube);
}
}
geometry = new THREE.BoxGeometry(sz, sz, sz);
cube = new THREE.Mesh(geometry, mat_floor);
cube.position.set(0,sz,2*sz);
cube.castShadow = true;
scene.add(cube);
scene.add(group);
scene.add(floor);
function render(time) {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
Can anybody point out where I got it wrong?
Out of curiosity, tried ThreeJs for the first time to answer this question, so might not be good enough in explanation.
Result
To fix cube shadow I changed this:
const light = new THREE.PointLight(color, intensity);
light.position.set(0, 2*sz, 2*sz);
To this:
const light = new THREE.PointLight(color, intensity);
light.position.set(0, 3 * sz, 3 * sz);
I guess it was just issue with light emitter being inside of the cube?
And cylinder just misses property to cast shadow. I would recommend changing cube variable name to something more meaningful.
var geometry = new THREE.CylinderGeometry(sz / 2, sz / 1.5, 3 * sz, 8, 8);
var cube = new THREE.Mesh(geometry, material);
cube.castShadow = true;
How to get the bounding sphere for a whole scene in three.js?
I may try to get the bounding sphere for each object and compute the resulting union of them, but I think there may be a more straight forward method.
There are different methods to get a boundingSphere of multiple objects dynamically. You can get first the bounding box of all of them, and then create a sphere of that bounding box... here is a sample fiddle I have shaped on boundingSphere of a boundingBox.
Basically you put all the geometries into a Group, you get the Box3 of the group, and then you do getBoundingSphere from the Box3 and position at the center. Code in the fiddle would be this.
let g = new THREE.Group();
scene.add(g);
for (let i = 0; i < 5; i++) {
// geometry
var geometry = new THREE.BoxGeometry(20, 20, 20);
// material
var material = new THREE.MeshToonMaterial({
color: 0xff0000,
opacity: 0.7,
});
// mesh
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(100 * Math.random(), 100 * Math.random(), 100 * Math.random());
g.add(mesh);
}
//g.updateWorldMatrix(true);
var gridHelper = new THREE.GridHelper(400, 40, 0x0000ff, 0x808080);
gridHelper.position.y = 0;
gridHelper.position.x = 0;
scene.add(gridHelper);
let bbox = new THREE.Box3().setFromObject(g);
let helper = new THREE.Box3Helper(bbox, new THREE.Color(0, 255, 0));
scene.add(helper);
const center = new THREE.Vector3();
bbox.getCenter(center);
let bsphere = bbox.getBoundingSphere(new THREE.Sphere(center));
let m = new THREE.MeshStandardMaterial({
color: 0xffffff,
opacity: 0.3,
transparent: true
});
var geometry = new THREE.SphereGeometry(bsphere.radius, 32, 32);
let sMesh = new THREE.Mesh(geometry, m);
scene.add(sMesh);
sMesh.position.copy(center);
EDITED: If you want to include in the boundingSphere for the scene including the lights (which could get you a huge sphere), just start from let bbox = new THREE.Box3().setFromObject(scene)
I want to make a 3D building using Three.js. For example, I made 6 walls and a floor by checkerboard texture. I used clippingPlanes for wall1 and wall4:
floor1.material.clippingPlanes = [plane1,plane4];
I made my planes(plane1 and plane4) by my walls(wall1 and wall4). For example, my wall4 planeGeometry and plane4 code is here:
var wallGeometry4 = new THREE.PlaneGeometry(40, Floor_Height, 1, 1);
var wall4 = createMesh(wallGeometry4, "brick_diffuse.jpg", THREE.DoubleSide, 1024, 1024);
unit1.add(wall4);
wall4.position.x = -10;
wall4.position.y = 0;
wall4.position.z = -20;
wall4.rotation.y = 1.5 * Math.PI;
wall4.add(new THREE.EdgesHelper(wall4, 0x000000));
var plane4 = new THREE.Plane();
var normal4 = new THREE.Vector3();
var point4 = new THREE.Vector3();
normal4.set(0, 0, -1).applyQuaternion(wall4.quaternion);
point4.copy(wall4.position);
plane4.setFromNormalAndCoplanarPoint(normal4, point4);
But I see an empty area between wall5 and wall6, because plane4(that used for clipping the floor) isn't the same size of wall4. I think Plane4 is whole of the scene. How to change size of my plane to clip correctly? Or Is there any way to make floor bounded in walls?
One way to achieve this as suggested is to use ShapeGeometry.
When you are creating your walls you can save the x and z co-ordinate of their starting and ending points in an array to form a loop of points of Vector2. Then you can create a new custom shape from these points using shapeGeometry.
points = [{x:0,y:0},{x:0,y:10},{x:10,y:10},{x:10,y:0},{x:0,y:0}]
function getShapeFromPoints(points){
const shape = new THREE.Shape();
shape.moveTo(points[0].x, points[0].y);
for (let i = 1; i < points.length; i++) {
shape.lineTo(points[i].x, points[i].y);
}
return shape;
}
function createPlaneFromPoints(points) {
const planeMaterial = getPlaneMaterial();
const shape = getShapeFromPoints(points);
const geometry = new THREE.ShapeBufferGeometry(shape);
geometry.rotateX(degreeToRadians(-90));
const mesh = new THREE.Mesh(geometry, planeMaterial);
return mesh;
}
Hope that helps you!
I am using Three.js to create an animation of 100 cubes.
For each cube I set its geometry and material and then add it to the scene with:
scene.add(cube);
I have been storing the positions of the cubes in three arrays: cube_x[99] cube_y[99] cube_z[99]
I do not know how to tell the renderer their positions as they are modified. The following works for one cube when I have only one cube in the scene:
cube.position.x=300;
cube.position.y=000;
I tried making cube into an array to hold the xyz positions of 100 cubes. But I can not seem to call scene.add(cube[i]);
How can I update the position of 100 cubes after scene.add(cube);
How do I let three.js know which cube I am trying to move?
Thanks - javascript beginner
You would basically store a reference to each cube in an array:
// reference array
var cubes = [];
// create cube mesh
var cube = new THREE.Mesh(new THREE.BoxGeometry(), someMaterial);
// push a reference to array
cubes.push(cube);
// add same cube to scene
scene.add(cube);
Now you can iterate over each cube and move it:
cubes.forEach(function(cube) {
cube.position.z -= 0.1; // move for example in Z direction
});
You would do this within your animation loop.
// Setup a simple demo scene
var scene = new THREE.Scene();
var cam = new THREE.PerspectiveCamera(40, innerWidth / innerHeight, 0.1, maxDist);
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setPixelRatio(devicePixelRatio);
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
// STore our cubes somewhere, dist for random generation and clipping
var cubes = [], maxDist = 500, r = Math.random, materials = [];
for(var i = 0; i < 7; i++) materials.push(new THREE.MeshBasicMaterial({color: (r()*0xffffff)&0xffffff|0x999999}));
// Add and store some cubes
for(i = 0; i < 3000; i++) {
var cube = new THREE.Mesh(new THREE.BoxBufferGeometry(), materials[i % materials.length]);
cubes.push(cube); // store to our array
cube.position.set(r()*maxDist*2-maxDist, r()*maxDist*2-maxDist, r()*maxDist*2-maxDist); // random positions
}
scene.add(...cubes); //es6, add to scene
cam.position.z = maxDist;
// Animate cubes
function loop(time) {
for(i = 0, cube; i < cubes.length; i++) { // for each cube do:
cube = cubes[i];
cube.position.z -= 1; // move on Z in this demo
if (cube.position.z < -maxDist) cube.position.z = maxDist; // reset position for demo
}
cam.rotation.z = Math.sin(time*0.0003); // add some camera action
cam.rotation.y = Math.sin(time*0.0005)*0.3; // add some camera action
renderer.render(scene, cam); // render everything
requestAnimationFrame(loop)
};
requestAnimationFrame(loop)
body {margin:0:overflow:hidden}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js"></script>
Somewhat new to Three.js and 3d libraries in general.
I merged two geometries (a quarter cylinder and a plane) using this code:
var planeGeo = new THREE.PlaneGeometry(planeW, planeD / 2, 199, 399);
var planeMesh = new THREE.Mesh(planeGeo);
planeMesh.updateMatrix();
var cylinderGeo = new THREE.CylinderGeometry(100, 100, planeW, 199, 399, true, 0, Math.PI / 2);
cylinderGeo.rotateZ(Math.PI / 2).translate(0, 200, -100);
var cylinderMesh = new THREE.Mesh(cylinderGeo);
cylinderMesh.updateMatrix();
var singleGeometry = new THREE.Geometry();
singleGeometry.merge(planeMesh.geometry, planeMesh.matrix);
singleGeometry.merge(cylinderMesh.geometry, cylinderMesh.matrix);
var testmaterial = new THREE.MeshPhongMaterial({ color: 0x666666 });
mesh = new THREE.Mesh(singleGeometry, testmaterial);
scene.add(mesh);
I then would like to use a single material (png) over the entire thing. This code doesn't work:
textureLoader.load('data/test.png', function (texture) {
material = new THREE.MeshLambertMaterial({
map: texture
});
});
Later in the block with the merging...
mesh = new THREE.Mesh(singleGeometry, material);
scene.add(mesh);
This results in:
I would like the end result to be a single draped png over the entire merged geometry, but I can't find anything that suggests this is a normal thing to do. Is there a better way to achieve that result than merging geometries? Or am I just looking in the wrong places?
A poor-mans solution to achieve this, using the shape supplied in your post, is the following:
https://jsfiddle.net/87wg5z27/44/
Using code from this answer: https://stackoverflow.com/a/20774922/4977165
It sets the UVs based on the bounding box of the geometry, leaving out the z-coordinate (=0). Thats why the texture is a little bit stretched at the top, you can correct that manually or maybe its sufficent for you.
geometry.computeBoundingBox();
var max = geometry.boundingBox.max,
min = geometry.boundingBox.min;
var offset = new THREE.Vector2(0 - min.x, 0 - min.y);
var range = new THREE.Vector2(max.x - min.x, max.y - min.y);
var faces = geometry.faces;
geometry.faceVertexUvs[0] = [];
for (var i = 0; i < faces.length ; i++) {
var v1 = geometry.vertices[faces[i].a],
v2 = geometry.vertices[faces[i].b],
v3 = geometry.vertices[faces[i].c];
geometry.faceVertexUvs[0].push([
new THREE.Vector2((v1.x + offset.x)/range.x ,(v1.y + offset.y)/range.y),
new THREE.Vector2((v2.x + offset.x)/range.x ,(v2.y + offset.y)/range.y),
new THREE.Vector2((v3.x + offset.x)/range.x ,(v3.y + offset.y)/range.y)
]);
}
geometry.uvsNeedUpdate = true;