Lighting glb models in Three JS with minimal shadows - javascript

Not sure if I worded my title right but I'm getting my feet wet with three JS. Right now I have a simple glb model that I would like to import into my scene, but I can't get the lighting right. The image below is what I want to accomplish.
But when I import my glb into my scene and add some lighting this is what it looks like
The model is quite dark and I can't get it to light up ideally. I've tried adding ambient lights top-down, point lights as a child to the camera instance, hemisphere lights, etc. but I just can't get it to look right. Below is the code for the current lighting; I'm trying to achieve the look by using point lights atm.
var light = new THREE.PointLight( 0xffffff, 10 );
light.position.z = 10
camera.add(light)
var light2 = new THREE.PointLight( 0xffffff, 10 );
light2.position.set(0, -20, 30)
scene.add(light2)
If anyone could give me some insights as to what is the proper way to achieve what I am desiring that will be great.

So I did some digging in, and it turns out that Blender includes this thing called an environment map
https://discourse.threejs.org/t/exporting-blender-scene-lighting-issues/11887/8
So I had to recreate the environment in my scene as well.
After importing RoomEnvironment like so:
import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment';
I created the room environment:
const environment = new RoomEnvironment();
const pmremGenerator = new THREE.PMREMGenerator( renderer );
scene.environment = pmremGenerator.fromScene( environment ).texture;
Then I added the following attributes to my scene object:
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.2;
renderer.outputEncoding = THREE.sRGBEncoding;
After that, it lights up just as fine! I honestly don't really know what these toneMapping stuff do at the moment, but for now this solves my problem.

Related

THREEJS - How to traverse all layers of model loaded with GLTFLoader?

How does one traverse a mesh loaded with GLTFLoader properly to walk through all layers?
I am trying to do a simple selective bloom pass on a model by traversing the model’s all parts, setting them to the bloom layer, and then rendering the combined original + bloomed layers. However, as we can see in the images below, only the yellow outer part of the model is actually found during the traversal, does anyone know how to extract the rest of the model for layer setting?
For reproduction, the model can be downloaded from here:
https://github.com/whatsmycode/Models/blob/master/PrimaryIonDrive.glb
This is the code I currently use:
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
let BLOOM_LAYER = 1;
new GLTFLoader().load( 'models/PrimaryIonDrive.glb', function ( gltf ) {
const model = gltf.scene;
model.traverse( function( child ) {
child.layers.enable(BLOOM_LAYER);
});
scene.add( model );
});
This is the resulting image, bloom is applied to the yellow outer rings only.
This is the bloom-mask only
The issue was that I had not added the point- and ambient lights to both layers. The bloomed object has properties that requires light to show color for all parts except for the emitting yellow rings. To fix the problem, I simply enabled the lights for both layers before adding the lights to the scene.
const pointLight = new THREE.PointLight(0xffffff);
pointLight.layers.enable(ENTIRE_LAYER);
pointLight.layers.enable(BLOOM_LAYER);
const ambientLight = new THREE.AmbientLight(0xffffff);
ambientLight.layers.enable(ENTIRE_LAYER);
ambientLight.layers.enable(BLOOM_LAYER);
scene.add(pointLight, ambientLight);

javascript games ThreeJS and Box2D conflicts?

