Three.js odd transparency artifact - javascript

I just recently started creating a 3D model for a website I am creating.. and for some reason one of the arms of the character is transparent, but only partially as in you can see inside it. As seen here:
Transparent arm and I can't figure it out. I tried back tracking some of my code to no avail, and I tried re rendering it in blender (as it is a .obj import) and it still has the same issue. Does anyone happen to know a fix for the issue?
<body>
<script src="./JS/three.js"></script>
<script src="./JS/DDSLoader.js"></script>
<script src="./JS/MTLLoader.js"></script>
<script src="./JS/OBJLoader.js"></script>
<script src="./JS/OrbitControls.js"></script>
<script src="./JS/Detector.js"></script>
<script src="./JS/stats.min.js"></script>
<script>
var container, stats;
var camera, scene, renderer;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
renderer = new THREE.WebGLRenderer( {
alpha: true,
antialias: true
} );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( 200,300);
container.appendChild( renderer.domElement );
camera = new THREE.PerspectiveCamera( 14, window.innerWidth / window.innerHeight, .3, 1000 );
camera.position.z = 1;
camera.position.y = 8;
//camera.rotation.y = -90*(180/3.14159265)
var orbit = new THREE.OrbitControls( camera, renderer.domElement );
orbit.enableZoom = false;
orbit.enablePan = false;
orbit.autoRotate = true;
// scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0xffffff );
scene.add( ambient );
var dirLight = new THREE.DirectionalLight(0xffffff, .41);
dirLight.position.set(100, 100, 50);
scene.add(dirLight);
var light = new THREE.PointLight( 0xf8f8ff, 0.25, 10000 );
light.position.set( 0, 100,-75);
scene.add( light );
//var directionalLight = new THREE.DirectionalLight( 0xf8f8ff );
//directionalLight.position.set( 0, 0, 1 ).normalize();
//scene.add( directionalLight );
// model
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 ) { };
THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setBaseUrl( './Avatar/' );
mtlLoader.setPath( './Avatar/' );
mtlLoader.load( 'Avatar.mtl', function( materials ) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials( materials );
objLoader.setPath( './Avatar/' );
objLoader.load( 'Avatar.obj', function ( object )
{
object.alphaTest = 0;
object.transparent = false;
object.side = THREE.DoubleSide;
//object.rotation.y = -25*(180/3.14159265)
//object.rotation.x = -35*(180/3.14159265)
object.position.y = 0;
scene.add( object );
}, onProgress, onError );
});
//
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
//renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX ) / 2;
mouseY = ( event.clientY - windowHalfY ) / 2;
}
//
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
//camera.position.x += ( mouseX - camera.position.x ) * .005;
//camera.position.y += ( - mouseY - camera.position.y ) * .005;
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
</script>
I'm a bit lost at this point and could really use your help! Thanks in advance. If you have any questions, or not sure about something in my code let me know.

Face normals of the arm might be flipped backside. As you may know, if the face normal is facing backside, it will not be rendered, and only back-side will be rendered, even in 3d modeler too such as blender.
You can check normal direction in three.js using FaceNormalsHelper as well.
Docs for the helper:
http://threejs.org/docs/#Reference/Extras.Helpers/FaceNormalsHelper
http://threejs.org/examples/#webgl_helpers
If normal direction were wrong, flip them in your 3d modeler app.

Related

THREE.MTLLoader does not load texture image

