texture map doesn't work properly in threejs - javascript

I made a shape and extruded it. here is the code:
import wood from "./wood_texture.jpg"
let generate_box = (width, height, depth) => {
let extrudeSettings = {
depth: depth,
bevelEnabled: false,
}
const shape = new THREE.Shape();
shape.lineTo( 0, this.height );
shape.lineTo( width, this.height );
shape.lineTo( width, 0 );
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
return geometry
};
let box_geo = generate_box(0.5, 2.2, 0.35)
let textureLoader = new THREE.TextureLoader()
let material = new THREE.MeshStandardMaterial( { color: "rgb(248,
247, 247)", map: textureLoader.load(wood)} )
box_mesh = new THREE.Mesh(box_geo, material)
scene.add(box_mesh)
and this is the result
As can be seen, the texture hasn't been mapped correctly. Ideally, it should look like this:

Related

Three.js texture streches when the width is large

You can see when the width is less the texture fits perfectly and when the wall is long it stretches.
const wallTex = new THREE.TextureLoader().load(wallTexture);
wallTex.encoding = THREE.sRGBEncoding;
wallTex.wrapS = THREE.RepeatWrapping;
wallTex.wrapT = THREE.RepeatWrapping;
wallTex.repeat.set(4, 1);
const wallMaterial = new THREE.MeshBasicMaterial({ map: wallTex, side: THREE.DoubleSide });
When I dont set texture.repeat.set(4,1) I get the result as below
Is there a way to add texture irrespective of its width?
const wallGeometry = new THREE.BufferGeometry();
const wallVertices = new Float32Array([
-x1,
y1,
0, // vertex 1
-x1,
y1,
10, // vertex 2
-x2,
y2,
0, // vertex 3
-x2,
y2,
10, // vertex 4
]);
// define texture coordinates for wallGeometry
const wallUvs = new Float32Array([
0,
0, // vertex 1
0,
1, // vertex 2
1,
0, // vertex 3
1,
1, // vertex 4
]);
wallGeometry.setAttribute('uv', new THREE.BufferAttribute(wallUvs, 2));
wallGeometry.setAttribute('position', new THREE.BufferAttribute(wallVertices, 3));
wallGeometry.setIndex([0, 2, 1, 2, 3, 1]);
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
const geo = new THREE.EdgesGeometry(wall.geometry);
const wireframe = new THREE.LineSegments(geo, lineMaterial);
wall.add(wireframe);
wall.castShadow = true;
wall.receiveShadow = true;
scene!.add(wall);

Creating same circles with PointsMaterial and CircleGeometry

I would like to create circles in two different ways:
With a circle sprite, then draw it with Points and PointsMaterial
With basic circle buffer geometries
However, I cannot make them match together because of PointsMaterial size.
const width = window.innerWidth;
const height = window.innerHeight;
const fov = 40;
const near = 10;
const far = 200;
const camera = new THREE.PerspectiveCamera(fov, width / height, near, far);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
const circle_sprite = new THREE.TextureLoader().load(
'https://fastforwardlabs.github.io/visualization_assets/circle-sprite.png'
);
const factor = 280;
const positions = [
{ x: 100, y: -5 },
{ x: 6, y: 50 }
];
const circleRadius = 20;
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xefefef);
/* First method */
const pointsGeometry = new THREE.Geometry();
const colors = [];
for (const position of positions) {
// Set vector coordinates from data
const vertex = new THREE.Vector3(position.x, position.y, 0);
pointsGeometry.vertices.push(vertex);
const color = new THREE.Color(0xff0000);
colors.push(color);
}
pointsGeometry.colors = colors;
const pointsMaterial = new THREE.PointsMaterial({
size: (factor * circleRadius) / fov,
sizeAttenuation: false,
vertexColors: true,
map: circle_sprite,
transparent: true,
opacity: 0.5
});
const firstPoints = new THREE.Points(pointsGeometry, pointsMaterial);
scene.add(firstPoints);
/* Second method */
const circleGeometry = new THREE.CircleBufferGeometry(circleRadius, 32);
const circleMaterial = new THREE.MeshBasicMaterial({
color: 0xffff00,
transparent: true,
opacity: 0.5
});
positions.forEach((position) => {
const circleMesh = new THREE.Mesh(circleGeometry, circleMaterial);
circleMesh.position.set(position.x, position.y, 0);
scene.add(circleMesh);
});
/* Render loop */
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
camera.position.set(0, 0, far);
I try to find the factor variable value but I also discovered that width or height are involved in this factor.
How can I draw same circles with PointsMaterial?
Having this: https://github.com/mrdoob/three.js/issues/12150#issuecomment-327874431, you can compute the size of points.
body {
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://threejs.org/build/three.module.js";
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(50, innerWidth / innerHeight, 1, 100);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
var grid = new THREE.GridHelper(10, 10);
grid.rotation.x = Math.PI * 0.5;
scene.add(grid);
// geometry
var cGeom = new THREE.CircleBufferGeometry(1, 32);
var cMat = new THREE.MeshBasicMaterial({
color: "magenta"
});
var circle = new THREE.Mesh(cGeom, cMat);
circle.position.set(0, 3, 0);
scene.add(circle);
// points
var g = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3()]);
var c = document.createElement("canvas");
c.width = 128;
c.height = 128;
var ctx = c.getContext("2d");
ctx.clearRect(0, 0, 128, 128);
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(64, 64, 64, 0, 2 * Math.PI);
ctx.fill();
var tex = new THREE.CanvasTexture(c);
var desiredSize = 2;
var pSize = desiredSize / Math.tan( THREE.Math.degToRad( camera.fov / 2 ) );
var m = new THREE.PointsMaterial({
size: pSize,
color: "aqua",
map: tex,
alphaMap: tex,
alphaTest: 0.5
});
var p = new THREE.Points(g, m);
scene.add(p);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera)
});
</script>

