Three.js transparency model texture bug - javascript

I have searched over all internet and tried a lot of ways, but no results..
How to remove texture transparency bug? Check on image
So, on 3dsmax model looks okey. I have converted to .js format from .obj with python script in order .png files were transparent (.obj does not make transparence).
How to solve my problem? Thank you
var loader = new THREE.JSONLoader();
loader.load('tree_model.js', function(geometry, materials) {
var material = new THREE.MeshFaceMaterial(materials);
var object = new THREE.Mesh(geometry, material);
object.traverse( function ( child ) {
if ( child.material instanceof THREE.MeshPhongMaterial ) {
// this code is unattainable, but anyway without if (..) it does not work
child.material.alphaTest = 0.5;
child.material.depthWrite = false;
child.material.depthTest = false;
child.material.side = THREE.BackSide;
child.material.transparent = true;
}
});
scene.add(object);
});
});
And renderer:
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, premultipliedAlpha: true });

You have to set alphaTest on your materials. Additionally setting the leafes and branches to THREE.DoubleSide ensures that they are not disappearing when viewed from the other side.
The code you have posted contains various errors, so replace it with this:
var loader = new THREE.JSONLoader();
loader.load('model/Elka.js', function(geometry, materials) {
for( var i = 0; i < materials.length; i ++ ) {
var material = materials[ i ];
material.alphaTest = 0.5;
material.side = THREE.DoubleSide;
// not-so-good practice
if ( material.name === "NorwaySpruceBark" ) {
material.transparent = false;
}
}
var material = new THREE.MeshFaceMaterial(materials);
var object = new THREE.Mesh(geometry, material);
scene.add(object);
});
To further reduce transparency artefacts, set the trunk to non-transparent. Your model should contain the correct material settings, so this is kind of a bad practice.
Edit: Setting alpha and premultipliedAlpha in the renderer is not necessary for this problem.
Result:

Related

Three.js PLYLoader to save geometry to object (async issue)

I have an object in which I want to store the geometry contained on a Ply file, for which I have the following:
function MyObject(filename) {
var container = this.createObject(filename);
this.mesh = container.children[0];
this.geometry = this.mesh.geometry;
};
MyObject.prototype.createObject = function(filename) {
var loader = THREE.PLYLoader();
var container = new THREE.Object3D();
loader.load(filename, function ( geometry ) {
var material = new THREE.MeshStandardMaterial( { color: 0x0055ff } );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.y = -0.25;
mesh.rotation.x = -Math.PI / 2;
mesh.scale.multiplyScalar( 0.04 );
container.add( mesh );
});
return container;
};
But the loader is run asynchronously and therefore when creating this.mesh it becomes "undefined" making the program fail at this.geometry.
I thought the return function would fix this but it does not and after reading about asynchronicity in javascript I haven't found the solution. Does anyone know how to implement three.js loaders with objects?

Phong material on a .obj model not reacting to area light in three.js

