Model renders using MeshBasicMaterial but not MeshPhongMaterial - javascript

I am making a page with three.js. I have made this code, which works:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.z = 7;
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.x = 20;
pointLight.position.y = 20;
pointLight.position.z = 20;
scene.add(pointLight);
var material = new THREE.MeshBasicMaterial( { color: 0xFFFFFF } );
var thebox, loader = new THREE.OBJLoader();
loader.load("https://dl.dropbox.com/s/kasjdw78lx3bkk0/weirdshape.obj?dl=0", function (obj) {
obj.material = material;
obj.traverse(function(child) { child.material = material; });
scene.add(obj);
thebox = obj;
render();
});
function render() {
thebox.rotation.y += 0.01;
thebox.rotation.x += 0.01;
thebox.rotation.z += 0.01;
requestAnimationFrame( render );
renderer.render( scene, camera );
}
This code does work, but when I try to change the MeshBasicMaterial to a MeshPhongMaterial, all I see is a black screen, even though my lighting seems to be in order. How can I fix this?

Read your source .obj file. Your model has no vertex normals. MeshPhongMaterial requires normals.
You can have three.js compute some. In your loader callback function, add:
if ( child.geometry ) child.geometry.computeVertexNormals();
Note: Because the loader in this case returns BufferGeometry, this work-around is correct. If your loader returned Geometry, instead, then you may have to first call geometry.computeFaceNormals() if they do not exist either.
three.js r.73

Related

Plane always black

I want to add a texture to my plane that repeats horizontal and vertical. The thing is, when I try to apply the texture, it is always black. I don't get any errors, and i already tried to add some lights, but the problem is still there; I don't know how to solve it... Here is what I did:
window.onload = function init()
{
scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
var light = new THREE.AmbientLight( 0x404040 ); // soft white light
scene.add( light );
var spotlight = new THREE.SpotLight( 0xffffff);
spotlight.position.set( -50, 40, 0 );
scene.add( spotlight );
var axes = new THREE.AxisHelper( 20 ); scene.add(axes);
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xEEEEEE);
renderer.setSize(window.innerWidth, window.innerHeight);
desenhaMapa();
document.body.appendChild( renderer.domElement );
renderer.render(scene, camera);
}
function desenhaMapa()
{
labirinto = new THREE.Object3D();
var texturaPlano = new THREE.TextureLoader().load("texturaPac.jpg");
geometryPlano = new THREE.PlaneGeometry(50,50);
materialPlano = new THREE.MeshPhongMaterial( {map: texturaPlano} );
var planoPacMan = new THREE.Mesh(geometryPlano,materialPlano);
planoPacMan.rotation.x = -0.5 * Math.PI;
scene.add(planoPacMan);
}
Any suggestions?
TextureLoader.load() is an asynchronous method. That is why it has an onload argument.
You are calling render() before the texture loads. One solution is to call render() in the loader callback.
var loader = new THREE.TextureLoader();
var texture = loader.load( 'myTexture.jpg', function ( texture ) {
renderer.render( scene, camera );
} );
Another solution is to have an animation loop. But that is not required for static scenes.
three.js r.78

Javascript script gives error "TypeError: n is undefined"

I have created a javascript script to do something, and mostly it worked. There are two issues I had with the script, so I planned to put a working example here on SO. However, during the 'reduction' of the code to something less complex a third error occurred, which completely made the script stop working. Here is the javascript part of the script:
var container, stats;
var camera, scene, renderer;
var group1, group2;
var mouseX = 0, mouseY = 0;
var map_width = 512;
var map_height = 512;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function createMesh(filename) {
var geometry = new THREE.SphereGeometry( 70, 40, 40 );
var texture = new THREE.TextureLoader(filename);
texture.needsUpdate = true;
var material = new THREE.MeshBasicMaterial( { map: texture, overdraw: 0.5 } );
var mesh = new THREE.Mesh( geometry, material );
return mesh;
}
function init() {
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 500;
scene = new THREE.Scene();
group1 = new THREE.Group();
var mesh = createMesh("textures/tree1.jpg");
group1.add( mesh );
group1.position.x = 00;
scene.add( group1 );
group2 = new THREE.Group();
var mesh = createMesh("textures/tree2.jpg");
group2.add( mesh );
group2.position.x = 250;
scene.add( group2 );
renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setClearColor( 0xffffff, 0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * 0.05;
camera.position.y += ( - mouseY - camera.position.y ) * 0.05;
camera.lookAt( scene.position );
group1.rotation.y -= 0.005;
group2.rotation.y -= 0.015;
renderer.render( scene, camera );
}
The idea is to have several spheres with some texture on it; to try just use any image. However, when I call this code within a html I get the error
TypeError: n is undefined
all over again. The error seems to originate in THREE.js. How can I fix this error, so I see two spheres with a 'tree' texture on it...?
The body of the html code is as follows:
<body>
<div id="container"></div>
<script src="js/build/three.min.js"></script>
<script>
>> the code from above <<
</script>
</body>
This problem is caused by the following line:
var texture = new THREE.TextureLoader(filename);
The solution is to use a loader instead:
var loader = new THREE.TextureLoader();
var texture = loader.load( filename );

Three.js not rendering texture on external model properly

I am trying to create a very realistic scene using Three.js. So far I have implemented mouse controls, loading model from Maya and I came to applying textures. The code works however, as it can be seen on the images below the textures don't fill the box as one would expect. I assume the problem is because each face of the model is filled separately, which occurred to me when I displayed my model as in wireframe mode.
My UV values look like this :
[[0.375,0,0.625,0,0.375,0.25,0.625,0.25,0.375,0.5,0.625,0.5,0.375,0.75,0.625,0.75,0.375,1,0.625,1,0.875,0,0.875,0.25,0.125,0,0.125,0.25]]
function init() {
// renderer
renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
renderer.setSize(window.innerWidth, window.innerHeight);
container = document.getElementById('container');
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 10000 );
camera.position.z = 5;
cameraControls = new THREE.TrackballControls(camera, renderer.domElement);
cameraControls.target.set(0, 0, 0);
scene = new THREE.Scene();
light = new THREE.AmbientLight( 0xffffff );
scene.add( light );
material = new THREE.MeshPhongMaterial( {
map: THREE.ImageUtils.loadTexture('images/box_texture.jpg')
} );
group = new THREE.Object3D();
var loader = new THREE.JSONLoader();
loader.load('models/cube_1.js', modelLoadedCallback);
window.addEventListener( 'resize', onWindowResize, false );
}
function modelLoadedCallback(geometry) {
mesh = new THREE.Mesh( geometry, material );
group.add(mesh);
// scene.add (new THREE.Mesh (geometry,
// new THREE.MeshBasicMaterial ({ color: 0x000000, wireframe: true })));
scene.add (new THREE.Mesh (geometry, material));
scene.add( group );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
render();
}
function animate() {
var delta = clock.getDelta();
requestAnimationFrame(animate);
cameraControls.update(delta);
renderer.render(scene, camera);
}
It can be helpful
var texture = THREE.ImageUtils.loadTexture('images/box_texture.jpg') ;
and add:
texture.flipY = false;

