How to declare a "mask" material using A-Frame.js - three.js

I'm trying to make a scene where there's a "hole in the wall".
This requires a plane with square removed, and then a material applied to the plane with the following properties:
Invisible to the camera
Hides any other objects from being rendered that are behind it
There's an example of this with three.js here, but how can I do it with the a-frame material syntax?

The "Mask".
Looking at the box-hole example, to create the illusion, Lee creates two boxes.
1) The box which is "in the hole"
2) A slightly bigger invisible box without a top - to cloak the first one. The top is removed to work as a "hole" through which you can see the first box
How it can be done in THREE.js
The cloaking is done by preventing the second box from rendering any color. From Lee's example:
let material = new THREE.MeshBasicMaterial({
colorWrite: false;
})
The docs state, that the flag can be used to create invisible objects hiding others.
How it can be done in a-frame
I'm afraid you can't simply make the "cloak" material in a-frame. The colorWrite property is not exposed in the material component.
What i think the simplest way would be - is creating a cloak component, which will create the second box in THREE.js:
AFRAME.registerComponent('cloak', {
init: function() {
let geometry = new THREE.BoxGeometry(1, 1, 1)
geometry.faces.splice(4, 2) // cut out the top faces
let material = new THREE.MeshBasicMaterial({
colorWrite: false
})
let mesh = new THREE.Mesh(geometry, material)
mesh.scale.set(1.1, 1.1, 1.1)
this.el.object3D.add(mesh)
}
})
and use it like this:
<a-box material="src: myPic.png; side: back;" cloak>
Check it out in this codepen. With a HIRO marker, you should get a hole like this:
Using models, or other objects as "cloaks"
Here we need to apply the colorWrite=false magic to each node/child of the model.
init: function() {
// make sure the model is loaded first
this.el.addEventListener('model-loaded', e=>{
let mesh = this.el.getObject3D('mesh') // grab the mesh
if (mesh === undefined) return; // return if no mesh :(
mesh.traverse(function(node) { // traverse through and apply settings
if (node.isMesh && node.material) { // make sure the element can be a cloak
node.material.colorWrite = false
node.material.needsUpdate = true;
}
});
})
}
Also make sure the cloak is rendered before the elements that needs cloaking:
<a-marker>
<a-entity gltf-model="#wall-with-a-hole" cloak-component></a-entity>
<!-- the other stuff that needs to be cloaked-->
</a-marker

Related

A-frame: How to give a primitive like Ring or Cylinder depth?

Is it possible to give a 2D primitive like Ring thickness/depth? I'm trying to make a 3D door with primitives and I want to make either a ring that has some depth or a cylinder with a thicker mesh. Even if it's at the three.js layer I would like to learn how to do this so I don't have to rely on imported 3D objects for the whole design.
To extrude a shape, you will need to use THREE.js. Below is an example for how to do this.
https://threejs.org/examples/?q=extrude#webgl_geometry_extrude_shapes2
How to use THREE.js geometery inside AFrame?
You make a custom component that creates a new THREE.Geometry, and inside that component, you build your shape and extrude it (see the THREE.js example, click (<>) button in lower right to see the code).
Below is an example that makes a custom quad in a component.
You can see the glitch here.
https://glitch.com/edit/#!/custom-quad
Look in the quad.js for details.
You should be able to copy code from the three.js extrude demo, and place it into your custom component init function to build the extruded shape.
Then you can create parameters, like 'thickness', and put them in the schema, and then they can be accessed from the component name on the entity.
If you don't yet know how to write a custom component, you will need to practice this to understand how it works.
https://aframe.io/docs/0.9.0/introduction/writing-a-component.html
Here is a snippet from my glitch that makes a custom quad. It shows how to make THREE.Geometry in an AFrame custom component.
init: function (data) {
var geometry = new THREE.Geometry();
geometry.vertices = data.vertices.map(function (vertex) {
var points = vertex.split(' ').map(function(x){return parseFloat(x);});
return new THREE.Vector3(points[0], points[1], points[2], points[3]);
});
// Build the UVs on the faces.
geometry.faceVertexUvs[0].push(
[ new THREE.Vector2(1,0), new THREE.Vector2(0,1), new THREE.Vector2(1,1)
],
[ new THREE.Vector2(1,0), new THREE.Vector2(0,0), new THREE.Vector2(0,1)
]);
geometry.computeBoundingBox();
geometry.faces.push(new THREE.Face3(0, 2, 1), new THREE.Face3(0, 3, 2));
geometry.mergeVertices();
geometry.computeFaceNormals();
geometry.computeVertexNormals();
this.geometry = geometry;
//console.log(data);
}

Emit light from an object