i create a three js program that load mtl and obj and show on scene
when run program show no texture and show black object.
i use mtl loader and obj loader and add files in root folder of host
this code work on three js site fine and i copy this code in my program but dont work for me.
i also use light ambien light and point light.
its link my model : my program
here my code:
import * as THREE from './three.module.js';
import { DDSLoader } from './jsm/loaders/DDSLoader.js';
import { MTLLoader } from './jsm/loaders/MTLLoader.js';
import { OBJLoader } from './jsm/loaders/OBJLoader.js';
var container;
var camera, scene, renderer;
var mouseX = 0, mouseY = 0;
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, 1, 2000 );
camera.position.z = 250;
// scene
scene = new THREE.Scene();
var ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );
scene.add( ambientLight );
var pointLight = new THREE.PointLight( 0xffffff, 0.8 );
camera.add( pointLight );
scene.add( camera );
// model
var onProgress = function ( xhr ) {
if ( xhr.lengthComputable ) {
var percentComplete = xhr.loaded / xhr.total * 100;
console.log( Math.round( percentComplete, 2 ) + '% downloaded' );
}
};
var onError = function () { };
var manager = new THREE.LoadingManager();
manager.addHandler( /\.dds$/i, new DDSLoader() );
// comment in the following line and import TGALoader if your asset uses TGA textures
// manager.addHandler( /\.tga$/i, new TGALoader() );
new MTLLoader( manager )
.setPath( '/' )
.load( 'char4.mtl', function ( materials ) {
console.log(materials);
materials.preload();
new OBJLoader( manager )
.setMaterials( materials )
.setPath( '/' )
.load( 'char4.obj', function ( object ) {
object.scale.set(20,20,20);
scene.add( object );
}, onProgress, onError );
} );
//
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX ) / 2;
mouseY = ( event.clientY - windowHalfY ) / 2;
}
//
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY - camera.position.y ) * .05;
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
Your MLT file contains a Kd entry which represents the diffuse color and a map_Kd entry which represents a diffuse color texture. E.g.
Kd 0.00 0.00 0.00
map_Kd char4.png
So the diffuse color is black. The (unofficial) MTL spec says: material diffuse is multiplied by the texture value. And that means all final color values are black in your case.
You can easily fix the issue my changing all Kd values to 1.00 1.00 1.00.

Mouse Controls OBJ mesh threejs

When using the obj loader that three.js provides the movement around the object is very limited. How can I make it fully dynamic? E.g like on the OrbitControls three.js provides.
Here is my code so far.
<script>
var container, stats;
var camera, scene, renderer;
var mouseX = 0, mouseY = 0;
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, 1, 10000 );
camera.position.z = 1000;
// scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0x444444 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
directionalLight.position.set( 0, 0, 1 ).normalize();
scene.add( directionalLight );
// model
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 mtlLoader = new THREE.MTLLoader();
mtlLoader.setPath( 'obj/cylinder/' );
mtlLoader.load( 'cylinder.mtl', function( materials ) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials( materials );
objLoader.setPath( 'obj/cylinder/' );
objLoader.load( 'cylinder.obj', function ( object ) {
object.position.y = 0;
scene.add( object );
}, onProgress, onError );
});
//
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX ) / 1;
mouseY = ( event.clientY - windowHalfY ) / 2;
}
//
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .1;
camera.position.y += ( - mouseY - camera.position.y ) * .1;
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
</script>
</body>

Three.js texture not load properly?

I use OBJLoader to load the 3D model and I want to put a wood texture but the texture was not load properly or maybe not render properly(I dunno). Here the screenshot
This the image of the wood texture
Please help me with this. Thank you in advance.
Here's the code:
<script>
var container;
var camera, scene, renderer;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var container, stats;
var camera, scene, renderer;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var globalObject;
var text;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.z = 10;
controls = new THREE.OrbitControls( camera );
//scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0x101030 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
directionalLight.position.set( 0, 0, 1 );
scene.add( directionalLight );
//manager
var manager = new THREE.LoadingManager();
manager.onProgress = function (item, loaded, total) {
console.log( item, loaded, total );
};
//model
var loader = new THREE.OBJLoader( manager );
loader.load( 'image/table.obj', function (object) {
//store global reference to .obj
globalObject = object;
object.traverse( function (child) {
if ( child instanceof THREE.Mesh ) {
child.material.map = THREE.ImageUtils.loadTexture( 'image/texture.jpg')
/*child.material.map.x = 100 / 800;
child.material.map.y = 100 / 800;*/
child.material.needsUpdate = true;
}
});
object.position.y = 0;
scene.add( object );
});
//render
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0xffffff );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX ) / 2;
mouseY = ( event.clientY - windowHalfY ) / 2;
}
//animate
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
//camera.position.x += ( mouseX - camera.position.x ) * .05;
//camera.position.y += ( - mouseY - camera.position.y ) * .05;
//camera.lookAt( scene.position );
renderer.render( scene, camera );
}
render();
</script>
// This the MTL file
Blender MTL File: 'None'
Material Count: 1
newmtl Material.002
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
map_Kd wood2.jpg
In three.js the object color and the texture colors are blended together, so you can get the original texture color if you change the material color to white.
When you set a map to the material, try to change the original ambient and diffuse color to white. The concrete code depends on the type of the material. Something like this:
child.material.ambient = 0xffffff;
child.material.diffuse = 0xffffff;

webGL thee.js: pointing a camera at an .obj model