imported 3D objects are not casting shadows with three.js

I'm currently wrapping my brain around three.js and I've imported 3d model I made in C4D via the three.OBJMTLLoader successfully, but I can't get the object to cast a shadow. I've used object.castShadow = true but its not working but I can get geometry created in three.js to cast a shadow so I know the scene is setup ok.
The test scene is currently here: http://kirkd.co.uk/dev/ and has now been updated with the fix suggested below.
The code is below, if someone could kindly point out either what I'm doing wrong or even if imported objects can cast shadows then I'd be eternally grateful.
Ta.
<script>
var container;
var controls;
var camera, scene, renderer;
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(45, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.z = 500;
camera.position.y = 500;
scene = new THREE.Scene();
controls = new THREE.OrbitControls( camera );
controls.addEventListener( 'change', render );
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
var spotLight = new THREE.SpotLight( 0xffffff );
spotLight.position.set( 500, 1000, 500 );
spotLight.castShadow = true;
spotLight.shadowMapWidth = 1024;
spotLight.shadowMapHeight = 1024;
scene.add( spotLight );
var companion = new THREE.OBJMTLLoader();
companion.load( 'companion2.obj', 'companion.mtl', function ( object ) {
object.position.set(0,-20,0);
object.scale.set( 0.8, 0.8, 0.8 );
object.castShadow = true;
scene.add( object );
});
var floorGeometry = new THREE.CubeGeometry(1000,4,1000);
var floorMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.position.x=0;
floor.position.y=-130;
floor.position.z=0;
floor.receiveShadow = true;
scene.add(floor);
var geometry = new THREE.BoxGeometry( 100, 100, 100 );
mesh = new THREE.Mesh( geometry);
scene.add( mesh );
mesh.position.set(-100,200,10);
mesh.castShadow = true;
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xEEEEEE, 1.0);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild( renderer.domElement );
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;
spotLight.shadowCameraVisible = true;
var step=0;
render();
};
function render() {
camera.lookAt(scene.position);
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame( animate );
render();
}
</script>
Your object has child meshes, each of which needs to have castShadow set to true.
In your loader callback, add this:
object.traverse( function( node ) { if ( node instanceof THREE.Mesh ) { node.castShadow = true; } } );
three.js r.66

Why is the object not defined in the JavaScript console's opinion in the rendering function? (three.js)

This is the code, and it's running in my browser. It seems that the object with the texture is rendered properly, but JavaScript console writes that mycylinder is not defined. Why? How can I resolve this problem?
var texture,material,mycylinder;
var WIDTH = 400, HEIGHT = 300;
var VIEW_ANGLE = 45, ASPECT =WIDTH/HEIGHT, NEAR=0.1, FAR =10000;
var renderer= new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
var camera= new THREE.PerspectiveCamera(VIEW_ANGLE, window.innerWidth / window.innerHeight, NEAR, FAR);
var scene= new THREE.Scene();
scene.add(camera);
camera.position.set(0,150,400);
camera.lookAt(scene.position);
document.body.appendChild( renderer.domElement );
var light = new THREE.PointLight(0xffffff);
light.position.set(0,250,0);
scene.add(light);
var ambientLight = new THREE.AmbientLight(0x444444);
scene.add(ambientLight);
var cylinderLoader = new THREE.JSONLoader();
cylinderLoader.load( "models/probahenger.js", addModel );
function addModel( geometry, materials ) {
texture= new THREE.ImageUtils.loadTexture("images/Henger_anyag3.png");
material= new THREE.MeshLambertMaterial({map: texture});
mycylinder = new THREE.Mesh( geometry,material);
mycylinder.scale.set(30,30,30);
mycylinder.position.y=0;
scene.add( mycylinder );
};
function render() {
requestAnimationFrame(render);
mycylinder.rotation.x += 0.01;
mycylinder.rotation.y += 0.01;
renderer.render(scene, camera);
}
render();
Most likely the object hasn't been loaded by the time you're trying to rotate it. This should do the trick:
if ( mycylinder !== undefined ) {
mycylinder.rotation.x += 0.01;
mycylinder.rotation.y += 0.01;
}

Categories

Resources