Setting global rotation of a vector using a quaternion in THREE.js

I would like to set the absolute global rotation of a vector using a quaternion.
Let's say I have a function f(v, q) which rotates vector v to quaternion q.
THREE.Vector3 has a .applyQuaternion function which applies a rotation to a vector, but this is a relative rotation. For example, the function call f(f(v, q), q) will apply q twice. Instead, I would expect f(f(v, q), q) = f(v, q) since q is the absolute rotation of the vector.
I know that I can compute some deltaQ = currentQ.premultiply(q.inverse()) and then call .applyQuaternion, but I don't know how to query the currentQ, or the current rotation of the vector, so that I can "undo" it and apply the new quaternion.
Thanks for any help!
EDIT:
I would ideally like to rotate about a point defined in world coordinate space, which is why I have the object.worldToLocal and object.localToWorld calls. However, the orbiting object flies off the map when I make these conversions.
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
/* const orbitControls = new THREE.OrbitControls(camera, renderer.domElement); */
// CONSTRUCT ORBITER
const orbitGeometry = new THREE.SphereGeometry(0.1, 32, 32);
const orbitMaterial = new THREE.MeshBasicMaterial({colors: 0xff0000});
const orbiter = new THREE.Mesh(orbitGeometry, orbitMaterial);
// CONSTRUCT GLOBAL AXES
const xDir = new THREE.Vector3(1, 0, 0);
const yDir = new THREE.Vector3(0, 1, 0);
const zDir = new THREE.Vector3(0, 0, 1);
const origin = new THREE.Vector3(0, 0, 0);
const xArrowWorld = new THREE.ArrowHelper(xDir, origin, 3, 0xFF00FF);
const yArrowWorld = new THREE.ArrowHelper(yDir, origin, 3, 0xffff00);
const zArrowWorld = new THREE.ArrowHelper(zDir, origin, 3, 0x00b7eb);
const worldAxes = new THREE.Group();
worldAxes.add(xArrowWorld);
worldAxes.add(yArrowWorld);
worldAxes.add(zArrowWorld);
// CONSTRUCT MARKER
const sphereGeometry = new THREE.SphereGeometry(0.1, 32, 32);
const sphereMaterial = new THREE.MeshBasicMaterial({color: 0xffffff});
const marker = new THREE.Mesh(sphereGeometry, sphereMaterial);
// scene.add(axes);
scene.add(marker);
scene.add(orbiter);
scene.add(worldAxes);
camera.position.z = 5;
camera.position.y = 3;
camera.position.x = 3;
camera.lookAt(worldAxes.position);
marker.position.set(1, 0, 0);
function rotateEuler(object, point, eulerX, eulerY, eulerZ, isExtrinsic = false) {
if (isExtrinsic) {
object.rotation.order = 'ZYX';
} else {
object.rotation.order = 'XYZ';
}
const q = new THREE.Quaternion();
const eul = new THREE.Euler(eulerX, eulerY, eulerZ, object.rotation.order);
q.setFromEuler(eul);
rotateQuaternionAroundPoint(object, point, q);
}
function rotateQuaternionAroundPoint(object, point, q) {
object.rotation.set(0, 0, 0)
object.position.set(0, 0, 0);
object.localToWorld(object.position);
object.position.sub(point);
object.position.applyQuaternion(q);
object.position.add(point);
object.worldToLocal(object.position);
}
let rotY = 0;
let rotationPoint = marker.localToWorld(marker.position.clone());
function animate() {
requestAnimationFrame( animate );
rotY += 0.01;
rotateEuler(orbiter, rotationPoint, 0, rotY, 0, true);
renderer.render( scene, camera );
}
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
The easiest way to do this is to reset the Vector3 to its initial state, then apply the quaternion to this reset vector. This erases any previous rotation from the equation. For instance, assuming the default state of the vector is pointing 'UP':
function f(v, q) {
v.set(0, 1, 0);
v.applyQuaternion(q);
return v;
}

