Can't set material for loaded model - three.js

May you help me to find out why material don't apply to mesh?
http://websaints.net/model/index2.html — loading model with THREE.MeshNormalMaterial() material — ок.
http://websaints.net/model/index.html — trying to load with texture — not ok.
I create model with makehuman.org, save model as obj and then convert obj to json with help of grunt-three-obj
I think I do everything right … but somewhere I am wrong and get err:
Uncaught TypeError: Cannot set property 'needsUpdate' of undefined
Code:
var angle = .05,
scene = new THREE.Scene(),
camera = new THREE.PerspectiveCamera(63, 4/3, 0.1, 1000),
renderer = new THREE.WebGLRenderer({
canvas: document.getElementById("screen"),
antialias: true
}),
json_loader = new THREE.JSONLoader(),
model = null
json_loader.load('http://websaints.net/model/json/human.js', modelLoaded, 'http://websaints.net/model/json/');
renderer.setSize(600, 400);
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = -10;
function render() {
if(model!=null){
model.rotation.y+=.05;
}
requestAnimationFrame(render);
renderer.render(scene, camera);
}
render();
function modelLoaded(geometry, materials){
model = new THREE.Mesh(geometry, new THREE.MeshNormalMaterial());
model.position.z = 10;
model.position.y = -8;
scene.add(model);
camera.lookAt({x:0,y:0,z:0});
}
With all best regards. Anton.

Related

load and dispose textures during runtime

I'm trying to understand how to be memory efficient in three.js. In my main application i try to load and dispose textures depending on the distance to the camera to save memory in the gpu. Since i work with a lot of textures and large textures in my main application i can't possibly work with a texture atlas into which i initially load all the textures. Here I have a code example of how i imagine it. Loading the texture works but not deleting it. But i don't see why that is. According to the three.js docu this should work with dispose() but it doesn't work here.Wwhere is my mistake?
In general: If someone thinks there is something in my concept that could be improved, i'd be happy to hear your advice. I only do what i do within the framework of what i can best imagine based on what i know so far. I like to learn about it. For example, i would be interested in how i not only load and dispose textures. Also the object that i have created here should be efficiently added and deleted as a whole, added again and deleted, ... after all, that's the purpose of class objects, being able to be created and deleted again at will.
var camera, controls, scene, renderer, container, aspect;
var cube;
async function main() {
await init();
animate();
}
async function init() {
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setClearColor( 0x000000 );
container = document.getElementById('container');
renderer.setSize(container.clientWidth, container.clientHeight);
container.appendChild( renderer.domElement );
aspect = container.clientWidth / container.clientHeight;
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x000000 );
camera = new THREE.PerspectiveCamera( 45, aspect, 1, 1000000 );
camera.position.set(0, 0, 300);
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enableZoom = true;
controls.enabled = true;
controls.target.set(0, 0, 0);
//------End three.js setup-------
cube = new Cube();
scene.add(cube.box);
}//-------End init----------
function animate() {
requestAnimationFrame( animate );
render();
}//-------End animate----------
function render() {
var distance = camera.position.distanceTo( cube.box.position );
cube.update(distance);
camera.updateMatrixWorld();
camera.updateProjectionMatrix();
renderer.render(scene, camera);
}//-------End render----------
class Cube{
constructor(){
this.texture;
this.texstatus = false;
const geometry = new THREE.BoxGeometry( 10, 10, 10 );
const material = new THREE.MeshBasicMaterial( {
color: 0x00ff00,
map: null
} );
this.box = new THREE.Mesh( geometry, material );
}//end constructor
update(distToCam){
if(distToCam <= 100 && this.texstatus == false){
var loader = new THREE.TextureLoader();
this.texture = loader.load("grass.jpg");
this.box.material.map = this.texture;
this.texstatus = true;
}
if(distToCam > 100 && this.texstatus == true){
//prvious solution deactivated
//this.texture.dispose();
//this.texstatus = false;
//new solution
this.box.material.map = null;
this.texture.dispose();
this.texstatus = false;
this.texture = undefined;
}
this.box.material.needsUpdate = true;
}//end update
}//end Cube class

Three.js LegacyGLTFLoader.js shadows missing

