Make a renderer's background transparent but not shapes three.js - javascript

I'm using three.js where I'm showing a static background image and having a renderer animate shapes on top of it. The reason for doing this is to freely rotate the shapes' renderer without affecting the image. The problem is that the background sits behind the renderer so my question is, is it possible to make the renderer's "background" transparent (allowing the shapes to still show) in order to fully see the background image?
Things I've tried:
Setting opacity on renderer's domElement
Setting transparency for rgba with renderer's setClearColor method
Here's a simplified fiddle and you'll see that the square (thus renderer) is covering the background.
var square = new THREE.Shape();
square.moveTo(0, 0);
square.lineTo(0, length);
square.lineTo(length, length);
square.lineTo(length, 0);
square.lineTo(0, 0);
var geometry = new THREE.ShapeGeometry(square);
var material = new THREE.MeshBasicMaterial({
color: 'rgb(59, 89, 152)',
opacity: 1,
transparent: true
});
var mesh = new THREE.Mesh(geometry, material);
mesh.position.x = 50;
mesh.position.y = 50;
scene.add(mesh);
// and the camera
scene.add(camera);
renderer.render( scene, camera );

You need to set the webGLRenderer alpha parameter.
var renderer = new THREE.WebGLRenderer( { alpha: true } );
You can leave the clear color at the default value.
renderer.setClearColor( 0x000000, 0 ); // the default
Updated fiddles: http://jsfiddle.net/7McS2/3/ or http://jsfiddle.net/7McS2/4/
three.jr r.63

Related

SpotLight not working in ThreeJS

