I'm about to learn three.js. I've downloaded the library from github: three.js
I tried to run the first example of three.js in the link above. The weird problem is, it's working in jsFiddle but it not working in my computer.
I've this error in console:
TypeError: document.body is null
[Break On This Error]
document.body.appendChild( renderer.domElement );
jsFiddle Live Working Demo
And here is my code exactly copied from link above:
And yes the three.js included in the page.
var camera, scene, renderer;
var geometry, material, mesh;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 1000;
scene = new THREE.Scene();
geometry = new THREE.CubeGeometry( 200, 200, 200 );
material = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe: true } );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.CanvasRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function animate() {
// note: three.js includes requestAnimationFrame shim
requestAnimationFrame( animate );
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render( scene, camera );
}
Your code is running before the browser has parsed the HTML document and built the DOM. That's why document.body is undefined.
Use a "load" event handler:
window.onload = function() {
// all that code here
};
Your jsfiddle version worked because that site does that for you; it's what the top selection thing is for on the left-hand side control panel.
Oh, also, you have to be careful when copying code from jsfiddle. Unless they've fixed it with the recent facelift, it's really easy to pick up weird hidden characters when you copy/paste, causing mysterious JavaScript errors.
Just write your code inside onload. it works fine.
window.onload = function() { // your code }
Related
I'm a beginner to three.js.I'm trying to build something similar to this https://virtualshowroom.nissan.in/car-selected.html?selectedCar=ext360_deep_blue_pearl. I built everything using three.js, but I'm not able to figure out how to create a hotspot(like the red dot in the above link) and show pop up when you click on it. below is my project code, let me know if anything else is required.
<html>
<head>
<title>My first three.js app</title>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<h1></h1>
<script src="./three.js"></script>
<script type="module">
import { GLTFLoader } from 'https://threejs.org/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'https://threejs.org/examples/jsm/controls/OrbitControls.js';
var renderer,scene,camera;
scene = new THREE.Scene();
scene.background = new THREE.Color(0xfff6e6)
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var loader = new GLTFLoader();
var hlight = new THREE.AmbientLight(0x404040, 100)
scene.add(hlight)
var directionalLight = new THREE.DirectionalLight(0xffffff, 100)
directionalLight.position.set(0,1,0)
directionalLight.castShadow = true
scene.add(directionalLight)
var light = new THREE.PointLight(0xffffff, 10)
light.position.set(0, 300, 500)
scene.add(light)
var light2 = new THREE.PointLight(0xffffff, 10)
light2.position.set(500, 100, 0)
scene.add(light2)
var light3 = new THREE.PointLight(0xffffff, 10)
light3.position.set(0, 100, -500)
scene.add(light3)
var light4 = new THREE.PointLight(0xffffff, 10)
light4.position.set(-5000, 300, 0)
scene.add(light4)
var controls = new OrbitControls(camera, renderer.domElement);
document.body.appendChild(renderer.domElement)
var loader = new GLTFLoader();
loader.load( './scene.gltf', function ( gltf )
{
scene.add( gltf.scene );
}, undefined, function ( error ) { console.error( error ); } );
// load a image resource
camera.position.z = 5;
var animate = function () {
requestAnimationFrame( animate );
renderer.render( scene, camera );
};
animate();
</script>
</body>
</html>
Those “hotspots” as you call them are Annotations where the annotation content is basically pure HTML.
The tutorial in the link is probably the best step-by-step readiness you can follow to learn how to do it in your scene.
I can give a walkthrough on the steps required to get the desired effect since I have done it a few times myself.
define a 3d point in your scene where the hotspot should be. You can optionally nest this in a an other Object3D to make sure it scales, moves and rotates with the model / parent.
Add a plane to this point load a image texture to this plane. and there you have your visible hotspot
update the hotspots to make sure they are always looking at the camera by using the lookAt function.
when the user clicks the screen cast a raycast against all the hotspots you have in your scene. Easiest way to do this is by storing all your hotspots in an array.
When the raycast hits a hotspot get the location either of the hitpoint or the hotspots location. Transform that to screen coordinates. Search on stackoverflow how to do this. I am sure there is a post about this.
Final step display your html on the correct location you obtained from the previous step.
The advantage of this method is that the hotspot will integrate nicely with the model in your scene. Since html based hotspots will always be on top of the scene.
That is about all that is to it. Let me know if you need any further clarification!
I have a three.js project with 3d-models, a ground and a grid in it.
The 3d-models getting outlined with outlinePass (https://threejs.org/examples/?q=outl#webgl_postprocessing_outline).
I am able to move the objects with Transformcontrol (https://threejs.org/examples/?q=transf#misc_controls_transform) and i can change my camera position with Orbitcontrols (https://threejs.org/examples/?q=orbit#misc_controls_orbit)
The problem: The graphics seem kind of badly rendered, here some screenshots:
https://imgur.com/gallery/3FrZt3s
I don't really know which settings i should use here:
renderer = new THREE.WebGLRenderer();//{ antialias: true } ); With antialiasing or without?
//antialiasing is only needed when not using fxaa, right??
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.gammaOutput = true;
renderer.physicallyCorrectLights = true;
camera = new THREE.PerspectiveCamera( 45, container.offsetWidth / container.offsetHeight, 0.001, 1000 );
camera.addEventListener( 'change', render ); //Is this necessary? Seems like it has no use
FXAA is probably necessary for outlinePass (also in the outlinePass-example linked above).
composer = new EffectComposer( renderer );
var renderPass = new RenderPass( scene, camera );
composer.addPass( renderPass );
effectFXAA = new ShaderPass( FXAAShader );
effectFXAA.uniforms[ 'resolution' ].value.set( 1 / window.innerWidth, 1 / window.innerHeight );
effectFXAA.renderToScreen = true;
composer.addPass( effectFXAA );
orbitControls = new OrbitControls( camera, renderer.domElement );
orbitControls.update();
orbitControls.addEventListener( 'change', render );
function render(){
renderer.render(scene, camera);
//composer.render(); // don't know if needed
}
So i have to say, i have not really a clue how i can solve the rendering issue and which settings i have to set to make the most out of my project. I'm happy for every hint and answers and maybe i can put these answers together and solve the issue.
When using post-processing with WebGL 1, you have to use FXAA for antialiasing. Passing { antialias: true } to true when creating WebGLRenderer activates MSAA but only if you render to the default framebuffer (directly to screen).
In any event, you configure the FXAA pass like so:
effectFXAA = new ShaderPass( FXAAShader );
effectFXAA.uniforms[ 'resolution' ].value.x = 1 / ( window.innerWidth * pixelRatio );
effectFXAA.uniforms[ 'resolution' ].value.y = 1 / ( window.innerHeight * pixelRatio );
composer.addPass( effectFXAA );
You have to honor the pixelRatio. Besides, setting renderToScreen to true is not necessary anymore. The last pass in the post-processing chain is automatically rendered to screen now.
When using EffectComposer, you do not call renderer.render(scene, camera);. You have to use composer.render(); instead.
camera.addEventListener( 'change', render ); can also be deleted. I'm not sure where you seen this but it has no effect.
three.js R109
I'm trying out Three.js, I followed a tutorial step-by-step. In the code editor I'm using( Visual Studio Code 2019) everything seems normal, but when I test it, nothing appears on the page.
the editor I'm using, used the desktop as the place to locate my code, since it is a .html file I could run it. When I did that, the only thing that appeared was the navbar I programmed, nothing else
This is the entire three.js code:
<script src="three.js-dev/build/three.min.js"> </script>
<script>
var scene = new THREE.scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight);
var renderer = new THREE.WebGLRenderer({antialias = true});
renderer.setSize( window.innerWidth, window.innerHeight);
$('body').append( renderer.domElement);
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial ( { color = 0xff0000 });
var cube = new THREE.Mesh( geometry, material) ;
scene.add( cube );
cube.position.z = -5;
var animate = function () {
cube.rotation.x += 0.01;
renderer.render(scene, camera);
requestAnimationFrame( animate );
};
animate();
and this the code before it:
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<script
src="https://code.jquery.com/jquery-2.1.4.js"
integrity="sha256-siFczlgw4jULnUICcdm9gjQPZkw/YPDqhQ9+nAOScE4="
crossorigin="anonymous"></script>
</head>
<body>
<div id="navbar"><span>Three.js Tut</span></div>
I expected a red cube rotating, but nothing appeared. ¿Is there something I'm doing wrong?
You have a few errors in your code:
THREE.scene should be THREE.Scene
{antialias = true} should be {antialias: true}
{ color = 0xff0000 } should be { color: 0xff0000 }
Live demo with your code: https://jsfiddle.net/so736vxj/
BTW: If you are using VSCode, I'm a bit irritated that no errors are highlighted. Especially the last two syntax errors should be reported since it is no valid JavaScript.
three.js R105
I´ve started programming using three.js a little time ago, and I would like to know how to make a menu on a page before it loaded the program using three itself.So, for instance I would like before running my game to have two options:"start" and "music control" being desplayed before it loaded itself.
Is this made using three, or do you use html, javascript files?(Sorry, I really don´t know how to even start it).
If possible, I would like some tutorials or even videos about it.
I would like something like this for example:
http://www.dilladimension.com/
http://lights.helloenjoy.com/
You probably do want to use html to display the menu and js to control the instantiation of your scene.
Rather than use the page load event to set up your renderer, etc, put them inside a function, and use js events to begin the function:
function start() {
var renderer = new THREE.WebGLRenderer();
...
// more setup
...
renderer.render(scene, camera);
};
Then activate this method with a button or a link:
<button onclick="start()">
Start Scene
</button>
Here's a test implementation:
function start() {
var renderer = new THREE.WebGLRenderer();
renderer.setSize(800, 600);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
35, // Field of view
800 / 600, // Aspect ratio
0.1, // Near plane
10000 // Far plane
);
camera.position.set(-15, 10, 10);
camera.lookAt(scene.position);
var geometry = new THREE.BoxGeometry(5, 5, 5);
var material = new THREE.MeshLambertMaterial({
color: 0xFF0000
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
var light = new THREE.PointLight(0xFFFF00);
light.position.set(10, 0, 10);
scene.add(light);
renderer.setClearColor(0xdddddd, 1);
renderer.render(scene, camera);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r77/three.min.js"></script>
<button onclick="start()">
Start Scene
</button>
You can switch from Three.js Orbit Controls to FirstPerson controls without a problem. But when you go from First Person to Orbit the display gets stuck in a 'mousedown' mode.
What do you need to do to go back and forth between First Person and Orbit controls seamlessly?
The jsBin with the demo
http://jsbin.com/jekebo/2/edit?html,js,output
The two functions
function setControlsFirstPerson() {
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( -150, 20, 0 );
controls = new THREE.FirstPersonControls( camera, renderer.domElement );
controls.lookSpeed = 0.05;
controls.movementSpeed = 50;
}
function setControlsOrbit() {
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 100, 100, 100 );
controls = new THREE.OrbitControls( camera, renderer.domElement );
}
Any help appreciated.
It is not working because you are sending the second parameter to the OrbitControls. This for some reason seems to stop the end event being fired.
controls = new THREE.OrbitControls( camera, renderer.domElement );
Without out it it seems to work as expected:
controls = new THREE.OrbitControls( camera );
I have gone ahead and edited your jsbin changing the parameters sent to the constructors of both controls.
http://jsbin.com/fabalomave/1/edit?js,output
I also changed how the cameras are created and changed so that a new camera and control instance is not created each time the desired camera is changed, instead it just stores a reference and changes which camera and control that the global camera and controls variables refer to.