I have a GLTF version 1.0 model that I am importing into Three.js using LegacyGLTFLoader.js. When I do so, everything looks good, except that the model does not receive shadows. I am guessing that this is because the imported model's material is THREE.RawShaderMaterial, which does not support receiving shadows (I think). How can I fix this so that my imported model can receive shadows?
Here is sample code:
// Construct scene.
var scene = new THREE.Scene();
// Get window dimensions.
var width = window.innerWidth;
var height = window.innerHeight;
// Construct camera.
var camera = new THREE.PerspectiveCamera(75, width/height);
camera.position.set(20, 20, 20);
camera.lookAt(scene.position);
// Construct renderer.
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
renderer.shadowMapEnabled = true;
document.body.appendChild(renderer.domElement);
// Construct cube.
var cubeGeometry = new THREE.BoxGeometry(10, 1, 10);
var cubeMaterial = new THREE.MeshPhongMaterial({color: 0x00ff00});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
cube.translateY(15);
scene.add(cube);
// Construct floor.
var floorGeometry = new THREE.BoxGeometry(20, 1, 20);
var floorMaterial = new THREE.MeshPhongMaterial({color: 0x00ffff});
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.receiveShadow = true;
scene.add(floor);
// Construct light.
var light = new THREE.DirectionalLight(0xffffff);
light.position.set(0, 20, 0);
light.castShadow = true;
scene.add(light);
// Construct light helper.
var lightHelper = new THREE.DirectionalLightHelper(light);
scene.add(lightHelper);
// Construct orbit controls.
new THREE.OrbitControls(camera, renderer.domElement);
// Construct GLTF loader.
var loader = new THREE.LegacyGLTFLoader();
// Load GLTF model.
loader.load(
"https://dl.dropboxusercontent.com/s/5piiujui3sdiaj3/1.glb",
function(event) {
var model = event.scene.children[0];
var mesh = model.children[0];
mesh.receiveShadow = true;
scene.add(model);
},
null,
function(event) {
alert("Loading model failed.");
}
);
// Animates the scene.
var animate = function () {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
// Animate the scene.
animate();
Here are my resources:
https://dl.dropboxusercontent.com/s/y2r8bsrppv0oqp4/three.js
https://dl.dropboxusercontent.com/s/5wh92lnsxz2ge1e/LegacyGLTFLoader.js
https://dl.dropboxusercontent.com/s/1jygy1eavetnp0d/OrbitControls.js
https://dl.dropboxusercontent.com/s/5piiujui3sdiaj3/1.glb
Here is a JSFiddle:
https://jsfiddle.net/rmilbert/8tqc3yx4/26/
One way to fix the problem is to replace the instance of RawShaderMaterial with MeshStandardMaterial. To get the intended effect, you have to apply the existing texture to the new material like so:
var newMaterial = new THREE.MeshStandardMaterial( { roughness: 1, metalness: 0 } );
newMaterial.map = child.material.uniforms.u_tex.value;
You also have to compute normal data for the respective geometry so lighting can be computed correctly. If you need no shadows, the unlint MeshBasicMaterial is actually the better choice.
Updated fiddle: https://jsfiddle.net/e67hbj1q/2/

three.js - Model showing with blank (black) texture despite material loaded

I have a 3D model file 3dbaotang.obj and a material file 3dbaotang.mtl. I've loaded both of them using three.js OBJLoader and MTLLoader. The model has shown up, but not the material, as it's solely covered with black. Can anyone help?
Here is my code:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth /
window.innerHeight, 0.1, 10000);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var keyLight = new THREE.DirectionalLight('hsl(30, 100%, 75%)', 1.0);
var fillLight = new THREE.DirectionalLight('hsl(240, 100%, 75%)', 0.75);
var backLight = new THREE.DirectionalLight(0xffffff, 1.0);
backLight.position.set(100, 0, -100).normalize();
keyLight.position.set(-100, 0, 100);
fillLight.position.set(100, 0, 100);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = true;
controls.update();
renderer.setSize(window.innerWidth, window.innerHeight, false);
renderer.setClearColor(new THREE.Color(0xf2f2f2), 1);
document.body.appendChild(renderer.domElement);
// LOAD MODEL
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setResourcePath('/models/');
mtlLoader.setPath('/models/');
mtlLoader.load('/3dbaotang.mtl', (materials) => {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.setPath('/models/');
objLoader.load('3dbaotang.obj', (object) => {
scene.add(object);
});
});
camera.position.z = 200;
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
Result:
Adding to Brother Eye's answer, I'm brand new and and spent quite a lot of time in the dark but this got me on to the solution so I thought I'd elaborate for anyone in the same position.
Adding a light can be some simply as follows;
//Add light
var light = new THREE.AmbientLight(0xffffff);
scene.add(light);
The reference docs are located at https://threejs.org/docs/#api/en/lights/AmbientLight
It was the lack of light that caused this problem, not the material. After adding ambient light to the scene, the object can be seen normally

Three.js recover camera values

