ThreeJS texture reversed - three.js

I'm new to threejs just doing a basic cube with a texture to the backside. I have words on colour sides to the texture. However the words come out mirrored like. How can I get them to come out correctly.

You can negatively scale your cube to undo the mirror effect, like this:
cube.scale.x = -1;

There are two things you can do:
Reverse or rotate your UV coordinates on each face on the cube until you get the desired result. This is easy since the UV coordinates of a cube are usually 0.0 and 1.0.
Use an image package to rotate the textures as you want them.

I think I had the same problem as you with regards to texturing a cube.
As I understand it all surfaces come out correct orientation except the backside. The way i got around this was to place the textures on the cube per face and then alter the UV mapping of the back face.
This solved the problem of the back face being oriented incorrectly and also as a result of UV mapping I am now able to put textures on irregular faces to like pyramids etc.
Here is the solution by changing the UV of the backface. Just replace the loaded texture with a local texture cut and paste into notepad and save as html file and your good to go.
<html>
<head>
</head> <body> <script src="js/three.min.js"></script> <script> var
scene, camera, renderer; var geometry, material; var modarray=[];
var material=[]; var rotation=0; init(); animate(); function init()
{
renderer = new THREE.WebGLRenderer();
//renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize( window.innerWidth, 100 );
document.body.appendChild( renderer.domElement );
/////////// // Camera// ///////////
camera = new THREE.OrthographicCamera( window.innerWidth / - 2,
window.innerWidth / 2, 100 / 2, 100 / - 2, - 500, 1000 );
camera.position.z = 2000; camera.position.y = 0; camera.position.x = 0; scene= new THREE.Scene();
geometry = new THREE.BoxGeometry( 50, 50, 50 ); geometry2 = new THREE.BoxGeometry( 50, 50, 50 );
/////////////////////////////// // Store Materials for blocks//
/////////////////////////////// var bricks; material[0] = new
THREE.MeshPhongMaterial( { map:
THREE.ImageUtils.loadTexture('10.png') } );
var basex=-455; //////////////////////////////////////////////////
// Vector array to hold where UV will be placed //
////////////////////////////////////////////////// bricks = [new
THREE.Vector2(1, 0), new THREE.Vector2(1, 1), new
THREE.Vector2(0, 1), new THREE.Vector3(0, 0)];
///////////////////////////////////////////////////// // choose what
face this eccects from vertex array // // in this case backside
// // choose the orientation of the triangles //
/////////////////////////////////////////////////////
geometry.faceVertexUvs[0][10] = [ bricks[0], bricks[1], bricks[3]];
geometry.faceVertexUvs[0][11] = [ bricks[1], bricks[2], bricks[3]];
modarray[0] = new THREE.Mesh( geometry, material[0]); modarray[1] = new THREE.Mesh( geometry2, material[0]);
modarray[0].position.x=basex; modarray[0].position.z=1000;
modarray[0].position.y=0;
scene.add(modarray[0]);
modarray[1].position.x=basex+65; modarray[1].position.z=1000;
modarray[1].position.y=0;
scene.add(modarray[0]); scene.add(modarray[1]);
////////// // LIGHT// ////////// var light2 = new
THREE.AmbientLight(0xffffff); light2.position.set(0,100,2000);
scene.add(light2);
}
//////////////////// // Animation Loop // ///////////////////
function animate() {
requestAnimationFrame( animate ); var flag=0;
for(n=0; n<2; n++) {
modarray[n].rotation.x=rotation;
} rotation+=0.03;
renderer.render( scene, camera );
}
</script> <p>The cube on the left is with UV mapping to correct the
back surface.
The cube on the right is without the UV mapping.</p> </body>
</html>

Related

ThreeJS - Create cube where the surfaces are transparent instead of the cube volume