I am trying to work out how to direct the camera directly at an obj model. I have deliberately moved the obj model off center, so that when i achieve this aim, it will become centered in the frame. I've tried using camera.lookAt( object.position ); after camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 ); but unfortunately this just renders a black screen.
<script>
var container, stats;
var camera, scene, renderer;
//var mouseX = 0, mouseY = 0;
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, 1, 2000 );
camera.position.z = 300;
camera.position.x = 100;
camera.position.y = 500
// scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0x444444 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
directionalLight.position.set( 0, 0, 1 ).normalize();
scene.add( directionalLight );
// model
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 ) {
};
THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
var loader = new THREE.OBJMTLLoader();
loader.load( 'obj/male02/male02.obj', 'obj/male02/male02_dds.mtl', function ( object ) {
object.position.y = - 80;
object.position.x = - 80;
object.position.z = - 80;
scene.add( object );
}, onProgress, onError );
//
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
//document.addEventListener( 'mousemove', onDocumentMouseMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
</script>

Change texture of loaded .obj in three.js at runtime

I'm trying to swap image texture at runtime on a loaded three.js .obj. Here's the code straight from three.js examples with slight modification:
var container, stats;
var camera, scene, renderer;
var mouseX = 0, mouseY = 0;
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, 1, 2000 );
camera.position.z = 100;
//scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0x101030 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
directionalLight.position.set( 0, 0, 1 );
scene.add( directionalLight );
//manager
var manager = new THREE.LoadingManager();
manager.onProgress = function ( item, loaded, total ) {
console.log( item, loaded, total );
};
//model
var loader = new THREE.OBJLoader( manager );
loader.load( 'obj/female02/female02.obj', function ( object ) {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
//create a global var to reference later when changing textures
myMesh = child;
//apply texture
myMesh.material.map = THREE.ImageUtils.loadTexture( 'textures/ash_uvgrid01.jpg');
myMesh.material.needsUpdate = true;
}
} );
object.position.y = - 80;
scene.add( object );
} );
//render
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function newTexture() {
myMesh.material.map = THREE.ImageUtils.loadTexture( 'textures/land_ocean_ice_cloud_2048.jpg');
myMesh.material.needsUpdate = true;
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX ) / 2;
mouseY = ( event.clientY - windowHalfY ) / 2;
}
//animate
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY - camera.position.y ) * .05;
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
The only thing I added was the newTexture function and a reference to the mesh as myMesh. Here's the original example (http://threejs.org/examples/webgl_loader_obj.html). The function doesn't throw any errors but the .obj does not update. I know I'm just missing something fundamental here..
Update: Per the excellent answer below, here's the correct code with some additions to swap texture via an input field:
var container, stats;
var camera, scene, renderer;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var globalObject;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.z = 100;
//scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0x101030 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
directionalLight.position.set( 0, 0, 1 );
scene.add( directionalLight );
//manager
var manager = new THREE.LoadingManager();
manager.onProgress = function (item, loaded, total) {
console.log( item, loaded, total );
};
//model
var loader = new THREE.OBJLoader( manager );
loader.load( 'obj/female02/female02.obj', function (object) {
//store global reference to .obj
globalObject = object;
object.traverse( function (child) {
if ( child instanceof THREE.Mesh ) {
child.material.map = THREE.ImageUtils.loadTexture( 'textures/grid.jpg');
child.material.needsUpdate = true;
}
});
object.position.y = - 80;
scene.add( object );
});
//render
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX ) / 2;
mouseY = ( event.clientY - windowHalfY ) / 2;
}
//animate
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY - camera.position.y ) * .05;
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
function newTexture() {
var newTexturePath = "textures/" + document.getElementById("texture").value + "";
globalObject.traverse( function ( child ) {
if (child instanceof THREE.Mesh) {
//create a global var to reference later when changing textures
child;
//apply texture
child.material.map = THREE.ImageUtils.loadTexture(newTexturePath);
child.material.needsUpdate = true;
}
});
}
The problem here is that there are multiple child meshes in the object variable. By having only one myMesh global variable, you are storing only the last child mesh. Thus when you try to update the texture using this global variable, only one of the meshes gets a texture update, probably a small hidden part that is not clearly visible.
The solution would be to either:
Store a myMeshes array global variable and push myMesh into it every time there is one. Then in your newTexture() function, iterate through all items in this array and update their maps/materials.
Or, store the object variable (the one returned from OBJLoader's callback) into one single global variable. Then in your newTexture() function, iterate through all the meshes like how it is done in the code (using traverse() and if statement), and update their maps/materials.
Lastly, calling newTexture() somewhere in your code would also help ;)

Categories