In three.js how to make light on sphere sharp? - javascript

I have simple three.js scene with sphere and directional light. On the sphere you can see that it get gradually darker. How to make the transition from light to dark faster. How to make light "sharper"?
var scene, camera, renderer, controls, sphere, geometry, material, light;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.x = 3;
camera.position.z = 6;
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
document.body.appendChild(renderer.domElement);
geometry = new THREE.SphereGeometry(1, 32, 32);
material = new THREE.MeshPhongMaterial({
color: 0x777777
});
sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 10, 10);
scene.add(light);
renderer.render(scene, camera);
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.js"></script>

One way to achieve the intended affect is via toon shading. Hence, give MeshToonMaterial a try. You can also defined a gradientMap that defines the distinct lit areas of the material.
var scene, camera, renderer, controls, sphere, geometry, material, light;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.x = 3;
camera.position.z = 6;
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
document.body.appendChild(renderer.domElement);
geometry = new THREE.SphereGeometry(1, 32, 32);
material = new THREE.MeshToonMaterial({
color: 0x777777
});
sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 10, 10);
scene.add(light);
renderer.render(scene, camera);
<script src="https://cdn.jsdelivr.net/npm/three#0.115/build/three.js"></script>

You can set material.shininess. See this
var scene, camera, renderer, controls, sphere, geometry, material, light;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.x = 1;
camera.position.z = 2;
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
document.body.appendChild(renderer.domElement);
geometry = new THREE.SphereGeometry(1, 32, 32);
material = new THREE.MeshPhongMaterial({
color: 0x777777,
shininess: 400,
});
sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 10, 10);
scene.add(light);
renderer.render(scene, camera);
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.js"></script>
Even with a MeshToonMaterial you need to set shininess to 0 if you want no specular highlights
var scene, camera, renderer, controls, sphere, geometry, material, light;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.x = 1;
camera.position.z = 2;
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
document.body.appendChild(renderer.domElement);
geometry = new THREE.SphereGeometry(1, 32, 32);
material = new THREE.MeshToonMaterial({
color: 0x777777,
shininess: 0,
});
sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 10, 10);
scene.add(light);
renderer.render(scene, camera);
<script src="https://cdn.jsdelivr.net/npm/three#0.115/build/three.js"></script>
further, the relative amount of light is hard coded unless you use a gradientMap
var scene, camera, renderer, controls, sphere, geometry, material, light;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.x = 0;
camera.position.z = 2;
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
document.body.appendChild(renderer.domElement);
geometry = new THREE.SphereGeometry(1, 32, 32);
material = new THREE.MeshToonMaterial({
color: 'red',
shininess: 0,
gradientMap: new THREE.DataTexture(
new Uint8Array([
0, // black (color away from light multiplied by color of material)
255, // white (color toward light multiplied by material
]),
2, // width of texture
1, // height of texture,
THREE.LuminanceFormat, // format of texture
),
});
sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(50, 50, 30);
scene.add(light);
renderer.render(scene, camera);
<script src="https://cdn.jsdelivr.net/npm/three#0.115/build/three.js"></script>
Note the gradientMap above is a 2x1 pixel texture so the cutoff will be 50% between facing the light and not facing the light. To move the cutoff make the gradientMap bigger. For example a 5x1 pixel texture with black, black, white, white, white will be 40% black, 60% white
var scene, camera, renderer, controls, sphere, geometry, material, light;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.x = 0;
camera.position.z = 2;
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
document.body.appendChild(renderer.domElement);
geometry = new THREE.SphereGeometry(1, 32, 32);
material = new THREE.MeshToonMaterial({
color: 'red',
shininess: 0,
gradientMap: new THREE.DataTexture(
new Uint8Array([
0, // black (color away from light multiplied by color of material)
0,
255, // white (color toward light multiplied by material
255,
255,
]),
5, // width of texture
1, // height of texture,
THREE.LuminanceFormat, // format of texture
),
});
sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(50, 50, 30);
scene.add(light);
renderer.render(scene, camera);
<script src="https://cdn.jsdelivr.net/npm/three#0.115/build/three.js"></script>
If you want to set the 2 colors explicitly then make the gradientMap have the colors and set the material color to white
var scene, camera, renderer, controls, sphere, geometry, material, light;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.x = 0;
camera.position.z = 2;
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
document.body.appendChild(renderer.domElement);
geometry = new THREE.SphereGeometry(1, 32, 32);
material = new THREE.MeshToonMaterial({
color: 'white',
shininess: 0,
gradientMap: new THREE.DataTexture(
new Uint8Array([
255, 0, 255, // purple (color away from light multiplied by color of material)
0, 255, 255, // cyan (color toward light multiplied by material
]),
2, // width of texture
1, // height of texture,
THREE.RGBFormat, // format of texture
),
});
sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(50, 50, 30);
scene.add(light);
renderer.render(scene, camera);
<script src="https://cdn.jsdelivr.net/npm/three#0.115/build/three.js"></script>
If you want more than 2 colors/divisions then put more than 2 colors in the gradientMap