I am using the following code to create this 3D transparent cube.
// Create the cube itself
const cubeGeom = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( {color: 0x00ff00, opacity:0.4, transparent:true});
const cube = new THREE.Mesh( cubeGeom, material );
// Also add a wireframe to the cube to better see the depth
const _wireframe = new THREE.EdgesGeometry( cubeGeom ); // or WireframeGeometry( geometry )
const wireframe = new THREE.LineSegments( _wireframe);
// Rotate it a little for a better vantage point
cube.rotation.set(0.2, -0.2, -0.1)
wireframe.rotation.set(0.2, -0.2, -0.1)
// add to scene
scene.add( cube )
scene.add( wireframe );
As can been seen, the cube appears as a single volume that is transparent. Instead, I would want to create a hollow cube with 6 transparent faces. Think of a cube made out of 6 transparent and colored window-panes. See this example: my desired result would be example 1 for each of the 6 faces, but now it is like example 2.
Update
I tried to create individual 'window panes'. However the behavior is not as I would expect.
I create individual panes like so:
geometry = new THREE.PlaneGeometry( 1, 1 );
material = new THREE.MeshBasicMaterial( {color: 0x00ff00, side: THREE.DoubleSide, transparent:true, opacity:0.2});
planeX = new THREE.Mesh( geometry, material);
planeY = new THREE.Mesh( geometry, material);
planeZ = new THREE.Mesh( geometry, material);
And then I add all three planes to wireframe.
Then I rotate them a little, so they intersect at different orientations.
const RAD_TO_DEG = Math.PI * 2 / 360;
planeX.rotation.y = RAD_TO_DEG * 90
planeY.rotation.x = RAD_TO_DEG * 90
Now I can see the effect of 'stacking' the panes on top of each other, however it is not as it should be.
I would instead expect something like this based on real physics (made with terrible paint-skills). That is, the color depends on the number of overlapping panes.
EDIT
When transparent panes overlap from the viewing direciton, transparancy appears to work perfectly. However, when the panes intersect it breaks.
Here I have copied the snipped provided by #Anye and added one.rotation.y = Math.PI * 0.5 and commented out two.position.set(0.5, 0.5, 0.5); so that the panes intersect.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var cube = new THREE.Group();
one = new Pane();
two = new Pane();
one.rotation.y = Math.PI * 0.5
one.position.z = 0.2;
// two.position.set(0.5, 0.5, 0.5);
cube.add(one);
cube.add(two);
cube.rotation.set(Math.PI / 4, Math.PI / 4, Math.PI / 4);
scene.add(cube);
function Pane() {
let geometry = new THREE.PlaneGeometry(1, 1);
let material = new THREE.MeshBasicMaterial({color:0x00ff00, transparent: true, opacity: 0.4});
let mesh = new THREE.Mesh(geometry, material);
return mesh;
}
camera.position.z = 2;
var animate = function () {
requestAnimationFrame( animate );
renderer.render(scene, camera);
};
animate();
body {
margin: 0;
overflow: hidden;
}
canvas {
width: 640px;
height: 360px;
}
<html>
<head>
<title>Demo</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
</body>
</html>
EDIT
The snipped looks pretty good; it clearly shows a different color where the panes overlap. However, it does not show this everywhere. See this image. The left is what the snippet generates, the right is what it should look like. Only the portion of overlap that is in front of the intersection shows the discoloration, while the section behind the intersection should, but does not show discoloration.
You might want to take a look at CSG, Constructive Solid Geometry. With CSG, you can create a hole in your original cube using a boolean. To start, you could take a look at this quick tutorial. Below are some examples of what you can do with CSG.
var cube = new CSG.cube();
var sphere = CSG.sphere({radius: 1.3, stacks: 16});
var geometry = cube.subtract(sphere);
=>
CSG, though, has some limitations, since it isn't made specifically for three.js. A cheap alternative would be to create six individual translucent panes, and format them to create a cube. Then you could group them:
var group = new THREE.Group();
group.add(pane1);
group.add(pane2);
group.add(pane3);
group.add(pane4);
group.add(pane5);
group.add(pane6);
Update
Something may be wrong with your code, which is why it isn't shading accordingly for you. See this minimal example, which shows how the panes shade appropriately based on overlaps.
Update 2
I updated the snippet so the 2 panes aren't touching at all... I am still able to see the shading. Maybe if you were to try to reproduce this example?
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var cube = new THREE.Group();
one = new Pane();
two = new Pane();
one.rotation.y = Math.PI * 0.5;
one.position.z = 0.2;
cube.add(one);
cube.add(two);
cube.rotation.set(Math.PI / 4, Math.PI / 4, Math.PI / 4);
scene.add(cube);
function Pane() {
let geometry = new THREE.PlaneGeometry(1, 1);
let material = new THREE.MeshBasicMaterial({color:0x00ff00, transparent: true, opacity: 0.4});
material.depthWrite = false
let mesh = new THREE.Mesh(geometry, material);
return mesh;
}
camera.position.z = 2;
var animate = function () {
requestAnimationFrame( animate );
renderer.render(scene, camera);
};
animate();
body {
margin: 0;
overflow: hidden;
}
canvas {
width: 640px;
height: 360px;
}
<html>
<head>
<title>Demo</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
</body>
</html>
Update 3
Below is a screenshot of what I see in your snippet... Seems to be working fine...
You're experiencing one of my first head-scratchers:
ShaderMaterial transparency
As the answer to that question states, the three.js transparency system performs order-dependent transparency. Normally, it will take whichever object is closest to the camera (by mesh position), but because all of your planes are centered at the same point, there is no winner, so you get some strange transparency effects.
If you move the plane meshes out to form the actual sides of the box, then you should see the effect you're looking for. But that won't be the end of strange transparency effects, And you would need to implement your own Order-Independent Transparency (or find an extension library that does it for you) to achieve more physically-accurate transparency effects.

