I wanted to load a 3d object to three.js, so since I'm quite new to that, I started from the very first basic example in the documentation and everything seemed to be ok to me. But then when simply added the import line, following the documentation I couldn't see anymore the scene and I got this error from the console:
(index):1 Uncaught TypeError: Failed to resolve module specifier "three/examples/jsm/loaders/GLTFLoader.js". Relative references must start with either "/", "./", or "../"
I've just copied this line from three.js documentation so I'm not really sure about what is wrong in referencing the loader.
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
Can you help me understand what's the issue? I'm working on macOS using atom live server for the previw
Thank you!
I don't know how your project is organized so you will have to replace {path} or even the file name with the actual static path of the file.
<script type="module">
import * as THREE from '{path}/three.module.js'
import { GLTFLoader } from '{path}/GLTFLoader.js'
</script>
If you don't host threejs file just use a CDN
import { GLTFLoader } from 'https://cdn.jsdelivr.net/npm/three/examples/jsm/loaders/GLTFLoader.js'
EDIT
NPM and serving files with any kind of server extension is something you need to understand clearly and is not something that works out of the box. I suggest that you create a single index.html file and run it directly on the browser.
As an example I've replaced every file access with a CDN link.
First run is slow after that the model is in cache.
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - glTF loader</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="https://ghcdn.rawgit.org/mrdoob/three.js/dev/examples/main.css">
</head>
<body>
<div id="info">
three.js - GLTFLoader<br />
Battle Damaged Sci-fi Helmet by
theblueturtle_<br />
Royal Esplanade by HDRI Haven
</div>
<script type="module">
import * as THREE from 'https://cdn.jsdelivr.net/npm/three#0.123.0/build/three.module.js';
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'https://cdn.jsdelivr.net/npm/three/examples/jsm/loaders/GLTFLoader.js';
import { RGBELoader } from 'https://cdn.jsdelivr.net/npm/three/examples/jsm/loaders/RGBELoader.js';
import { RoughnessMipmapper } from 'https://cdn.jsdelivr.net/npm/three/examples/jsm/utils/RoughnessMipmapper.js';
let camera, scene, renderer;
init();
render();
function init() {
const container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20 );
camera.position.set( - 1.8, 0.6, 2.7 );
scene = new THREE.Scene();
new RGBELoader()
.setDataType( THREE.UnsignedByteType )
.setPath( 'https://ghcdn.rawgit.org/mrdoob/three.js/dev/examples/textures/equirectangular/' )
.load( 'royal_esplanade_1k.hdr', function ( texture ) {
const envMap = pmremGenerator.fromEquirectangular( texture ).texture;
scene.background = envMap;
scene.environment = envMap;
texture.dispose();
pmremGenerator.dispose();
render();
// model
// use of RoughnessMipmapper is optional
const roughnessMipmapper = new RoughnessMipmapper( renderer );
const loader = new GLTFLoader().setPath( 'https://ghcdn.rawgit.org/mrdoob/three.js/dev/examples/models/gltf/DamagedHelmet/glTF/' );
loader.load( 'DamagedHelmet.gltf', function ( gltf ) {
gltf.scene.traverse( function ( child ) {
if ( child.isMesh ) {
// TOFIX RoughnessMipmapper seems to be broken with WebGL 2.0
// roughnessMipmapper.generateMipmaps( child.material );
}
} );
scene.add( gltf.scene );
roughnessMipmapper.dispose();
render();
} );
} );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1;
renderer.outputEncoding = THREE.sRGBEncoding;
container.appendChild( renderer.domElement );
const pmremGenerator = new THREE.PMREMGenerator( renderer );
pmremGenerator.compileEquirectangularShader();
const controls = new OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // use if there is no animation loop
controls.minDistance = 2;
controls.maxDistance = 10;
controls.target.set( 0, 0, - 0.2 );
controls.update();
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
render();
}
//
function render() {
renderer.render( scene, camera );
}
</script>
</body>
</html>
Related
I am running a simple threejs scene from VSCode using Live Server to Chrome. This code runs fine, a spinning box appears as expected:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 0; }
</style>
</head>
<body>
<script type="module">
import * as THREE from './build/three.module.js';
// import {GLTFLoader} from './examples/jsm/loaders/GLTFLoader.js';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add(cube);
// const loader = new GLTFLoader();
// loader.load(
// // resource URL
// '../assets/tree.glb',
// // called when the resource is loaded
// function ( gltf ) {
// scene.add( gltf.scene );
// });
camera.position.z = 5;
function animate() {
requestAnimationFrame( animate );
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render( scene, camera );
};
animate();
</script>
</body>
</html>
However, in an attempt to add a 3d model, if I add the line (currently commented out above):
import {GLTFLoader} from './examples/jsm/loaders/GLTFLoader.js';
I then get this error:
Uncaught TypeError: Failed to resolve module specifier "three". Relative references must start with either "/", "./", or "../".
My folder structure looks like this (essentially just three.js cloned from github with an index.html in the root).
Equally, I have the identical problem if I use CDNs like so:
<script type = "module">
import * as THREE from 'https://cdn.skypack.dev/three';
import {GLTFLoader} from 'https://cdn.skypack.dev/three/examples/jsm/loaders/GLTFLoader.js';
etc...
Thanks for the advice.
im a bit of a noob when it comes to three.js and html but ive created other js files and they would import into the html page just fine but im confused why my basic box copy pasta code isnt showing up on the page. it works fine when scripting it all in html but not when import the js file in the same folder. could yall help me understand whats going on?
index.js
/*import * as THREE from 'three'
const canvas=document.querySelector('.webgl')
const scene = new THREE.Scene();
const geo = new THREE.BoxGeometry(1,1,1)
const mats = new THREE.MeshBasicMaterial({
color:'red'
})
const boxmesh=new THREE.Mesh(geo,mats)
scene.add(boxmesh)
const camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,100)
camera.position.set(0,1,2)
scene.add(camera)
const renderer=new THREE.WebGL1Renderer({
canvas:canvas
})
renderer.setSize(window.innerWidth,window.innerHeight)
renderer.setPixelRatio(Math.min(window.devicePixelRatio,2))
renderer.shadowMap.enabled=true
renderer.gammaOutput=true
renderer.render(scene, camera)*/
import * as THREE from 'three';
// init
const camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 1;
const scene = new THREE.Scene();
scene.background= new THREE.Color('black')
const geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 );
const material = new THREE.MeshNormalMaterial();
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animation );
document.body.appendChild( renderer.domElement );
// animation
function animation( time ) {
mesh.rotation.x = .1;
mesh.rotation.y = .1;
renderer.render( scene, camera );
}
animation()
index.html
<!DOCTYPE html>
<html>
<head>
<meta name="learning html" content="This is learing making websites">
<title>Learing GLTF</title>
<script src="index.js"></script>
</head>
<body>
<canvas class="webgl"></canvas>
?
</body>
</html>
When using the latest versions of three.js(e.g. r140), then it's important to define an import map in your HTML. Otherwise the import import * as THREE from 'three'; does not work. A import map is required so the bare module specifier three can be resolved in browsers. For testing, try it with:
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three#0.140/build/three.module.js"
}
}
</script>
I want a bloom effect for my scene when using an emissive map like shown in this ThreeJS Example.
I've tried to understand the code a little bit but I'm basically stuck. The examples are all made with NPM and I do not use this method for my project. I'm sure it is possible to have the bloom effect without the help of this but I struggle to make sense of it all.
As for what I have already, just a basic setup with StandarMeshMaterial:
scene = new THREE.Scene();
loader = new THREE.TextureLoader()
camera = new THREE.PerspectiveCamera( 47, (window.innerWidth/window.innerHeight) / (windowHeight*heightRatio), 0.01, 10000 );
renderer = new THREE.WebGLRenderer( { canvas: document.getElementById( canvasElement ), antialias: true, alpha: true } );
controls = new THREE.OrbitControls( camera, renderer.domElement );
ect..
function animate() {
requestAnimationFrame( animate );
controls.update();
renderer.render( scene, camera );
};
ect..
I really just want to apply some post-processing effect so my emissive materials actually appear to be glowing, which is not whats happening at the moment but I just cannot figure out how..
What would be the simplest way to get this result?
The examples are not made with npm.
Here's the example running below. The only thing changed is the paths of the modules and the url of the model.
#info > * {
max-width: 650px;
margin-left: auto;
margin-right: auto;
}
<link type="text/css" rel="stylesheet" href="https://threejs.org/examples/main.css">
<div id="container"></div>
<div id="info">
three.js - Bloom pass by Prashant Sharma and Ben Houston
<br/>
Model: Primary Ion Drive by
Mike Murdock, CC Attribution.
</div>
<script type="module">
import * as THREE from 'https://threejs.org/build/three.module.js';
import Stats from 'https://threejs.org/examples/jsm/libs/stats.module.js';
import { GUI } from 'https://threejs.org/examples/jsm/libs/dat.gui.module.js';
import { OrbitControls } from 'https://threejs.org/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'https://threejs.org/examples/jsm/loaders/GLTFLoader.js';
import { EffectComposer } from 'https://threejs.org/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'https://threejs.org/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'https://threejs.org/examples/jsm/postprocessing/UnrealBloomPass.js';
let camera, stats;
let composer, renderer, mixer, clock;
const params = {
exposure: 1,
bloomStrength: 1.5,
bloomThreshold: 0,
bloomRadius: 0
};
init();
function init() {
const container = document.getElementById( 'container' );
stats = new Stats();
container.appendChild( stats.dom );
clock = new THREE.Clock();
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.toneMapping = THREE.ReinhardToneMapping;
container.appendChild( renderer.domElement );
const scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 100 );
camera.position.set( - 5, 2.5, - 3.5 );
scene.add( camera );
const controls = new OrbitControls( camera, renderer.domElement );
controls.maxPolarAngle = Math.PI * 0.5;
controls.minDistance = 1;
controls.maxDistance = 10;
scene.add( new THREE.AmbientLight( 0x404040 ) );
const pointLight = new THREE.PointLight( 0xffffff, 1 );
camera.add( pointLight );
const renderScene = new RenderPass( scene, camera );
const bloomPass = new UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 1.5, 0.4, 0.85 );
bloomPass.threshold = params.bloomThreshold;
bloomPass.strength = params.bloomStrength;
bloomPass.radius = params.bloomRadius;
composer = new EffectComposer( renderer );
composer.addPass( renderScene );
composer.addPass( bloomPass );
new GLTFLoader().load( 'https://threejs.org/examples/models/gltf/PrimaryIonDrive.glb', function ( gltf ) {
const model = gltf.scene;
scene.add( model );
mixer = new THREE.AnimationMixer( model );
const clip = gltf.animations[ 0 ];
mixer.clipAction( clip.optimize() ).play();
animate();
} );
const gui = new GUI();
gui.add( params, 'exposure', 0.1, 2 ).onChange( function ( value ) {
renderer.toneMappingExposure = Math.pow( value, 4.0 );
} );
gui.add( params, 'bloomThreshold', 0.0, 1.0 ).onChange( function ( value ) {
bloomPass.threshold = Number( value );
} );
gui.add( params, 'bloomStrength', 0.0, 3.0 ).onChange( function ( value ) {
bloomPass.strength = Number( value );
} );
gui.add( params, 'bloomRadius', 0.0, 1.0 ).step( 0.01 ).onChange( function ( value ) {
bloomPass.radius = Number( value );
} );
window.addEventListener( 'resize', onWindowResize );
}
function onWindowResize() {
const width = window.innerWidth;
const height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize( width, height );
composer.setSize( width, height );
}
function animate() {
requestAnimationFrame( animate );
const delta = clock.getDelta();
mixer.update( delta );
stats.update();
composer.render();
}
</script>
What you need to do is use <script type="module"> so that modern import statements work
You should then copy the three.js files as tree like this
someFolder
|
├-build
| |
| +-three.module.js
|
+-examples
|
+-jsm
|
+-controls
| |
| +-OrbitControls.js
| +-TrackballControls.js
| +-...
|
+-loaders
| |
| +-GLTFLoader.js
| +-...
|
...
And adjust your paths as appropriate
See this article
First, NPM is not a framework. It is a package manager to install libraries your project depends on without manually downloading and copying the scripts to your project folder. What I read from your question is that you are not familiar with that module approach. You want to insert scripts and all three.js related stuff should be available under the global namespace THREE?
Assuming that you downloaded three.js to a folder named three, you could import the scripts as follows. Ensure to load the scripts from examples/js and not examples/jsm.
<script src="three/build/three.min.js"></script>
<script src="three/examples/js/controls/OrbitControls.js"></script>
<script src="three/examples/js/loaders/GLTFLoader.js"></script>
<script src="three/examples/js/postprocessing/EffectComposer.js"></script>
<script src="three/examples/js/postprocessing/RenderPass.js"></script>
<script src="three/examples/js/postprocessing/UnrealBloomPass.js"></script>
Now, you can use these classes under the THREE namespace.
const renderScene = new THREE.RenderPass( scene, camera );
const bloomPass = new THREE.UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 1.5, 0.4, 0.85 );
Follow the example code, remove the import statements and add THREE where missing.
I've been attempting to get the SSAO post-processing shader to work with the latest (r77) version of three.js. I've been using the EffectComposer, with the code entirely duplicated from the example page here:
http://threejs.org/examples/webgl_postprocessing_ssao.html
The relevant code being:
var renderPass = new THREE.RenderPass( Engine.scene, Engine.camera );
ssaoPass = new THREE.ShaderPass( THREE.SSAOShader );
ssaoPass.renderToScreen = true;
// ...various ShaderPass setup parameters
Engine.effectComposer = new THREE.EffectComposer( Engine.renderer );
Engine.effectComposer.addPass(renderPass);
Engine.effectComposer.addPass(ssaoPass);
The issue I have been having is that that SSAO doesn't seem to work with SkinnedMeshes. It seems to take into account the position of the meshes before the skinning calculations are performed.
It looks like this:
Problem with SSAO on SkinnedMesh
Does anyone have any experience with this on the latest version? I've looked all over the place, but can't find any documentation about how to start fixing this at all.
I found mention of a fix for this in another SO post (ThreeJS SSAO Shader w/ Skinned/Animated Models), but the solution has been deprecated.
Thanks in advance, and happy to go into more detail if needed.
As requested, here is the complete code for the simple demo page:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="js/libs/jquery.min.js"></script>
<script type="text/javascript" src="js/libs/three.min.js"></script>
<script type="text/javascript" src="js/libs/postprocessing/CopyShader.js"></script>
<script type="text/javascript" src="js/libs/postprocessing/EffectComposer.js"></script>
<script type="text/javascript" src="js/libs/postprocessing/MaskPass.js"></script>
<script type="text/javascript" src="js/libs/postprocessing/RenderPass.js"></script>
<script type="text/javascript" src="js/libs/postprocessing/ShaderPass.js"></script>
<script type="text/javascript" src="js/libs/postprocessing/DotScreenShader.js"></script>
<script type="text/javascript" src="js/libs/postprocessing/SSAOShader.js"></script>
<link rel="stylesheet" type="text/css" href="demo.css" />
</head>
<body>
<div id="demo-container"></div>
</body>
</html>
<script>
$(document).ready(function() {
window.doPostPro = 0;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 1, 1000);
camera.position.set(0, 200, 200);
camera.lookAt(new THREE.Vector3(0, 0, 0));
clock = new THREE.Clock();
// Setup the renderer.
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0xFFFFFF );
function setupPostProcessing() {
// Setup render pass
var renderPass = new THREE.RenderPass( scene, camera );
// Setup depth pass
depthMaterial = new THREE.MeshDepthMaterial();
depthMaterial.depthPacking = THREE.RGBADepthPacking;
depthMaterial.blending = THREE.NoBlending;
depthRenderTarget = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
stencilBuffer: true
});
// Setup SSAO pass
ssaoPass = new THREE.ShaderPass( THREE.SSAOShader );
ssaoPass.renderToScreen = true;
//ssaoPass.uniforms[ "tDiffuse" ].value will be set by ShaderPass
ssaoPass.uniforms[ "tDepth" ].value = depthRenderTarget.texture;
ssaoPass.uniforms[ 'size' ].value.set( window.innerWidth, window.innerHeight );
ssaoPass.uniforms[ 'cameraNear' ].value = camera.near;
ssaoPass.uniforms[ 'cameraFar' ].value = camera.far;
//ssaoPass.uniforms[ 'onlyAO' ].value = ( postprocessing.renderMode == 1 );
ssaoPass.uniforms[ 'aoClamp' ].value = 0.3;
ssaoPass.uniforms[ 'lumInfluence' ].value = 0.5;
// Add pass to effect composer
effectComposer = new THREE.EffectComposer( renderer );
effectComposer.addPass( renderPass );
effectComposer.addPass( ssaoPass );
};
setupPostProcessing();
// Load the mesh.
var loader = new THREE.JSONLoader();
loader.load( "js/zombie.js", function( geometry, materials ) {
var originalMaterial = materials[ 0 ];
originalMaterial.skinning = true;
geometry.computeVertexNormals();
var mesh = new THREE.SkinnedMesh(geometry, originalMaterial);
window.animMixer = new THREE.AnimationMixer(mesh);
var animAction = animMixer.clipAction(geometry.animations[0]);
animAction.play();
scene.add(mesh);
});
var ambientLight = new THREE.AmbientLight( 0xCCCCCC );
scene.add( ambientLight );
$("#demo-container").append( renderer.domElement );
function render() {
if ( doPostPro ) {
// Render depth into depthRenderTarget
scene.overrideMaterial = depthMaterial;
renderer.render( scene, camera, depthRenderTarget, true );
// Render renderPass and SSAO shaderPass
scene.overrideMaterial = null;
effectComposer.render();
}
else {
renderer.render( scene, camera );
}
}
function animate() {
requestAnimationFrame( animate );
if ( window.animMixer != undefined ) {
var deltaTime = clock.getDelta();
animMixer.update(deltaTime);
}
render();
}
animate();
$(document).keyup(function(e) {
// P is pressed.
if ( e.which == 80 ) {
window.doPostPro = !window.doPostPro;
}
});
});
</script>
If you are skinning, and using this pattern in your render loop
// Render depth into depthRenderTarget
scene.overrideMaterial = depthMaterial;
renderer.render( scene, camera, depthRenderTarget, true );
you have to make sure to set
depthMaterial.skinning = true;
Thing is, if there are other elements in the scene that are not skinned, doing so will throw errors. In which case, this may be a three.js design issue that will have to be addressed.
three.js r.77
So I am trying to get a JSONLoader to work from threejs.org
Three.js is working for sure because I have no problem creating a cube. But when I try to load a js file throuh JSONLoader then nothing happens.
<html>
<head>
<title>My first Three.js app</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
<script src="three.js"></script>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer( { alpha: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// instantiate a loader
var loader = new THREE.JSONLoader();
// load a resource
loader.load(
// resource URL
'logo.js',
// Function when resource is loaded
function ( geometry, materials ) {
var material = new THREE.MultiMaterial( materials );
var object = new THREE.Mesh( geometry, material );
scene.add( object );
}
);
camera.position.z = 5;
var render = function () {
renderer.setClearColor( 0x000000, 0 );
requestAnimationFrame( render );
renderer.render(scene, camera);
};
render();
</script>
</body>
</html>
As mentioned in the title then the code is copy pasted from threejs own website and should be working.
Can someone help me figure what is going wrong?
here is a fiddle with the script of logo.js https://jsfiddle.net/380z6096/
the object has been exported from 3ds max with the 3ds Max JSExporter
I am using xampp and chrome.
Your camera is inside your geometry.
You can determine the dimensions of your geometry like so
geometry.computeBoundingSphere();
console.log( geometry.boundingSphere );
or
geometry.computeBoundingBox();
console.log( geometry.boundingBox );
Scale your geometry
object.scale.multiplyScalar( 0.01 );
Or move your camera back.
three.js r.75