Why does a mesh rotate over its own axis but Objects3D rotate over the world axis?

Here's some code as example:
var geometry = new THREE.BoxGeometry( larguraX,altura,comprimentoZ);
var material = new THREE.MeshBasicMaterial( {color: "pink"} );
var mmesh = new THREE.Mesh( geometry, material );
var objj = new THREE.Object3D();
objj.add(mmesh);
...
Why isn't this:
mmesh.translateX(50);
mmesh.rotateY(Math.PI/2);
resulting just like this:
objj.translate(50);
objj.rotateY(Math.PI/2);
They are the same.
Note the red and blue cubes are perfectly aligned making a purple box
'use strict';
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas: canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 200;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(50, 0, 2);
const scene = new THREE.Scene();
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
const material1 = new THREE.MeshBasicMaterial({
color: 'red', transparent: true, opacity: 0.5,
});
const material2 = new THREE.MeshBasicMaterial({
color: 'blue', transparent: true, opacity: 0.5,
});
const cube1 = new THREE.Mesh(geometry, material1);
const cube2 = new THREE.Mesh(geometry, material2);
const obj = new THREE.Object3D();
scene.add(cube1);
scene.add(obj);
obj.add(cube2);
cube1.translateX(50);
cube1.rotateY(Math.PI / 2);
obj.translateX(50);
obj.rotateY(Math.PI / 2);
renderer.render(scene, camera);
}
main();
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r94/three.min.js"></script>
you probably need to post more code

Three.js Animated curve

I try to animate a 2D curve in Three.js over time.
I'll need more than 4 control points, so I'm not using Bezier curves.
I created a Three.Line based on a SplineCurve.
If I log the geometry.vertices position of my line they'll change over time, but geometry.attributes.position remains the same. Is it possible to animate the line based on the curve animation ? I managed to do this with Bezier Curves, but can't find a way with SplineCurve.
Thank you for your help, here's my code :
First I create the line :
var curve = new THREE.SplineCurve( [
new THREE.Vector3( -10, 0, 10 ),
new THREE.Vector3( -5, 5, 5 ),
new THREE.Vector3( 0, 0, 0 ),
new THREE.Vector3( 5, -5, 5 ),
new THREE.Vector3( 10, 0, 10 )
] );
var points = curve.getPoints( 50 );
var geometry = new THREE.BufferGeometry().setFromPoints( points );
var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
// Create the final object to add to the scene
curveObject = new THREE.Line( geometry, material );
scene.add( curveObject );
curveObject.curve = curve;
Then I try to update it :
curveObject.curve.points[0].x += 1;
curveObject.geometry.vertices = curveObject.curve.getPoints( 50 );
curveObject.geometry.verticesNeedUpdate = true;
curveObject.geometry.attributes.needsUpdate = true;
You can do it like that:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, 1, 1, 1000);
camera.position.set(8, 13, 25);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
var canvas = renderer.domElement;
document.body.appendChild(canvas);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
scene.add(new THREE.GridHelper(20, 40));
var curve = new THREE.CatmullRomCurve3([
new THREE.Vector3(-10, 0, 10),
new THREE.Vector3(-5, 5, 5),
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(5, -5, 5),
new THREE.Vector3(10, 0, 10)
]);
var points = curve.getPoints(50);
var geometry = new THREE.BufferGeometry().setFromPoints(points);
var material = new THREE.LineBasicMaterial({
color: 0x00ffff
});
var curveObject = new THREE.Line(geometry, material);
scene.add(curveObject);
var clock = new THREE.Clock();
var time = 0;
render();
function resize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render() {
if (resize(renderer)) {
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
time += clock.getDelta();
curve.points[1].y = Math.sin(time) * 2.5;
geometry = new THREE.BufferGeometry().setFromPoints(curve.getPoints(50));
curveObject.geometry.dispose();
curveObject.geometry = geometry;
requestAnimationFrame(render);
}
html,
body {
height: 100%;
margin: 0;
overflow: hidden;
}
canvas {
width: 100%;
height: 100%;
display;
block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Categories

Resources