Video texture on THREE.Geometry - three.js

The only examples i can find are of video textures being used on PlaneGeometry's or BoxGeometry's. I have a custom plane that i have a video applied to. The video plays (colors change) and shows however the scale is off (looks like its zoomed in).
Video playing on top right, green plane is a custom Geometry which the video is on.
THREE.Geometry
this.geometry = new THREE.Geometry();
this.geometry.vertices.push(
new THREE.Vector3(900, 0, 840)
new THREE.Vector3(-900, 0, 1200),
new THREE.Vector3(900, 900, 840),
new THREE.Vector3(-900, 900, 1200),
);
this.geometry.faces.push(
new THREE.Face3(0, 1, 2),
new THREE.Face3(1, 3, 2),
);
this.geometry.computeFaceNormals();
this.object = new THREE.Mesh(this.geometry, this._videoMaterial());
videoMaterial function
_videoMaterial() {
let videoImage = document.createElement('canvas');
videoImage.width = 480;
videoImage.height = 204;
this.videoImageContext = videoImage.getContext('2d');
this.videoImageContext.fillStyle = '#211e79';
this.videoImageContext.fillRect(0, 0, videoImage.width, videoImage.height);
this.videoTexture = new THREE.Texture(videoImage);
this.videoTexture.minFilter = THREE.LinearFilter;
this.videoTexture.magFilter = THREE.LinearFilter;
this.videoTexture.format = THREE.RGBFormat;
return new THREE.MeshBasicMaterial({
map: this.videoTexture,
overdraw: true,
side: THREE.DoubleSide
});
}

As prisoner849 said, you need to add UVs to your geometry so it knows how to map the texture on its surface
// Add UVs of face 1
this.geometry.faceVertexUvs[0].push([
new THREE.Vector2(0, 0)
new THREE.Vector2(0, 1),
new THREE.Vector2(1, 0)
]);
// Add UVs of face 2
this.geometry.faceVertexUvs[0].push([
new THREE.Vector2(0, 1)
new THREE.Vector2(1, 0),
new THREE.Vector2(1, 1)
]);
this.geometry.uvsNeedUpdate();
You might need to tweak the order of the UVs to match the order of your vertices on the faces, but you want an arrangement like this:
https://threejs.org/docs/index.html#api/en/core/Geometry.faceVertexUvs

Related

how to draw a flat shape in 3-space in three.js?

I'm trying to construct a collection of flat shapes in three.js. Each one is defined as a series of coplanar Vector3 points, but the shapes are not all coplanar. Imagine two flat rectangles as the roof of a house, but with much more complex shapes.
I can make flat Shape objects and then rotate and position them, but since my shapes are conceived in 3d coordinates, it would be much simpler to keep it all in 3-space, which the Shape object doesn't like.
Is there some much more direct way to simply specify an array of coplanar Vector3's, and let three.js do the rest of the work?
I thought about this problem and came up with the idea, when you have a set of co-planar points and you know the normal of the plane (let's name it normal), which your points belong to.
We need to rotate our set of points to make it parallel to the xy-plane, thus the normal of that plane is [0, 0, 1] (let's name it normalZ). To do it, we find quaternions with .setFromUnitVectors() of THREE.Quaternion():
var quaternion = new THREE.Quaternion().setFromUnitVectors(normal, normalZ);
var quaternionBack = new THREE.Quaternion().setFromUnitVectors(normalZ, normal);
Apply quaternion to our set of points
As it's parallel to xy-plane now, z-coordinates of points don't matter, so we can now create a THREE.Shape() object of them. And then create THREE.ShapeGeometry() (name it shapeGeom) from given shape, which will triangulate our shape.
We need to put our points back to their original positions, so we'll apply quaternionBack to them.
After all, we'll assign our set of points to the .vertices property of the shapeGeom.
That's it. If it'll work for you, let me know ;)
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 20, 40);
camera.lookAt(scene.position);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.target = new THREE.Vector3(10, 0, 10);
controls.update();
var grid = new THREE.GridHelper(50, 50, 0x808080, 0x202020); // xy-grid
grid.geometry.rotateX(Math.PI * 0.5);
scene.add(grid);
var points = [ // all of them are on the xz-plane
new THREE.Vector3(5, 0, 5),
new THREE.Vector3(25, 0, 5),
new THREE.Vector3(25, 0, 15),
new THREE.Vector3(15, 0, 15),
new THREE.Vector3(15, 0, 25),
new THREE.Vector3(5, 0, 25),
new THREE.Vector3(5, 0, 5)
]
var geom = new THREE.BufferGeometry().setFromPoints(points);
var pointsObj = new THREE.Points(geom, new THREE.PointsMaterial({
color: "red"
}));
scene.add(pointsObj);
var line = new THREE.LineLoop(geom, new THREE.LineBasicMaterial({
color: "aqua"
}));
scene.add(line);
// normals
var normal = new THREE.Vector3(0, 1, 0); // I already know the normal of xz-plane ;)
scene.add(new THREE.ArrowHelper(normal, new THREE.Vector3(10, 0, 10), 5, 0xffff00)); //yellow
var normalZ = new THREE.Vector3(0, 0, 1); // base normal of xy-plane
scene.add(new THREE.ArrowHelper(normalZ, scene.position, 5, 0x00ffff)); // aqua
// 1 quaternions
var quaternion = new THREE.Quaternion().setFromUnitVectors(normal, normalZ);
var quaternionBack = new THREE.Quaternion().setFromUnitVectors(normalZ, normal);
// 2 make it parallel to xy-plane
points.forEach(p => {
p.applyQuaternion(quaternion)
});
// 3 create shape and shapeGeometry
var shape = new THREE.Shape(points);
var shapeGeom = new THREE.ShapeGeometry(shape);
// 4 put our points back to their origins
points.forEach(p => {
p.applyQuaternion(quaternionBack)
});
// 5 assign points to .vertices
shapeGeom.vertices = points;
var shapeMesh = new THREE.Mesh(shapeGeom, new THREE.MeshBasicMaterial({
color: 0x404040
}));
scene.add(shapeMesh);
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.90.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.90.0/examples/js/controls/OrbitControls.js"></script>