Related

Problem rendering first 2 objects in three.js

everything works fine but sphere and box geometry dont show, plane works fine too! does anyone have a clue? i suspect something wrong maybe in the render function or animation frame, im fairly new to three.js and cant seem to know why the scene wont show the other geometry, i tried different materials, rotation, clear color, etc
var renderer,
scene,
camera,
myCanvas = document.getElementById('myCanvas');
//RENDERER
renderer = new THREE.WebGLRenderer({
canvas: myCanvas,
antialias: true,
});
renderer.setClearColor(0x000000);
renderer.setPixelRatio(window.devicePixelRatio); //device pixel ratio
renderer.setSize(window.innerWidth, window.innerHeight); //canvas size
//CAMERA
var camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 300, 10000)
//SCENE
var scene = new THREE.Scene();
//LIGHT
var light = new THREE.AmbientLight(0xffff, 0.5)
scene.add(light);
var light1 = new THREE.PointLight(0xffff, 0.5)
scene.add(light1);
//MATERIAL
var material = new THREE.MeshLambertMaterial({
color: 0xF3FFE2,
lightMap: null,
lightMapIntensity: 1,
emissive: 0x000000,
emissiveMap: null,
emissiveIntensity: 1,
specularMap: null
});
//GEOMETRY
var geometry = new THREE.BoxGeometry(100, 100, 100);
var mesh = new THREE.Points(geometry, material);
mesh.position.z = -1000;
mesh.position.x = -100;
scene.add(mesh);
var geometry2 = new THREE.SphereGeometry(50, 20, 20);
var mesh2 = new THREE.Points(geometry2, material);
mesh2.position.z = -1000;
mesh2.position.x = 100;
scene.add(mesh2);
var geometry3 =  new THREE.PlaneGeometry(10000, 10000, 100, 100);
var mesh3 = new THREE.Mesh(geometry3, material);
mesh3.rotation.x = -90 * Math.PI / 180;
mesh3.position.y = -100;
scene.add(mesh3);
//RENDER LOOP
render();
function render() {
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.01;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
Tried your code, everything is ok but the 2 objects need to be THREE.Mesh
var geometry = new THREE.BoxGeometry(100, 100, 100);
var mesh = new THREE.Mesh(geometry, material);
mesh.position.z = -1000;
mesh.position.x = -100;
scene.add(mesh);
var geometry2 = new THREE.SphereGeometry(50, 20, 20);
var mesh2 = new THREE.Mesh(geometry2, material);
mesh2.position.z = -1000;
mesh2.position.x = 100;
scene.add(mesh2);

Flat shading with phong material with spot light

I am trying to achieve flat shading with the phong material. As the documentation says we have to set flatShading:true in config when creating the material. I have created a cube with these settings and created a spot light to focus at the center of the cube (not touching the vertices of the cube).
I assume the face of the cube should look same at all pixels but its not looking so. I see a reflection of the light at the center of the face. Indeed, changing the flatShading from true to false, is not affecting the light.
Here is the JS fiddle link to what i tried. https://jsfiddle.net/dj03gktb/.
Below is the code.
var camera, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 4;
scene.add(camera);
scene = new THREE.Scene();
var ambientLight = new THREE.AmbientLight(0xcccccc, 0.2);
scene.add(ambientLight);
var pointLight = new THREE.SpotLight({ color: 0xffffff, angle: Math.PI / 10, intensity: 2 });
scene.add(pointLight);
//pointLight.position.x = 5;
pointLight.position.z = 1.5;
pointLight.position.y = 0;
var geometry = new THREE.BoxGeometry(1, 1, 1);
//geometry.computeFlatVertexNormals();
//geometry.rotateY(0.5);
var material = new THREE.MeshPhongMaterial({ color: 0xff0000, flatShading: true, shininess: 100, specular: 0x00ff00 });
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
May be i have under stood flat shading wrongly or could be bug with the THREE.js or problem with the render engine.
Any help would be great.
Unfortunately, using a cube to demonstrate flat shading is a bad example since you won't see any difference. Try it with a sphere instead:
var camera, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 4;
scene = new THREE.Scene();
var ambientLight = new THREE.AmbientLight( 0xcccccc, 0.2 );
scene.add( ambientLight );
var spotLight = new THREE.SpotLight({ color: 0xffffff, angle: Math.PI / 10, intensity: 2});
spotLight.position.z = 1.5;
scene.add(spotLight);
var geometry = new THREE.SphereBufferGeometry( 1, 12, 16);
var material = new THREE.MeshPhongMaterial({color: 0xff0000, flatShading: true, shininess: 100, specular: 0x00ff00});
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
body {
margin: 0;
}
canvas {
display: block;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.118.3/build/three.js"></script>
I assume the face of the cube should look same at all pixels but its not looking so.
This is not what flat shading does. Normally vertex normals are interpolated in the fragment shader which produces the typical smooth rendering. Flat shading just ensures that the normals of a face are equal across its (flat) surface. So there is no interpolation of vertex normals. Apart from that, lighting calculations are not affected. Hence, you also see specular highlights.

Three.js: draw a octahedron

I want to draw a Octahedron,but the shape of the result is below expectation.I dont know the reason and next step I want to draw a Dodecahedron, any suggestions?The result of the Octahedron
My code:
var mesh, renderer, scene, camera, controls;
init();
animate();
function init() {
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera(40, window.innerWidth /
window.innerHeight, 1, 10000);
camera.position.set(20, 20, 20);
// controls
controls = new THREE.OrbitControls(camera);
// ambient
scene.add(new THREE.AmbientLight(0x222222));
// light
var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(20, 20, 0);
scene.add(light);
// axes
scene.add(new THREE.AxisHelper(20));
// geometry
//var geometry = new THREE.SphereGeometry(5, 12, 8);
var vertices = [
0.5,0,0, 0,0.3,0, -0.5,0,0, 0,-0.3,0, 0,0,0.3, 0,0,-0.3
];
var faces = [
0,1,4, 1,2,4, 2,3,4, 3,0,4, 0,1,5, 1,2,5, 2,3,5, 3,0,5
];
var geometry = new THREE.PolyhedronGeometry(vertices, faces, 5, 1);
// material
var material = new THREE.MeshPhongMaterial({
color: 0x00ffff,
shading: THREE.FlatShading,
wireframe:true,
transparent: true,
opacity: 0.7,
});
// mesh
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
}
function animate() {
requestAnimationFrame(animate);
//controls.update();
renderer.render(scene, camera);
}
Use
var geometry = new THREE.OctahedronGeometry( 5, 0 ); // or DodecahedronGeometry
Read and understand the source code of OctahedronGeometry.js if you want to use PolyhedronGeometry directly.
If wireframe is true, use MeshBasicMaterial, instead.
three.js r.85

Can't animate wireframe rotation in Three.js

I tried to make the wireframe rotate in the scene.
The animation works when I removed the BoxHelper, but I want to animate the cube wireframe without diagonal line instead of a solid object.
Codepen demo :
Demo
Code :
var w = window.innerWidth, h = window.innerHeight,
scene = new THREE.Scene(),
camera = new THREE.PerspectiveCamera(75, w/h, 0.1, 1000),
renderer = new THREE.WebGLRenderer(),
geometry = new THREE.BoxGeometry( 1, 1, 1 ),
material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ),
mesh = new THREE.Mesh( geometry, material ),
cube = new THREE.BoxHelper(mesh);
cube.material.color.setRGB(25,25,25);
scene.add(cube);
camera.position.z = 2;
renderer.setSize(w,h);
document.body.appendChild(renderer.domElement);
function render(){
requestAnimationFrame( render );
cube.rotation.x += 1;
renderer.render(scene, camera );
}
render();
The position of your THREE.BoxHelper instance is tied to the position of the THREE.Mesh. For your code to work you will have to add the mesh to the scene and rotate the mesh. Your box helper will follow.
If you don't want to show the mesh you can simply set mesh.visible = false;
This code works:
var w = window.innerWidth, h = window.innerHeight,
scene = new THREE.Scene(),
camera = new THREE.PerspectiveCamera(75, w/h, 0.1, 1000),
renderer = new THREE.WebGLRenderer(),
geometry = new THREE.BoxGeometry( 1, 1, 1 ),
mesh = new THREE.Mesh( geometry );
camera.position.z = 2;
renderer.setSize(w,h);
document.body.appendChild(renderer.domElement);
mesh.visible = false; //<-- hide mesh
scene.add(mesh); //<-- add mesh to scene
cube = new THREE.BoxHelper(mesh);
cube.material.color.setRGB(25,25,25);
scene.add(cube);
function render(){
mesh.rotation.y += 0.01; //<-- rotate the mesh
requestAnimationFrame( render );
renderer.render( scene, camera );
}
render();

Issue with three.js and shadows

I am trying to make an opaque box with three.js, but the light is still going thought the box:
I have one "THREE.SpotLight" and 2 objects in my scene: the sphere is projecting the shadow onto the box.
There are 2 shadows (I think there should be only one):
the first, at the top of the box seems correct,
the second is weird (as if the box where transparent).
Illustration here: http://i.stack.imgur.com/ZNmI3.png
var scene, camera, renderer;
var material;
init();
function init() {
//Renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.shadowMapEnabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
scene = new THREE.Scene();
//Camera
camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(0, 20, 20);
camera.lookAt(new THREE.Vector3(0, 15, 0));
//Material
material = new THREE.MeshLambertMaterial({
color: 0xffffff
});
//Box
var box = new THREE.Mesh(new THREE.BoxGeometry(7, 16, 7), material);
box.position.y = 8;
box.castShadow = true;
box.receiveShadow = true;
scene.add(box);
//Sphere
geometry = new THREE.SphereGeometry(.8, 48, 48);
var sphere = new THREE.Mesh(geometry, material);
sphere.position.set(-5, 17, -3);
sphere.castShadow = true;
sphere.receiveShadow = true;
scene.add(sphere);
// Lights
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-50, 40, -50);
spotLight.target.position.set(-5, 17, -3);
spotLight.castShadow = true;
spotLight.shadowMapWidth = 2048;
spotLight.shadowMapHeight = 2048;
spotLight.shadowCameraVisible = true;
scene.add(spotLight);
var ambientLight = new THREE.AmbientLight(0x444444);
scene.add(ambientLight);
document.body.appendChild(renderer.domElement);
}
renderer.render(scene, camera);
My question is:
How can i make the box opaque?
Jsfiddle here: http://jsfiddle.net/v81gs6u1/4/
It is not the light that is going through the box but the shadow. Try playing with the shadow bias.
In your case adding:
spotLight.shadowBias = 0.001;
takes care of the artifact.

Categories

Resources