3D model not casting shadows in ThreeJS - javascript

I am importing a glTF model into ThreeJS and have a PlaneGeometry acting as a ground. I need the model to cast shadows onto the plane/ground.
I've tried enabling
renderer.shadowMap.enabled = true;
on
const renderer = new THREE.WebGLRenderer({ alpha: true });
I also have 2 lights:
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
hemiLight.position.set(0, 10, 0);
scene.add( hemiLight );
const dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(0, 0, 10);
dirLight.castShadow = true;
dirLight.shadow.camera.top = 200;
dirLight.shadow.camera.bottom = -200;
dirLight.shadow.camera.left = - 200;
dirLight.shadow.camera.right = 200;
dirLight.shadow.camera.near = 0.1;
dirLight.shadow.camera.far = 500;
scene.add( dirLight );
Final code
body
{
margin: 0px;
padding: 0px;
}
div#container canvas
{
cursor: grab;
}
div#container canvas:active
{
cursor: grabbing;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Import 3D Model into Three JS</title>
<script type="module" defer>
import * as THREE from 'https://cdn.skypack.dev/three#0.129.0/build/three.module.js';
import { GLTFLoader } from 'https://cdn.skypack.dev/three#0.129.0/examples/jsm/loaders/GLTFLoader.js'; // for .glb and .gltf.glb
// import { OBJLoader } from 'https://cdn.skypack.dev/three#0.129.0/examples/jsm/loaders/OBJLoader.js'; // for .obj
import { OrbitControls } from 'https://cdn.skypack.dev/three#0.129.0/examples/jsm/controls/OrbitControls.js';
const container = document.querySelector('div#container');
const path_to_model = './ImportModel/Mini-Game Variety Pack/Models/gltf/tree_forest.gltf.glb';
const loader = new GLTFLoader();
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 500);
// Add lights
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
hemiLight.position.set(0, 10, 0);
scene.add( hemiLight );
const dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(0, 0, 10);
dirLight.castShadow = true;
dirLight.shadow.camera.top = 200;
dirLight.shadow.camera.bottom = -200;
dirLight.shadow.camera.left = - 200;
dirLight.shadow.camera.right = 200;
dirLight.shadow.camera.near = 0.1;
dirLight.shadow.camera.far = 500;
scene.add( dirLight );
// Make renderer
const renderer = new THREE.WebGLRenderer({
alpha: true
});
// Make transparent
renderer.setClearColor(0xffffff, 0);
// Set it to window size
renderer.setSize(window.innerWidth, window.innerHeight);
// Force shadows
renderer.shadowMap.enabled = true;
// Helper (optional)
// const camera_helper = new THREE.CameraHelper(dirLight.shadow.camera);
// scene.add(camera_helper);
// Double quality
const quality = 2;
renderer.setSize(window.innerWidth * quality, window.innerHeight * quality, false);
// Add mouse movement
const controls = new OrbitControls(camera, renderer.domElement);
// Add floor (plane)
const plane_geometry = new THREE.PlaneGeometry(10, 10);
const plane_material = new THREE.MeshPhongMaterial({
color: 0xffff00,
side: THREE.DoubleSide
});
const plane = new THREE.Mesh(
plane_geometry,
plane_material
);
plane.rotation.x = 1.5708;
plane.castShadow = true;
plane.receiveShadow = true;
scene.add(plane);
console.log(plane);
// Import glTF
loader.load(path_to_model, function (gltf)
{
//gltf.scene.position.y = -5;
//gltf.scene.center();
//gltf.scene.scale.set(0.1, 0.1, 0.1);
// Make it cast shadows
gltf.scene.castShadow = true;
gltf.scene.traverse(function(node)
{
if (node.isMesh)
{
node.castShadow = true;
//node.receiveShadow = true;
}
});
console.log(gltf);
console.log('Adding glTF model to scene...');
scene.add(gltf.scene);
console.log('Model added.');
console.log('Moving camera 5z...');
camera.position.z = 5;
console.log('Camera moved.');
}, undefined, function (error)
{
console.error(error);
});
container.appendChild(renderer.domElement);
function animate()
{
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
</script>
</head>
<body>
<div id="container">
</div>
</body>
</html>
Update: changed MeshBasicMaterial to MeshPhongMaterial in plane as suggested by #Justin.

The hemisphere light seems to be the main culprit. If you change the position of it you can start getting shadow. Most likely it was casting light everywhere thus drowning out any chance of shadows. Moving it away from the plane helped. Also the dirLight.shadow.camera.top/bottom/left/right are all too high. I used 2, vice 200, along with changing the hemi light and it seems to be working.
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Import 3D Model into Three JS</title>
</head>
<body>
<div id="container">
</div>
</body>
</html>
CSS
body
{
margin: 0px;
padding: 0px;
}
div#container canvas
{
cursor: grab;
}
div#container canvas:active
{
cursor: grabbing;
}
JS
import * as THREE from "https://cdn.skypack.dev/three#0.129.0/build/three.module.js";
import { GLTFLoader } from "https://cdn.skypack.dev/three#0.129.0/examples/jsm/loaders/GLTFLoader.js"; // for .glb and .gltf.glb
// import { OBJLoader } from 'https://cdn.skypack.dev/three#0.129.0/examples/jsm/loaders/OBJLoader.js'; // for .obj
import { OrbitControls } from "https://cdn.skypack.dev/three#0.129.0/examples/jsm/controls/OrbitControls.js";
const container = document.querySelector("div#container");
const path_to_model =
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/stacy_lightweight.glb";
const loader = new GLTFLoader();
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.01,
500
);
// Add lights
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
hemiLight.position.set(20, 20, 10);
scene.add(hemiLight);
const dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(-10, 20, 6);
dirLight.castShadow = true;
dirLight.shadow.camera.top = 2;
dirLight.shadow.camera.bottom = -2;
dirLight.shadow.camera.left = -2;
dirLight.shadow.camera.right = 2;
dirLight.shadow.camera.near = 0.1;
dirLight.shadow.camera.far = 500;
scene.add(dirLight);
// Make renderer
const renderer = new THREE.WebGLRenderer({
alpha: true
});
// Make transparent
renderer.setClearColor(0xffffff, 0);
// Set it to window size
renderer.setSize(window.innerWidth, window.innerHeight);
// Force shadows
renderer.shadowMap.enabled = true;
// Helper (optional)
// const camera_helper = new THREE.CameraHelper(dirLight.shadow.camera);
// scene.add(camera_helper);
// Double quality
const quality = 2;
renderer.setSize(
window.innerWidth * quality,
window.innerHeight * quality,
false
);
// Add mouse movement
const controls = new OrbitControls(camera, renderer.domElement);
// Add floor (plane)
const plane_geometry = new THREE.PlaneGeometry(10, 10);
const plane_material = new THREE.MeshPhongMaterial({
color: 0xffff00,
side: THREE.DoubleSide
});
const plane = new THREE.Mesh(plane_geometry, plane_material);
plane.rotation.x = 1.5708;
plane.castShadow = true;
plane.receiveShadow = true;
scene.add(plane);
console.log(plane);
//scene.add(box)
// Import glTF
loader.load(
path_to_model,
function (gltf) {
//gltf.scene.position.y = -5;
//gltf.scene.center();
//gltf.scene.scale.set(0.1, 0.1, 0.1);
// Make it cast shadows
gltf.scene.castShadow = true;
gltf.scene.traverse(function (node) {
if (node.isMesh) {
node.castShadow = true;
//node.receiveShadow = true;
}
});
console.log(gltf);
console.log("Adding glTF model to scene...");
scene.add(gltf.scene);
console.log("Model added.");
console.log("Moving camera 5z...");
camera.position.z = 5;
console.log("Camera moved.");
},
undefined,
function (error) {
console.error(error);
}
);
container.appendChild(renderer.domElement);
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();