I am trying to add a phong material to my .obj model along with a shader Area light, However i tried to use Meshface material using array to load both materials but the model completely disappears when i do so.
The scenario works when i use either of them but not both together, is there anyway to overcome this issue ?
Here is the DEMO:
CODE:
// area light
var lightColor = 0xffffff;
var lightIntensity = 1;
var geometry = new THREE.PlaneGeometry(100, 50);
var material = new THREE.MeshBasicMaterial({
color: lightColor,
transparent: true,
opacity: 0.7,
side: THREE.FrontSide
});
areaLight = new THREE.Mesh(geometry, material);
areaLight.position.set(0, 25, -25);
areaLight.rotation.set(0, 0, 0);
areaLight.scale.set(1, 1, 1);
scene.add(areaLight);
var Black = new THREE.MeshPhongMaterial({
color: 0x000000,
})
// wireframe hack
areaLight.add(new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
wireframe: true
})));
// areaLight verts
var vertices = areaLight.geometry.vertices;
var verts = [];
verts.push(vertices[0]);
verts.push(vertices[1]);
verts.push(vertices[3]);
verts.push(vertices[2]);
// uniforms
var uniforms = {
color: {
type: "c",
value: new THREE.Color(0xaaaadd)
},
lightColor: {
type: "c",
value: areaLight.material.color
},
lightIntensity: {
type: "f",
value: lightIntensity
},
lightverts: {
type: "v3v",
value: verts
},
lightMatrixWorld: {
type: "m4",
value: areaLight.matrixWorld
}
};
// attributes
var attributes = {};
// material
var material = new THREE.ShaderMaterial({
attributes: attributes,
uniforms: uniforms,
vertexShader: document.getElementById('vertex_shader').textContent,
fragmentShader: document.getElementById('fragment_shader').textContent,
shading: THREE.SmoothShading
});
var onError = function(xhr) {};
THREE.Loader.Handlers.add(/\.dds$/i, new THREE.DDSLoader());
var mtl1Loader = new THREE.MTLLoader();
mtl1Loader.setBaseUrl('neavik/newmail/');
mtl1Loader.setPath('neavik/newmail/');
mtl1Loader.load('chandelier.mtl', function(materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.setPath('neavik/newmail/');
objLoader.load('chandelier.obj', function(object4) {
object4.castShadow = true;
object4.receiveShadow = true;
object4.updateMatrix();
object4.position.set(40, 28, 40); //(0,-5,0.5);
object4.scale.x = 0.09;
object4.scale.y = 0.05;
object4.scale.z = 0.09
var mats = [];
mats.push(new THREE.MeshPhongMaterial({
color: 0x000000
}));
mats.push(material);
var faceMaterial = new THREE.MeshFaceMaterial(mats);
object4.traverse(function(child) {
if (child instanceof THREE.Mesh) {
if (child.material.name == "chandelier_Outside") {
child.material = Black; // When i use faceMaterial here the object disappears
child.castShadow = true;
child.receiveShadow = true;
}
}
});
scene.add(object4);
});
})
// plane geometry
var geometry = new THREE.PlaneGeometry(200, 200);
geometry.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI / 2));
// plane
mesh = new THREE.Mesh(geometry, material);
mesh.position.y = -0.1;
scene.add(mesh);
// torus knot
var geometry = new THREE.TorusKnotGeometry(10, 4, 256, 32, 1, 3, 1);
// mesh
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 28, 0);
scene.add(mesh);
}
The main issue section
var mats = [];
mats.push(new THREE.MeshPhongMaterial({
color: 0x000000
}));
mats.push(material);
var faceMaterial = new THREE.MeshFaceMaterial(mats);
object4.traverse(function(child) {
if (child instanceof THREE.Mesh) {
if (child.material.name == "chandelier_Outside") {
child.material = Black; // When i use faceMaterial here the object disappears
child.castShadow = true;
child.receiveShadow = true;
}
}
});
scene.add(object4);
});
})
Area light reference:http://jsfiddle.net/hh74z2ft/1/
Expected result is similar to this image that even the chandelier should be illuminated like torusknot along with phongness [no phongness in this image ]
[]2
Unfortunately, if I understood you correctly, this will not work.
A material (among other things) defines how a given fragment of a triangle will be shaded. There can always be only one material for any given triangle. The face-material is for handling the case where you want to render different faces with different materials, but it's still just one material per triangle.
There is a concept in three.js named multiMaterial (have a look at THREE.SceneUtils.createMultiMaterialObject()). But that will simply create multiple meshes with the same geometry and different materials, so every triangle will get rendered twice, once with every material.
Now the Problem is that the AreaLight implementation has it's very own concept of how the light is represented (via shader-uniforms), and that is completely different from how the phong-material works. So, even if you did somehow add a phong-material to the object to get the specular reflections, the phong-material wouldn't know how to deal with the area-light source (it wouldn't even know that there is any light in the scene).
In order to get the behaviour you like to see you would need to add a specular term (the calculation-formula for specular reflections of the surface) to the fragment-shader and maybe some uniforms to control it's behaviour. This is a really complicated thing to do, especially for area-lights (if it were easy, we'd already have it in three.js).
However, there is some work being done based on latest research in that field, you might want to have a look at this issue and the demo here. There is also a pull-request for this here.

Bounding Box of a stl object in three.js

