Why is texture not rendering on vertices in Three.js (missing UVs) - three.js

I have created a scene with THREE.js. Most of the surfaces are BoxGeometries with zero width and a texture applied. Everything works well, including transparent windows on the house. When I went to add roof, I needed to make angled flat panels and triangular spaces. I decided to use a technique borrowed from another StackOverflow page (How to create a custom mesh on THREE.JS?).
The mesh is rendering, but not the texture. I've tried with different texture images and it does change the COLOR of the rendered panel, but still no visible texture. I'm missing something. If I can get this to work, I'll start using more vertex-based meshes to fill in my building. Why isn't the texture rendering?
//texture
var texture = new THREE.ImageUtils.loadTexture("shingles.jpg");
texture["shingles"] = new THREE.MeshBasicMaterial({ map:texture, side:THREE.DoubleSide});
//roof
var v1 = new THREE.Vector3(farRight,level+height,back);
var v2 = new THREE.Vector3(farRight,level+height,front);
var v3 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),front-(3*width/2));
var v4 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),back+(3*width/2));
var geom = new THREE.Geometry();
geom.vertices.push(v1);
geom.vertices.push(v2);
geom.vertices.push(v3);
geom.vertices.push(v4);
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
geom.faces.push( new THREE.Face3( 3, 0, 2 ) );
var object = new THREE.Mesh( geom, textures["shingles"] );
scene.add(object);
----------------Updated with full answer-------------------------------
As noted in the accepted answer, I missed adding the UV vectors. After some reading I was able to figure out how they work and get the textures to map appropriately. I'm including the full solution here for future reference. First two snippets of code.
var v3 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),front-(3*width/2));
var v4 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),back+(3*width/2));
var v5 = new THREE.Vector3(farLeft,level+height,back);
var v6 = new THREE.Vector3(farLeft,level+height,front);
geom = new THREE.Geometry();
geom.vertices.push(v3);
geom.vertices.push(v4);
geom.vertices.push(v5);
geom.vertices.push(v6);
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
geom.faces.push( new THREE.Face3( 3, 0, 2 ) );
geom.faceVertexUvs[0].push([new THREE.Vector2(.3, 1),
new THREE.Vector2(.7, 1),
new THREE.Vector2(1, 0)]);
geom.faceVertexUvs[0].push([new THREE.Vector2(0, 0),
new THREE.Vector2(.3, 1),
new THREE.Vector2(1, 0)]);
object = new THREE.Mesh( geom, textures["shingles"] );
scene.add(object);
var v1 = new THREE.Vector3(farRight,level+height,back);
var v4 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),back+(3*width/2));
var v5 = new THREE.Vector3(farLeft,level+height,back);
geom = new THREE.Geometry();
geom.vertices.push(v1);
geom.vertices.push(v4);
geom.vertices.push(v5);
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
geom.faceVertexUvs[0].push([new THREE.Vector2(0, 0),
new THREE.Vector2(.5, 1),
new THREE.Vector2(1, 0)]);
object = new THREE.Mesh( geom, textures["shingles"] );
scene.add(object);
Now a picture and an explanation. The two geometries in the code above are highlighted in the picture. Because I got fancy, vertices 3 and 4 are slightly inward making a trapezoid shape. Likewise the UV vertices that correspond to those points are .3 and .7 inward from the corners to make a trapezoid. The triangular geometry has two points at the base and one at the top-middle.
Basically, create the vertices, add the vertices, define the faces, and (this is the part I missed) add UV vertices corresponding to the locations in the texture.

You will need to add texture coordinates as well.. (aka uv coordinates). There is a field called faceVertexUVs that has to be filled out. You will need to set 0,0 for the top left corner of each face, 1,0 for top right, 0,1 for bottom left and 1,1 for bottom right corner.

Related

Three js raycaster WITHOUT camera