I've been trying to experiment with box2d and threejs.
So box2d has a series of js iterations, I've been successful at using them so far in projects as well as threejs in others, but I'm finding when including the latest instance of threejs and box2dweb, threejs seems to be mis-performing when just close to box2dweb but maybe I'm missing something really simple, like a better way to load them in together, or section them off from one another?
I've tried a few iterations of the box2d js code now and I always seemed to run into the same problem with later versions of threejs and box2d together! - currently version 91 threejs.
The problem I'm seeing is very weird.
I'm really hoping someone from either the box2d camp or threejs camp can help me out with this one, please?
Below is a very simple example where I don't initialize anything to do with box2d, but just by having the library included theres problems and you can test by removing that resource, then it behaves like it should.
The below demo uses threejs 91 and box2dweb. It is supposed to every couple of seconds create a box or a simple sphere each with a random colour. Very simple demo, you will see the mesh type never changes and the colour seems to propagate across all mesh instances. However if you remove the box2dweb resource from the left tab then it functions absolutely fine, very odd :/
jsfiddle link here
class Main {
constructor(){
this._container = null;
this._scene = null;
this._camera = null;
this._renderer = null;
console.log('| Main |');
this.init();
}
init(){
this.initScene();
this.addBox(0, 0, 0);
this.animate();
}
initScene() {
this._container = document.getElementById('viewport');
this._scene = new THREE.Scene();
this._camera = new THREE.PerspectiveCamera(75, 600 / 400, 0.1, 1000);
this._camera.position.z = 15;
this._camera.position.y = -100;
this._camera.lookAt(new THREE.Vector3());
this._renderer = new THREE.WebGLRenderer({antialias:true});
this._renderer.setPixelRatio( 1 );
this._renderer.setSize( 600, 400 );
this._renderer.setClearColor( 0x000000, 1 );
this._container.appendChild( this._renderer.domElement );
}
addBox(x,y,z) {
var boxGeom = new THREE.BoxGeometry(5,5,5);
var sphereGeom = new THREE.SphereGeometry(2, 5, 5);
var colour = parseInt(Math.random()*999999);
var boxMat = new THREE.MeshBasicMaterial({color:colour});
var rand = parseInt(Math.random()*2);
var mesh = null;
if(rand == 1) {
mesh = new THREE.Mesh(boxGeom, boxMat);
}
else {
mesh = new THREE.Mesh(sphereGeom, boxMat);
}
this._scene.add(mesh);
mesh.position.x = x;
mesh.position.y = y;
mesh.position.z = z;
}
animate() {
requestAnimationFrame( this.animate.bind(this) );
this._renderer.render( this._scene, this._camera );
}
}
var main = new Main();
window.onload = main.init;
//add either a box or a sphere with a random colour every now and again
setInterval(function() {
main.addBox(((Math.random()*100)-50), ((Math.random()*100)-50), ((Math.random()*100)-50));
}.bind(this), 4000);
so the way im including the library locally is just a simple...
<script src="js/vendor/box2dweb.js"></script>
So just by including the box2d library threejs starts to act weird, I have tested this across multiple computers too and multiple version of both box2d (mainly box2dweb) and threejs.
So with later versions of threejs it seems to have some comflicts with box2d.
I found from research that most of the box2d conversions to js are sort of marked as not safe for thread conflicts.
Im not sure if this could be the cause.
I also found examples where people have successfully used box2d with threejs but the threejs is always quite an old version, however you can see exactly the same problems occurring in my example, when I update them.
So below is a demo I found and I wish I could credit the author, but here is a copy of the fiddle using threejs 49
jsfiddle here
.....and then below just swapping the resource of threejs from 49 to 91
jsfiddle here
its quite an odd one and maybe the two libraries just don't play together anymore but would be great if someone can help or has a working example of them working together on latest threejs version.
I have tried a lot of different box2d versions but always found the same problem, could this be a problem with conflicting libraries or unsafe threads?
but also tried linking to the resource include in the fiddles provided.
Any help really appreciated!!

Make a scene a 3D texture in THREE.js (3D Render Targets)

I'm looking at the THREE.js example located here and wondering how to prevent the 'flattening' of scenes rendered as textures. In other words, the scene loses the illusion of having depth when set as a WebGLRenderTarget.
I have looked everywhere, including in THREE.js documentation, and have found no mention of this kind of functionality, probably because it would put a significant load on the user's processor unnecessarily (except for in very particular cases). Perhaps this is possible in pure WebGL, though?
EDIT: Downvoters - why is this question poor? I have done significant research into this matter, but since I'm new to WebGL, I can't exactly spout senseless code... How do I improve my query?
I think you want to use screenspace projections instead of UV projections if that makes sense. Given your tv example, the screen would have the UV points that do get transformed as you move the camera around. You want something that stays put, ie. no matter how much you move, you're looking at the same thing. I'm not sure how this is done without shaders, but in fragment shaders you have gl_FragCoord
Because THREE.js "flattens" every scene it renders, all that's needed is a change of perspective (relative to the main camera of the main scene) to maintain the illusion of depth in render targets. Here's a skeleton of something that would do that:
var scene = new THREE.Scene(),
rtScene = new THREE.Scene(),
camera = new THREE.PerspectiveCamera( ..... ),
rtCamera = new THREE.PerspectiveCamera( ..... ),
rtCube = new THREE.Mesh(new THREE.CubeGeometry(1,1,1), new THREE.MeshSimpleMaterial({ color: 0x0000ff }),
rtTexture = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBFormat }),
material = new THREE.MeshBasicMaterial({ map: rtTexture }),
cube = new THREE.Mesh(new THREE.CubeGeometry(1,1,1), material);
function init() {
//manipulate cameras
//add any textures, lighting, etc
rtScene.add( rtCube );
scene.add( cube );
}
function update() {
//some function of cube.rotation & cube.position
//that changes the rtCamera rotation & position,
//depending on the desired effect.
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
renderer.clear();
update();
renderer.render( rtScene, rtCamera, rtTexture, true );
renderer.render( scene, camera );
}
init();
animate();
I assumed in my code that camera would remain stationary while cube rotates around the y axis. Each face has its own updating instance of material. The update() function for each face is a bunch of trigonometric gibberish that can be derived easily with the law of cosines. I will post a jsFiddle example as soon as I have my local working properly.