Good day everyone,
I have come across several posts about this topic but no solution would work. I have an object that I load with the help of the STLLoader in three.js for which I would like to get a bounding box.
// Add stl objects and a name
function addSTLObject(url, name) {
var loader = new THREE.STLLoader();
loader.load(url, function (geometry) {
var material = new THREE.MeshPhongMaterial({ color: 0xff5533 });
var mesh = new THREE.Mesh(geometry, material);
// To scale element, use:
// mesh.scale.set(0.01, 0.01, 0.01);
// Add a name to the object to find it if it needs to be removed
mesh.name = name;
mesh.position.x = 0;
mesh.position.y = 0;
mesh.position.z = 0;
scene.add(mesh);
});
}
and I am loading this object as follows:
addSTLObject('model/cases/iphone5.stl', 'phone-model');
var phoneModelAdded = scene.getObjectByName('phone-model', true);
Now I have tried the solutions provided here: https://github.com/mrdoob/three.js/issues/3471 and here Any way to get a bounding box from a three.js Object3D?
var bbox = new THREE.Box3().setFromObject(phoneModelAdded);
var geometry = phoneModel.children[0].children[0].geometry;
geometry.computeBoundingBox();
While the first solution gives me an error saying "Cannot read property 'updateMatrixWorld' of undefined", the second gives me no error at all but does nothing and if I try accessing the "geometry" property it says that it doesn't exist.
Does someone have a working solution?
Any help is appreciated.
Have a good day!
EDIT:
// Add stl objects and a name
function addSTLObject(url, name) {
var bbox;
var loader = new THREE.STLLoader();
loader.load(url, function (geometry) {
var material = new THREE.MeshPhongMaterial({ color: 0xff5533 });
var mesh = new THREE.Mesh(geometry, material);
// To scale element, use:
// mesh.scale.set(0.01, 0.01, 0.01);
// Add a name to the object to find it if it needs to be removed
mesh.name = name;
mesh.position.x = 0;
mesh.position.y = 0;
mesh.position.z = 0;
bbox = new THREE.Box3().setFromObject(mesh);
scene.add(mesh);
return bbox;
});
}
and aftwards
var bbox = addSTLObject('model/cases/iphone5.stl', 'phone-model');
scene.add(bbox);
Error: "THREE.Object3D.add: object not an instance of THREE.Object3D"
EDIT 2:
var bbox, bboxComputed = false;
function addSTLObject(url, name) {
var bbox;
var loader = new THREE.STLLoader();
loader.load(url, function (geometry) {
var material = new THREE.MeshPhongMaterial({ color: 0xff5533 });
var mesh = new THREE.Mesh(geometry, material);
// To scale element, use:
// mesh.scale.set(0.01, 0.01, 0.01);
// Add a name to the object to find it if it needs to be removed
mesh.name = name;
mesh.position.x = 0;
mesh.position.y = 0;
mesh.position.z = 0;
bbox = new THREE.BoundingBoxHelper(mesh);
bbox.update();
bboxComputed = true;
scene.add(mesh);
});
}
addSTLObject('model/cases/iphone5.stl', 'phone-model');
var myInterval = setInterval( function(){
if( bboxComputed ) {
alert( bbox.box.min, bbox.box.max, bbox.box.size() );
scene.add(bbox);
clearInterval( myInterval );
bboxComputed = false;
}
}, 100 );
This won't work.
EDIT 3:
I was trying to set up a function that would have everything I need and give back an object with all of the calculated information:
function calculateSTLProperties(url, name) {
var STLObject, STLbbox, STLComputed = false, STLGeometry;
var loader = new THREE.STLLoader();
loader.load(url, function (geometry) {
var material = new THREE.MeshPhongMaterial({ color: 0xff5533 });
var mesh = new THREE.Mesh(geometry, material);
// To scale element, use:
// mesh.scale.set(0.01, 0.01, 0.01);
// Add a name to the object to find it if it needs to be removed
mesh.name = name;
mesh.position.x = 0;
mesh.position.y = 0;
mesh.position.z = 0;
// Compute a bounding box for the element
STLbbox = new THREE.BoundingBoxHelper(mesh);
STLbbox.update();
// Get the geometry of the case
STLGeometry = geometry;
STLComputed = true;
});
// Set an interval to wait for the corresponding bounding box and geometry to be computed
var myInterval = setInterval( function(){
if( STLComputed ) {
STLObject = {
"geometry" : STLGeometry,
"bbox" : STLbbox,
"x" : STLbbox.box.size().x,
"y" : STLbbox.box.size().y,
"z" : STLbbox.box.size().z
};
clearInterval( myInterval );
bboxComputed = false;
}
}, 100 );
return STLObject;
}
Unfortunately, somehow the object doesn't get passed through and I end up with an "undefined" in the end when trying to save it:
var STLObjectLoaded = calculateSTLProperties('model/cases/iphone5.stl', 'phone-model');
console.log(STLObjectLoaded);
What am I missing?
Model loading is asynchronous so all you computations should happen after the model has loaded. Add a callback, which is called when the model has loaded and add there the call to the bbox. The way you have it the scene.getObjectByName() call is being made with an empty object which you also verify ("if I try accessing the "geometry" property it says that it doesn't exist")
Update:
Use:
var bbox = new THREE.BoundingBoxHelper( mesh ); bbox.update();
scene.add( bbox );
from inside the loader.load() function.
The setFromObject() call only create the bounding box but not the geometry for the bbox.
Update II
If you want the bbox to be available outside your loader.load() function you would need to use a global variable
var bboxComputed = false;
which you set to true right after you compute the bbox inside your loader.load() function:
scene.add( bbox );
bboxComputed = true;
Then in your main instead of using:
console.log( bbox.box.min, bbox.box.max, bbox.box.size() );
you would use something like:
var myInterval = setInterval( function(){
if( bboxComputed ) {
console.log( bbox.box.min, bbox.box.max, bbox.box.size() );
clearInterval( myInterval );
}
}, 100 );
I am setting a delay of 0.1 second and when the bbox has finally been computed I clear the interval so that it does not run forever.
As of three.js R125, the recommended way to do this is with the loadAsync method, which is now native to three.js:
https://threejs.org/docs/#api/en/loaders/Loader.loadAsync
That method returns a promise. You could then use a 'then' to get the geometry of the STL and create the mesh. You could also use a traditional callback as in earlier answers, or an async/await structure, but I think the example below using the native three.js method is the simplest way. The example shows how you can get geometry to a global variable once the promise is resolved and the STL file is loaded:
// Global variables for bounding boxes
let bbox;
const loader = new STLLoader();
const promise = loader.loadAsync('model1.stl');
promise.then(function ( geometry ) {
const material = new THREE.MeshPhongMaterial();
const mesh = new THREE.Mesh( geometry, material );
mesh.geometry.computeBoundingBox();
bbox = mesh.geometry.boundingBox;
scene.add( mesh );
buildScene();
console.log('STL file loaded!');
}).catch(failureCallback);
function failureCallback(){
console.log('Could not load STL file!');
}
function buildScene() {
console.log('STL file is loaded, so now build the scene');
// !VA bounding box of the STL mesh accessible now
console.log(bbox);
// Build the rest of your scene...
}