When I start the script, camera has starting values. When I will move it and click button to set up startign values it is never same. What values I missed?
The best way, I suppose, it is to look at the example.
I used console.log for debbuging camera values.
HTML:
<button id="buttonTest">
TEST
</button>
Please, move cube before click!
<div id="wrapper">
</div>
JS:
var camera, scene, renderer, geometry, material, mesh;
init();
animate();
function init() {
window.wrapper = document.getElementById('wrapper');
var buttonTest = document.getElementById('buttonTest');
buttonTest.addEventListener('click', function() {
test();
});
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 500;
scene.add(camera);
geometry = new THREE.CubeGeometry(200, 200, 200);
material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({
preserveDrawingBuffer: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(new THREE.Color("hsl(193, 50%, 57%)"));
wrapper.appendChild(renderer.domElement);
controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.rotateSpeed = 4.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.1;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( 'change', render );
console.log('camera_default: '+camera.position.x+', '+camera.position.y+', '+camera.position.z);
console.log('quaternion_default: '+camera.quaternion.x+', '+
camera.quaternion.y+', '+camera.quaternion.z+', '+camera.quaternion.w);
}
function animate() {
requestAnimationFrame(animate);
controls.update();
render();
}
function render() {
camera.lookAt(scene.position);
renderer.render(scene, camera);
}
function test() {
// lines below shows actual settings
console.log('camera_now: '+camera.position.x+', '+camera.position.y+', '+camera.position.z);
console.log('quaternion_now: '+camera.quaternion.x+', '+
camera.quaternion.y+', '+camera.quaternion.z+', '+camera.quaternion.w);
window.setTimeout(function() {
// this is recovering camera values like
// it was on the sart of script
// it is not enought, what I missed?
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 500;
camera.quaternion.x = 0.0;
camera.quaternion.y = 0.0;
camera.quaternion.z = 0.0;
camera.quaternion.w = 1.0;
console.log('camera_recover_default: '+camera.position.x+', '+camera.position.y+', '+camera.position.z);
console.log('quaternion_recover_default: '+camera.quaternion.x+', '+
camera.quaternion.y+', '+camera.quaternion.z+', '+camera.quaternion.w);
},1500);
}
I suggest simply doing controls.reset(), it should fix your problems.
replace
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 500;
camera.quaternion.x = 0.0;
camera.quaternion.y = 0.0;
camera.quaternion.z = 0.0;
camera.quaternion.w = 1.0;
with
controls.reset();
Be sure to reset your camera's up vector. When you call camera.lookAt, it uses the up vector to calculate the new rotation matrix, which is then applied as a quaternion.
camera.up.set(0, 1, 0);
Add that after resetting your position/quaterion, and the camera should appear at its starting point.

Three.js - Faces missing after Blender import

I tried to import a JSON exported file of my 3D model and import it in Three.js but it seems some faces are missing.
I am not sure if it was an export problem because when I rotate it, the left face exists but the right face does not, vice versa.
Here is my original model in Blender:
var scene, camera, renderer;
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;
var SPEED = 0.01;
function init() {
scene = new THREE.Scene();
initMesh();
initCamera();
initLights();
initRenderer();
document.body.appendChild(renderer.domElement);
}
function initCamera() {
camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 1, 10);
camera.position.set(0, 3.5, 5);
camera.lookAt(scene.position);
}
function initRenderer() {
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
}
function initLights() {
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
directionalLight.position.set( 0, 1, 0 );
scene.add( directionalLight );
}
var mesh = null;
function initMesh() {
var loader = new THREE.JSONLoader();
loader.load('./model.json', function(geometry, materials) {
mesh = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.75;
mesh.translation = THREE.GeometryUtils.center(geometry);
mesh.position.x = -5;
scene.add(mesh);
});
}
function rotateMesh() {
if (!mesh) {
return;
}
mesh.rotation.y -= SPEED;
}
function render() {
requestAnimationFrame(render);
rotateMesh();
renderer.render(scene, camera);
}
init();
render();
Hope you can help me with this problem.
Thanks in advance!
I would suspect your problem has to do with the face-normals pointing in the wrong direction. To check if this is the case you could try to set all materials to double-sided:
materials.forEach(function(mat) {
mat.side = THREE.DoubleSide;
});
With Double-Sided mode, the faces are drawn regardless of the normals direction, so you should see all faces if enabled.
Or you could use the THREE.FaceNormalsHelper to have a look at the normals yourself.
scene.add(new THREE.FaceNormalsHelper(mesh, 2, 0x00ff00, 1));
This will render arrows for all faces indicating the normal-direction.
If the normals are wrong you can fix this in blender by selecting all affected faces and using the command Mesh>Faces>Flip Normals from the menu or in the Tools-Panel on the right-hand side in the "Shading/UV"-Tab. Sometimes just selecting all faces and running "Recalculate Normals" from the Tools will work as well.
Blender also has a display-mode for face-normals in the right hand menu in the "Mesh Display"-Section.

Resources