Three js 25000 BoxGeometry 200 models How to increase performance - javascript

I am knew to WebGL and Three js having been able to get enough code together to be able to add 25K BoxGeometry Meshes and about 200 models which move simultaneously and interact with the BoxGeometry.
I need to be able to move the camera smoothly through these boxes.
So, I have tried to merge all the geometry together (got that idea from a google search) and that made the performance worse.
I watched several YouTube videos and did not a lot about performance.
I am currently using mrdoob-three.js-4bab468 if that helps.
The functions addCube20 addCube40 are called a little over 25,000 times with different coordinates to display at.
then they are not called again unless something in the scene alters their position.
MJMotion.js is my keyboard camera view controls that do not need to be included. You can remove them by altering the render section taking out
keyboardControls();
updateCamera();
and replacing them with your own controls.
this thing gets about 3 fps.
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<meta name="viewport" content="width=device-width, minimum-scale=1.0">
<title>CubeTest</title>
<style>
body {
margin: 0;
}
</style>
<script>
var pageE;
</script>
</head>
<body style="touch-action:none">
<div id="canvas_container" style="position: absolute; left:0px; top:0px; touch-action:pan-y;"></div>
<script src="js/three.js"></script>
<script src="js/Cache.js"></script>
<script src="js/DDSLoader.js"></script>
<script src="js/MTLLoader.js"></script>
<script src="js/OBJMTLLoader.js"></script>
<script src="js/OBJLoader.js"></script>
<script src="js/XHRLoader.js"></script>
<script src="js/LoadingManager.js"></script>
<script src="js/JSONLoader.js"></script>
<script src="js/MJMotion.js"></script>
<script src="js/stats.min.js"></script>
<script>
// Our Javascript will go here.
var domContainer = null;
domContainer = document.getElementById("canvas_container");
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
stats.domElement.style.zIndex = 100;
domContainer.appendChild( stats.domElement );
var camera = new THREE.PerspectiveCamera( 30, window.innerWidth/window.innerHeight, 0.1, 500.0);
var aspectratio = window.innerWidth/window.innerHeight;
var renderer = new THREE.WebGLRenderer();
var connected = false;
var SaveAUseB = true;
var UpdateCount = 0;
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.cullFace = THREE.CullFaceFrontBack;
renderer.gammaInput = true;
renderer.gammaOutput = true;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
domContainer.appendChild( renderer.domElement );
var INTERSECTED;
var mouse = new THREE.Vector2();
var raycaster = new THREE.Raycaster();
var scene = new THREE.Scene();
var objects = [], plane;
var mouseDown = 0;
scene.add(camera);
scene.fog = new THREE.Fog( 0xaaccff,10,150);
window.addEventListener('resize', resize, false);
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
renderer.domElement.addEventListener('mousewheel',onMouseWheel, false);
renderer.domElement.addEventListener( 'touchstart',touchstart, false );
renderer.domElement.addEventListener( 'touchend', touchend, false );
renderer.domElement.addEventListener('touchmove', touchmove, false);
// this must be defined before MJMotion.js
// we use it in there.
var scenesizereduction = 100.0;
// defined in MJMotion.js
SetScreenReduction(scenesizereduction);
var directionalLight = new THREE.DirectionalLight(0xffffff, 0.15);
directionalLight.position.set(-100/scenesizereduction, 1000/scenesizereduction,2000/scenesizereduction);
scene.add(directionalLight);
var hemiLight = new THREE.HemisphereLight(0xAAAAAA, 0x080808, 0.25);
hemiLight.position.set(0, 500/scenesizereduction, 0);
scene.add(hemiLight);
var dirLight = new THREE.DirectionalLight( 0xffffff, 0 );
dirLight.color.setHSL( 0.1, 1, 0.95 );
dirLight.position.set( -1, 1.75, 1 );
dirLight.position.multiplyScalar( 50 );
scene.add( dirLight );
dirLight.castShadow = true;
dirLight.shadowMapWidth = 2048;
dirLight.shadowMapHeight = 2048;
var d = 150;
dirLight.shadowCameraLeft = -d;
dirLight.shadowCameraRight = d;
dirLight.shadowCameraTop = d;
dirLight.shadowCameraBottom = -d;
dirLight.shadowCameraFar = 3500;
dirLight.shadowBias = -0.00005;
// 50 perccent
var pixelsperfoot = 2.0 / scenesizereduction;
var GroundWidth = 30346/scenesizereduction;
var GroundLength = 6001/scenesizereduction;
var xoffset = (GroundWidth / 2.0) - (87.0390550000001 * pixelsperfoot);
var zoffset = (GroundLength/2.0) - (607.80843 * pixelsperfoot);
var textures20Geometry = new THREE.BoxGeometry(20 * pixelsperfoot, 9.5 * pixelsperfoot, 8.0 * pixelsperfoot);
var textures40Geometry = new THREE.BoxGeometry(40 * pixelsperfoot, 9.5 * pixelsperfoot, 8.0 * pixelsperfoot);
var Cube20Mesh= new THREE.Mesh( textures20Geometry);
var Cube40Mesh= new THREE.Mesh(textures40Geometry);
var CubeGeometry = new THREE.Geometry();
var cubeMaterial1 = new THREE.MeshLambertMaterial({
map: THREE.ImageUtils.loadTexture('textures/cube01.png')
});
var cubeMaterial2 = new THREE.MeshLambertMaterial({
map: THREE.ImageUtils.loadTexture('textures/cube02.png')
});
var cubeMaterial3 = new THREE.MeshLambertMaterial({
map: THREE.ImageUtils.loadTexture('textures/cube03.png')
});
var cubeMaterial4 = new THREE.MeshLambertMaterial({
map: THREE.ImageUtils.loadTexture('textures/cube04.png')
});
var cubeMaterial5 = new THREE.MeshLambertMaterial({
map: THREE.ImageUtils.loadTexture('textures/cube05.png')
});
var cubeMaterial6 = new THREE.MeshLambertMaterial({
map: THREE.ImageUtils.loadTexture('textures/cube06.png')
});
var cubeMaterial7 = new THREE.MeshLambertMaterial({
map: THREE.ImageUtils.loadTexture('textures/cube07.png')
});
var cubeMaterial8 = new THREE.MeshLambertMaterial({
map: THREE.ImageUtils.loadTexture('textures/cube08.png')
});
var cubeMaterial9 = new THREE.MeshLambertMaterial({
map: THREE.ImageUtils.loadTexture('textures/cube09.png')
});
var cubeMaterial10 = new THREE.MeshLambertMaterial({
map: THREE.ImageUtils.loadTexture('textures/cube10.png')
});
var cubeMaterial11 = new THREE.MeshLambertMaterial({
map: THREE.ImageUtils.loadTexture('textures/cube11.png')
});
var cubeMaterial12 = new THREE.MeshLambertMaterial({
map: THREE.ImageUtils.loadTexture('textures/cube12.png')
});
for(var i = 0; i < 2500; i++)
{
addCube20("cube" + i, Math.random() * 800 - 400,Math.random() * 800 - 400,Math.random() * 2 * Math.PI,1);
addCube40("cube" + i, Math.random() * 800 - 400,Math.random() * 800 - 400,Math.random() * 2 * Math.PI,1);
}
DisplayCubes();
function addCube20(name, x1,z1,azimuth,MaterialIndex)
{
Cube20Mesh.position.set(x1, ((0.5 + 4.25) * pixelsperfoot) + ((MaterialIndex - 1) * (9.5 * pixelsperfoot)), z1);
Cube20Mesh.rotation.y = (Math.PI / 180.0) * azimuth;
Cube20Mesh.name = name;
Cube20Mesh.updateMatrix();
CubeGeometry.merge(Cube20Mesh.geometry, Cube20Mesh.matrix,MaterialIndex);
};
function addCube40(name, x1,z1,azimuth,MaterialIndex)
{
Cube40Mesh.position.set(x1, ((0.5 + 4.25) * pixelsperfoot) + ((MaterialIndex - 1) * (9.5 * pixelsperfoot)), z1);
Cube40Mesh.rotation.y = (Math.PI / 180.0) * azimuth;
Cube40Mesh.name = name;
Cube40Mesh.updateMatrix();
CubeGeometry.merge(Cube40Mesh.geometry, Cube40Mesh.matrix, MaterialIndex);
};
function DisplayCubes()
{
var CubeMaterials = [];
CubeMaterials.push(cubeMaterial1);
CubeMaterials.push(cubeMaterial2);
CubeMaterials.push(cubeMaterial3);
CubeMaterials.push(cubeMaterial4);
CubeMaterials.push(cubeMaterial5);
CubeMaterials.push(cubeMaterial6);
CubeMaterials.push(cubeMaterial7);
CubeMaterials.push(cubeMaterial8);
CubeMaterials.push(cubeMaterial9);
CubeMaterials.push(cubeMaterial10);
CubeMaterials.push(cubeMaterial11);
CubeMaterials.push(cubeMaterial12);
var CubeGroup = new THREE.Mesh(CubeGeometry, new THREE.MeshFaceMaterial(CubeMaterials));
CubeGroup.matrixAutoUpdate = false;
CubeGroup.updateMatrix();
scene.add(CubeGroup);
};
function resize()
{
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
};
function animate()
{
requestAnimationFrame( animate );
render();
stats.update();
};
function render()
{
keyboardControls();
updateCamera()
renderer.render(scene, camera);
};
animate();
</script>