adding external texture to three.js scene object

just getting my hands dirty with three.js and im curious on something that may be relatively simpleā€¦
I built a scene in the three.js editor and have figured out how to load the scene. In the editor, I added an image as a map texture but I realize it wont know where it is loaded externally on my server. So I've loaded the scene and objects and lights, but how can I map my textures to the objects?
// MATERIALS
var wireframe = THREE.ImageUtils.loadTexture( 'textures/wireframe.jpg' );
wireframe.wrapS = wireframe.wrapT = THREE.RepeatWrapping;
wireframe.repeat.set( 4, 4 );
var wireframeMaterial = new THREE.MeshLambertMaterial({
map : wireframe,
side : THREE.DoubleSide
});
// LOAD SCENE
var loader = new THREE.ObjectLoader();
loader.load( 'js/scene.js', function ( obj ) {
obj.traverse( function( node ) {
if ( node instanceof THREE.Mesh ) {
node.castShadow = true;
node.receiveShadow = true;
var plane = scene.getObjectByName( "plane", true );
plane.material = wireframeMaterial;
}
});
scene.add( obj );
render();
});
When adding plane.material = wireframeMaterial; into the loader, all my objects just disappearā€¦ How can I properly map the wireframeMaterial the plane object?
working example with var plane and plane.material commented out:
http://goo.gl/czSg7P
Scene:
http://goo.gl/BAVgVS
To map the texture you have to create a material using your texture and apply it to your plane.
If necessary you can set repetition of your texture with:
wireframe.wrapS = THREE.RepeatWrapping;
wireframe.wrapT = THREE.RepeatWrapping;
wireframe.repeat.set( 4, 4 );
And then you need to do something like this:
material = new THREE.MeshLambertMaterial({ map : wireframe, side : THREE.DoubleSide });
plane.material = material;
EDIT
You create a scene inside a scene. Since the loader returns a scene and then you add it again to a scene. This will give issues for sure.
Try to replace scene.add( obj ); with this in your loader:
for( var i = 0; i < obj.children.length; i++ ){
scene.add( obj.children[i] );
}
Not sure if that causes the problem, but I made a fiddle and it all seems to work fine for me. The problem is that I can't load external sources in my fiddle so instead I had to use your image as base64 string and the code becomes a bit different.
This means you have to change it a bit to make it work again with an image from your local folder.
If you exchange the Whole //TEXTURE part of the code with the following it should work:
var texture = new THREE.ImageUtils.loadTexture( 'img/wireframe.jpg' );
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 1, 1 );
var textureMaterial = new THREE.MeshLambertMaterial({ map: texture, side : THREE.DoubleSide });