I seem to find only examples to use the raycaster with the camera, but none that just have a raycaster from Point A to Point B.
I have a working raycaster, it retrieves my Helpers, Lines etc. but it seems it does not recognize my sphere.
My first thought was my points are off, so i decided to create a line from my pointA to my pointB with a direction like so:
var pointA = new Vector3( 50, 0, 0 );
var direction = new Vector3( 0, 1, 0 );
direction.normalize();
var distance = 100;
var pointB = new Vector3();
pointB.addVectors ( pointA, direction.multiplyScalar( distance ) );
var geometry = new Geometry();
geometry.vertices.push( pointA );
geometry.vertices.push( pointB );
var material = new LineBasicMaterial( { color : 0xff0000 } );
var line = new Line( geometry, material );
This will show a line from my point (50 0 0) to (50 100 0) right trough my sphere which is at point (50, 50, 0) so my pointA and direction values are correct.
Next i add a raycaster:
To avoid conflicts with any side effects i recreated my points here:
var raycaster = new Raycaster(new Vector3( 50, 0, 0 ), new Vector3( 0, 1, 0 ).normalize());
var intersects = raycaster.intersectObject(target);
console.log(intersects);
Seems pretty straight forward to me, i also tried to use raycaster.intersectObjects(scene.children) but it gives Lines, helpers etc. but not my sphere.
What am i doing wrong? I am surely missing something here.
IMG of the line and the sphere:
What you see is explained in the following github issue:
https://github.com/mrdoob/three.js/issues/11449
The problem is that the ray emitted from THREE.Raycaster does not directly hit a face but its vertex which results in no intersection.
There are several workarounds to solve this issue e.g. slightly shift the geometry or the ray. For your case:
var raycaster = new THREE.Raycaster( new THREE.Vector3( 50, 0, 0 ), new THREE.Vector3( 0, 1, 0.01 ).normalize() );
However, a better solution is to fix the engine and make the test more robust.
Demo: https://jsfiddle.net/kzwmoug2/3/
three.js R106

How to set pivot point for an arrow-like shape

I'm creating this arrow-like shape with three.js:
let planeShape = new THREE.Shape()
planeShape.moveTo(0, 0)
planeShape.lineTo(3, 1)
planeShape.lineTo(0, 2)
planeShape.lineTo(1, 1)
planeShape.lineTo(0, 0)
var geometry = new THREE.ShapeGeometry( planeShape )
// Appropriate rotation or translation should take place here
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } )
var mesh = new THREE.Mesh( geometry, planeMaterial )
scene.add( mesh )
And I want to be able to use mesh.lookAt(position) so that the tip of the arrow ends up pointing the specified position. However, I haven't been able to set an appropriate pivot point. I think I'm having problems because I'm creating the arrow shape points in 2D but I end up with a 3D object losing track of my origin point.
Here's an example of what I want to accomplish: https://threejs.org/examples/misc_lookat.html
After var geometry = new THREE.ShapeGeometry( planeShape ) add
geometry.translate(0, -1, 0); // center with x-axis
geometry.rotateY(-Math.PI * 0.5); // align with z-axis to use with lookAt()

How to create a half Cylinder in Three js?

I´m very new to three js and I want to create a half Cylinder as roof.
var geometry = new THREE.CylinderGeometry(100,100,150);
var material = new THREE.MeshNormalMaterial();
var cylinder = new THREE.Mesh( geometry, material);
scene.add(cylinder);
This is the Basic Cylinder but unfortunately I couldn´t find an specific answer to a half cylinder yet.
Is it possible to create this with vertices?
The last 2 params of CylinderGeometry constructor allows this. For example:
var geometry = new THREE.CylinderGeometry(100,100,150, 8, 1, false, 0, Math.PI);
0 is the start angle and Math.PI is the end angle; i.e. half of a circle.

How to add a texture to one side of a cube without THREE.MultiMaterial to reduce draw calls