Three.js skinned animation mesh disappears when material skinning is true

I've exported an animated model from Blender which doesn't seem to have any issue instantiating. I'm able to create the THREE.Animation and model, but I was finding there was no animation. I realized I needed to set skinning true on each material, but when I do that the entire mesh goes missing.
Below is my (quick and messy) code trying to get everything to work.
function loadModel() {
var loader = new THREE.JSONLoader();
loader.load('assets/models/Robot.js', function(geom, mat) {
_mesh = new THREE.Object3D();
_scene.add(_mesh);
geom.computeBoundingBox();
ensureLoop(geom.animation);
THREE.AnimationHandler.add(geom.animation);
for (var i = 0; i < mat.length; i++) {
var m = mat[i];
//m.skinning = true; <-- Uncommenting this makes the model disappear
//m.morphTargets = true; <-- This causes all sorts of WebGL warnings
m.wrapAround = true;
}
var mesh = new THREE.SkinnedMesh(geom, new THREE.MeshFaceMaterial(mat));
mesh.scale.set(400, 400, 400);
mesh.position.set(0, -200, 0);
mesh.rotation.set(Utils.toRadians(-90), 0, 0);
_mesh.add(mesh);
_robot = mesh;
Render.startRender(loop);
var animation = new THREE.Animation(mesh, geom.animation.name);
animation.JITCompile = false;
animation.interpolationType = THREE.AnimationHandler.LINEAR;
animation.play();
});
}
I believe I'm updating the AnimationHandler correctly in my loop
function loop() {
_mesh.rotation.y += 0.01;
var delta = 0.75 * _clock.getDelta();
THREE.AnimationHandler.update(delta);
}
In the section metadata of the exported JSON file the number of morphTargets and bones are both greater than 0?
I think that you followed the example here:
http://threejs.org/examples/#webgl_animation_skinning_morph
in which the animated model uses Morph Target and Skeletal Animation (see Wikipedia for the theoretical concepts).
If the animated model uses only Skeletal Animation as in this example http://alteredqualia.com/three/examples/webgl_animation_skinning_tf2.html
you have to instantiate a THREE.SkinnedMesh Object and then set only the m.skinning property to true.
I was having the same problem just now. What worked for me was to remake the model with applied scale and have keyframes for LocRotScale, not just location.
lately, I've encoutered a similar issue of mesh disapearing while exporting blender skinning animation to json. It turned out, the mesh I was using had double vertex (one vertice hidding another). All looks good While creating the vertex groups and the animations in blender, but when I imported the mesh via three.js, it kept disapearing as soon as the animation started. In other words, If 1 vertice from your mesh is omitted from the vertex groups, you will experience this disapearing behavior. To prevent this issue, I now use the "remove doubles" function from blender to validate the mesh integrity before exporting it to json. You might have encountered the same issue and redoing your mesh work fix it... Anyways, the question is pretty old, but the topic is still valid as of today, so I hope this fresh info will help someone out there...
Peace INF1

WebGL & Three.js - Render a scene successively in two renderers

I'm trying to render a scene in two different renderers (successively not at the same time) but it leads to the error "GL_INVALID_OPERATION".
Here is a sample script:
var scene1 = new THREE.Scene();
var camera1 = new THREE.PerspectiveCamera( ... );
var renderer1= new THREE.WebGLRenderer( ... );
var renderer2= new THREE.WebGLRenderer( ... );
var camera2 = new THREE.PerspectiveCamera( ... );
//Render scene1 in renderer1
renderer1.render( scene1, camera1 );
//[After some user event...]
//Render scene1 in renderer2
renderer2.render( scene1, camera2 ); //This fails. getError()=1282 (i.e. GL_INVALID_OPERATION)
I know it often deprecated to render a scene in two different renderers even not at the same time, but I could think of no other way of solving my issue as it is part of a very big project.
I understand there are GL data associated to scene1 that are linked to renderer1 but how can I remove those data so that I could render the scene1 again in an other renderer ???
Beware that I am not trying to render the scene in the two renderes simutaneously (which is different than https://github.com/mrdoob/three.js/issues/189).
Thanks for the help.
Regards.
The problem was related to objects/material/textures being bound to specific OpenGL buffers. The solution is thus to unbind from any buffer all the children of the object to be removed from a specific scene, before adding it to an other scene.
I'll post the code of my solution asap.
Regards.

Categories

Resources