Stretch a texture across multiple faces using Three.js - 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]];

Related

Merging line geometries in three.js

I'm having a problem merging multiple lines into one geometry. Line geometry was built using the CubicBezierCurve3:
const curve = new CubicBezierCurve3(
point1,
point2,
point3,
point4
);
const geometry = new BufferGeometry();
const points = curve.getPoints(16);
geometry.setFromPoints(points);
Then these two geometries were merged using BufferGeometryUtils:
const line = new Line(BufferGeometryUtils.mergeBufferGeometries([line1Geometry, line2Geometry], false), new LineBasicMaterial())
As a result, the desired figure turned out, but an extra line came from somewhere that connects them.
line
If I change the order when merging, then I get a different line. I don't understand how to solve this problem.
line2
You can't use THREE.Line for this use case since it represents a continuous line. So if you merge two separate lines into one, there will be no gap that separates both.
You have to use THREE.LineSegments however that means you have to pre-process your curve geometries. Try it like in the following live example:
let camera, scene, renderer;
init();
render();
function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.set(0, 0.5, 2);
scene = new THREE.Scene();
const curve1 = new THREE.CubicBezierCurve3(
new THREE.Vector3(1, 0, 0),
new THREE.Vector3(2, 0, 0),
new THREE.Vector3(1, 1, 0),
new THREE.Vector3(2, 1, 0)
);
const geometry1 = createGeometry(curve1.getPoints(32));
const curve2 = new THREE.CubicBezierCurve3(
new THREE.Vector3(-1, 0, 0),
new THREE.Vector3(-2, 0, 0),
new THREE.Vector3(-1, 1, 0),
new THREE.Vector3(-2, 1, 0)
);
const geometry2 = createGeometry(curve2.getPoints(32));
const geometry = THREE.BufferGeometryUtils.mergeBufferGeometries([geometry1, geometry2]);
const material = new THREE.LineBasicMaterial();
mesh = new THREE.LineSegments(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function render() {
renderer.render(scene, camera);
}
function createGeometry(points) {
const vertices = [];
const segments = points.length - 1;
for (let i = 0; i < segments; i++) {
const point1 = points[i];
const point2 = points[i + 1];
vertices.push(point1.x, point1.y, point1.z);
vertices.push(point2.x, point2.y, point2.z);
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
return geometry;
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.145/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.145/examples/js/utils/BufferGeometryUtils.js"></script>

Drawing a tetrahedron with triangles

Trying to create a tetrahedron with three triangles. I could'nt orient faces. Here is the code :
HTML
<div id="myid"></div>
JAVASCRIPT
const divid = document.getElementById("myid");
let facesVectors = [ // triangles vertices
[0, 0, 0],
[-1, 0, 0],
[0, 1, 0],
]
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
1,
1000
);
let renderer = new THREE.WebGLRenderer({ alpha: true });
divid.appendChild(renderer.domElement);
camera.position.z = 2.5;
// equilateral triangle template
const geometry = new THREE.Geometry()
geometry.vertices.push(new THREE.Vector3(-0.48, 0, 0))
geometry.vertices.push(new THREE.Vector3(0.48, 0, 0))
geometry.vertices.push(new THREE.Vector3(0, 0.72, 0))
geometry.faces.push(new THREE.Face3(0, 1, 2))
let material, face, i
let faces = []
for (i = 0; i < facesVectors.length; i++) {
material = new THREE.MeshBasicMaterial({ color: 0x00FF00, side: THREE.DoubleSide })
face = new THREE.Mesh(geometry, material)
face.position.set(facesVectors[i][0], facesVectors[i][1], facesVectors[i][2])
if (i === 1) {
face.rotation.x = -(Math.PI / 2)
} else if (i === 2) {
face.rotation.x = -(Math.PI / 2)
}
faces.push(face)
scene.add(face)
}
let animate = function() {
requestAnimationFrame(animate);
faces.forEach((face)=>{
face.rotation.x += 0.005;
face.rotation.y += 0.01;
})
renderer.render(scene, camera);
};
animate();
demo : https://codepen.io/jeffprod/pen/GRJGYoQ
How to rotate triangles 1 and 2 so that i get a tetrahedron ? Is there a simplest way to do ?
Of course i don't want to use the TetrahedronBufferGeomety because i will need to manage faces colors and a lot of other triangles oriented differently.
Based on this forum topic. Just commented out two lines for faces and uvs:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, -1, 2);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
// tetrahedron
// ---------------------------------------------------------------------------------------
var pts = [ // https://en.wikipedia.org/wiki/Tetrahedron#Coordinates_for_a_regular_tetrahedron
new THREE.Vector3(Math.sqrt(8 / 9), 0, -(1 / 3)),
new THREE.Vector3(-Math.sqrt(2 / 9), Math.sqrt(2 / 3), -(1 / 3)),
new THREE.Vector3(-Math.sqrt(2 / 9), -Math.sqrt(2 / 3), -(1 / 3)),
new THREE.Vector3(0, 0, 1)
];
var faces = [ // triangle soup
//pts[0].clone(), pts[2].clone(), pts[1].clone(),
pts[0].clone(), pts[1].clone(), pts[3].clone(),
pts[1].clone(), pts[2].clone(), pts[3].clone(),
pts[2].clone(), pts[0].clone(), pts[3].clone()
];
var geom = new THREE.BufferGeometry().setFromPoints(faces);
geom.rotateX(-Math.PI * 0.5);
geom.computeVertexNormals();
geom.setAttribute("uv", new THREE.Float32BufferAttribute([ // UVs
//0.5, 1, 0.06698729810778059, 0.2500000000000001, 0.9330127018922194, 0.2500000000000001,
0.06698729810778059, 0.2500000000000001, 0.9330127018922194, 0.2500000000000001, 0.5, 1,
0.06698729810778059, 0.2500000000000001, 0.9330127018922194, 0.2500000000000001, 0.5, 1,
0.06698729810778059, 0.2500000000000001, 0.9330127018922194, 0.2500000000000001, 0.5, 1
], 2));
// ---------------------------------------------------------------------------------------
var tetrahedron = new THREE.Mesh(geom, new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/uv_grid_opengl.jpg"),
side: THREE.DoubleSide
}));
scene.add(tetrahedron);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