I have found the issue. It is the materials. If I use images it slows down to 7 fps. If I use something simple like a MeshNormalMaterial it works at 60fps.
Is there any way to rectify this?

Related

Why does placing more than 1 place marker on an planet object3D not work anymore?

I have a piece of code from 3 years ago that appears to not be working anymore from Three.js.
I have updated all of the other issues of the migration to ES6 for Three.js, but when I try to add as I did in the past more than 1 place marker on the planet surface, only 1 place marker is displayed, with only the last added place marker being successfully displayed in the desired position... with all other place markers being erased.
Problem example -> https://jsfiddle.net/jhL6s579/6/
Complete code:
HTML
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://s3-eu-west-2.amazonaws.com/bckld/lab/loading.js"></script>
JS
// ------ Marker object ------------------------------------------------
function Marker() {
var radius = 0.005;
var sphereRadius = 0.02;
var height = 0.05;
var material = new THREE.MeshPhongMaterial({ color: 0xbab68f });
var cone = new THREE.Mesh(new THREE.ConeBufferGeometry(radius, height, 8, 1, true), material);
cone.position.y = height * 0.5;
cone.rotation.x = Math.PI;
var sphere = new THREE.Mesh(new THREE.SphereBufferGeometry(sphereRadius, 16, 8), material);
sphere.position.y = height * 0.95 + sphereRadius;
this.add(cone, sphere);
}
Marker.prototype = Object.create(new THREE.Object3D());
// ------ Earth object -------------------------------------------------
function Earth(radius, texture) {
this.userData.radius = radius;
var earth = new THREE.Mesh(
new THREE.SphereBufferGeometry(radius, 64.0, 48.0),
new THREE.MeshPhongMaterial({
map: texture
})
);
this.add(earth);
}
Earth.prototype = Object.create(new THREE.Object3D());
Earth.prototype.createMarker = function (lat,lon ) {
var marker = new Marker();
var lonRad = -lon * (Math.PI / 180);
var latRad = lat * (Math.PI / 180);
var r = this.userData.radius;
marker.position.set(Math.cos(latRad) * Math.cos(lonRad) * r, Math.sin(latRad) * r, Math.cos(latRad) * Math.sin(lonRad) * r);
marker.rotation.set(0.0, -lonRad, latRad - Math.PI * 0.5);
this.attach(marker);
};
// ------ Three.js code ------------------------------------------------
var scene, camera, renderer;
var controls;
init();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, 4 / 3, 0.1, 100);
camera.position.set(0.0, 1.5, 3.0);
renderer = new THREE.WebGLRenderer({ antialias: true });
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.autoRotate = true;
controls.autoRotateSpeed = -1.0;
controls.enablePan = false;
var ambient = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambient);
var direcitonal = new THREE.DirectionalLight(0xffffff, 0.5);
direcitonal.position.set(5.0, 2.0, 5.0).normalize();
scene.add(direcitonal);
// just some code for the loading
var manager = createLoader(renderer.domElement, animate);
var texLoader = new THREE.TextureLoader(manager).setCrossOrigin(true);
var texture = texLoader.load('https://s3-eu-west-2.amazonaws.com/bckld/lab/textures/earth_latlon.jpg');
texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
var earth = new Earth(1.0, texture);
earth.createMarker(48.856700, 2.350800); // Paris
earth.createMarker(28.524167, -80.650833); // Paris
scene.add(earth);
window.addEventListener('resize', onResize);
onResize();
document.body.appendChild(renderer.domElement);
}
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
CSS
body {
background: #000;
margin: 0;
overflow: hidden;
}
I have also tried doing this.attach(marker) but still not working...
You have to derive your custom classes Marker and Earth in ES6 now. Meaning you have to use the class syntax like below since Object3D is now a class, too.
// ------ Marker object ------------------------------------------------
class Marker extends THREE.Object3D {
constructor() {
super();
var radius = 0.005;
var sphereRadius = 0.02;
var height = 0.05;
var material = new THREE.MeshPhongMaterial({
color: 0xbab68f
});
var cone = new THREE.Mesh(new THREE.ConeBufferGeometry(radius, height, 8, 1, true), material);
cone.position.y = height * 0.5;
cone.rotation.x = Math.PI;
var sphere = new THREE.Mesh(new THREE.SphereBufferGeometry(sphereRadius, 16, 8), material);
sphere.position.y = height * 0.95 + sphereRadius;
this.add(cone, sphere);
}
}
// ------ Earth object -------------------------------------------------
class Earth extends THREE.Object3D {
constructor(radius, texture) {
super();
this.userData.radius = radius;
var earth = new THREE.Mesh(
new THREE.SphereBufferGeometry(radius, 64.0, 48.0),
new THREE.MeshPhongMaterial({
map: texture
})
);
this.add(earth);
}
createMarker(lat, lon) {
var marker = new Marker();
var lonRad = -lon * (Math.PI / 180);
var latRad = lat * (Math.PI / 180);
var r = this.userData.radius;
marker.position.set(Math.cos(latRad) * Math.cos(lonRad) * r, Math.sin(latRad) * r, Math.cos(latRad) * Math.sin(lonRad) * r);
marker.rotation.set(0.0, -lonRad, latRad - Math.PI * 0.5);
this.attach(marker);
}
}
// ------ Three.js code ------------------------------------------------
var scene, camera, renderer;
var controls;
init();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, 4 / 3, 0.1, 100);
camera.position.set(0.0, 1.5, 3.0);
renderer = new THREE.WebGLRenderer({
antialias: true
});
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.autoRotate = true;
controls.autoRotateSpeed = -1.0;
controls.enablePan = false;
var ambient = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambient);
var direcitonal = new THREE.DirectionalLight(0xffffff, 0.5);
direcitonal.position.set(5.0, 2.0, 5.0).normalize();
scene.add(direcitonal);
// just some code for the loading
var manager = createLoader(renderer.domElement, animate);
var texLoader = new THREE.TextureLoader(manager).setCrossOrigin(true);
var texture = texLoader.load('https://s3-eu-west-2.amazonaws.com/bckld/lab/textures/earth_latlon.jpg');
texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
var earth = new Earth(1.0, texture);
earth.createMarker(48.856700, 2.350800); // Paris
earth.createMarker(28.524167, -80.650833); // Paris
scene.add(earth);
window.addEventListener('resize', onResize);
onResize();
document.body.appendChild(renderer.domElement);
}
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.137.5/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.137.5/examples/js/controls/OrbitControls.js"></script>
<script src="https://s3-eu-west-2.amazonaws.com/bckld/lab/loading.js"></script>