three.js load object/model and then manipulate sub-parts/children

I have a 3D model of a robot arm, that I want displayed and manipulated in the browser.
My question is: how do I load the model into three.js, so that I can manipulate all sub-parts of the robot arm.
As an example I have a rotary motor and a shaft attached as an assembly in Inventor.
Image: http://i.stack.imgur.com/custz.png
This is exported as an stl file and imported in Three.js using STLLoader.js.
Image: http://i.stack.imgur.com/nLmBe.png
I want to know how I can manipulate the shaft to turn to a specified angle.
I have loaded the model using the following code:
<div id="container"></div>
<script src="three.js\build\three.min.js"></script>
<script src="js\STLLoader.js"></script>
<script>
// Set size variables
var SIZE_x = 400, SIZE_y = 400;
// Set three main THREE variables
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, SIZE_x/SIZE_y, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
// Set renderer options
renderer.setSize(SIZE_x, SIZE_y);
renderer.setClearColor(0xEEEEEE, 1.0);
renderer.clear();
// Append to HTML Dom
//document.body.appendChild(renderer.domElement);
$('#container').append(renderer.domElement);
// Create light
var pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;
scene.add(pointLight);
// Move camera
camera.position.x = 0;
camera.position.y = 20;
camera.position.z = 20;
var loader = new THREE.STLLoader();
loader.addEventListener( 'load', function ( event ) {
var geometry = event.content;
var material = new THREE.MeshLambertMaterial( { ambient: 0xff5533, color: 0xff5533 } );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
mesh.traverse(function ( child ) {
//if ( child instanceof THREE.Mesh ) {
console.log("Hej: " + child);
//}
});
//scene.add( new THREE.Mesh( geometry ) );
console.log('Loaded');
} );
loader.load( 'models/AssemblySimple1.stl' );
// Render loop
var render = function () {
requestAnimationFrame(render);
camera.lookAt(scene.position);
renderer.render(scene, camera);
};
render();
</script>
Any points and hints are welcome. Also if there is a preferred export file format. I have both SolidWorks and Inventor at my disposal. Or if I've taken a completely wrong approach to the problem, please let me know of other ways.
Thanks
Three js has function called THREE.STLLoader() .This one can be used to load stl file.
Here is the way how it is loaded
var loader = new THREE.STLLoader();
var group = new THREE.Object3D();
loader.load("../lib/SolidFz.stl", function (geometry) {
console.log(geometry);
var mat = new THREE.MeshLambertMaterial({color: 0x7777ff});
group = new THREE.Mesh(geometry, mat);
group.rotation.x = -0.5 * Math.PI;
group.scale.set(0.6, 0.6, 0.6);
scene.add(group);
});
Here scene
var scene new THREE.Scene();
After that you have a created 3d object and loaded one added into that 3d object.Then you can control that 3d object as you wish. According to this way you can load several parts and do the what you want to do with that

Categories

Resources