How to build a perpendicular in three.js?

I need to build a perpendicular (line at an angle of 90 degrees to the original line) across the mid point of a straight line.
line = new THREE.Line( geometry, material );
geometry.vertices.push(
new THREE.Vector3( 100, 200, 0 ),
new THREE.Vector3( 300, 500, 0 ) );
In the manual I was not found information about it.
https://threejs.org/docs/#api/en/objects/Line
Thank you!
You can find the normal of a line by subtracting its start point from its end point (thus you'll get its direction), then rotate the resulted vector at 90 degrees (Math.PI * 0.5), then normalize it, and this is it, you've got the normal.
In the code snippet, the line itself is blueish (aqua), its normal is red.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var grid = new THREE.GridHelper(10, 10);
grid.rotation.x = -Math.PI * 0.5;
scene.add(grid);
var lineVertices = [
new THREE.Vector3(1, 2),
new THREE.Vector3(3, 5)
];
var lineGeom = new THREE.BufferGeometry().setFromPoints(lineVertices);
var lineMat = new THREE.LineBasicMaterial({color: 0x00ffff});
var line = new THREE.Line(lineGeom, lineMat);
scene.add(line);
var midPoint = new THREE.Vector3()
.subVectors(lineVertices[1], lineVertices[0])
.multiplyScalar(0.5)
.add(lineVertices[0]);
var normal = new THREE.Vector3()
.subVectors(lineVertices[1], lineVertices[0])
.applyAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI * 0.5)
.normalize();
var normalVertices = [
normal.clone().setLength(2).add(midPoint),
normal.clone().negate().setLength(2).add(midPoint)
];
var normalGeom = new THREE.BufferGeometry().setFromPoints(normalVertices);
var normalMat = new THREE.LineBasicMaterial({color: 0xff0000 });
var normal = new THREE.Line(normalGeom, normalMat);
scene.add(normal);
renderer.setAnimationLoop(()=>{renderer.render(scene, camera)});
body {
overflow:hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>

Vertically repeat texture on side faces only

I have a geometry of a cube that I need to texture,
In the top of the cube - need to be one image
And in each side of the cube - need to be a second image that vertically repeat based on the cube height
This is the texture:
This is what I need:
And this is what I have so far: three.js/texture1
Any ideas how to achieve what I need, but stay with one material and a few faces as possible?
Thanks.
My code:
function create_geometry() {
var geometry = new THREE.Geometry();
var w = 0.5;
var h = 1;
geometry.vertices.push(
new THREE.Vector3(w, h, w),
new THREE.Vector3(w, h, -w),
new THREE.Vector3(w, 0, w),
new THREE.Vector3(w, 0, -w),
new THREE.Vector3(-w, h, -w),
new THREE.Vector3(-w, h, w),
new THREE.Vector3(-w, 0, -w),
new THREE.Vector3(-w, 0, w)
);
geometry.faces.push(
// wall
new THREE.Face3(0, 2, 1),
new THREE.Face3(2, 3, 1),
// wall
new THREE.Face3(4, 6, 5),
new THREE.Face3(6, 7, 5),
// wall
new THREE.Face3(5, 7, 0),
new THREE.Face3(7, 2, 0),
// wall
new THREE.Face3(1, 3, 4),
new THREE.Face3(3, 6, 4),
//roof
new THREE.Face3(4, 5, 1),
new THREE.Face3(5, 0, 1),
);
geometry.computeFaceNormals();
geometry.faceVertexUvs[0] = [];
geometry.faces[0].materialIndex = 0;
geometry.faces[1].materialIndex = 0;
geometry.faces[2].materialIndex = 0;
geometry.faces[3].materialIndex = 0;
geometry.faces[4].materialIndex = 0;
geometry.faces[5].materialIndex = 0;
geometry.faces[6].materialIndex = 0;
geometry.faces[7].materialIndex = 0;
geometry.faces[8].materialIndex = 0;
geometry.faces[9].materialIndex = 0;
var roof = [
new THREE.Vector2(0.0,1.0),
new THREE.Vector2(0.0,0.5),
new THREE.Vector2(1.0,0.5),
new THREE.Vector2(1.0,1.0),
];
var wall = [
new THREE.Vector2(0.0,0.5),
new THREE.Vector2(0.0,0.0),
new THREE.Vector2(1.0,0.0),
new THREE.Vector2(1.0,0.5),
];
// wall
geometry.faceVertexUvs[0][0] = [ wall[0], wall[1], wall[3] ];
geometry.faceVertexUvs[0][1] = [ wall[1], wall[2], wall[3] ];
// wall
geometry.faceVertexUvs[0][2] = [ wall[0], wall[1], wall[3] ];
geometry.faceVertexUvs[0][3] = [ wall[1], wall[2], wall[3] ];
// wall
geometry.faceVertexUvs[0][4] = [ wall[0], wall[1], wall[3] ];
geometry.faceVertexUvs[0][5] = [ wall[1], wall[2], wall[3] ];
// wall
geometry.faceVertexUvs[0][6] = [ wall[0], wall[1], wall[3] ];
geometry.faceVertexUvs[0][7] = [ wall[1], wall[2], wall[3] ];
// roof
geometry.faceVertexUvs[0][8] = [ roof[0], roof[1], roof[3] ];
geometry.faceVertexUvs[0][9] = [ roof[1], roof[2], roof[3] ];
var matrix = new THREE.Matrix4();
//matrix.makeRotationX ( (270 * Math.PI)/180 );
geometry.applyMatrix( matrix );
geometry.uvsNeedUpdate = true;
return geometry;
}
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(800,800);
var container = document.getElementById("container");
container.appendChild(renderer.domElement);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45,800/800,1,1000);
camera.position.set(0,10,20);
scene.add(camera);
var controls = new THREE.TrackballControls( camera, renderer.domElement );
controls.minDistance = 15;
controls.maxDistance = 100;
var stats = new Stats();
container.appendChild( stats.dom );
var rendererStats = new THREEx.RendererStats();
rendererStats.domElement.style.position = 'absolute';
rendererStats.domElement.style.right = '0px';
rendererStats.domElement.style.top = '0px';
document.body.appendChild( rendererStats.domElement );
// plane
var plane = new THREE.Mesh(
new THREE.PlaneGeometry( 80, 80, 1 ),
new THREE.MeshBasicMaterial( {color: 0x333333, side: THREE.DoubleSide} )
);
plane.rotation.x = (90 * Math.PI)/180;
scene.add(plane);
//
var geometry = create_geometry();
var texture = new THREE.TextureLoader().load( "texture1.png" );
var material = new THREE.MeshBasicMaterial({ map:texture });
for(var i=0; i<10; i++) {
var cube = new THREE.Mesh( geometry, material );
cube.position.x = i*1.5 - 5*1.5;
//cube.position.y = Math.random()*80 - 40;
cube.scale.y = i + 1;
scene.add( cube );
}
function render() {
controls.update();
stats.update();
rendererStats.update(renderer);
renderer.render( scene, camera );
requestAnimationFrame( render );
}
render();