How to have two Three-JS Objects run and animate at the same time?

I've created two 3JS objects and scenes for learning purposes. Both housed in separate PHP files (not in the jsfiddle). However, I can't figure out why by adding a second object the first object stops animating and doesn't run anymore? How can I go about figuring out why my objects are canceling one another out and how to create two different or more multiple threejs items that animate at the same time?
//GRID OBJ is the first object, and the second is //BALL
When I remove the second script calling for 3js ball the grid will animate, when the ball is added the grid stops animating and only the ball animates.
http://jsfiddle.net/tdqh4jno/
//GRID OBJ
var container = document.getElementById('grid')
var vertexHeight = 90,
planeDefinition = 25,
planeSize = 900,
totalObjects = 1,
background = "#002135",
meshColor = "#ff3057";
var camera2 = new THREE.PerspectiveCamera( 110, 1, 5)
camera2.position.y = 500;
var scene2 = new THREE.Scene();
scene2.background = new THREE.Color( 0x08080c);
var renderer = new THREE.WebGLRenderer({alpha: true});
renderer.setClearColor( 0x000000, 0 );
var planeGeo = new THREE.PlaneGeometry(planeSize, planeSize, planeDefinition, planeDefinition);
var plane = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({
color: meshColor,
wireframe: true
}));
plane.rotation.x -= Math.PI * .5;
scene2.add(plane);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(background, 1);
container.appendChild(renderer.domElement);
function updatePlane() {
for (var i = 0; i < planeGeo.vertices.length; i++) {
planeGeo.vertices[i].z += Math.random() * vertexHeight;
planeGeo.vertices[i]._myZ = planeGeo.vertices[i].z
}
};
var count = 0
function renderGrid() {
var gridSpeed = 0.4;
var gridInterlop = -0.4;
var gridWaveSpeed = 0.003;
var gridWaveHeight = 0.00003;
requestAnimationFrame(renderGrid);
for (var i = 0; i < planeGeo.vertices.length; i++) {
var z = +planeGeo.vertices[i].z;
planeGeo.vertices[i].z = Math.sin(( i + count * gridWaveHeight)) * (planeGeo.vertices[i]._myZ - (planeGeo.vertices[i]._myZ * gridWaveSpeed))
plane.geometry.verticesNeedUpdate = true;
count += gridInterlop
}
renderer.render(scene2, camera2);
}
updatePlane();
renderGrid();
//BALL ITEM
var camera2 = new THREE.PerspectiveCamera(100, window.innerWidth/window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.autoClear = false;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var sceneTop = new THREE.Scene(); // initialising the scene
sceneTop.background = new THREE.Color( 0x08080c);
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(100,100,100);
spotLight.castShadow = false;
sceneTop.add(spotLight);
function Mat(){
var material = new THREE.MeshPhongMaterial({
color : new THREE.Color(0xff3266),
emissive : new THREE.Color(0x08080c),
specular : new THREE.Color(0x08080c),
shininess : 0,
wireframe : true,
transparent: 0.5,
opacity : 0.55
});
return material;
}
var geometry = new THREE.IcosahedronGeometry(45 , 1);
var obj = new THREE.Mesh(geometry, Mat());
sceneTop.add(obj);
camera2.position.z = 90;
function myrender(){
renderer.setClearColor( 0x000000, 0 );
obj.rotation.x += 0.0004;
obj.rotation.y += 0.0006;
obj.rotation.z += Math.random() * 0.0005;
renderer.render(sceneTop, camera2);
requestAnimationFrame(myrender);
}
window.addEventListener('resize', onWindowResize, true);
function onWindowResize() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera2.updateProjectionMatrix();
};
myrender();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>
<div id="grid"></div>
You create 2 THREE.WebGLRenderer objects, but both assigned to the variable renderer. This breaks down the animation of the first (grid) object in the function renderGrid.
In your code are created 2 different scenes referenced by 2 different variables (sceneTop, scene2). Create 2 different render objects, referenced by 2 different variables (renderer, renderer2), too:
var scene2 = new THREE.Scene();
scene2.background = new THREE.Color( 0x08080c);
var renderer2 = new THREE.WebGLRenderer({alpha: true});
renderer2.setClearColor( 0x000000, 0 );
// [...]
renderer2.setSize(window.innerWidth, window.innerHeight);
renderer2.setClearColor(background, 1);
container.appendChild(renderer2.domElement);
function renderGrid() {
// [...]
renderer2.render(scene2, camera2);
}
In the following you have to respect, that renderer corresponds to camera respectively sceneTop, but renderer2 corresponds to scene2 and camera2:
var camera = new THREE.PerspectiveCamera(100, window.innerWidth/window.innerHeight, 0.1, 1000);
[...]
camera.position.z = 90;
function myrender(){
// [...]
renderer.render(sceneTop, camera);
requestAnimationFrame(myrender);
}
See the example, where I applied the suggested changes to your original code:
//GRID OBJ
var container = document.getElementById('grid')
var vertexHeight = 90,
planeDefinition = 25,
planeSize = 900,
totalObjects = 1,
background = "#002135",
meshColor = "#ff3057";
var camera2 = new THREE.PerspectiveCamera( 110, 1, 5)
camera2.position.y = 500;
var scene2 = new THREE.Scene();
scene2.background = new THREE.Color( 0x08080c);
var renderer2 = new THREE.WebGLRenderer({alpha: true});
renderer2.setClearColor( 0x000000, 0 );
var planeGeo = new THREE.PlaneGeometry(planeSize, planeSize, planeDefinition, planeDefinition);
var plane = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({
color: meshColor,
wireframe: true
}));
plane.rotation.x -= Math.PI * .5;
scene2.add(plane);
renderer2.setSize(window.innerWidth, window.innerHeight);
renderer2.setClearColor(background, 1);
container.appendChild(renderer2.domElement);
function updatePlane() {
for (var i = 0; i < planeGeo.vertices.length; i++) {
planeGeo.vertices[i].z += Math.random() * vertexHeight;
planeGeo.vertices[i]._myZ = planeGeo.vertices[i].z
}
};
var count = 0
function renderGrid() {
var gridSpeed = 0.4;
var gridInterlop = -0.4;
var gridWaveSpeed = 0.003;
var gridWaveHeight = 0.00003;
requestAnimationFrame(renderGrid);
for (var i = 0; i < planeGeo.vertices.length; i++) {
var z = +planeGeo.vertices[i].z;
planeGeo.vertices[i].z = Math.sin(( i + count * gridWaveHeight)) * (planeGeo.vertices[i]._myZ - (planeGeo.vertices[i]._myZ * gridWaveSpeed))
plane.geometry.verticesNeedUpdate = true;
count += gridInterlop
}
renderer2.render(scene2, camera2);
}
updatePlane();
renderGrid();
//BALL ITEM
var camera = new THREE.PerspectiveCamera(100, window.innerWidth/window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.autoClear = false;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var sceneTop = new THREE.Scene(); // initialising the scene
sceneTop.background = new THREE.Color( 0x08080c);
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(100,100,100);
spotLight.castShadow = false;
sceneTop.add(spotLight);
function Mat(){
var material = new THREE.MeshPhongMaterial({
color : new THREE.Color(0xff3266),
emissive : new THREE.Color(0x08080c),
specular : new THREE.Color(0x08080c),
shininess : 0,
wireframe : true,
transparent: 0.5,
opacity : 0.55
});
return material;
}
var geometry = new THREE.IcosahedronGeometry(45 , 1);
var obj = new THREE.Mesh(geometry, Mat());
sceneTop.add(obj);
camera.position.z = 90;
function myrender(){
renderer.setClearColor( 0x000000, 0 );
obj.rotation.x += 0.0004;
obj.rotation.y += 0.0006;
obj.rotation.z += Math.random() * 0.0005;
renderer.render(sceneTop, camera);
requestAnimationFrame(myrender);
}
window.addEventListener('resize', onWindowResize, true);
function onWindowResize() {
renderer.setSize(window.innerWidth, window.innerHeight);
renderer2.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
camera2.aspect = window.innerWidth / window.innerHeight;
camera2.updateProjectionMatrix();
};
myrender();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>
<div id="grid"></div>
Alternatively both scenes can be rendered to different parts of the viewport.
Create 1 THREE.WebGLRenderer, with the .autoClear property set to false:
var renderer = new THREE.WebGLRenderer({alpha: true, preserveDrawingBuffer: true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor( 0x000000, 0 );
renderer.autoClear = false;
document.body.appendChild(renderer.domElement);
Render both scenes 2 different parts of the viewport. The viewport rectangle can be changed by .setViewport:
function myrender(){
// [...]
renderer.setViewport(0,0,window.innerWidth, window.innerHeight);
renderer.clear();
renderer.setViewport(window.innerWidth/2,0,window.innerWidth/2, window.innerHeight);
renderer.render(sceneTop, camera, 0, false);
renderer.setViewport(0,0,window.innerWidth/2, window.innerHeight);
renderer.render(scene2, camera2, 0, false);
requestAnimationFrame(myrender);
}
Ensure that the .background property is only set for the THREE.Scene which is rendered first.
See the example:
//GRID OBJ
var container = document.getElementById('grid')
var vertexHeight = 90,
planeDefinition = 25,
planeSize = 900,
totalObjects = 1,
background = "#002135",
meshColor = "#ff3057";
var renderer = new THREE.WebGLRenderer({alpha: true, preserveDrawingBuffer: true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor( 0x000000, 0 );
renderer.autoClear = false;
document.body.appendChild(renderer.domElement);
var camera2 = new THREE.PerspectiveCamera( 110, window.innerWidth/2/window.innerHeight, 5)
camera2.position.y = 500;
var scene2 = new THREE.Scene();
var planeGeo = new THREE.PlaneGeometry(planeSize, planeSize, planeDefinition, planeDefinition);
var plane = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({
color: meshColor,
wireframe: true
}));
plane.rotation.x -= Math.PI * .5;
scene2.add(plane);
function updatePlane() {
for (var i = 0; i < planeGeo.vertices.length; i++) {
planeGeo.vertices[i].z += Math.random() * vertexHeight;
planeGeo.vertices[i]._myZ = planeGeo.vertices[i].z
}
};
var count = 0
updatePlane();
//BALL ITEM
var camera = new THREE.PerspectiveCamera(100, window.innerWidth/2/window.innerHeight, 0.1, 1000);
var sceneTop = new THREE.Scene(); // initialising the scene
sceneTop.background = new THREE.Color( 0x08080c);
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(100,100,100);
spotLight.castShadow = false;
sceneTop.add(spotLight);
function Mat(){
var material = new THREE.MeshPhongMaterial({
color : new THREE.Color(0xff3266),
emissive : new THREE.Color(0x08080c),
specular : new THREE.Color(0x08080c),
shininess : 0,
wireframe : true,
transparent: 0.5,
opacity : 0.55
});
return material;
}
var geometry = new THREE.IcosahedronGeometry(45 , 1);
var obj = new THREE.Mesh(geometry, Mat());
sceneTop.add(obj);
camera.position.z = 90;
function myrender(){
var gridSpeed = 0.4;
var gridInterlop = -0.4;
var gridWaveSpeed = 0.003;
var gridWaveHeight = 0.00003;
for (var i = 0; i < planeGeo.vertices.length; i++) {
var z = +planeGeo.vertices[i].z;
planeGeo.vertices[i].z = Math.sin(( i + count * gridWaveHeight)) * (planeGeo.vertices[i]._myZ - (planeGeo.vertices[i]._myZ * gridWaveSpeed))
plane.geometry.verticesNeedUpdate = true;
count += gridInterlop
}
obj.rotation.x += 0.0004;
obj.rotation.y += 0.0006;
obj.rotation.z += Math.random() * 0.0005;
renderer.setViewport(0,0,window.innerWidth, window.innerHeight);
renderer.clear();
renderer.setViewport(window.innerWidth/2,0,window.innerWidth/2, window.innerHeight);
renderer.render(sceneTop, camera, 0, false);
renderer.setViewport(0,0,window.innerWidth/2, window.innerHeight);
renderer.render(scene2, camera2, 0, false);
requestAnimationFrame(myrender);
}
window.addEventListener('resize', onWindowResize, true);
function onWindowResize() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / 2 / window.innerHeight;
camera.updateProjectionMatrix();
camera2.aspect = window.innerWidth / 2 / window.innerHeight;
camera2.updateProjectionMatrix();
};
myrender();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>

Preserve constant size regardless of perspective in Three.js

How can I preserve constant size of meshes regardless of perspective using Three.js?
Lets assume I have multiple meshes of the same size. I want them to always have the same on screen size. I also have to use perspective camera.
I have found some related answers, but seems that I miss something:
First approach is to use Euclidean distance to objects from
camera and scale them respectively every animation frame.
Three JS Keep Label Size On Zoom
I have tried to modify jsfiddle given in the answer to apply scale to meshes instead of sprites, but that doesn't work for me. The result looks as follows https://jsfiddle.net/a2ogz9vx/258/
var camera, scene, renderer, controls;
var planets = [];
var timestamp = 0;
var scaleVector = new THREE.Vector3();
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(0, 100, 100);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
controls = new THREE.OrbitControls(camera, renderer.domElement);
var createPlanet = function(name, radius, orbit, speed) {
var geom = new THREE.SphereGeometry(radius, 32, 16);
var mat = new THREE.MeshBasicMaterial({
color: Math.random() * 0xFFFFFF,
});
var planet = new THREE.Mesh(geom, mat);
planet.userData.orbit = orbit;
planet.userData.speed = speed;
var canvas = document.createElement('canvas');
canvas.width = 256;
canvas.height = 256;
var tex = new THREE.Texture(canvas);
tex.needsUpdate = true;
var spriteMat = new THREE.SpriteMaterial({
map: tex
});
var sprite = new THREE.Sprite(spriteMat);
planet.add(sprite);
planets.push(planet);
scene.add(planet);
};
createPlanet("One", 11, -10, 5);
createPlanet("Two", 11, 5, 5);
createPlanet("Three", 11, 10, 5);
}
function animate() {
requestAnimationFrame(animate);
planets.forEach(function(planet) {
var scaleFactor = 100;
var scale = scaleVector.subVectors(planet.position, camera.position).length() / scaleFactor;
planet.scale.set(scale, scale, scale);
var orbit = planet.userData.orbit;
var speed = planet.userData.speed;
planet.position.x = speed * orbit;
planet.position.z = speed * orbit;
});
render();
}
function render() {
renderer.render(scene, camera);
}
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/build/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
Second approach is to use on screen width calculation. https://stackoverflow.com/a/13351534/8465699
Result also doesn't look right https://jsfiddle.net/a2ogz9vx/260/
var camera, scene, renderer, controls;
var planets = [];
var timestamp = 0;
var scaleVector = new THREE.Vector3();
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(0, 100, 100);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
controls = new THREE.OrbitControls(camera, renderer.domElement);
var createPlanet = function(name, radius, orbit, speed) {
var geom = new THREE.SphereGeometry(radius, 32, 16);
var mat = new THREE.MeshBasicMaterial({
color: Math.random() * 0xFFFFFF,
});
var planet = new THREE.Mesh(geom, mat);
planet.userData.orbit = orbit;
planet.userData.speed = speed;
var canvas = document.createElement('canvas');
canvas.width = 256;
canvas.height = 256;
var tex = new THREE.Texture(canvas);
tex.needsUpdate = true;
var spriteMat = new THREE.SpriteMaterial({
map: tex
});
var sprite = new THREE.Sprite(spriteMat);
planet.add(sprite);
planets.push(planet);
scene.add(planet);
};
createPlanet("One", 11, -10, 5);
createPlanet("Two", 11, 0, 5);
createPlanet("Three", 11, 10, 5);
}
function animate() {
requestAnimationFrame(animate);
planets.forEach(function(planet) {
const dist = planet.position.distanceTo(camera.position);
const vFOV = THREE.Math.degToRad(camera.fov);
const size = 2 * Math.tan(vFOV / 2) * dist;
const scaleFactor = 130;
const scale = size / scaleFactor;
planet.scale.set(scale, scale, scale);
var orbit = planet.userData.orbit;
var speed = planet.userData.speed;
planet.position.x = speed * orbit;
planet.position.z = speed * orbit;
});
render();
}
function render() {
renderer.render(scene, camera);
}
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/build/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
Any suggestions how to fix the code or other approaches are welcome!

Insufficient buffer size, a buffer must be bound

I'm trying to add a lamp to my scene with an OBJLoader object. The object is exported from Blender. If I set its children[i].castShadow property to true the following errors show up but still it appears and its shadow can be seen on the wall:
What am I doing wrong? If and when I add some other object to my scene and set its castShadow property to true I get no error messages whatsoever. This is the only problematic object I have currently.
One more thing. My project won't work in Firefox, but it displays in Edge or Chrome for example. What is so special about Firefox?
<!DOCTYPE html>
<html>
<head>
<title>Jaj de jó</title>
<script type="text/javascript" src="libs/three.js"></script>
<script type="text/javascript" src="libs/OBJLoader.js"></script>
<script type="text/javascript" src="libs/MTLLoader.js"></script>
<script type="text/javascript" src="libs/OBJMTLLoader.js"></script>
<script type="text/javascript" src="libs/OrbitControls.js"></script>
<script type="text/javascript" src="libs/ThreeBSP.js"></script>
<script type="text/javascript" src="libs/stats.js"></script>
<script type="text/javascript" src="libs/dat.gui.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
<meta charset="UTF-8">
</head>
<body>
<div id="WebGL-output"></div>
<script type="text/javascript">
var webGLRenderer;
function init() {
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
webGLRenderer = new THREE.WebGLRenderer();
webGLRenderer.setClearColor(new THREE.Color(0x666666, 1.0));
webGLRenderer.setSize(window.innerWidth, window.innerHeight);
webGLRenderer.shadowMapEnabled = true;
webGLRenderer.shadowMapType = THREE.PCFShadowMap;
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 50;
camera.lookAt(new THREE.Vector3(0, 0, 0));
var orbitControls = new THREE.OrbitControls(camera);
orbitControls.autoRotate = false;
var clock = new THREE.Clock();
document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);
/* prepare, load textures and add geometries to the scene */
var planeGeometry = new THREE.PlaneBufferGeometry(200, 200, 1, 1);
var texture = THREE.ImageUtils.loadTexture("textures/floor_wood.jpg");
var planeMaterial = new THREE.MeshPhongMaterial();
planeMaterial.map = texture;
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.x = 200 / 100;
texture.repeat.y = 200 / 100;
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 0;
plane.position.y = 0;
plane.position.z = -20;
scene.add(plane);
/* FLOOR ADDED */
/* WALLS START */
var wallGeometry = new THREE.BoxGeometry(150, 100, 175);
texture = THREE.ImageUtils.loadTexture("textures/brick-wall.jpg");
var material = new THREE.MeshPhongMaterial();
material.map = texture;
var wall = new THREE.Mesh(wallGeometry, material);
wall.receiveShadow = true;
wall.material.side = THREE.BackSide;
wall.material.map.repeat.set(200 / 100, 175 / 100);
wall.material.map.wrapS = THREE.RepeatWrapping;
wall.material.map.wrapT = THREE.RepeatWrapping;
wall.material.needsUpdate = true;
wall.position.y = 25;
wall.position.z -= 25;
scene.add(wall);
/* WALLS END */
/* LAMP */
var loader = new THREE.OBJLoader();
loader.load("models/lamp.obj", function (object) {
console.log("LAMP");
console.log(object);
var blackMaterial = new THREE.MeshPhongMaterial({ color: 0x222222 });
for(var i = 0; i < object.children.length; i++) {
object.children[i].material = blackMaterial;
object.children[i].castShadow = true;
}
object.scale.set(5, 5, 5);
object.position.x = -14.5;
object.position.y = 40;
object.position.z = -17;
scene.add(object);
});
/* LAMP END */
// add spotlight for the shadows
var spotLight = new THREE.SpotLight(0xffffff, 1);
spotLight.position.set(75, 75, 15);
spotLight.castShadow = true;
spotLight.shadowCameraNear = 20;
spotLight.shadowDarkness = .6;
spotLight.shadowMapWidth = 512;
spotLight.shadowMapHeight = 512;
spotLight.shadowCameraFov = 64;
scene.add(spotLight);
render();
function render() {
var delta = clock.getDelta();
orbitControls.update(delta);
requestAnimationFrame(render);
webGLRenderer.render(scene, camera);
}
}
window.onload = init;
</script>
</body>
</html>

