Run the following jsfiddle code with size = 256, we got ~60ms/frame performance. While setting size = 1024, we got ~10ms/frame. Does anyone know why is a larger sized texture renders faster in this case?
Here is the jsfiddle link: https://jsfiddle.net/weilun/bfchn7yv/11/, check console log for printed timing info.
var width = window.innerWidth, height = window.innerHeight;
var size = 1024; // use these we got 60ms / frame, while use 1024 we got 10ms / frame
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
var camera, scene, renderer, geometry, texture, mesh;
function changeCanvas() {
ctx.font = '20pt Arial';
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.fillRect(10, 10, canvas.width - 20, canvas.height - 20);
ctx.fillStyle = 'black';
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(new Date().getTime(), canvas.width / 2, canvas.height / 2);
}
function init() {
renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(70, width / height, 1, 1000);
camera.position.z = 500;
scene.add(camera);
texture = new THREE.Texture(canvas);
var material = new THREE.MeshBasicMaterial({ map: texture });
geometry = new THREE.CubeGeometry( 200, 200, 200 );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
canvas.width = canvas.height = size;
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
var s = performance.now()
mesh.rotation.y += 0.01;
for (var i=0; i<100; i++) {
changeCanvas();
texture.needsUpdate = true;
renderer.render(scene, camera);
}
console.log('total: ', performance.now() - s)
}
init();
animate();
I'm trying to do some optimizations on another project by switching to smaller textures, but it actually made it worse :-(
Remove the for loop in the animate function.
I can see no use for it and it is generating, applying the texture and rendering the scene 100 times per frame.
function animate() {
requestAnimationFrame(animate);
var s = performance.now()
mesh.rotation.y += 0.01;
for (var i=0; i<100; i++) { <----remove this line
changeCanvas();
texture.needsUpdate = true;
renderer.render(scene, camera);
} <-----and this line
console.log('total: ', performance.now() - s)
}
Related
I have a THREE.ArrayCamera with 4 cameras and trying to make my object visible from only 1 camera. I saw I can use Layers, which work well when I have 1 camera and doesn’t work at all with Array cameras. Object isn’t visible inside all cameras, despite it has layer 1 and camera.cameras[2] has layer1 enabled. JSFIDDLE: https://jsfiddle.net/h7u02jLw/
mesh = new THREE.Mesh( geometryCylinder, materialCylinder );
mesh.castShadow = true;
mesh.receiveShadow = true;
scene.add( mesh );
mesh.layers.set(1);
camera.cameras[2].layers.enable(1);
light.layers.enable(1);
console.log(camera.cameras[2].layers.test(mesh.layers))
var material2 = new THREE.MeshPhongMaterial({color: 0x00FF00});
mesh2 = new THREE.Mesh( geometryCylinder, material2 );
mesh2.castShadow = true;
mesh2.receiveShadow = true;
scene.add( mesh2 );
The combination of ArrayCamera and Layers has only limited support.
To make the red cylinder render in only one view, it's not sufficient to just enabled the layer on the sub camera. All layers you are going to use have to be enabled on the array camera, too. The following code demonstrated this.
However, you immediately see a rendering issue since shadows are rendered on all views. That happens because the shadow map is updated only once per frame and the layer configuration of the array camera is evaluated. Since all layers are enabled, all views show the shadow of the red cylinder. Shadow maps per sub camera are not supported.
let camera, scene, renderer;
let mesh, mesh2;
const AMOUNT = 2;
init();
animate();
function init() {
const ASPECT_RATIO = window.innerWidth / window.innerHeight;
const WIDTH = (window.innerWidth / AMOUNT) * window.devicePixelRatio;
const HEIGHT = (window.innerHeight / AMOUNT) * window.devicePixelRatio;
const cameras = [];
for (let y = 0; y < AMOUNT; y++) {
for (let x = 0; x < AMOUNT; x++) {
const subcamera = new THREE.PerspectiveCamera(40, ASPECT_RATIO, 0.1, 10);
subcamera.viewport = new THREE.Vector4(Math.floor(x * WIDTH), Math.floor(y * HEIGHT), Math.ceil(WIDTH), Math.ceil(HEIGHT));
subcamera.position.x = (x / AMOUNT) - 0.5;
subcamera.position.y = 0.5 - (y / AMOUNT);
subcamera.position.z = 1.5;
subcamera.position.multiplyScalar(2);
subcamera.lookAt(0, 0, 0);
subcamera.updateMatrixWorld();
cameras.push(subcamera);
}
}
camera = new THREE.ArrayCamera(cameras);
camera.layers.enable(1);
camera.position.z = 3;
scene = new THREE.Scene();
scene.add(new THREE.AmbientLight(0x222244));
const light = new THREE.DirectionalLight();
light.position.set(0.5, 0.5, 1);
light.castShadow = true;
light.shadow.camera.zoom = 4; // tighter shadow map
scene.add(light);
const geometryBackground = new THREE.PlaneGeometry(100, 100);
const materialBackground = new THREE.MeshPhongMaterial({
color: 0x000066
});
const background = new THREE.Mesh(geometryBackground, materialBackground);
background.receiveShadow = true;
background.position.set(0, 0, -1);
scene.add(background);
const geometryCylinder = new THREE.CylinderGeometry(0.5, 0.5, 1, 32);
const materialCylinder = new THREE.MeshPhongMaterial({
color: 0xff0000
});
mesh = new THREE.Mesh(geometryCylinder, materialCylinder);
mesh.castShadow = true;
mesh.receiveShadow = true;
scene.add(mesh);
mesh.layers.set(1);
camera.cameras[2].layers.enable(1);
//light.layers.set(1);
var material2 = new THREE.MeshPhongMaterial({
color: 0x00FF00
});
mesh2 = new THREE.Mesh(geometryCylinder, material2);
mesh2.castShadow = true;
mesh2.receiveShadow = true;
scene.add(mesh2);
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
//
window.addEventListener('resize', onWindowResize);
}
function onWindowResize() {
const ASPECT_RATIO = window.innerWidth / window.innerHeight;
const WIDTH = (window.innerWidth / AMOUNT) * window.devicePixelRatio;
const HEIGHT = (window.innerHeight / AMOUNT) * window.devicePixelRatio;
camera.aspect = ASPECT_RATIO;
camera.updateProjectionMatrix();
for (let y = 0; y < AMOUNT; y++) {
for (let x = 0; x < AMOUNT; x++) {
const subcamera = camera.cameras[AMOUNT * y + x];
subcamera.viewport.set(
Math.floor(x * WIDTH),
Math.floor(y * HEIGHT),
Math.ceil(WIDTH),
Math.ceil(HEIGHT));
subcamera.aspect = ASPECT_RATIO;
subcamera.updateProjectionMatrix();
}
}
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
mesh.rotation.x += 0.005;
mesh.rotation.z += 0.01;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.126.1/build/three.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>
I have simple scene with one cube and one directional light with shadows enabled.
Cube cast and receive shadow. But shadows on right side of the cube ara strange. In middle of shadowcamera shadow gets darker. Why?
Here is example:
code below:
var SCREEN_WIDTH = window.innerWidth - 100;
var SCREEN_HEIGHT = window.innerHeight - 100;
var camera, scene;
var canvasRenderer, webglRenderer;
var container, mesh, geometry, plane;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 100000);
camera.position.set(500, 500, -1000);
camera.lookAt({ x: 0, y: 0, z: 0 });
scene = new THREE.Scene();
// LIGHTS
scene.add(new THREE.AmbientLight(0x666666));
var light;
light = new THREE.DirectionalLight(0xdfebff, 1.75);
light.position.set(100, 50, 20);
light.position.multiplyScalar(1.3);
light.castShadow = true;
light.shadowCameraVisible = true;
light.shadowMapWidth = light.shadowMapHeight = 2048;
var d = 200;
light.shadowCameraLeft = -d;
light.shadowCameraRight = d;
light.shadowCameraTop = d;
light.shadowCameraBottom = -d;
light.shadowCameraFar = 500;
light.shadowDarkness = 0.5;
//light.shadowBias = 0.001;
scene.add(light);
var box = new THREE.Mesh(new THREE.CubeGeometry(1000, 500, 100), new THREE.MeshLambertMaterial({ color: 0xFF0000 }));
box.castShadow = true;
box.receiveShadow = true;
scene.add(box);
// RENDERER
webglRenderer = new THREE.WebGLRenderer();
webglRenderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
webglRenderer.domElement.style.position = "relative";
webglRenderer.shadowMapEnabled = true;
//webglRenderer.shadowMapSoft = true;
webglRenderer.shadowMapType = THREE.PCFSoftShadowMap;
//webglRenderer.sortObjects = false;
//webglRenderer.setFaceCulling(THREE.CullFaceNone);
//webglRenderer.autoClear = false;
//webglRenderer.shadowMapCullFace = THREE.CullFaceNone;
container.appendChild(webglRenderer.domElement);
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
webglRenderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
var timer = Date.now() * 0.0002;
//camera.position.x = Math.cos(timer) * 1000;
//camera.position.z = Math.sin(timer) * 1000;
requestAnimationFrame(animate);
render();
}
function render() {
camera.lookAt(scene.position);
webglRenderer.render(scene, camera);
}
UPDATE:
changed fiddler code, now light is outside cube, and light is rotating around cube.
Now my problem is even more visible. When light direction vector is almost perpendicular to cube normal on shadow there is sharp line in the middle of light shadowCamera.
new fiddler code: http://jsfiddle.net/gbwojcg6/2/
The way you have your scene set up currently, the light source is inside the object.
So there are several things you can do.
move the light outside the object
reduce the very big value of camera far
use a negative shadow bias light.shadowBias = -0.01;
updated fiddle
It seams to be an issue with three.js prior r67, new version fix my problem (darker shadow in the middle of shadowcamera)
I've looked through a number of other S.O. questions, followed all of the advice, but I'm still clueless as to why I can't get shadows to render on this very basic scene.
http://jsfiddle.net/4Txgp/
[Updated] Code:
var SCREEN_WIDTH = window.innerWidth - 25;
var SCREEN_HEIGHT = window.innerHeight - 25;
var camera, scene;
var canvasRenderer, webglRenderer;
var container, mesh, geometry, plane;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.x = 1200;
camera.position.y = 1000;
camera.lookAt({
x: 0,
y: 0,
z: 0
});
scene = new THREE.Scene();
var groundMaterial = new THREE.MeshLambertMaterial({
color: 0x6C6C6C
});
plane = new THREE.Mesh(new THREE.PlaneGeometry(10000, 10000, 100, 100), groundMaterial);
plane.rotation.x = -Math.PI / 2;
plane.receiveShadow = true;
scene.add(plane);
// LIGHTS
// scene.add(new THREE.AmbientLight(0x666666));
/*
var light;
light = new THREE.DirectionalLight(0xdfebff, 1.75);
light.position.set(600, 800, 100);
//light.position.multiplyScalar(1.3);
light.castShadow = true;
light.shadowCameraVisible = true;
light.shadowMapWidth = light.shadowMapHeight = 2048;
var d = 50;
light.shadowCameraLeft = -d;
light.shadowCameraRight = d;
light.shadowCameraTop = d;
light.shadowCameraBottom = -d;
light.shadowCameraFar = 500;
light.shadowDarkness = 0.5;
scene.add(light);
*/
var spotLight = new THREE.SpotLight( 0xffffff );
spotLight.position.set( 700, 1000, 100 );
spotLight.castShadow = true;
spotLight.shadowCameraVisible = true;
spotLight.shadowMapWidth = 2048;
spotLight.shadowMapHeight = 2048;
spotLight.shadowCameraNear = 100;
spotLight.shadowCameraFar = 2000;
spotLight.shadowCameraFov = 30;
scene.add( spotLight );
var boxgeometry = new THREE.CubeGeometry(100, 200, 100);
var boxmaterial = new THREE.MeshLambertMaterial({
color: 0x0aeedf
});
var cube = new THREE.Mesh(boxgeometry, boxmaterial);
cube.position.x = 0;
cube.position.y = 100;
cube.position.z = 0;
scene.add(cube);
// RENDERER
webglRenderer = new THREE.WebGLRenderer();
webglRenderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
//webglRenderer.domElement.style.position = "relative";
webglRenderer.shadowMapEnabled = true;
webglRenderer.shadowMapSoft = true;
container.appendChild(webglRenderer.domElement);
}
function animate() {
var timer = Date.now() * 0.0002;
camera.position.x = Math.cos(timer) * 1000;
camera.position.z = Math.sin(timer) * 1000;
requestAnimationFrame(animate);
render();
}
function render() {
camera.lookAt(scene.position);
webglRenderer.render(scene, camera);
}
I have a scene with a plane, an object (cube), a spotlight (copied directly from http://threejs.org/docs/58/#Reference/Lights/SpotLight for testing purposes) , and a camera. It renders fine, except that the cube is not casting a shadow on the "ground" (plane), and the shading looks like everything has been done in a basic material. I'm using a combo of Phongs and Lamberts.
My directional light is set to castShadow = true;, and my plane is set with receiveShadow = true, along with shadow map settings. The renderer itself has shadowMapEnabled = true.
I've tried various solutions, I remember with previous versions of ThreeJS there would be external library calls depending on what you wanted to do, but I've also seen other examples on JSFiddle just calling ThreeJS by itself, as well as examples from the official site, that work fine.
Any hints/info/constructive remarks about overlooking something simple and small?
You need to set
cube.castShadow = true;
and make sure the shadow camera far plane reaches the cube.
three.js r.144
I'm trying to animate a Canvas-based texture that is mapped onto a plane, like a billboard. I've made a point of including material.needsUpdate & texture.needsUpdate, but I'm still unable to get the texture to come to life. I've also included a rotating cube just so I know the animation routine is functioning on some level.
Here is the code:
<body>
<script src="http://mrdoob.github.com/three.js/build/three.min.js"></script>
<script>
if (window.innerWidth === 0) {
window.innerWidth = parent.innerWidth;
window.innerHeight = parent.innerHeight;
}
var camera, scene, renderer;
var mesh, geometry, material;
var light, sign, animTex;
var canvas, context;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1200);
camera.position.z = 700;
scene = new THREE.Scene();
material = new THREE.MeshLambertMaterial(
{
color: 0x885522,
wireframe: false,
overdraw: false
});
geometry = new THREE.CubeGeometry(80, 120, 100, 1, 1, 1);
mesh = new THREE.Mesh(geometry, material);
sign = createSign();
light = new THREE.DirectionalLight(0xFFFFFF, 3.0);
light.position = new THREE.Vector3(5, 10, 7);
light.target = new THREE.Vector3(0, 0, 0);
scene.add(mesh);
scene.add(sign);
scene.add(light);
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function createSign() {
canvas = document.createElement("canvas");
context = canvas.getContext("2d");
canvas.width = 200;
canvas.height = 200;
context = canvas.getContext("2d");
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var material = new THREE.MeshBasicMaterial({ map : texture, overdraw: true });
material.needsUpdate = true;
var mesh = new THREE.Mesh(new THREE.PlaneGeometry(200, 200), material);
mesh.doubleSided = true;
return mesh;
}
function animate() {
var time = Date.now()*0.01;
var sinTime = Math.sin(time * 0.05) * 100;
var cosTime = Math.cos(time * 0.05) * 100;
mesh.rotation.y = sinTime*0.01;
requestAnimationFrame(animate);
context.fillStyle = "black";
context.fillRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "white";
context.fillRect((canvas.width/2) + sinTime, (canvas.height/2) + cosTime, 20, 20)
renderer.render(scene, camera);
}
This runs, but I can't seem to get the Canvas texture material to update. What have I overlooked?
Place this right before your render() call:
sign.material.map.needsUpdate = true;
The needsUpdate flag is reset (to false) every time the texture is used (every render loop), so it needs to be set to true in the render loop (before the render call, or it'll be a frame off). So in your example, put sign.material.map.needsUpdate = true before renderer.render( scene, camera ). texture.needsUpdate = true and material.needsUpdate = true are not needed.
Also, you only need to set the needsUpdate flag on the texture, as the material properties are not changing.