I'm making a Three.js scene in which I have stars object and I would like to be able to make them "glow".
By glow I mean make them really emit light not just put a "halo" effect around them.
I tried to put a PointLight object at the same position as the star, this make light emit from the object but as you can see, it doesn't make the object "glow" which make a weird effect.
My current code looks like this:
class Marker extends THREE.Object3D {
constructor() {
super();
// load obj model
const loader = new OBJLoader();
loader.load(
"https://supersecretdomain.com/star.obj",
object => {
object.traverse(child => {
if (child instanceof THREE.Mesh) {
// child.material.map = texture;
child.material = new THREE.MeshLambertMaterial({
color: 0xffff00
});
child.scale.set(0.01, 0.01, 0.01);
}
});
this.add(object);
}
);
const light = new THREE.PointLight(0xffff00, 0.5, 5);
this.add(light);
}
}
Any idea of how to do this ? ;)
Adding a point light to the star is the correct way to make other objects be affected by its light. To make the star itself shine, you can set the emissive color of the material to something other than black (for best results, you probably want it to be the same color as the light).
In addition to setting up your light, and setting the material emissive property as mentioned by Jave above..
You might want to look into the THREE PostProcessing examples.. Specifically Unreal Bloom pass... If you can get that working, it really sells the effect.
https://threejs.org/examples/webgl_postprocessing_unreal_bloom.html
Notice how the highlight glow actually bleeds into the scene around the object...

Three.js FBXLoader2, change color of single Mesh (in Group or detached)

I have a .fbx model which load via FBXLoader2.
When I add the model (which include 3 meshes) to the scene, it is added as a Group. Now I try to change the color of a single Mesh in the Group, but all 3 meshes are getting the color. Then i thought they might be "linked" because of the Group. So i detached them (SceneUtils.detach).
Now I have all the meshes in the scene. Still, when I change the color of a single mesh, all three of them get the color. When I console.log the mesh (in the group or detached) it shows me the correct mesh.
var loader = new THREE.FBXLoader( manager );
loader.load( 'somemodel.fbx', function( object ) {
model = object;
var modelLength = model.children.length;
for (i=0;i<modelLength;i++) {
THREE.SceneUtils.detach(model.children[0], model, scene);
}
scene.children[0].material.emissive.setHex( 0xff0000 );
}
When i try to change position or scale the mesh, it works fine.
Anybody had the problem before?
Just clone the material and replace it with original once.
scene.children[0].material = scene.children[0].material.clone();
scene.children[0].material.emissive.setHex( 0xff0000 );

three.js and openlayers coordinates don't line up

I'm using three.js on a separate canvas on top of a openlayers map. the way I synchronize the view of the map and the three.js camera (which looks straight down onto the map) looks like this:
// calculate how far away the camera needs to be, to
// see this part of the map
function distanceFromExtentAndFOV(h, vFOV) {
return h / (2 * Math.tan(vFOV / 2));
}
// keep three.js camera in sync with visible part of openlayers map
function updateThreeCam(
group, // three.js group, containing camera
view, // ol view
map // ol map
) {
extent = getViewExtent(view, map);
const h = ol.extent.getHeight(extent);
mapCenter = ol.extent.getCenter(extent);
camDist = distanceFromExtentAndFOV(h, constants.CAM_V_FOV_RAD);
const pos = [...mapCenter, camDist];
group.position.set(...pos);
group.updateMatrixWorld();
}
// whenever view changes, update three.js camera
view.on('change:center', (event) => {
updateThreeCam(group, event.target, map);
});
view.on('change:resolution', (event) => {
updateThreeCam(group, event.target, map);
});
updateThreeCam(group, view, map);
I'm using three.js for custom animations of objects, that in the end land on the ground. however, when I turn the objects into openlayers features, the original three.js objects and the features don't line up perfectly (although their position coordinates are exactly the same). — also, when you pan, you can see that s.th. is slightly off.
// turn three.js planes into openlayers features:
const features = rects.map((rect) => {
const point = new ol.geom.Point([
rect.position.x,
tect.position.y
]);
return new ol.Feature(point);
});
vectorSource.addFeatures(features);
as I am pretty sure that my the code for synchronizing three.js and ol is correct, I am not really sure what what is going wrong. am I missing s.th. here?
It could be that you are using PerspectiveCamera and that will change the size/position based on depth. Try OrtographicCamera instead.

Raycaster inside box with BackSide material doesn't work

I am doing a project on Tree.js (NICE lib!).
I have a problem. The rayCaster does not work when the ray is made inside the box.
I have an example here: https://jsfiddle.net/manumid/7sqd6xzs/7/
(extracted from https://github.com/mrdoob/three.js/issues/3969).
The box materials are THREE.BackSide, and I am using MultiMaterial because I need to represent a room with different textures. The material is made like this:
var materials = [
new THREE.MeshBasicMaterial({color:0xff0000,
side:THREE.BackSide}), // Right
new THREE.MeshBasicMaterial({color:0x00ff00,
side:THREE.BackSide}), // Left
new THREE.MeshBasicMaterial({color:0x0000ff,
side:THREE.BackSide}), // Top
new THREE.MeshBasicMaterial({color:0x222222,
side:THREE.BackSide}), // Bottom
new THREE.MeshBasicMaterial({color:0xffff00,
side:THREE.BackSide}), // Back
new THREE.MeshBasicMaterial({color:0x00ffff,
side:THREE.BackSide}), // Front
];
var cubeSidesMaterial = new THREE.MultiMaterial(materials);
var roomGeometry = new THREE.BoxGeometry(4, 4, 4);
var newRoom = new THREE.Mesh(roomGeometry, cubeSidesMaterial);
When the ray is made from outside, there is intersection, but against the external faces of the box (can it be a bug? The external faces have no textures!). This can be seen with the distance of the intersection.
Anyway, as a temporal solution, I can make the room with individual Planes joined on a Group.
Thank you!

Resources