I have a ribbon that displays a few thumbnails. Just to give a background, the thumbnail images are painted on a canvas, which is then added to Texture.
var texture = new THREE.Texture(textureCanvas);
The mesh is created as follows
loader.load('mesh_blender.js', function(geom) {
var mesh = new THREE.Mesh(geom, new THREE.MeshLambertMaterial({
color: 0xffffffff,
opacity: 0.7,
overdraw: 0.21,
side: THREE.DoubleSide,
transparent: true,
//shading: THREE.SmoothShading,
map: texture
}));
Everything till here is fine. The ribbon is created as follows
I have no complaints with this. But I need this additional effect you see in the image below. As it can be seen, the thumbnail that is in the centre (focus) needs to have a darker effect to show it is being highlighted/selectable. All the remaining thumbnails have a transparent effect depicting they are not selectable.
I am trying to wrap my head around this using Lights in Threejs but not very successful. I thought of using an AmbientLight to throw light on the entire ribbon and then an additional SpotLight only on the centre image (with a darker color maybe) to achieve the desired effect. But that didn't work. I have got something like this
But the centre focused image has no effect. As you can see in the image, I have used a helper to show the Light direction but I can't really see any light on the image. This is the code I use to develop that SpotLight
var spotLight = new THREE.SpotLight( 0xffeedd );
spotLight.position.set( 0, -50, 50 );
spotLight.castShadow = true;
//spotLight.penumbra = 0.2;
spotLight.decay = 2;
spotLight.distance = 300;
spotLight.angle = 0.5;
var helper = new THREE.SpotLightHelper( spotLight, 2.5 );
scene.add(helper);
scene.add( spotLight );
I am very new to Threejs and 3d graphics. Any help will be appreciated. Thanks.I am open to any other suggestion as well, if Lights are not to be used to achieve the end result.
You have given the opacity of the material as 0.7 so adding another light will not exactly give you the expected result. I would suggest using a raycaster to identify the object in the center and making the opacity of that object as 1 and the rest as 0.7.
var intersects = raycaster.intersectObjects( objects );
use this to get the objects that are intersecting in an array. And in the renderer function set the opacity of the first element in the array that is the object in the middle to 1. This only works if the thumbnails are all separate objects.
//set as the center of you vision
var mouse = new THREE.Vector2();
mouse.x = 0;
mouse.y = 0;
function highlightObject() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
//sets the object in the center opacity = 0.2
if(intersects.length > 0) {
intersects[0].object.material.opacity = 0.2;
}
//sets the opacity of the rest of the objects in scene to opacity = 1.0
for (var i = scene.children.length - 1; i >= 0; i--) {
var obj = scene.children[i];
obj.material.opacity = 1.0;
}
}

Threejs Change Mesh Color doesn't get recognized

I have been trying to change the color of a mesh I created using PlaneGeometry and MeshBasic Material.
var planeSegments = 20,
plane = new THREE.Mesh(
new THREE.PlaneGeometry(horizon, horizon, planeSegments, planeSegments),
new THREE.MeshBasicMaterial({ color:0xFFFFFF })
);
plane.rotation.x = Math.PI / 2;
plane.position.y = 0;
planes.push(plane);
If I add this to MeshBasicMaterial object, it renders the wireframe with the current color. But I don't want the wireframe but a static color.
wireframe: true
Thank you.
Have a look at this fiddle:
http://jsfiddle.net/exiara/mrwdrwkn/
It seems your code is working but maybe you need to rotate your plane to -Math.PI/2 instead of just Math.PI/2 . Otherwise you maybe looking at the plane from the backside.

Keep light fixed when using THREE.ShaderTerrain in three.js

I have a sphere and light source (basically sun and earth). On the sphere, I'm using a greyscale heightmap for terrain texture so I am using three.js's ShaderTerrain.js. I'm also using a directional light source. My code:
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
lightCamera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
controls = new THREE.OrbitControls( camera, renderer.domElement );
lightControls = new THREE.TrackballControls(lightCamera, renderer.domElement);
light = new THREE.DirectionalLight(0xffffff, 1.5);
light.position.set(0,0,20);
lightCamera.position.z = 225;
lightCamera.add(light);
camera.add(lightCamera);
scene.add(camera);
var terrainShader = THREE.ShaderTerrain[ "terrain" ];
uniformsTerrain = THREE.UniformsUtils.clone(terrainShader.uniforms);
// displacement is heightmap (greyscale image)
uniformsTerrain[ "tDisplacement" ].value = THREE.ImageUtils.loadTexture('heightmap.jpg');
uniformsTerrain[ "uDisplacementScale" ].value = 15;
// diffuse is texture overlay
uniformsTerrain[ "tDiffuse1" ].value = THREE.ImageUtils.loadTexture('earth.jpg');
uniformsTerrain[ "enableDiffuse1" ].value = true;
var material = new THREE.ShaderMaterial({
uniforms: uniformsTerrain,
vertexShader: terrainShader.vertexShader,
fragmentShader: terrainShader.fragmentShader,
lights: true,
fog: false
});
var geometry = new THREE.SphereGeometry(100,100,100);
geometry.computeTangents();
scene.add(new THREE.Mesh(geometry, material));
With this code, the sphere is created just fine with the raised texture and everything.
The problem is that when I rotate the sphere, the light source doesn't appear to stay fixed. It rotates with the sphere and you end up with dark spots (rather than the light always coming from the front and keeping what the users sees illuminated).
If I create a simple sphere instead, like so:
geometry = new THREE.SphereGeometry(100,100,100);
material = new THREE.MeshLambertMaterial({color: 0x00ee00, wireframe: true, transparent: true, needsUpdate: true});
sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
Everything works perfectly. The light source stays fixed while the sphere/camera rotates.
I've also tried code that simply rotates the (first) sphere (the one using the ShaderMaterial) and not the camera (attaching a function to the mousemove event and simply doing a sphere.rotation.x/y with the mouse position). This doesn't work either; When the sphere rotates, there are still shadows that appear to the user.
Not sure what I'm missing here.
Here's a jsfiddle: http://jsfiddle.net/Z5sS5/1/. Left click/drag to spin everything (camera + light), right click/drag to spin only the light. To see it working, keep basicSphere() uncommented. To see it not working, comment basicSphere() and uncomment terrainSphere().

Does three.js renderer clone the objects positions?

I created a small scene with 3 spheres and a triangle connecting the 3 centers of the spheres, i.e. the triangle vertex positions are the same variables as the sphere positions.
Now I expected that if i change the position of one of the spheres, the triangle vertex should be moved together with it (since it's the same position object) and therefore still connect the three spheres.
However, if I do this coordinate change AFTER the renderer was called, the triangle is NOT changed. (Though it does change if I move the sphere BEFORE the renderer is called.)
This seems to indicate that the renderer doesnt use the original position objects but a clone of them.
Q: Is there a way to avoid this cloning behaviour (or whatever is the reason for the independent positions) so I can still change two objects with one variable change? Or am I doing something wrong?
The code:
var width = window.innerWidth;
var height = window.innerHeight;
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene;
var camera = new THREE.PerspectiveCamera(30, width / height, 0.1, 10000);
camera.position=new THREE.Vector3(50,50,50);
camera.lookAt(new THREE.Vector3(0,0,0));
scene.add(camera);
var pointLight = new THREE.PointLight(0xffffff);
pointLight.position=camera.position;
scene.add(pointLight);
var sphere=[];
var sphereGeometry = new THREE.SphereGeometry(1,8,8);
var sphereMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000 });
var triGeom = new THREE.Geometry();
for (var i=0; i<3; i++) {
sphere[i] = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere[i].position=new THREE.Vector3(10*i,20+5*(i-1)^2,0);
scene.add(sphere[i]);
triGeom.vertices.push(sphere[i].position);
}
triGeom.faces.push( new THREE.Face3( 0, 1, 2 ) );
triGeom.computeFaceNormals();
var tri= new THREE.Mesh( triGeom, new THREE.MeshLambertMaterial({side:THREE.DoubleSide, color: 0x00ff00}) );
scene.add(tri);
sphere[0].position.x+=10; // this changes both sphere and triangle vertex
renderer.render(scene, camera);
sphere[1].position.x+=10; // this changes only the sphere
renderer.render(scene, camera);
This is probably because of geometry caching feature. You will have to set triGeom.verticesNeedUpdate = true every time you change vertex position.