Related

How would you place hyperlinks on 3D Objects/Mesh using ThREE?

I am trying to create a simple box that can link when clicked.
It sounds simple but it didn't work for me so help is appreciated. I've been trying to find answers to this, I don't know what I did wrong. I found like only 2 questions about this on stackoverflow but they still didn't help me answer.
const coolTexture = new THREE.TextureLoader().load('cool.jpg');
const cool = new THREE.Mesh(
new THREE.BoxGeometry(3, 3, 3),
new THREE.MeshBasicMaterial({
color: Math.random() * 0xffffff
})
);
cool.userData = {
URL: "http://stackoverflow.com"
};
scene.add(cool);
function linki() {
if (intersects.length > 0) {
window.open(intersects[0].object.userData.URL);
}
}
document.body.onclick = linki
If it's going to be only one box then use window.open(object.URL); when pointerdown event is detected.
let scene, camera, renderer, cube;
window.addEventListener("pointerdown", () => {
window.open(cube.URL);
});
//
function init() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0xf79862);
camera = new THREE.PerspectiveCamera(
90,
window.innerWidth / window.innerHeight,
1,
1000
);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const color = 0xffffff;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
const geometry = new THREE.BoxGeometry(3, 3, 0.05);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
cube = new THREE.Mesh(geometry, material);
cube.position.set(0, 0, -2);
cube.URL = "http://stackoverflow.com";
scene.add(cube);
renderer.setAnimationLoop(loop);
}
function loop() {
renderer.render(scene, camera);
}
init();
body {
font-family: sans-serif;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.145.0/three.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="app"></div>
<script src="src/index.js">
</script>
</body>
</html>
But if you plan to create more URLs you should use Raycaster and check which box was clicked.
let scene, camera, renderer;
let objsToTest = [];
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
mouse.x = mouse.y = null;
window.addEventListener("pointermove", (event) => {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
});
let selectState = false;
window.addEventListener("pointerdown", () => {
selectState = true;
});
window.addEventListener("pointerup", () => {
selectState = false;
});
window.addEventListener("touchstart", (event) => {
selectState = true;
mouse.x = (event.touches[0].clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.touches[0].clientY / window.innerHeight) * 2 + 1;
});
window.addEventListener("touchend", () => {
selectState = false;
mouse.x = null;
mouse.y = null;
});
//
function init() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0xf79862);
camera = new THREE.PerspectiveCamera(
90,
window.innerWidth / window.innerHeight,
1,
1000
);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const color = 0xffffff;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
const geometry = new THREE.BoxGeometry(0.5, 0.3, 0.05);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
cube.position.set(-0.5, 0, -2);
cube.URL = "url1";
scene.add(cube);
const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00 });
const cube2 = new THREE.Mesh(geometry, material2);
cube2.position.set(0.5, 0, -2);
cube2.URL = "url2";
scene.add(cube2);
objsToTest.push(cube, cube2);
renderer.setAnimationLoop(loop);
}
function loop() {
renderer.render(scene, camera);
updateButtons();
}
function updateButtons() {
let intersect;
if (mouse.x !== null && mouse.y !== null) {
raycaster.setFromCamera(mouse, camera);
intersect = raycast();
}
if (intersect && selectState) {
window.open(intersect.object.URL);
}
}
//
function raycast() {
return objsToTest.reduce((closestIntersection, obj) => {
const intersection = raycaster.intersectObject(obj, true);
if (!intersection[0]) return closestIntersection;
if (
!closestIntersection ||
intersection[0].distance < closestIntersection.distance
) {
intersection[0].object = obj;
return intersection[0];
}
return closestIntersection;
}, null);
}
init();
body {
font-family: sans-serif;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.145.0/three.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="app"></div>
<script src="src/index.js">
</script>
</body>
</html>

OrbiterControls not working properly in three.js app

I'm trying to create a basic scene with an orbitercontrols that can rotate around the center, i.e. always looking at (0,0,0). I want it to behave like this https://threejs.org/examples/misc_controls_orbit.html exactly. But in my version if I click and drag to the left/right, the camera just spins in a circle always looking at (0,0,0). How can I fix this?
<!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 'https://threejs.org/build/three.module.js';
import { OrbitControls } from 'https://threejs.org/examples/jsm/controls/OrbitControls.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 );
// ADD LIGHT
const light = new THREE.PointLight(0xffffff, 2)
light.position.set(0, 5, 10)
scene.add(light)
// ADD ORBITER
const controls = new OrbitControls( camera, renderer.domElement );
controls.listenToKeyEvents( window );
controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
controls.dampingFactor = 0.05;
//controls.screenSpacePanning = false;
//controls.minDistance = 100;
//controls.maxDistance = 500;
//controls.maxPolarAngle = Math.PI / 2;
//controls.minPolarAngle = Math.PI / 2;
//controls.maxPolarAngle = Math.PI / 2;
camera.position.set(0, -5, 5);
camera.lookAt( 0, 0, 0 );
camera.up.set( 0, 0, 1 );
//controls.target = new THREE.Vector3(0, 0, 0);
controls.update();
// ADD CUBE
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
// ADD MAP
const loader = new THREE.TextureLoader();
const height = loader.load('/data/images/height.png');
const texture = loader.load('/data/images/height.png');
const map_geometry = new THREE.PlaneBufferGeometry(10,10,64,64);
const map_material = new THREE.MeshStandardMaterial({
color:'orange',
//side: THREE.DoubleSide,
map:texture,
displacementMap: height,
displacementScale: 0.5,
//alphaMap: alpha,
//transparent:true
});
const map_plane = new THREE.Mesh(map_geometry, map_material);
//map_plane.position.set(0, 0, 0);
scene.add( map_plane );
// RENDER
function animate() {
requestAnimationFrame( animate );
//cube.rotation.x += 0.01;
//cube.rotation.y += 0.01;
renderer.render( scene, camera );
}
animate();
</script>
</body>
</html>
The code needed several modifications. Use the code below instead and compare it with yours to find out which parts did I change.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body {
padding: 0;
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<script type="module">
import * as THREE from 'https://threejs.org/build/three.module.js';
import {OrbitControls} from 'https://threejs.org/examples/jsm/controls/OrbitControls.js';
// BUILDING THE SCENE
const scene = new THREE.Scene();
// ADDING CAMERA
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, -5, 5);
scene.add(camera);
// CREATING THE RENDERER
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// ADD LIGHT
const light = new THREE.PointLight(0xffffff, 2);
light.position.set(0, 5, 10);
scene.add(light);
// ADD ORBITER
const controls = new OrbitControls(camera, renderer.domElement);
controls.maxPolarAngle = Math.PI/2.2;
controls.minDistance = 5;
controls.maxDistance = 10;
controls.target.set(0, 0, 0);
// ADD CUBE
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({color: 0x00ff00});
const cube = new THREE.Mesh(geometry, material);
cube.position.set(0, .5, 0);
scene.add(cube);
// ADD MAP
const loader = new THREE.TextureLoader();
const height = loader.load('/data/images/height.png');
const texture = loader.load('/data/images/height.png');
const map_geometry = new THREE.PlaneBufferGeometry(10,10,64,64);
const map_material = new THREE.MeshStandardMaterial({
color:'orange',
side: THREE.DoubleSide,
map:texture,
displacementMap: height,
displacementScale: 0.5,
});
const map_plane = new THREE.Mesh(map_geometry, map_material);
map_plane.rotation.x = Math.PI/2;
scene.add(map_plane);
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// ANIMATING THE SCENE
function animate() {
controls.update();
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>
Write controls.update(); in the animate function.
controls.maxPolarAngle = Math.PI/2.2; doesn't let the camera to go under the ground.
controls.target.set(0, 0, 0); makes the control stair at the center.
I added map_plane.rotation.x = Math.PI/2; so the plane will be placed horizontally as the floor.
And side: THREE.DoubleSide, helps the floor to be visible from all angels.
I also added an onWindowResize function to the script to make the applicaion responsive. Now, it'll response to the browser's width and height when its resized.
In case of CSS, the page has default padding as well. So, adding a padding: 0; to the CSS code is not a bad idea as well.

How do I avoid circular movements when placing a cube on the camera position, but a bit behind?

So this is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js</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="main.css">
<style>
body {
overflow: hidden;
margin: 0;
}
</style>
</head>
<body>
<script src="three.js"></script>
<script src="pointerlockcontrols.js"></script>
<script>
var camera, scene, renderer, controls;
var cube;
var previous_time = performance.now();
var color = new THREE.Color();
camera = new THREE.PerspectiveCamera(99, window.innerWidth / window.innerHeight, 1, 100);
camera.position.y = 10;
scene = new THREE.Scene();
scene.background = new THREE.Color(0x0000ff);
scene.fog = new THREE.Fog(0xffffff, 0, 750);
var light = new THREE.HemisphereLight(0xeeeeff, 0x777788, 0.75);
light.position.set(0.5, 1, 0.75);
scene.add(light);
controls = new THREE.PointerLockControls(camera);
document.body.addEventListener("click", function() { controls.lock(); });
scene.add(controls.getObject());
var geometry = new THREE.BoxBufferGeometry(0.8*5, 1.5*5, 0.4*5);
var material = new THREE.MeshPhongMaterial({ color: 0xffffff });
cube = new THREE.Mesh(geometry, material);
scene.add(cube);
raycaster = new THREE.Raycaster(new THREE.Vector3(), new THREE.Vector3(0, - 1, 0), 0, 10);
var floorGeometry = new THREE.PlaneBufferGeometry(2000, 2000, 100, 100);
floorGeometry.rotateX(- Math.PI / 2);
floorGeometry = floorGeometry.toNonIndexed(); // ensure each face has unique vertices
position = floorGeometry.attributes.position;
var colors = [];
for (var i = 0, l = position.count; i < l; i ++) {
color.setHSL(Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75);
colors.push(color.r, color.g, color.b);
}
floorGeometry.addAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
var floorMaterial = new THREE.MeshBasicMaterial({ vertexColors: THREE.VertexColors });
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
scene.add(floor);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener('resize', on_window_resize, false);
function on_window_resize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function loop() {
requestAnimationFrame(loop);
if (controls.isLocked === true) {
raycaster.ray.origin.copy(controls.getObject().position);
var vector = new THREE.Vector3();
controls.getObject().getWorldDirection(vector);
cube.rotation.y = Math.atan2(vector.x, vector.z);
cube.position.x = controls.getObject().position.x;
cube.position.z = controls.getObject().position.z+1;
}
renderer.render(scene, camera);
}
loop();
</script>
</body>
</html>
The important part here is:
// This makes the cube always look in the direction of the camera.
var vector = new THREE.Vector3();
controls.getObject().getWorldDirection(vector);
cube.rotation.y = Math.atan2(vector.x, vector.z);
// This makes the cube always be on the position of the camera (for later when there's movement)
cube.position.x = controls.getObject().position.x;
cube.position.z = controls.getObject().position.z+1;
And that works all pretty well, however because of the +1 (I want the cube to be a bit behind the camera) at the end, the cube is doing circular movements when rotating the camera.
I tried
cube.rotation.y = Math.atan2(vector.x, vector.z+1);
However that makes the cube not rotate at all.
How do I avoid the circular movements? I just want to place the cube on my position, a bit behind me, while it also faces the direction I look at.

OBJ file not rendering properly in threejs

Some faces are missing from the obj file where they are not rendered properly.
The part with grey colour shows the same object viewed in an online viewer, where all the face/parts are rendered correctly.
the below image is the one I'm trying to render in localhost. using THREE.js obj loader.
javascript file- main.js
window.onload=function(){
init();
animate();
}
function init() {
container = document.getElementById('container');
console.log(container)
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.x = 100;
camera.position.y = 50;
camera.position.z = 70;
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0xffff , 4);
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffffff ,0.5 );
directionalLight.position.set( 0, 0, 1000 );
scene.add( directionalLight );
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 5.0;
controls.zoomSpeed = 2;
controls.panSpeed = 1;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.5;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( 'change', render );
// texture
var manager = new THREE.LoadingManager();
manager.onProgress = function (item, loaded, total) {
console.log(item, loaded, total);
};
var texture = new THREE.Texture();
var onProgress = function (xhr) {
if (xhr.lengthComputable) {
var percentComplete = xhr.loaded / xhr.total * 100;
console.log(Math.round(percentComplete, 2) + '% downloaded');
}
};
var onError = function (xhr) {
};
var loader = new THREE.ImageLoader(manager);
loader.load('textures/GreenWall.jpg', function (image) {
texture.image = image;
texture.needsUpdate = true;
});
var loader = new THREE.OBJLoader(manager);
loader.load( './obj/male02/feder.jt.obj', function ( object ) {
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.material.map = texture;
}
});
scene.add(object);
}, onProgress, onError);
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xdddddd);
renderer.setSize( window.innerWidth/1.5 , window.innerHeight/1.5);
container.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
render();
controls.update();
}
function render() {
//camera.lookAt(scene.position);
renderer.render( scene, camera );
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - loaders - OBJ loader</title>
<meta charset="utf-8">
<meta io ="view" name="viewport" content="width=device-width, height=device-height user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>
<body>
<script src="./build/three.js"></script>
<script src="./build/Detector.js"></script>
<script src="./build/three.min.js"></script>
<script src="./loaders/OBJLoader.js"></script>
<script src="main.js"></script>
<script src= "Controls/TrackballControls.js"></script>
<div id="container" ></div>
</body>
</html>
By adding child.material.side = THREE.DoubleSide;inside the loader, it worked perfectly.

when I switch camera,camera control is bing valid

I want to swith camera type in my threejs Demo,so I read an official demo :https://threejs.org/examples/#webgl_camera
But some errors occur when I test my demo. When I press P to use cameraPerspective,it works well;But when I use O to switch cameraOrtho,cameracontrol doesn't work——I cannot rotate or move my model sample.
There is my code:
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>3D-earth</title>
<script src="JSandCSS/jquery-2.2.4.min.js"></script>
<script src="JSandCSS/three.min.js"></script>
<script src="JSandCSS/OrbitControls.js"></script>
<script src="JSandCSS/OBJLoader.js"></script>
<script type="text/javascript" src="JSandCSS/jquery.qrcode.min.js"></script>
<script type="text/javascript" src="JSandCSS/qrcode.js"></script>
<script src="JSandCSS/bootstrap.min.js"></script>
<script type="text/javascript" src="JSandCSS/CanvasRenderer.js"></script>
<script src="JSandCSS/Projector.js"></script>
<link href="JSandCSS/bootstrap.min.css" rel="stylesheet" type="text/css">
</head>
<body>
<div style="width: 50%;">
<script>
var scene = new THREE.Scene();
var group = new THREE.Group;
var mouse = new THREE.Vector2();
var raycaster = new THREE.Raycaster();
var objects = [];
init();
var container, stats, titleinfo;
scene.add(group);
var textureLoader = new THREE.TextureLoader();
var mat = new THREE.MeshLambertMaterial({
/* Roushness:1,
Metalness:0,*/
map: textureLoader.load("model/earth/texture.jpg"),
/* normalMap:textureLoader.load("model/earth/normal.jpg"),*/
specularMap: textureLoader.load("model/earth/specular.jpg"),
lightMap: textureLoader.load("model/earth/light.jpg"),
side:THREE.DoubleSide,
});
var loader = new THREE.OBJLoader();
loader.load('model/earth/earth.obj', function(obj) {
obj.traverse(function(child) {
if(child instanceof THREE.Mesh) {
child.material = mat;
}
});
mesh = obj;
obj.scale.set(2, 2, 2);
group.add(obj);
});
var light = new THREE.PointLight(0xffffff);
light.position.set(300, 400, 200);
light.intensity.set = 0.1;
scene.add(light);
scene.add(new THREE.AmbientLight(0x333333));
var cameraPerspective = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
cameraPerspective.position.set(200, 200, 200);
cameraPerspective.lookAt(scene.position);
cameraOrtho = new THREE.OrthographicCamera(window.innerWidth / -4, window.innerWidth / 4, window.innerHeight / 4, window.innerHeight / -4, -10000, 10000);
ActiveCamera = cameraPerspective;
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
renderer.render(scene, ActiveCamera);
renderer.setClearColor(0x808080, 0.5);
var controls = new THREE.OrbitControls(ActiveCamera);
controls.minDistance = 200;
controls.maxDistance = 400;
controls.autoRotate = true;
controls.addEventListener('change', render);
animate();
window.addEventListener('resize', handleWindowResize, false);
document.addEventListener('keydown', onKeyDown, false);
function render() {
/*group.rotation.y -= 0.004;*/
renderer.render(scene, ActiveCamera);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function onKeyDown(event) {
switch(event.keyCode) {
case 79:
ActiveCamera = cameraOrtho;
case 80:
ActiveCamera = cameraPerspective;
}
}
function handleWindowResize() {
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
renderer.setSize(WIDTH, HEIGHT);
ActiveCamera.aspect = WIDTH / HEIGHT;
ActiveCamera.updateProjectionMatrix();
}
</script>
</div>
</body>
try with single camera object of CombinedCamera and
to switch between Perspective and Ortho use below code.
//initial
var camera = new THREE.CombinedCamera($(canvasHold).innerWidth(), $(canvasHold).innerHeight(), 75, .1, 3e3, -5e3, 5e3);
camera.toOrthographic();
function switchCamera()
{
if(ortho) // Add condition
{
camera.toOrthographic();
camera.position.set(0, 0, 0);
camera.zoom = .5;
camera.up = new THREE.Vector3(0, 0, 0);
camera.lookAt(scene.position);
camera.setFov(75);
}
else
{
camera.toPerspective();
camera.zoom = 1;
camera.setFov(45);
}
}
Note: Please change variables and values according to your requirements.

Categories

Resources