[three.js]THREE.Geometry : can't apply textured material

I created a hexagon with THREE.JS, it works with a basic material with a color, but i can't make it works with a texture... When i set up a textured material, the object isn't rendered at all.
I had the same problem with a bufferGeometry, but i solved the issue by setting geometry.addAttribute('uv'..., but this time, looks like it's not a uvs issue.
Here's the JS code :
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 );
// Adding light
scene.add( new THREE.AmbientLight( 0x444444 ) );
// SPOT LIGHT
var pXL1 = 3; var pYL1 = 1; var pZL1 = 2;
var LightGeometry1 = new THREE.BoxGeometry( 0.1, 0.1, 0.1 );
var LightMaterial1 = new THREE.MeshBasicMaterial( { map: new THREE.ImageUtils.loadTexture( "textures/default.jpg" ) } ); // this one is well textured !!!!
var LightCube1 = new THREE.Mesh( LightGeometry1, LightMaterial1 );
LightCube1.position.set(pXL1, pYL1, pZL1);
scene.add( LightCube1 );
var light1 = new THREE.DirectionalLight( 0xffffff );
light1.position.set( pXL1, pYL1, pZL1 ).normalize();
scene.add(light1);
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(-0.866 , 0.5,1));
geometry.vertices.push(new THREE.Vector3(-0.866 , -0.5 ,1));
geometry.vertices.push(new THREE.Vector3(0, -1 ,1));
geometry.vertices.push(new THREE.Vector3(0.866, -0.5 ,1));
geometry.vertices.push(new THREE.Vector3(0.866,0.5 ,1));
geometry.vertices.push(new THREE.Vector3(0, 1 ,1));
geometry.faces.push(new THREE.Face3(1, 2, 3, new THREE.Vector3(0, 0, 1)));
geometry.faces.push(new THREE.Face3(1, 3, 4, new THREE.Vector3(0, 0, 1)));
geometry.faces.push(new THREE.Face3(4, 0, 1, new THREE.Vector3(0, 0, 1)));
geometry.faces.push(new THREE.Face3(4, 5, 0, new THREE.Vector3(0, 0, 1)));
geometry.faceVertexUvs[0] = [];
geometry.faceVertexUvs[0].push( THREE.Vector2(0,5), THREE.Vector2(0.933,0.2835), THREE.Vector2(0.933,0.7165));
geometry.faceVertexUvs[0].push( THREE.Vector2(0,5), THREE.Vector2(0.933,0.7165), THREE.Vector2(0.5,1));
geometry.faceVertexUvs[0].push( THREE.Vector2(0.5,1), THREE.Vector2(0.067,0.2835), THREE.Vector2(0,5));
geometry.faceVertexUvs[0].push( THREE.Vector2(0.5,1), THREE.Vector2(0.067,7165), THREE.Vector2(0.067,0.2835));
var hexagon_texture = new THREE.ImageUtils.loadTexture( "textures/default.jpg" );
hexagon_texture.wrapS = THREE.RepeatWrapping;
hexagon_texture.wrapT = THREE.RepeatWrapping;
hexagon_texture.repeat.set(1,1);
var hexagon_material = new THREE.MeshBasicMaterial( { color: 0xff0000} );
//var hexagon_material = new THREE.MeshPhongMaterial( { ambient: 0xffffff, specular: 0x555555, shininess: 50, map:hexagon_texture, side: THREE.DoubleSide } );
//var hexagon_material = new THREE.MeshBasicMaterial( { map: new THREE.ImageUtils.loadTexture( "textures/default.jpg" ) } );
var hexagon = new THREE.Mesh( geometry,hexagon_material );
scene.add(hexagon);
camera.position.z = 5;
controls = new THREE.OrbitControls( camera, renderer.domElement );
var render = function () {
requestAnimationFrame( render );
renderer.render(scene, camera);
};
render();
As it, it works and i have a red hexagon. But if i uncomment one of the var hexagon_materual = ..., then the hexagon isn't rendered.
What am i missing ?
Thank you

Resources