I am trying to achieve smooth camera rotation effects similar to this http://showroom.littleworkshop.fr/
But the camera rotates in all three axis (Yaw, Pitch and Roll).
My code is:
var targetQuat = new THREE.Quaternion();
OnMouseDownHandler() {
var mouseVector = new THREE.Vector3(VS.Mouse.x, VS.Mouse.y);
mouseVector.unproject(VS.Camera);
var dir = mouseVector.sub(VS.Camera.position).normalize();
var oldPos = VS.Camera.position.clone();
var cameraPosN = oldPos.normalize();
var v1 = new THREE.Vector3(dir.x, dir.y, dir.z);
var quaternion = new THREE.Quaternion();
quaternion.setFromAxisAngle( new THREE.Vector3( 0, 1, 0 ), Math.PI / 4 );
v1.applyQuaternion( quaternion );
targetQuat.setFromUnitVectors(cameraPosN, v1);
}
///////////
renderLoop() {
requestAnimationFrame(VS.RenderFrame);
var deltaTime = VS.Clock.getDelta();
VS.Camera.quaternion.slerp(targetQuat, deltaTime);
...
}
Related
My image texture is positioned relative to the center of 3d space instead of mesh and I don't quite understand what determines its size.
Here is example showing how the same image is positioned on different meshes:
https://imgur.com/glHE97L
I'd like the image be in the center of the mesh and it's size set similar as 'contain' in css.
The mesh is flat plane created using ShapeBufferGeometry:
const shape = new THREE.Shape( edgePoints );
const geometry = new THREE.ShapeBufferGeometry( shape );
To see any image I have to set:
texture.repeat.set(0.001, 0.001);
Not sure if that matters but after creating the mesh I than set its position and rotation:
mesh.position.copy( position[0] );
mesh.rotation.set( rotation[0], rotation[1], rotation[2] );
I've tried setting those:
mesh.updateMatrixWorld( true );
mesh.geometry.computeBoundingSphere();
mesh.geometry.verticesNeedUpdate = true;
mesh.geometry.elementsNeedUpdate = true;
mesh.geometry.morphTargetsNeedUpdate = true;
mesh.geometry.uvsNeedUpdate = true;
mesh.geometry.normalsNeedUpdate = true;
mesh.geometry.colorsNeedUpdate = true;
mesh.geometry.tangentsNeedUpdate = true;
texture.needsUpdate = true;
I've played with wrapS / wrapT and offset.
I've checked UV's - I don't yet fully understand this concept but it seems fine. Example of UV for one mesh (I understand those are XY coordinates and they seem to reflect the actual corners of my mesh):
uv: Float32BufferAttribute
array: Float32Array(8)
0: -208
1: 188
2: 338
3: 188
4: 338
5: 12
6: -208
7: 12
I've tried setting:
texture.repeat.set(imgHeight/geometryHeight/1000, imgWidth/geometryWidth/1000);
This is how THREE.ShapeGeometry() computes UV coordinate:
https://github.com/mrdoob/three.js/blob/e622cc7890e86663011d12ec405847baa4068515/src/geometries/ShapeGeometry.js#L157
But you can re-compute them, to put in range [0..1].
Here is an example, click the button to re-compute uvs of the shape geometry:
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 points = [
new THREE.Vector2(0, 5),
new THREE.Vector2(-5, 4),
new THREE.Vector2(-3, -3),
new THREE.Vector2(2, -5),
new THREE.Vector2(5, 0)
];
var shape = new THREE.Shape(points);
var shapeGeom = new THREE.ShapeBufferGeometry(shape);
var shapeMat = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/uv_grid_opengl.jpg")
});
var mesh = new THREE.Mesh(shapeGeom, shapeMat);
scene.add(mesh);
btnRecalc.addEventListener("click", onClick);
var box3 = new THREE.Box3();
var size = new THREE.Vector3();
var v3 = new THREE.Vector3(); // for re-use
function onClick(event) {
box3.setFromObject(mesh); // get AABB of the shape mesh
box3.getSize(size); // get size of that box
var pos = shapeGeom.attributes.position;
var uv = shapeGeom.attributes.uv;
for (let i = 0; i < pos.count; i++) {
v3.fromBufferAttribute(pos, i);
v3.subVectors(v3, box3.min).divide(size); // cast world uvs to range 0..1
uv.setXY(i, v3.x, v3.y);
}
uv.needsUpdate = true; // set it to true to make changes visible
}
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<button id="btnRecalc" style="position: absolute;">Re-calculate UVs</button>
Hello i'm trying to make a wave pattern on the surface of a cylinder. The waves should rotate with the rotation of the surface. and in a way the sine period is moving in circles, and the amplitudes are long mounds on the surface. Here's some pictures to better explain what i mean.
This is what i'm trying to get the top down view of the cylinder to look similar to:
this is the top view of my cylinder. I'd like the wave to change direction with the rotation of the circle, so it looks the same from all directions.
I feel like i'm very close, i'm just not sure what quaternion or angle to multiply against the vector:
var geometry = this.threeDHandler.threeD_meshes[0].geometry;
var vec3 = new THREE.Vector3(); // temp vector
for (let i = 0; i < geometry.vertices.length; i++) {
vec3.copy(geometry.vertices[i]); // copy current vertex to the temp vector
vec3.setX(0);
vec3.normalize(); // normalize
//part i'm confsude about
const quaternion = new THREE.Quaternion();
const xPos = geometry.vertices[i].x;
//trying to twist the sine around the circle
const twistAmount = 100;
const upVec = new THREE.Vector3(0, 0, 1);
quaternion.setFromAxisAngle(
upVec,
(Math.PI / 180) * (xPos / twistAmount)
);
vec3.multiplyScalar(Math.sin((geometry.vertices[i].x* Math.PI) * period) * amplitude) // multiply with sin function
geometry.vertices[i].add(vec3); // add the temp vector to the current vertex
geometry.vertices[i].applyQuaternion(quaternion);
}
geometry.verticesNeedUpdate = true;
geometry.computeVertexNormals();
You can use absolute value of the sin function of the angle, that a vertex belongs to.
In this case you can use THREE.Spherical() object that allows to get spherical coordinates of a vector:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 6);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var cylinderGeom = new THREE.CylinderGeometry(1, 1, 4, 128, 40, true);
var vec3 = new THREE.Vector3(); // temp vector
var vec3_2 = new THREE.Vector3(); // temp vector 2
var spherical = new THREE.Spherical();
cylinderGeom.vertices.forEach(v => {
vec3.copy(v); // copy current vertex to the temp vector
vec3.setY(0); // leave x and z (thus the vector is parallel to XZ plane)
vec3.normalize(); // normalize
vec3.multiplyScalar(Math.sin(v.y * Math.PI) * 0.25) // multiply with sin function
// radial wave
vec3_2.copy(v).setY(0).normalize();
spherical.setFromVector3(vec3_2);
vec3_2.setLength(Math.abs(Math.sin((spherical.theta * 4) + v.y * 2) * 0.25));
v.add(vec3).add(vec3_2); // add the temp vectors to the current vertex
})
cylinderGeom.computeVertexNormals();
var cylinder = new THREE.Mesh(cylinderGeom, new THREE.MeshNormalMaterial({
side: THREE.DoubleSide,
wireframe: false
}));
scene.add(cylinder);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
})
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.124.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.124.0/examples/js/controls/OrbitControls.js"></script>
I want to create a "U" shaped magnet in three.js. So can I use TubeGeometry for that?
So if this is the code for creating a 3D sin curve. How can I make it as "U" shaped Magnet?
var CustomSinCurve = THREE.Curve.create(
function ( scale ) { //custom curve constructor
this.scale = ( scale === undefined ) ? 1 : scale;
},
function ( t ) { //getPoint: t is between 0-1
var tx = t * 3 - 1.5;
var ty = Math.sin( 2 * Math.PI * t );
var tz = 0;
return new THREE.Vector3( tx, ty, tz ).multiplyScalar(this.scale);
}
);
var path = new CustomSinCurve( 10 );
var geometry = new THREE.TubeGeometry( path, 20, 2, 8, false );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
If the shape of the magnet's profile is not critical (rectangle instead of circle), then you can use THREE.ExtrudeGeometry():
var path = new THREE.Shape(); // create a U-shape with its parts
path.moveTo(-1, 1);
path.absarc(0, 0, 1, Math.PI, Math.PI * 2);
path.lineTo(1, 1);
path.lineTo(.8, 1);
path.absarc(0, 0, .8, Math.PI * 2, Math.PI, true);
path.lineTo(-.8,1);
path.lineTo(-1, 1);
var extOpt = { // options of extrusion
curveSegments: 15,
steps: 1,
amount: .2,
bevelEnabled: false
}
var uGeom = new THREE.ExtrudeGeometry(path, extOpt); // create a geometry
uGeom.center(); // center the geometry
var average = new THREE.Vector3(); // this variable for re-use
uGeom.faces.forEach(function(face){
average.addVectors(uGeom.vertices[face.a], uGeom.vertices[face.b]).add(uGeom.vertices[face.c]).divideScalar(3); // find the average vector of a face
face.color.setHex(average.x > 0 ? 0xFF0000 : 0x0000FF); // set color of faces, depends on x-coortinate of the average vector
});
var uMat = new THREE.MeshBasicMaterial({ vertexColors: THREE.FaceColors }); // we'll use face colors
var u = new THREE.Mesh(uGeom, uMat);
scene.add(u);
jsfiddle example
I am trying to create a flat ring in three.js with a concentric circles texture, like Saturn's rings. I cannot manage to do anything but lines that radiate from the center (like a bicycle wheel), no matter what I put in the image. It seems that textures are applied to RingGeometry in a very different fashion than CircleGeometry.
I could easily apply a concentric circles texture to a CircleGeometry, but a ring (with a hole in the middle) is really what I need. Is anybody aware of a way to have textures on rings do something else than radiate?
I did not find a way in Three.js documentation, nor on the web, to do what I want, as it seems that rings are seldom used by anybody...
Thank you
Go here http://jsfiddle.net/theo/VsWb9/ and replace
geometry = new THREE.CubeGeometry(200, 200, 200);
material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh(geometry, material);
with
geometry = new THREE.TorusGeometry( 100, .5 , 50 ,50);
material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh( geometry, material );
If you want to change the ring color to say black for instance
change
material = new THREE.MeshNormalMaterial();
to
material = new THREE.MeshBasicMaterial({color:0x000});
Stick any other material changes in that array input argument to the constructor
function THREE.MeshBasicMaterial({arguments here})
I found this for making the geometry. It creates a disk of theataSeegmens triangles
this.RingGeometry = function ( innerRadius, outerRadius, thetaSegments) {
THREE.Geometry.call( this )
innerRadius = innerRadius || 0
outerRadius = outerRadius || 50
thetaSegments = thetaSegments || 8
innerRadius*=Obj.Size*100;
outerRadius*=Obj.Size*100;
var normal = new THREE.Vector3( 0, 0, 1 )
for(var i = 0; i < thetaSegments; i++ ){
var angleLo = (i / thetaSegments) *Math.PI*2
var angleHi = ((i+1) / thetaSegments) *Math.PI*2
var vertex1 = new THREE.Vector3(innerRadius * Math.cos(angleLo), innerRadius * Math.sin(angleLo), 0);
var vertex2 = new THREE.Vector3(outerRadius * Math.cos(angleLo), outerRadius * Math.sin(angleLo), 0);
var vertex3 = new THREE.Vector3(innerRadius * Math.cos(angleHi), innerRadius * Math.sin(angleHi), 0);
var vertex4 = new THREE.Vector3(outerRadius * Math.cos(angleHi), outerRadius * Math.sin(angleHi), 0);
this.vertices.push( vertex1 );
this.vertices.push( vertex2 );
this.vertices.push( vertex3 );
this.vertices.push( vertex4 );
var vertexIdx = i * 4;
// Create the first triangle
var face = new THREE.Face3(vertexIdx + 0, vertexIdx + 1, vertexIdx + 2, normal);
var uvs = []
var uv = new THREE.Vector2(0, 0)
uvs.push(uv)
var uv = new THREE.Vector2(1, 0)
uvs.push(uv)
var uv = new THREE.Vector2(0, 1)
uvs.push(uv)
this.faces.push(face);
this.faceVertexUvs[0].push(uvs);
// Create the second triangle
var face = new THREE.Face3(vertexIdx + 2, vertexIdx + 1, vertexIdx + 3, normal);
var uvs = []
var uv = new THREE.Vector2(0, 1)
uvs.push(uv)
var uv = new THREE.Vector2(1, 0)
uvs.push(uv)
var uv = new THREE.Vector2(1, 1)
uvs.push(uv)
this.faces.push(face);
this.faceVertexUvs[0].push(uvs);
}
//this.computeCentroids();
//this.computeFaceNormals();
this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), outerRadius );
};
this.RingGeometry.prototype = Object.create( THREE.Geometry.prototype );
The above line is important to get it to work.
Here is a sugestion how to set the ring material.assuming that you have two pictures (just a sqare section) that can be used for alphamap and for
the actual ring.
var ringMaterial = new THREE.MeshPhongMaterial(
{
map: SaturnRingColor,
alphaMap:SaturnRingPattern,
color: 0xffffff,
specular: 0x555555,
shininess: 3,
emissive:10,
side: THREE.DoubleSide,
castshadow:true,
transparent : true,
opacity : 0.9,
} );
this.ringMesh = new THREE.Mesh( this.RingGeometry , RingMaterial );
I have a texture applied to a mesh I can change the offset with
mesh.material.map.offset.set
I can change the scaling with
mesh.material.repeat.set
so my question is, how can I rotate texture inside a plane?
Example:
From This:
To this
Thanks.
use 2D canvas as a texture
demo:
https://dl.dropboxusercontent.com/u/1236764/temp/stackoverflow_20130525/index.html
example code
var camera, scene, renderer, mesh;
var width = window.innerWidth;
var height = window.innerHeight;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 30, width / height, 1, 1000 );
camera.position.z = 100;
renderer = new THREE.WebGLRenderer();
renderer.setSize( width, height );
document.body.appendChild( renderer.domElement );
var img = new Image();
img.onload = createMeshThenRender;
img.src = 'img.jpg';
function createMeshThenRender () {
var imgWidth = imgHeight = 256;
var mapCanvas = document.createElement( 'canvas' );
mapCanvas.width = mapCanvas.height = 256;
// document.body.appendChild( mapCanvas );
var ctx = mapCanvas.getContext( '2d' );
ctx.translate( imgWidth / 2, imgHeight / 2 );
ctx.rotate( Math.PI / 4 );
ctx.translate( -imgWidth / 2, -imgHeight / 2 );
ctx.drawImage( img, 0, 0, imgWidth, imgHeight );
var texture = new THREE.Texture( mapCanvas );
texture.needsUpdate = true;
mesh = new THREE.Mesh(
new THREE.PlaneGeometry( 50, 50, 1, 1 ),
new THREE.MeshBasicMaterial( {
map : texture
} )
);
scene.add( mesh );
renderer.render( scene, camera );
}
three.js does not have a UV editing utility, so you either have to edit the geometry.faceVertexUvs manually, or rotate your image in an image editing program. I'd suggest the latter.
three.js r.58
three.js r85
For those looking to actually "rotate UVs" on a Plane sitting in XY plane (default plane) using a ShapeBufferGeometry or PlaneBufferGeometry.
var planeGeo = new THREE.PlaneBufferGeometry(24,24);
var planeMesh = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({map: yourTexture}));
scene.add(planeMesh);
rotateUVonPlanarBufferGeometry(45, planeMesh);
function rotateUVonPlanarBufferGeometry(rotateInDeg, mesh){
if(rotateInDeg != undefined && mesh){
var degreeInRad = THREE.Math.degToRad(rotateInDeg);
var tempGeo = mesh.geometry.clone();
var geo;
if(tempGeo instanceof THREE.BufferGeometry){
geo = new THREE.Geometry().fromBufferGeometry(tempGeo);
}else{
console.log('regular geometry currently not supported in this method, but can be if code is modified, so use a buffer geometry');
return;
}
// rotate the geo on Z-axis
// which will rotate the vertices accordingly
geo.applyMatrix(new THREE.Matrix4().makeRotationZ(degreeInRad));
// loop through the vertices which should now have been rotated
// change the values of UVs based on the new rotated vertices
var index = 0;
geo.vertices.forEach(function(v){
mesh.geometry.attributes.uv.setXY( index, v.x, v.y );
index++;
});
mesh.geometry.attributes.uv.needsUpdate = true;
}
}
three.js r121
In the newer version of the three.js, you can directly set rotation and rotation center of texture.
var texture = new THREE.Texture( ... );
texture.rotation = Math.PI/4;
texture.center = new Vector2d(0.5, 0.5); // center of texture.
The above solutions were not good to me, a little bit old. That's simple solution worked for me (Three.js 125)
imgData = canvasCtx.getImageData(0, 0, canvasElement.width, canvasElement.height);
texture = new THREE.DataTexture( imgData.data, canvasElement.width, canvasElement.height, THREE.SRGB );
texture.rotation = Math.PI;
texture.center = new THREE.Vector2(0.5, 0.5); // center of texture.
mymaterial = new THREE.MeshBasicMaterial({
map: texture,
alphaTest: 0.5,
transparent: true,
side: THREE.DoubleSide,
});