three.js - intersecting planes with transparent texture - javascript

I'm trying to generate three swoosh elements that orbit a central atom. I have the swoosh as a .png so I thought I could create three canvases, stick on the pictures, and rotate them on the spot.
When I try to do this with the WebGL renderer, even before I start rotating them, there are weird invisible bits where the canvases overlap.
WebGL renderer
When I try the Canvas renderer the elements overlap correctly, but the WebGL elements obviously don't, and the elements come out strangely bent.
Any help would be appreciated.
EDIT: a jsfiddle
https://jsfiddle.net/gallantfox/Lyb2arxu/4/
The relevant functions are generateImg and rotateNode as follows:
function generateImg (xpos, ypos, zpos,xrot,yrot,zrot) {
/////// draw image on canvas /////////
// modified from
// http://stemkoski.github.io/Three.js/Texture-From-Canvas.html
// create a canvas element
// load an image
var imageObj = new Image();
imageObj.src = "data:image/png;base64,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";
var canvas2 = document.createElement('canvas');
canvas2.width = 512;
canvas2.height = 512;
var context2 = canvas2.getContext('2d');
// canvas contents will be used for a texture
var texture2 = new THREE.Texture(canvas2);
// after the image is loaded, this function executes
imageObj.onload = function()
{
context2.drawImage(imageObj, 0, 0);
if ( texture2 ) // checks if texture exists
texture2.needsUpdate = true;
};
var material2 = new THREE.MeshBasicMaterial( {map: texture2, side:THREE.DoubleSide} );
material2.transparent = true;
material2.opacity = 0.8;
var mesh2 = new THREE.Mesh(
new THREE.PlaneGeometry(canvas2.width, canvas2.height),
material2
);
mesh2.position.set(xpos, ypos, zpos);
mesh2.rotation.set(xrot,yrot,zrot);
mesh2.name = "swoosh";
scene.add( mesh2 );
}
function rotateNodes() {
for (i = scene.children.length - 1; i > 0; i--) {
if (scene.children[i].name == "swoosh") {
scene.children[i].rotation.z -= .05;
}
}
}
function animate() {
requestAnimationFrame( animate );
if (stats) {
stats.update();
}
if (world == 'HOME') {
rotateNodes();
}
render();
}

What you want intersecting planes with transparent materials and I think this is where you almost run into the limits of three.js / WebGL. You can read also read more on this here.
I cleaned up your fiddle a bit and there you can see how I managed to get it to work
I used alphaTest: 0.5 on the material with the texture.
var material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide,
alphaTest: 0.5,
transparent: true
});
This solution was suggested by #mrdoob here on GitHub and here on stackoverflow

Related

How do I render a 2d canvas on WebGL canvas?