So lets say theres a cube with 2 materials.I'm using MultiMaterial but maybe thats not the correct approach because its showing 6 draw calls instead of 2. I'm worried about performance when it scales up.
http://codepen.io/glued/pen/JXmvzm
This is just an example, I know about FaceColors but would like to mix a meshBasicMaterial with another Material, say, with a texture.
var greenMaterial = new THREE.MeshBasicMaterial({ color: 0xc4f288 })
var orangeMaterial = new THREE.MeshBasicMaterial({ color: 0xf4511e })
var mats = [
orangeMaterial,
greenMaterial,
orangeMaterial,
orangeMaterial,
greenMaterial,
orangeMaterial
]
let box = new THREE.Mesh(geometry, new THREE.MultiMaterial( mats ))
If i used vertexColors: FaceColors and a texture:
new MeshBasicMaterial({ vertexColors: FaceColors, map:someTexture }))
how would i designate the texture for a specific face only?
I figured it out by creating a material with a texture and removing the UVs on the geometry faces that i'm not using
the texture is 128x256, See the codepen as i'm using a 2d canvas to generate
texture.repeat.y = 0.5
texture.offset.y = 0.5
let geometry = new THREE.BoxGeometry(50, 50, 50)
function assignUvAndColor(geo, i, color = 0x00cbff){
geo.faceVertexUvs[0][i] = new Array(3).fill(new THREE.Vector2(0, -1))
geo.faces[i].color.setHex(color)
}
const greenColor = 0xacffd3
assignUvAndColor(geometry, 3, greenColor)
assignUvAndColor(geometry, 2, greenColor)
assignUvAndColor(geometry, 0, greenColor)
assignUvAndColor(geometry, 1, greenColor)
assignUvAndColor(geometry, 4)
assignUvAndColor(geometry, 5)
assignUvAndColor(geometry, 6)
assignUvAndColor(geometry, 7)
let material = new THREE.MeshBasicMaterial({ map: texture, vertexColors: THREE.FaceColors })
let box = new THREE.Mesh(geometry, material)
http://codepen.io/glued/pen/grBEmo?editors=0010
multimaterial will always do N drawcalls where N = length of its material array
(see renderer implementation)
it does not even try to check whether some of its materials are duplicite in reference - so in your examples you have multimaterial with 6 materials = 6 drawcalls
you will have to change the geometry face material index or abandon using multimaterial and divide your geometry manually

Applying textures on cube geometry using THREE.js

I have a simple THREE.js app that renders a cube and applies textures on each of the faces like so:
var cubeGeometry = new THREE.CubeGeometry(5, 8, 1, 4, 4, 1);
var materials = [ new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture('front.jpg') }),
//.....Front, back, left, etc...
];
...
var cubeMesh = new THREE.Mesh(cubeGeometry, new THREE.MeshFaceMaterial(materials));
However, all I see is a black cube, i.e. the images dont appear ont the cube's faces.
Also, my code works fine in release 50 of the THREE.js library, so it seems like a change is a newer release has caused my code to break, and I cant seem to find any relavent documentation around it.
Any help is appreciated.
The following code should work as of release 97
// creates cubes geometry in front of camera (assuming your camera position and rotation has not changed)
var geometry = new THREE.CubeGeometry(1, 1, 1, -2, 0, 0);
// creates a texture loader for the cube
var texture = new THREE.TextureLoader();
// defines variables to make things easier
var counter, textures = [], materials = [];
// iterate through all 6 sides of the cube
for(counter = 0; counter < 6; counter ++) {
// loads and stores a texture (you might run into some problems with loading images directly from a source because of security protocols, so copying the image data is a for sure way to get the image to load)
textures[counter] = texture.load('data:image/restOfImageAddress');
// creates material from previously stored texture
materials.push(new THREE.MeshBasicMaterial({map: textures[counter]}));
}
// creates the cube by mixing the geometry and materials
var cubeMesh = new THREE.Mesh(geometry, materials);

Resources