THREE.JS does not cast shadows

I was doing a Three js tutorial and this code should cast shadows, but when render shadows does not exists over the plane.
What is wrong on code? I'm reading documentation and other codes and I can not find the problem.
Best regards!
EDIT:
If i make a loop with requestAnimationFrame just works... but only in that case... why?
<!DOCTYPE html>
<html>
<head>
<title>Learning THREE JS Basic</title>
<script src="../../libs/three.js"></script>
<script src="../../libs/jquery.js"></script>
<style>
body {
margin : 0;
overflow : hidden;
}
</style>
</head>
<body>
<div id="visor"></div>
<script>
$(function () {
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xEEEEEE);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
var axes = new THREE.AxisHelper(20);
scene.add(axes);
var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);
var planeMaterial = new THREE.MeshLambertMaterial({
color: 0xFFFFFF
});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -0.5*Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
plane.receiveShadow = true;
scene.add(plane);
var cubeGeometry = new THREE.BoxGeometry(4,4,4);
var cubeMaterial = new THREE.MeshLambertMaterial({
color: 0xFF0000
});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
cube.castShadow = true;
scene.add(cube);
var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
var sphereMaterial = new THREE.MeshLambertMaterial({
color: 0x7777FF
});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.x = 20;
sphere.position.y = 4;
sphere.position.z = 2;
sphere.castShadow = true;
scene.add(sphere);
var spotLight = new THREE.SpotLight(0xFFFFFF);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
$("#visor").append(renderer.domElement);
renderer.render(scene, camera);
});
</script>
</body>
</html>
three.js r.74 and r.75 have a bug in which shadows are not always rendered in the first call to render(). This bug has been fixed in three.js r.76dev.
Your work-around is to call renderer.render( scene, camera ) a 2nd time, or to have an animation loop.
three.js r.75

Categories