Soft shadow has an unintended offset

I'm currently working on a soft / blurred shadow effect that is casted on a plane directly under my object (just for giving it some more depth). The light source (DirectionalLight) shares the center coordinates of the object but with an offset in Y, so that it's straight above. It is pointing down to the center of the object.
I experimented a little bit with the shadow parameters of the light and found out that lowering the shadow map size gives me quite a nice soft shadow effect which would be sufficient for me. For example:
light.shadow.mapSize.width = 32;
light.shadow.mapSize.height = 32;
However, i noticed that there is an offset to the shadow which lets the observer assume that the light source is not coming directly from above:
I created this fiddle from which i created the image. As shadow type i use the PCFSoftShadowMap.
With this setup I would assume that the shadow effect is equally casted on all four sides of the cube, but it's obviously not. I also noticed that this 'offset' gets smaller when increasing the shadow map size and is barely noticable when using for example sizes like 512 or 1024.
This method would be an easy and performant solution for the desired effect, so I really appreciate any help on this
EDIT:
As stated out in the comments, tweaking the radius of the LightShadow isn't a satisfiying solution because the shadow gradient has hard edges instead of soft ones.
I think what is happening is that your shadowmap is low enough resolution, that you're seeing rounding error. If you switch back to THREE.BasicShadowMap, I think you will see that the physical lightmap pixels being hit happen to lie on the side of the object that you're seeing the larger edge, and as you move the object, the shadow will move in steps the size of the pixels on the map.
Generally in practice, you want to use a higher res lightmap, and keep its coverage area as tight around the focal point of your scene as possible to give you the most resolution from the lightmap. Then you can tweak the .radius of of the LightShadow to get the right softness.
One solution i came up with is using four light sources, all with a very slight positional offset, so that the 'shadow-offset' would come from four different directions (http://jsfiddle.net/683049eb/):
// a basic three.js scene
var container, renderer, scene, camera, controls, light, light2, light3, light4, cubeCenter, cube;
init();
animate();
function init() {
// renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xccccff);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
container = document.createElement('div');
document.body.appendChild(container);
container.appendChild(renderer.domElement);
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(0, 200, 800);
camera.lookAt(scene.position);
// (camera) controls
// mouse controls: left button to rotate,
// mouse wheel to zoom, right button to pan
controls = new THREE.OrbitControls(camera, renderer.domElement);
var size = 100;
// ambient light
var ambient = new THREE.AmbientLight(0xffffff, 0.333);
scene.add(ambient);
// mesh
var cubeGeometry = new THREE.BoxGeometry(size, size, size);
var cubeMaterial = new THREE.MeshLambertMaterial({
color: 0xff0000
});
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.y = size / 2.0;
cube.castShadow = true;
cube.receiveShadow = false;
scene.add(cube);
// Get bounding box center
var boundingBox = new THREE.Box3().setFromObject(cube);
cubeCenter = new THREE.Vector3();
boundingBox.getCenter(cubeCenter);
var position1 = new THREE.Vector3(0, size * 2, 0.0000001);
createDirectionalLight(scene, 0.15, position1, size, cubeCenter);
var position2 = new THREE.Vector3(0, size * 2, -0.0000001);
createDirectionalLight(scene, 0.15, position2, size, cubeCenter);
var position3 = new THREE.Vector3(0.0000001, size * 2, 0);
createDirectionalLight(scene, 0.15, position3, size, cubeCenter);
var position4 = new THREE.Vector3(-0.0000001, size * 2, 0);
createDirectionalLight(scene, 0.15, position4, size, cubeCenter);
// shadow plane
var planeGeometry = new THREE.PlaneGeometry(500, 500, 100, 100);
var planeMaterial = new THREE.MeshLambertMaterial({
// opacity: 0.6,
color: 0x65bf32,
side: THREE.FrontSide
});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -Math.PI / 2;
scene.add(plane);
// events
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize(event) {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
function createDirectionalLight(scene, intensity, position, cameraSize, targetPosition) {
var light = new THREE.DirectionalLight(0xffffff, intensity);
light.position.set(position.x, position.y, position.z);
light.target.position.set(targetPosition.x, targetPosition.y, targetPosition.z);
light.target.updateMatrixWorld(true);
light.castShadow = true;
scene.add(light);
light.shadow.mapSize.width = 32;
light.shadow.mapSize.height = 32;
light.shadow.camera.left = -cameraSize;
light.shadow.camera.right = cameraSize;
light.shadow.camera.bottom = -cameraSize;
light.shadow.camera.top = cameraSize;
light.shadow.camera.near = 1.0;
light.shadow.camera.far = cameraSize * 3;
light.shadow.bias = 0.0001;
scene.add(new THREE.CameraHelper(light.shadow.camera));
}
<script src="http://threejs.org/build/three.js"></script>
<script src="http://threejs.org/examples/js/controls/OrbitControls.js"></script>

RayCaster does not detect mesh created late with a cloned geometry

On the minimal example below (don't forget to adapt the URL of three.min.js) then open the html file in a window. You should see a (non-regular) tetrahedron. When moving the mouse over the canvas you should see the number of intersection of the ray from the camera to the mouse with all the objects of the scene object, tested with this line in the code:
raycaster.intersectObjects(scene.children,false);
Since apart from the ligths, there is only the tetrahedron, it says mostly 0 or 2 because it counts the number of faces that have been intersected by the infinite ray and because I have chosen a double sided material:
var material = new THREE.MeshLambertMaterial( { color: 0xd8f8b0, side: THREE.DoubleSide } );
Now click the checkbox. Another tetrahedron is created on the fly, its Geometry being a clone of the Geometry of the first Mesh.
geom2 = geom.clone();
I offset the new geom by adding 1 to all the coordinates of its vertices. However, the raycaster answers 0 for most rays intersecting the new object. Is there a bug or did I forget or misunderstand something?
If the geometry is not a clone (change clone=true; to clone=false; on the top of min.js) then it works.
Three.js version : r86
Minimal example
the html file:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="three.min.js"></script>
</head>
<body>
<div style="text-align: center;">
<canvas id="ze-canvas" width="800" height="600"></canvas>
<p>
<input type="checkbox" id="filBox"> click me
<p>
<span id="info-text"></span>
<p>
<script src="min.js"></script>
<script>visualiseur("ze-canvas","info-text","filBox");</script>
</div>
</body>
</html>
the file min.js
var visualiseur = function(canvas_name,info_name,box_name) {
var clone = true;
var canvas = document.getElementById(canvas_name);
var cbox = document.getElementById(box_name);
var textInfo = document.getElementById(info_name);
cbox.checked = false;
var camera = new THREE.PerspectiveCamera( 33, canvas.width / canvas.height, 0.1, 1000 );
camera.position.set(-2,4,8);
camera.lookAt(new THREE.Vector3(0,0,0));
var scene = new THREE.Scene();
scene.add( new THREE.AmbientLight( 0xffffff, .3) );
var light1 = new THREE.PointLight( 0xCCffff, .7, 0, 2 );
var light2 = new THREE.PointLight( 0xffffCC, .7, 0, 2 );
light1.position.set( 50, -50, 20 );
light2.position.set( -50, 150, 60 );
scene.add( light1 );
scene.add( light2 );
var material = new THREE.MeshLambertMaterial( { color: 0xd8f8b0, side: THREE.DoubleSide } );
var makeGeom = function(geom) {
geom.vertices.push(new THREE.Vector3(0,0,0));
geom.vertices.push(new THREE.Vector3(0,0,1));
geom.vertices.push(new THREE.Vector3(0,1,0));
geom.vertices.push(new THREE.Vector3(1,0,0));
geom.faces.push(new THREE.Face3(0,1,2));
geom.faces.push(new THREE.Face3(1,3,2));
geom.faces.push(new THREE.Face3(0,2,3));
geom.faces.push(new THREE.Face3(0,3,1));
geom.computeFlatVertexNormals();
}
var geom = new THREE.Geometry();
makeGeom(geom);
var mesh = new THREE.Mesh(geom,material);
scene.add(mesh);
var renderer = new THREE.WebGLRenderer({ canvas : canvas, antialias: true});
var render = function() {
renderer.render( scene, camera );
}
function getMousePos(evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
var raycaster = new THREE.Raycaster();
canvas.onmousemove = function(e) {
render();
var p=getMousePos(e);
p.x = p.x/canvas.width*2 - 1;
p.y = -p.y/canvas.height*2 + 1;
raycaster.setFromCamera( new THREE.Vector2(p.x,p.y), camera);
var intersects = raycaster.intersectObjects(scene.children,false);
textInfo.innerHTML=intersects.length+" intersections";
}
var done=false;
cbox.onclick = function(e) {
if(done) return;
done = true;
var geom2;
if(clone) {
geom2 = geom.clone();
}
else {
geom2 = new THREE.Geometry();
makeGeom(geom2);
}
geom2.vertices.forEach(function(v) {
v.x += 1;
v.y += 1;
v.z += 1;
});
geom2.verticesNeedUpdate=true;
geom2.computeFlatVertexNormals();
scene.add(new THREE.Mesh(geom2,material));
render();
}
render();
}
This was a tricky one but I found the solution.
In short: add
geom2.computeBoundingSphere();
just after changing the vertices.
Long version: Here's how I found the solution.
I started looking at the source code of Geometry.js and looking at every member function or variable in Geometry that I might have overlooked.
I finally noticed this bounding box and bounding sphere things, which I had never heard of in THREE.js before. Looking back at the Geometry section of the documentation of THREE.js, they are mentioned but without any explanation on what they are used for.
One would naturally think then they are used to accelerate the rendering on the graphics card by first computing the intersection of a ray with the box/sphere (this is fast I suppose) and if there is none we can skip the whole object.
This turns out to be a false assumption: on my minimal example, the second tetrahedron does show up even though its bounding sphere is wrong.
Then it got even more strange. I had the script log the bounding boxes and spheres of the geometries when I click the box, once just before the cloning and once just after the next rendering pass.
They never get a bounding box.
The first geometry has bounding sphere before and after.
The second has a bounding sphere before rendering only if clone = true (so no bounding sphere when created).
After rendering, both objects have a bounding sphere.
Conclusion : the bounding sphere is used by the Raycaster but not the rendered. (this is a surprise to me)
By inspecting the bounding sphere centers, I realized that the bounding sphere of the second geometry was wrong when it is cloned and the vertices moved, and is not updated by render().
When an object is created and render() called, then its bounding sphere is created and is correct. However, if you change the vertices, and even if you set the verticesNeedUpdate flag to true, the bounding sphere does not get updated, you have to call manually computeBoundingSphere().
It is all the more puzzling that the bounding sphere is secretly created when you call render() but not the bounding box.
Let me sum up what I understood of this all:
the bounding sphere is used by the Raycaster but not the renderer
I ignore if the bounding box is used by either (I have not spent time testing that)
if the bounding sphere does not exist, it will get created when calling render(). If it exists it is not updated by calling render() even when the flag verticesNeedUpdate is set to true.
the bounding box does not get created by render()
Is this the designed behaviour or is this a bug?

Object with a higher renderOrder being clipped by rotated element

A rotated object (cylinder in this case) cuts off objects (a triangle made by lines in this case) even though the renderOrder of the second object is higher. See this jsfiddle demo for the effect.
The triangle should be rendered completely on top of the cylinder but is cut off where the outside of the cylinder intersects with it. It's easier to understand what's happening when a texture is used, but jsfiddle is bad at using external images.
var mesh, renderer, scene, camera, controls;
init();
animate();
function init() {
renderer = new THREE.WebGLRenderer({
antialias: true,
preserveDrawingBuffer: true
});
renderer.setClearColor(0x24132E, 1);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 10000);
camera.position.set(0, 0, 7);
camera.lookAt(scene.position)
scene.add(camera);
var geometry = new THREE.CylinderGeometry(1, 1, 100, 32, 1, true);
var material = new THREE.MeshBasicMaterial({
color: 0x0000ff
});
material.side = THREE.DoubleSide;
mesh = new THREE.Mesh(geometry, material);
mesh.rotation.x = Math.PI / 2;
scene.add(mesh);
var c = 3, // Side length of the triangle
a = c / 2,
b = Math.sqrt(c * c - a * a),
yOffset = -b / 3; // The vertical offset (if 0, triangle is on x axis)
// Draw the red triangle
var geo = new THREE.Geometry();
geo.vertices.push(
new THREE.Vector3(0, b + yOffset, 0),
new THREE.Vector3(-a, 0 + yOffset, 0),
new THREE.Vector3(a, 0 + yOffset, 0),
new THREE.Vector3(0, b + yOffset, 0)
);
var lineMaterial = new THREE.LineBasicMaterial({
color: 0xff0000,
linewidth: 5,
linejoin: "miter"
});
plane = new THREE.Line(geo, lineMaterial);
// Place it on top of the cylinder
plane.renderOrder = 2; // This should override any clipping, right?
scene.add(plane);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
Am I doing something wrong or is this a bug?
for the effect that you want use a second scene and render it onto the first one
function init(){
.....
renderer.autoClear = false;
scene.add(tube);
overlayScene.add(triangle);
.....
}
function render() {
renderer.clear();
renderer.render(scene, camera);
renderer.clearDepth();
renderer.render(overlayScene, camera);
}
renderOrder does not mean what you think it means, look at the implementation in WebGLRenderer
objects are sorted by the order, if it meant what you anticipated from it, there would always be some fixed rendering order and colliding objects would be seen through each other, renderOrder is AFAIK used when you have issues with order of transparent/ not opaque objects
I worte a little plugin for three.js for flares for my game. Three.js built-in flares plugin is slow and I preferred not to run another rendering pass which was cutting framerate in half. Here's how I got flares visible on top of objects which were actually in front of them.
Material parameters:
{
side: THREE.FrontSide,
blending: THREE.AdditiveBlending,
transparent: true,
map: flareMap,
depthWrite: false,
polygonOffset: true,
polygonOffsetFactor: -200
}
depthWrite - set to false
polygonOffset - set to true
polygonOffsetFactor - give negative number to get object in front of others. Give it some really high value to be really on top of everything i.e. -10000
Ignore other params, they are needed for my flares

pick geometry from the scene and remove geometry and its wireframe together

I want to add and remove a geometry with its wireframe together. I can use raycaster to pick object from the scene but it is hard to pick a wireframe.
One way I can think of is creating a group of object and its wireframe, when the raycaster intersect with the object (e.g. obj.geometry.type == "BoxGeometry"), find its parent and delete the parent. However, the wireframe has to be some geometry that can be added as a child. I am using a Boxhelper to create a wireframe for a cube, which should be added to the scene directly, not to be added as a child to any object. What is a good way to solve this?
Thanks.
I am not sure if i understand what you are after exactly, but maybe ou can create a clone of your object, and render that one as a wireframe. The clone wireframe object can then be added as a child to the original object. So when the original object is picked, you can remove it from the scene, and then the wireframe object will be removed aswell.
Clone your object and change its material to wireframe:
var wireframe = cube.clone();
wireframe.material = new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true});
cube.add( wireframe ); // adding wireframe as child to the cube
When object is picked: check whether it is a cubegeometry (if you only want it to work with cubes) and check whether its material is wireframe (if you dont want to be able to remove the wireframe without removing the cube aswell)
if (pickedObject.geometry.type == "BoxGeometry" &&
!pickedObject.material["wireframe"]){
pickedObject.parent.remove(pickedObject); //this will remove object from
// scene if it has no parents
}
Working example:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
var wireframe = cube.clone();
wireframe.material = new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true});
cube.add( wireframe );
//picking stuff
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
function onMouseClick( event ) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
// 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 );
for ( var i = 0; i < intersects.length; i++ ) {
if (intersects[ i ].object.geometry.type == "BoxGeometry" &&
!intersects[ i ].object.material["wireframe"]){
intersects[ i ].object.parent.remove(intersects[ i ].object);
}
}
}
camera.position.z = 5;
var render = function () {
requestAnimationFrame( render );
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render(scene, camera);
};
window.addEventListener( 'mouseup', onMouseClick, false );
render();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"></script>
There is also a way of doing what you require with the Three.EdgesHelper instead of cloning, as exemplified in this fiddle. This shows the wireframes without the diagonals.
Even if you don't want to use the Three.EdgesHelper, I noticed that while implementing the clone solution above, it didn't completely show the wireframe, because it was slightly hidden.
To avoid this hiding I added the following code to the constructor of material, which offsets the original shape slightly so that the wireframe can be completely seen:
var material = new THREE.MeshLambertMaterial({ polygonOffset: true, polygonOffsetFactor: 1, polygonOffsetUnits: 1 })
I hope this is somehow useful.

Resources