I am using multiple canvas'es for animating (scaling / changing opacity) images on 2D canvas'es. Just read somewhere using webgl will increase overall FPS. I have new to WEBGL and found it quite different from 2d canvases. Can some guide me how can I render my existing 2d canvases on a main webgl canvas.
Here's a demo code, depicting a webgl canvas and virtual 2d canvas with an image drawn. How can I draw this 2d canvas with image drawn on a webgl canvas?
let webglCanvas = $('#webgl-canvas')[0];
let webglContext = backgroundCanvas.getContext('webgl');
/* setting up 2d canvas */
let virtualCanvas = document.createElement('canvas');
let virtualContext = virtualCanvas.getContext('2d');
/* setting height and width */
webglCanvas.height = 100;
webglCanvas.width = 150;
virtualCanvas.height = 100;
virtualCanvas.width = 150;
/* fetching image to draw on 2d context */
let image = new Image();
image.src = "http://ahiliahomes.saibbywebdemos.online/assets/images/brown-back.jpg";
image.onload = () => drawImageOn2D()
function drawImageon2D() {
virtualContext.save();
virtualContext.clearRect(0, 0, virtualCanvas.width, virtualCanvas.height, 0, 0);
virtualContext.drawImage(image, 0, 0, image.width, image.height, 0, 0, virtualCanvas.width, virtualCanvas.height);
virtualContext.restore();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="webgl-canvas"></canvas>
If you're not opposed to using a third party library (well, I strongly recommend it !), here's how I would do it with three.js (I'm using another image because I'm encountering CORS issues with yours).
The idea is basically to create a plane on the WebGL canvas and apply the image as a texture on it. The rest is boilerplate code to make the scene be rendered (aniamte) and visible (light):
Link to fiddle
const IMG_WIDTH = 390;
const IMG_HEIGHT = 260;
var scene = new THREE.Scene();
// these are the default PerspectiveCamera initialization settings
var camera = new THREE.PerspectiveCamera( 45, IMG_WIDTH / IMG_HEIGHT, 1, 1000 );
// you'll have to play with the camera z position to get closer
// or further to the image, so render it smaller or bigger
camera.position.z = 10;
var renderer = new THREE.WebGLRenderer();
renderer.setSize( IMG_WIDTH, IMG_HEIGHT );
// LIGHT
var light = new THREE.PointLight( 0xffffff, 1, 0 );
light.position.set(1, 1, 100 );
scene.add(light)
var loader = new THREE.TextureLoader();
// IMAGE
var planeMaterial = new THREE.MeshLambertMaterial({
map: loader.load("https://i.imgur.com/fHyEMsl.jpg")
});
var planeGeometry = new THREE.PlaneGeometry(10, 10 * (IMG_HEIGHT/IMG_WIDTH));
var mesh = new THREE.Mesh(planeGeometry, planeMaterial);
mesh.position.set(0, 0, 0);
scene.add(mesh);
document.body.appendChild( renderer.domElement );
// here is the (stereotyped) render loop
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>

stereocubes(12*1) is getting rendered as black screen using threejs + webVr

I am trying to render 12x1 stereocube image strip using Three.js and want it to view in VR. But instead black screen is getting rendered every time without any error in console. basically I am extracting 12 images from the single stereocube image file using the function get TexturesFromAtlasFile , forming 2 meshes and passing to the scene. I am using a cube geometry. I am new to webVr and ThreeJs, please help. Code snippet used is given below:-
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/93/three.js"></script>
<script src="https://threejs.org/examples/js/controls/VRControls.js"></script>
<script src="https://threejs.org/examples/js/effects/VREffect.js"></script>
<script>
//Setup three.js WebGL renderer
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// Append the canvas element created by the renderer to document body element.
document.body.appendChild(renderer.domElement);
// Create a three.js scene.
var scene = new THREE.Scene();
// Create a three.js camera.
var camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 1, 1000);
//camera.layers.enable( 1 );
// Apply VR headset positional data to camera.
var controls = new THREE.VRControls(camera);
// Apply VR stereo rendering to renderer.
var effect = new THREE.VREffect(renderer);
effect.setSize(window.innerWidth, window.innerHeight);
//create a sphere - we'll use the inner surface to project our Mars image on to it
var geometry = new THREE.CubeGeometry(100, 100, 100);
var textures = this.getTexturesFromAtlasFile("img/san_miguel_vr_Samsung.png", 12);
var materialArray = [];
for (var i = 0; i < 6; i++) {
materialArray.push(new THREE.MeshBasicMaterial({
map: textures[i]
}));
}
// create the mesh based on geometry and material
//var mesh = new THREE.Mesh(geometry, materialArray);
var skybox = new THREE.Mesh(geometry, materialArray);
//skybox.layers.set( 1 );
scene.add(skybox);
var materialsR = [];
for (var i = 6; i < 12; i++) {
materialsR.push(new THREE.MeshBasicMaterial({
map: textures[i]
}));
}
var skyBoxR = new THREE.Mesh(geometry, materialsR);
//skyBoxR.layers.set( 2 );
scene.add(skyBoxR);
// Create a VR manager helper to enter and exit VR mode.
var manager = new WebVRManager(renderer, effect, {
hideButton: false
});
function animate(timestamp) {
// Update VR headset position and apply to camera.
controls.update();
// Render the scene through the manager.
manager.render(scene, camera, timestamp);
requestAnimationFrame(animate);
}
// Kick off animation loop
animate();
function getTexturesFromAtlasFile(atlasImgUrl, tilesNum) {
var textures = [];
for (var i = 0; i < tilesNum; i++) {
textures[i] = new THREE.Texture();
}
var imageObj = new Image();
imageObj.onload = function() {
var canvas, context;
var tileWidth = imageObj.height;
for (var i = 0; i < textures.length; i++) {
canvas = document.createElement('canvas');
context = canvas.getContext('2d');
canvas.height = tileWidth;
canvas.width = tileWidth;
context.drawImage(imageObj, tileWidth * i, 0, tileWidth, tileWidth, 0, 0, tileWidth, tileWidth);
textures[i].image = canvas
textures[i].needsUpdate = true;
}
};
imageObj.src = atlasImgUrl;
return textures;
}
</script>

Change color of an svg dynamically and apply as texture in three.js