Three.js: Show object only on one side of PlaneGeometry

I created a PlaneGeometry in Three.js and placed an object on top of it.
If I move the camera so that I can see the PlaneGeometry from below I can still see parts from the object on top. How can I define that the object is only seen from above the PlaneGeometry?
Image from above
Image from below
// Creating PlaneGeometry
var floorGeometry = new THREE.PlaneGeometry( 100, 100 );
floorGeometry.rotateX( - Math.PI / 2 );
var floorTexture = new THREE.TextureLoader().load( '../img/wood-texture.jpg' );
floorTexture.wrapS = THREE.RepeatWrapping;
floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.repeat.set(20, 20);
var floorMaterial = new THREE.MeshBasicMaterial({map: floorTexture, side: THREE.DoubleSide});
var floor = new THREE.Mesh( floorGeometry, floorMaterial );
scene.add( floor );
// Creating object on top
var cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
var cubeMaterial = new THREE.MeshBasicMaterial({color: 0x444444,wireframe: true});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(0, 0.5, 0);
scene.add(cube);
As workaround I set the floor lower. If I zoom in I can still see the gap but it seems to be the best / only solution.
floor.position.y = -0.5;

threejs raycasting does not work

I'm having a problem trying to write a unit test to check collision detection. I simplify code as only this possible - I have a plane in (0, 0, 0) and I do raycasting from above this plane (from (0, 100, 0)) to bottom (0, -1, 0) and I suppose to find intersections with that plane but no luck.
console.clear();
var intersections,
from = new THREE.Vector3(0, 100, 0);
direction = new THREE.Vector3(0, -1, 0),
raycaster = new THREE.Raycaster();
var geometry = new THREE.PlaneGeometry(10, 10, 1, 1);
var ground = new THREE.Mesh(geometry);
ground.position.set(0, 0, 0);
ground.rotation.x = THREE.Math.degToRad(-90);
raycaster.set(from, direction);
intersections = raycaster.intersectObjects([ground]);
console.log(intersections);
What is wrong here? Why this simple code does not show any intersections? (r73).
jsfiddle example
You need to update the world transform of the ground mesh prior to raycasting. (Normally, the renderer does this for your in the render() call.)
console.clear();
var intersections,
from = new THREE.Vector3(0, 100, 0);
direction = new THREE.Vector3(0, -1, 0),
raycaster = new THREE.Raycaster();
var geometry = new THREE.PlaneGeometry(10, 10, 1, 1);
var ground = new THREE.Mesh(geometry);
ground.position.set(0, 0, 0);
ground.rotation.x = THREE.Math.degToRad(-90);
ground.updateMatrixWorld(); // add this
raycaster.set(from, direction);
intersections = raycaster.intersectObjects([ground]);
console.log(intersections);
three.js r.73

Stretch a texture across multiple faces using Three.js

