I am new to Three.JS and am trying to load a load a very simple (single cube) Sketchup model into Three.JS via the ColladaLoader, I get no errors but nothing is displayed:
var renderer = new THREE.WebGLRenderer();
var loader = new THREE.ColladaLoader();
var scene = new THREE.Scene();
var camera = new THREE.Camera();
loader.load('Test.dae', function (result) {
scene.add(result.scene);
});
camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set(0, 0, 5);
scene.add(camera);
renderer.render(scene,camera);
Can anyone spot any immediate errors? Thanks
Fixed. Whilst I had declared the renderer, I hadn't attached it to the document. The following code works:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(100, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera.position.set(0, 0, 4);
var loader = new THREE.ColladaLoader();
loader.load("test.dae", function (result) {
scene.add(result.scene);
});
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
render();
You need to rerender the scene after the collada is loaded. Something like
loader.load('Test.dae', function (result) {
scene.add(result.scene);
renderer.render(scene,camera);
});
Additionally:
The camera might be inside your geometry. Faces are one-sided by default, they are invisible when looking from back/inside. You could try changing the camera or object location and/or set material.side = THREE.DoubleSide so the faces are visible from both front and back.
The scale of the model can be way off, so it can be too small or large to show. Try different camera positions (eg. z= -1000, -100, -10, -1, 1, 10, 100, 1000) and use lookAt() to point it to 0,0,0 or, I think colladaloader has a scale setting nowadays, not sure.
Loader defaults to position 0,0,0, so center of the world/scene. That doesn't necessarily mean center of screen, depends on camera. In some cases the Collada model itself can be such that the center is away from visible objects, so when it's placed on the scene it can be effectively "off-center". That's quite unlikely though.
You dont have any lights in the scene.
loader.load('test.dae', function colladaReady(collada) {
localObject = collada.scene;
localObject.scale.x = localObject.scale.y = localObject.scale.z = 2;
localObject.updateMatrix();
scene.add(localObject);
I think you need to add the object in the collada scene, or there might be problem with the scaling of the object that you are adding, scale it and than update the matrix of the object.
If your still having trouble it may be that some versions of IE stop you downloading local files (your Test.dae) so it may be worth trying it using firefox or putting your code up on a server.
Related
The program I am writing will be used to rotate a cube. The html file will allow the user to input a csv file with quaternion data. Then, the javascript code rotates the cube using each quaternion in the data file at a constant rate. In this case, each new quaternion is applied to the cube every 16.7 milliseconds (60 fps). The current program I have written uses setTimeout to execute this goal.
Here is the javascript code I currently have:
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff70 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
renderer.render(scene,camera);
var animate = function() {
quat_data = [];
var rotate = function(quat) {
cube.applyQuaternion(quat);
}
for(i=0; i<quat_data.length; i++) {
time = 16.7;
var next_quat = quat_data[i];
setTimeout(rotate(next_quat), time);
}
}
</script>
The user clicks a button on the top of the browser display to execute the animate function. Also, note that quat_data is currently empty. To test the javascript code, I set quat_data equal to an array of sample quaternions. I have not yet written code to convert the inputted csv file to an array. Once I do, I will use that instead of the sample quaternions.
The problem is that when I run the program, the cube displays, but it does not rotate. I include enough sample quaternions to be able to observe the rotation, so lack of quaternions is not the issue. I get no console errors. I've tried including the line
renderer.render(scene,camera);
within the rotate function and at several other locations in the code, but that did not re-render the cube.
What code should I include in the animate function to re-render the cube after each quaternion is applied? Or, is there a way to execute the same task with requestAnimationFrame?
First thing is, you need to use the renderer.render(scene,camera); inside the rotate function because after changing the cube's orientation, you need to re-render the scene.
The second thing is, you are using the setTimeout function in a wrong way. You want to call the rotate function after each of 16.7, 33.4, 50.1 ... milliseconds. So you have to pass the time that way. Also you have to pass a callback function instead of directly calling the function. Try this following code -
let time = 0;
for(let i=0; i<quat_data.length; i++) {
time += 16.7;
setTimeout(function() {
rotate(quat_data[i])
}, time);
}
The last thing is, try to avoid setTimeout function for animation, instead use window.requestAnimationFrame. This function will be called by the browser before the next repaint and you can change the cube orientation in this function and render the scene. Here is the sample code -
let i= 0;
var animate = function () {
requestAnimationFrame( animate );
i++;
if (i >= quat_data.length)
i = quat_data.length - 1;
let quat = quat_data[i];
cube.applyQuaternion(quat);
renderer.render( scene, camera );
};
animate();
I'm trying to set up a very simple scene with Three.js, showing an imported mesh rotating. I combined a couple of the examples from the Three.js documentation and arrived at the following code:
var scene, camera, renderer;
var geometry, material, mesh;
init();
animate();
function init(){
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 1000;
// geometry = new THREE.BoxGeometry(200, 200, 200);
var jsonLoader = new THREE.JSONLoader();
jsonLoader.load('handgun.js', object_to_scene(geometry, material));
}
function object_to_scene(geometry, material){
material = new THREE.MeshBasicMaterial({
color: 0xff0000,
wireframe: true
});
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate(){
requestAnimationFrame(animate);
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render(scene, camera);
}
You can see the original example commented out in the above, where we generated a Box using Three.js. That worked fine. However, importing the JSON file of the custom 3D model isn't working. Checking my console reveals the following error:
TypeError: onLoad is not a function three.js:18294:4
THREE.JSONLoader.prototype.load/<() three.js:18294
THREE.XHRLoader.prototype.load/<() three.js:18010
This appears to be an error with Three.js itself, however I've found only two instances of people reporting it on the Github account, and both were told that the Github was for reporting bugs, not asking for help (if this isn't a bug, then what is it?).
Has anyone else encountered this issue, and if you have resolved it, how did you do so?
Thanks!
Instead of
jsonLoader.load( 'handgun.js', object_to_scene( geometry, material ) );
You need to do this:
jsonLoader.load( 'handgun.js', object_to_scene );
object_to_scene( geometry, material ) is a function call that, in your case, returns undefined.
three.js r.75
I have a scene which contains 15-20 objects, 4 lights. And properties of my renderer are
function getRenderer(container, width, height) {
var renderer;
renderer = new THREE.WebGLRenderer({ alpha: false, antialias: true, preserveDrawingBuffer: false });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
container.appendChild(renderer.domElement);
renderer.shadowMapEnabled = true;
renderer.shadowMapType = THREE.PCFSoftShadowMap;
renderer.setClearColor(new THREE.Color(0xCCE0FF), 1);
renderer.gammaInput = true;
renderer.gammaOutput = true;
renderer.clear();
return renderer;
}
My render loop renders the scene every second.
function renderLoop() {
this.renderer.render(this.scene, this.camera);
setTimeout(function () {
renderLoop();
}, 1000);
}
The problem I am facing is this.renderer.render(this.scene, this.camera) is taking about 100 ms to render the scene but I want it to be below 33ms so that I can have frame rate of at least 30 fps.
Is there a way to optimize the renderer performance by any means (like changing any properties or something)?
I don't want to use worker.js as my scene is static and doesn't contain any complex calculations.
If you have a static scene, there is no reason to have an animation loop. You just have to render once after the scene -- and all your assets -- load.
That is why there are callbacks for the loader functions. And that is why there is a THREE.LoadingManager.
There are many possible use cases. Study the three.js examples to find solutions for your particular use case.
If you are using OrbitControls to control the camera, you can force a re-render whenever the camera moves, like so:
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // use if there is no animation loop
three.js r.75
I've exported a simplistic blender model I made earlier with the plugin provided by three.js, which created a .json file. I then tried to import that file into my project, but am having no success at all.
I have it on a local server, because of file transfers not being allowed through file://, therefore, I'm using HTTP.
The issue is that I cannot see the model. I'm using the following code to import it:
var loader = new THREE.JSONLoader();
loader.load( 'model/mountain.json', function ( geometry ) {
var mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial() );
scene.add( mesh );
});
I have both camera and scenes setup properly because I can create and add native three.js shapes, such as boxes and plains, and I'm 100% sure the file is in the correct place.
I asked on reddit, and /u/Egenimo helped me with the answer!
Here's the whole code, as I figure it might help people who need to set up a scene aswell.
var canvas = document.getElementById("bgcanvas"),
renderer = new THREE.WebGLRenderer({ canvas: canvas, alpha: true }),
windowHeight = window.innerHeight,
windowWidth = window.innerWidth,
camera, scene, renderer, loader;
init();
function init() {
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x000000, 0);
renderer.shadowMapEnabled = true;
renderer.shadowMapType = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// camera
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 5;
// scene
scene = new THREE.Scene();
loader = new THREE.JSONLoader();
loader.load( 'cube.json', function ( geometry ) {
var mesh = new THREE.Mesh(geometry, new THREE.MeshNormalMaterial({overdraw: true}));
scene.add( mesh );
});
console.log(scene.children.length);
render();
}
function render() {
requestAnimationFrame(function() {
render();
});
renderer.render(scene, camera);
}
I then ran into the issue of having a more complex model, with several objects, and ThreeJS would only import a single one, therefore, after looking it up, I found this answer by the Man himself!.
Hope this helps someone in need!
I have a 3D object from Blender: cylinder.obj which I am able to render on the screen using Three.js. I also have the code in place to rotate the object using mouse. All this scene is inside a big div, say 600x600 pixel div.
I have the following code for render logic:
// Renders the scene and updates the render as needed.
function animate() {
// Render the scene.
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
This in my understanding is creating a loop and rendering the object again and again on the page.
Is there a way I can render the object once in the scene and then only redraw when there is any change to the object like texture change or when rotating the object using mouse. Once this texture application is done or the mouse rotate is complete, I want the scene to say intact and not consume any CPU for 3D rendering.
So, in brief, is there a way to render the scene only when needed and then stay idle and not consume a lot of CPU when the scene is behaving like an image.
I am new to three.js. I am testing this application in Chrome and IE11. Let me know if any more information is needed for clarification.
EDIT: Adding full JS code:
// global variables
// Set up the scene, camera, and renderer as global variables.
var scene, camera, renderer;
var WIDTH = $("#myDiv").width(),
HEIGHT = $("#myDiv").height();
// Sets up the scene.
function init() {
// Create the scene and set the scene size.
scene = new THREE.Scene();
// Create a renderer and add it to the DOM.
if (Detector.webgl)
renderer = new THREE.WebGLRenderer({ antialias: true });
else
renderer = new THREE.CanvasRenderer(); //IE10 and below, and may be mobile devices
renderer.setSize(WIDTH, HEIGHT);
renderer.domElement.id = 'mycanvas'; //setting id for canvas element
console.log(renderer);
$("#myDiv").append(renderer.domElement);
// Create a camera, zoom it out from the model a bit, and add it to the scene.
camera = new THREE.PerspectiveCamera(45, WIDTH / HEIGHT, 0.1, 100);
camera.position.set(-3, 4, 12);
scene.add(camera);
// Create an event listener that resizes the renderer with the browser window.
window.addEventListener('resize', function () {
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
});
// Set the background color of the scene.
renderer.setClearColor(0x333F47, 1);
var light2 = new THREE.PointLight(0xffffff);
light2.position.set(0, 10, -10);
scene.add(light2);
var light3 = new THREE.PointLight(0xffffff);
light3.position.set(1, 5, 10);
scene.add(light3);
// Load in the mesh and add it to the scene.
var objLoader = new THREE.OBJLoader();
objLoader.load('objects/male.obj', function (object) {
object.position.y = 0;
object.scale.x = object.scale.y = object.scale.z = 1;
scene.add(object);
});
}
// Renders the scene and updates the render as needed.
function draw() {
//requestAnimationFrame(draw);
// Render the scene.
renderer.render(scene, camera);
}
init();
draw();
To do something only after something happens is called event-driven programming.
For example:
function OnMouseMove(evt) {
// do some transform update on object
// after done updating - draw!
draw();
}
function textureLoaded() {
// hurray, my texture's now loaded, I'm ready to draw now
draw();
}
function draw() {
renderer.render(scene, camera);
}
So instead drawing with timer, you draw thing only when you need them to, and in your case, after mouse move or after the texture's finished loading.
You could then attach OnMouseMove to an event listener, and textureLoaded as some Three.js's callback function for texture loading.
P. S. GPU is actually doing the drawing, not the CPU.
Figured out the logic using start - stop animation function on my events from link: Prevent requestAnimationFrame from running all the time