i´m trying to create a configurator with three.js but im at the point that i need to change the color of the product dinamycally, i though that i can use an svg as texture, change the fill property of the svg and update the texture easily, but im not having good results
at the moment what i have is
-A 3D model with an svg applied as texture with a TextureLoader, but the svg is placed in a external file ("images/texture.svg")(also three.js is reducing the size of the texture, but i think its a mapping problem)
-The svg is separated in layers, so i can manually change the color and apply it
But im trying to make it dynamic, you choose a color and it automatically updates the svg applied as texture
Any ideas?
Thanks!
I forked someone else's fiddle to show the solution. I added a click function which modifies the SVG and redraws the material on the model.
See http://jsfiddle.net/L4pepnj7/
Click anywhere on the example to change the color of the circle in the SVG.
The operation which converts the SVG to an encodedURI is to intensive to have in a constant loop, but you can put the color change in a click event while the renderer is doing it's own thing. I added a function called "clickBody" to the existing fiddle which changes the color of one of the SVG elements.
var mesh;
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(50, 500 / 400, 0.1, 1000);
camera.position.z = 10;
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(500, 400);
document.body.appendChild(renderer.domElement);
var svg = document.getElementById("svgContainer").querySelector("svg");
var svgData = (new XMLSerializer()).serializeToString(svg);
var canvas = document.createElement("canvas");
var svgSize = svg.getBoundingClientRect();
canvas.width = svgSize.width;
canvas.height = svgSize.height;
var ctx = canvas.getContext("2d");
var img = document.createElement("img");
var material;
img.setAttribute("src", "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(svgData))));
img.onload = function() {
ctx.drawImage(img, 0, 0);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var geometry = new THREE.SphereGeometry(3, 50, 50, 0, Math.PI * 2, 0, Math.PI * 2);
material = new THREE.MeshBasicMaterial({
map: texture
});
material.map.minFilter = THREE.LinearFilter;
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
};
var colors = ["red", "orange", "yellow", "green", "blue"];
var c = 0;
function clickBody() {
document.getElementById("test").setAttribute("fill", colors[c]);
var svgData = (new XMLSerializer()).serializeToString(svg);
img.setAttribute("src", "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(svgData))));
img.onload = function() {
ctx.drawImage(img, 0, 0);
var texture = new THREE.Texture(canvas);
material.map = texture;
material.map.needsUpdate = true;
renderer.render(scene, camera);
}
c = c + 1;
if (c == colors.length) {
c = 0;
}
}
document.body.addEventListener("click", clickBody)
var render = function() {
requestAnimationFrame(render);
mesh.rotation.y += 0.01;
renderer.render(scene, camera);
};
render();

Threejs apply clipping to specific area of the object

I'm using THREE.Plane to clip my STL model.
localPlane = new THREE.Plane( new THREE.Vector3( 0, -1, 0 ), 4);
.
.
.
material = new THREE.MeshPhongMaterial( {
color: 0xffffff,
side: THREE.DoubleSide,
clippingPlanes: [
localPlane,
],
clipShadows: true
} );
It's working; but the problem is that the whole object's top is clipped by this infinity sized plane. I want it to clip just a small part of it (It seems that there is no way to scale THREE.Plane)
I also tried using ThreeCSG.js but it seems inconvenient with STL objects!
Here is what I get:
Yes, the removal of the intersection of clipping planes is supported in three.js. You can use a pattern like this one:
// clipping planes
var localPlanes = [
new THREE.Plane( new THREE.Vector3( - 1, 0, 0 ), 1 ),
new THREE.Plane( new THREE.Vector3( 0, - 1, 0 ), 1 )
];
// material
var material = new THREE.MeshPhongMaterial( {
color: 0xffffff,
side: THREE.DoubleSide,
clippingPlanes: localPlanes,
clipIntersection: true
} );
Also, see the three.js example.
three.js r.85
Edit: Follow WestLangley's advice. I'll leave this her as an alternate though less efficient means of performing the clipping.
Clipping planes are infinite. There's no getting around that. So what can you do? Multiple clipping planes in multiple render passes!
To do this, you'll need to turn off auto-clearing, and do your own manual buffer clearing.
renderer = new THREE.WebGLRenderer();
renderer.autoClear = false;
Now let's say plane1 is the clipping plane you currently have.
material = new THREE.MeshPhongMaterial( {
...
clippingPlanes: [
plane1,
],
clipShadows: true
} );
var myMesh = new THREE.Mesh(geometry, material);
That clips the top half of myMesh when you call render. So you want to work with the remainder.
First, make another plane, plane2, be the inverse of plane1. plane2 will then clip the BOTTOM of myMesh. But if you render one pass using plane1, and another using plane2, then you're back with a full mesh. So you'll need a third clip plane, plane3, which clips only the desired half of myMesh. Putting plane2 and plane3 in the same render pass will result in only 1/4 of myMesh rendering.
var pass1ClipPlanes = [
plane1
],
pass2ClipLanes = [
plane2, // this plane is the inverse of plane 1, so it clips the opposite of plane1
plane3 // this clips the left/right half of the model
];
Then when you go to render, clear the draw buffers first, then call two render passes, updating the clip planes between them.
// clear the draw buffers
renderer.clear();
// clip the top
myMesh.material.clipPlanes = pass1ClipPlanes;
renderer.render(scene, camera);
// clip the bottom and one side
myMesh.material.clipPlanes = pass2ClipPlanes;
renderer.render(scene, camera);
The first pass renders the bottom of the model, and the second pass renders half of the top.
ETA: Example
var renderer, scene, camera, controls, stats;
var cube,
pass1ClipPlanes,
pass2ClipPlanes;
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
function init() {
document.body.style.backgroundColor = "slateGray";
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.localClippingEnabled = true;
renderer.autoClear = false;
document.body.appendChild(renderer.domElement);
document.body.style.overflow = "hidden";
document.body.style.margin = "0";
document.body.style.padding = "0";
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 50;
scene.add(camera);
controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.dynamicDampingFactor = 0.5;
controls.rotateSpeed = 3;
var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
resize();
window.onresize = resize;
// POPULATE EXAMPLE
var plane1 = new THREE.Plane(new THREE.Vector3(0, -1, 0), 0),
plane2 = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
plane3 = new THREE.Plane(new THREE.Vector3(-1, 0, 0), 0);
pass1ClipPlanes = [plane1];
pass2ClipPlanes = [plane2, plane3];
var cubeGeo = new THREE.BoxBufferGeometry(10, 10, 10),
cubeMat = new THREE.MeshPhongMaterial({
color: "red",
side: THREE.DoubleSide
});
cube = new THREE.Mesh(cubeGeo, cubeMat);
scene.add(cube);
animate();
}
function resize() {
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
if (renderer && camera && controls) {
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
controls.handleResize();
}
}
function render() {
renderer.clear();
cube.material.clippingPlanes = pass1ClipPlanes;
renderer.render(scene, camera);
cube.material.clippingPlanes = pass2ClipPlanes;
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame(animate);
render();
controls.update();
stats.update();
}
function threeReady() {
init();
}
(function() {
function addScript(url, callback) {
callback = callback || function() {};
var script = document.createElement("script");
script.addEventListener("load", callback);
script.setAttribute("src", url);
document.head.appendChild(script);
}
addScript("https://threejs.org/build/three.js", function() {
addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function() {
addScript("https://threejs.org/examples/js/libs/stats.min.js", function() {
threeReady();
})
})
})
})();

Weird behaviour of shadows in THREE.js

I am trying to render a set of photos, positioned in 3-dimensional space, which cast shadows on one another. I am starting with two rectangles, and here is my code
function loadScene() {
var WIDTH = 1200,
HEIGHT = 500,
VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.1,
FAR = 10000,
world = document.getElementById('world'),
renderer = new THREE.WebGLRenderer(),
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR),
scene = new THREE.Scene(),
texture = THREE.ImageUtils.loadTexture('image.jpg', {}, function() {
renderer.render(scene, camera);
}),
material = new THREE.MeshLambertMaterial({map: texture}),
solidMaterial = new THREE.MeshPhongMaterial({color: 0xCC0000}),
rectangle = new THREE.PlaneGeometry(200, 120),
mesh = new THREE.Mesh(rectangle, material),
mesh1 = new THREE.Mesh(rectangle, material),
spotLight = new THREE.SpotLight(0xFFFFFF, 1.3);
camera.position.set(0, 0, 200);
mesh.translateX(140);
mesh.translateZ(-70);
mesh.receiveShadow = true;
mesh1.castShadow = true;
spotLight.position.x = -100;
spotLight.position.y = 230;
spotLight.position.z = 230;
spotLight.castShadow = true;
scene.add(spotLight);
scene.add(mesh);
scene.add(mesh1);
renderer.setSize(WIDTH, HEIGHT);
renderer.shadowMapEnabled = true;
renderer.render(scene, camera);
world.appendChild(renderer.domElement);
}
Here solidMaterial is a solid red, and material is a textured material. What I get is the following:
If I use material for both the meshes, the rectangles appear as expected, but without shadows. Same if I use solidMaterial for both.
If I use material for mesh (the one farther away) and solidMaterial for mesh1, I see a red rectangle which casts shadow on a textured one. This is the only case that works as I would expect.
If I use solidMaterial for mesh (the one farther away) and material for mesh1, I see a red rectangle with the shadow on it, but the textured rectangle in front is not drawn at all.
What is the correct use of shadows?
It turns out the shadow does not appear when the two rectangles have the same material. I wonder whether this a bug in THREE.js.
On the other hand, if I use two different material, the shadow appears as expected, even if they have the same texture.

Categories