I'm trying to apply a texture on an Object3D with a custom geometry that consists of multiple faces. From the user's point of view this should be a single surface and the texture should stretch ideally keeping the sides ratio. I've tried various texture settings but so far no luck.
The top image is what I'm getting and the bottom one what I'm trying to achieve: https://dl.dropboxusercontent.com/u/20925853/combined.jpg
I've extracted a sample code to JsFiddle: http://jsfiddle.net/Immugio/Lg5vbzot/
function render() {
renderer.render(scene, camera);
};
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var light = new THREE.DirectionalLight(0xffffff);
light.intensity = 0.5;
light.position.set(0, 1, 1).normalize();
scene.add(light);
var ambient = new THREE.AmbientLight(0x777777);
scene.add(ambient);
var controls = new THREE.OrbitControls(camera);
controls.damping = 0.2;
controls.addEventListener('change', render);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
THREE.ImageUtils.crossOrigin = '';
var textureMaterial = new THREE.MeshPhongMaterial({
map: THREE.ImageUtils.loadTexture('https://dl.dropboxusercontent.com/u/20925853/crate.jpg', null, function() {
render();
})
});
var geo = new THREE.Geometry();
geo.vertices = [
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(0, 4, 0),
new THREE.Vector3(4, 4, 0),
new THREE.Vector3(4, 0, 0),
new THREE.Vector3(1, 4, 0),
new THREE.Vector3(1, 6, 0),
new THREE.Vector3(3, 6, 0),
new THREE.Vector3(3, 4, 0)
];
geo.faces = [
new THREE.Face3(0, 1, 2),
new THREE.Face3(0, 2, 3),
new THREE.Face3(4, 5, 6),
new THREE.Face3(4, 6, 7)
];
var faceuv = [
new THREE.Vector2(0, 1),
new THREE.Vector2(1, 1),
new THREE.Vector2(1, 0),
new THREE.Vector2(0, 0)
];
geo.faceVertexUvs[0] = [];
geo.faceVertexUvs[0][0] = [faceuv[0], faceuv[1], faceuv[2]];
geo.faceVertexUvs[0][1] = [faceuv[0], faceuv[2], faceuv[3]];
geo.faceVertexUvs[0][2] = [faceuv[0], faceuv[1], faceuv[2]];
geo.faceVertexUvs[0][3] = [faceuv[0], faceuv[2], faceuv[3]];
geo.computeFaceNormals();
var mesh = new THREE.Mesh(geo, textureMaterial);
mesh.rotation.x = Math.PI / 180 * 135;
mesh.rotation.z = Math.PI / 180 * 135;
scene.add(mesh);
camera.position.z = 10;
In order to strech the texture you need to map the UV accros yours multiple faces.
JsFiddle :http://jsfiddle.net/Lg5vbzot/1/
Replacing with the following code
var faceuv = [
new THREE.Vector2(0, 0),
new THREE.Vector2(0, 0.66),
new THREE.Vector2(1, 0.66),
new THREE.Vector2(1, 0),
new THREE.Vector2(0.25, 0.66),
new THREE.Vector2(0.25, 1),
new THREE.Vector2(0.75, 1),
new THREE.Vector2(0.75, 0.66)
];
geo.faceVertexUvs[0] = [];
geo.faceVertexUvs[0][0] = [faceuv[0], faceuv[1], faceuv[2]];
geo.faceVertexUvs[0][1] = [faceuv[0], faceuv[2], faceuv[3]];
geo.faceVertexUvs[0][2] = [faceuv[4], faceuv[5], faceuv[6]];
geo.faceVertexUvs[0][3] = [faceuv[4], faceuv[6], faceuv[7]];

Texture mapping on extrude geometry ThreeJS

I'm creating random shapes with extrude geometry on ThreeJS, now I want to apply textures on it but the result is ugly. I can't figure out how to generate UVMapping in order to display the texture correctly.
I fiddled my issue:
http://jsfiddle.net/PsBtn/8/
On the left mesh, one face is not correctly textured, as the face is flat I want the texture not to be skewed.
On the right, the texture is rotated but I don't want this result.
Is there an automatically way to generate UVMapping in order to get the texture render the same on these two meshes (Extrude Geometry) ?
Here is how I generate my meshes:
var points = []
points.push(new THREE.Vector2(-1000, -700));
points.push(new THREE.Vector2(-1000, -300));
points.push(new THREE.Vector2(-300, -300));
points.push(new THREE.Vector2(-300, -500));
shape = new THREE.Shape(points);
var geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings),
texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var material = new THREE.MeshBasicMaterial({
color: 0xFF00FF,
map: texture
});
geometry.faceUvs = [[]];
geometry.faceVertexUvs = [[]];
for (var f = 0; f < geometry.faces.length; f++) {
var faceuv = [
new THREE.Vector2(0, 1),
new THREE.Vector2(1, 1),
new THREE.Vector2(1, 0),
new THREE.Vector2(0, 0)
];
geometry.faceUvs[0].push(new THREE.Vector2(0, 1));
geometry.faceVertexUvs[0].push(faceuv);
}
mesh = THREE.SceneUtils.createMultiMaterialObject(geometry, [material, new THREE.MeshBasicMaterial({
color: 0x000000,
wireframe: true,
transparent: true
})]);
mesh.position.z = -100;
mesh.position.y = 200;
scene.add(mesh);
I found a solution, I have to compute the uv form my custom forms. This web page is really usefull for it :
http://paulyg.f2s.com/uv.htm

Resources