Unusual antialias while using basic texture material in three.js

In the following code I am setting up a very basic scene with an orthographic camera and a canvas mapped as a texture to a plane geometry.
I put some white text on to a transparent canvas, and if I use canvas.toDataURL(), the effect isn't present.
However, when I apply the canvas contents to a material as a texture and render it within a super-standard 2d scene, a black line outlines my text, probably the result of weird antialias stuff happening. In this example, the renderer clear color, material, and text are all pure white.
Here is a screenshot:
var camera = new THREE.OrthographicCamera(window.innerWidth / - 2,
window.innerWidth / 2,
window.innerHeight / 2,
window.innerHeight / - 2, 0, 10);
var scene = new THREE.Scene();
canvas = document.createElement('canvas');
canvas.width = 300;
canvas.height = 300;
var context = canvas.getContext('2d');
context.fillStyle = "white";
context.font = "bold 72px Arial";
context.fillText("Zibri", 50, 100);
var texture = new THREE.Texture(canvas);
var geometry = new THREE.PlaneGeometry(canvas.width, canvas.height);
texture.needsUpdate = true;
var material = new THREE.MeshBasicMaterial({ color: 0xffffff, map: texture, transparent: true });
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({ antialias: false });
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0xffffff)
document.body.appendChild( renderer.domElement );
camera.lookAt(scene.position);
renderer.render(scene, camera);
I've also been struggling with this problem, WestLangley's solution came part way to fixing the issue but left the rendered text with very poorly antialiased edges. After extensive research, I've come up with a solution that I'm happy with which I've outline here.
Before drawing the text, fill the canvas with the same colour as the text but with alpha set to 0.01
context.fillStyle = 'rgba(255,255,255,0.01)';
context.fillRect(0,0,canvas.width,canvas.height);
and then use the material's alphaTest property to discard pixels of this opacity:
var material = new THREE.MeshBasicMaterial({
color: 0xffffff,
map: texture,
transparent: true,
alphaTest:0.01
});
finally, set the texture map's premultiplyAlpha value to false:
texture.map.premultiplyAlpha = false;
texture.map.needsUpdate = true;

Categories

Resources