I am looking for a way to move the camera back with the code that I have here. Does anyone have any suggestions?
I have adjusted camera.position.set and added camera.position.x parameters.
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({
canvas
});
const fov = 45;
const aspect = 2;
const near = 0.1;
const far = 100;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 10, 20);
const controls = new THREE.OrbitControls(camera, canvas);
controls.target.set(0, 5, 0);
controls.update();
const scene = new THREE.Scene();
scene.background = new THREE.Color('black'); {
const planeSize = 0;
const loader = new THREE.TextureLoader();
const texture = loader.load('');
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.magFilter = THREE.NearestFilter;
const repeats = planeSize / 2;
texture.repeat.set(repeats, repeats);
const planeGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
const planeMat = new THREE.MeshPhongMaterial({
map: texture,
side: THREE.DoubleSide,
});
const mesh = new THREE.Mesh(planeGeo, planeMat);
mesh.rotation.x = Math.PI * -.5;
scene.add(mesh);
}
function frameArea(sizeToFitOnScreen, boxSize, boxCenter, camera) {
const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;
const halfFovY = THREE.Math.degToRad(camera.fov * .5);
const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);
const direction = (new THREE.Vector3())
.subVectors(camera.position, boxCenter)
.multiply(new THREE.Vector3(1, 0, 1))
.normalize();
camera.near = boxSize / 100;
camera.far = boxSize * 100;
camera.updateProjectionMatrix();
camera.lookAt(boxCenter.x, boxCenter.y, boxCenter.z);
}
const box = new THREE.Box3().setFromObject(root);
const boxSize = box.getSize(new THREE.Vector3()).length();
const boxCenter = box.getCenter(new THREE.Vector3());
frameArea(boxSize * 0.5, boxSize, boxCenter, camera);
controls.maxDistance = boxSize * 10;
controls.target.copy(boxCenter);
controls.update();
});
}
function resizeRendererToDisplaySize(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 (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
The answer to this question has been resolved.
I needed to increase the box size using this line of code:
frameArea(boxSize * 0.5, boxSize, boxCenter, camera);
The equations are based off of SOHCAHTOA. I needed to compute the distance and then move the camera that distance from the center of the box.
Related
juste starting to learn threejs and to do so decided to build a t-shirt configurator in combination with fabric js. my goal is that the user can upload a picture to the canvas (cnvs) which will be mapped to the gltf model of a t-shirt the probleme is that the texture doesn't show .
code :
var canvas2 = new fabric.Canvas('cnvs', {
backgroundColor: 'red'
});
fabric.Image.fromURL('eo.png', function(myImg) {
//i create an extra var for to change some image properties
var img1 = myImg.set({ left: 0, top: 0 ,width:150,height:150});
canvas2.add(img1);
canvas2.renderAll();
});
var canvasTexture = new THREE.CanvasTexture(canvas2);
canvasTexture.wrapS = THREE.RepeatWrapping;
canvasTexture.wrapT = THREE.RepeatWrapping;
const gltfLoader2 = new GLTFLoader();
gltfLoader2.load('tshirt2.gltf', (gltf2) => {
model = gltf2.scene;
model.traverse(child => {
console.log(child.material);
if (child.material && child.material.name === 'Pattern2D_13095') {
// Pattern2D_13095
child.material.map = new TextureLoader().load( new THREE.MeshStandardMaterial({
map: canvasTexture,
}));
}
});
canvas2.on("after:render", function() {
model.material.map.needsUpdate = true;
});
canvas2.on('mouse:down',function(event){
if(canvas2.getActiveObject()){
alert(event.target);
}
})
scene.add(model);
model.rotation.x = 1.5;
model.position.y=310;
model.position.x=-300;
model.position.z=-100;
model.scale.set(1000,1000,1000);
});
});
function resizeRendererToDisplaySize(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 (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
function animate(){
requestAnimationFrame(animate);
cloudParticles.forEach(p => {
p.rotation.z -= 0.001;
});
root.rotation.z -= 0.01;
renderer.render(scene, camera);
model.material.needsUpdate = true;
}
animate();
lights/camera:
let cloudParticles = [];
let root;
let model;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1,
1000 );
camera.position.z = 2;
camera.rotation.x = 1.16;
camera.rotation.y = -0.12;
camera.rotation.z = 0;
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
scene.fog = new THREE.FogExp2(0x11111f, 0.002);
renderer.setClearColor(scene.fog.color);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild( renderer.domElement );
let directionalLight = new THREE.DirectionalLight(0xff8c19);
directionalLight.position.set(0,0,1);
scene.add(directionalLight);
let orangeLight = new THREE.PointLight(0xcc6600,50,450,1.7);
orangeLight.position.set(200,300,100);
scene.add(orangeLight);
let redLight = new THREE.PointLight(0xd8547e,50,450,1.7);
redLight.position.set(100,300,100);
scene.add(redLight);
let blueLight = new THREE.PointLight(0x3677ac,50,450,1.7);
blueLight.position.set(300,300,200);
scene.add(blueLight);
let directionalLight2 = new THREE.DirectionalLight(0xffffff);
directionalLight2.position.set(-110,-110,-120);
scene.add(directionalLight2);
HTML:
<div class="cnvscon">
<canvas id="cnvs" height="256" width="256" ></canvas></div>
<canvas id="c"></canvas>
here is an img of the result ( the mesh is black ):
result
gltfviewer
child.material.map = new TextureLoader().load( new THREE.MeshStandardMaterial({
map: canvasTexture }));
I'm afraid this code is incorrect. It should be sufficient to assign canvasTexture to the respective map property. Meaning:
child.material.map = canvasTexture;
How can I use the object1.lookat(object2) function but freeze the z rotation? in Three.js
I have some objects in the field i and I want them to always face a new object2. This object2 can move around freely. However, the z rotation of object1 must always remain the same. The problem with the lookat() is that rotates every axis. Is there another way to do it?
Did you mean don’t rotate in X? lookAt already doesn’t rotate in Z
// Three.js - Responsive
// from https://threejsfundamentals.org/threejs/threejs-responsive.html
'use strict';
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 250;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(20, 10, 20);
const controls = new THREE.OrbitControls(camera, canvas);
controls.target.set(0, 0, 0);
controls.update();
const scene = new THREE.Scene();
scene.background = new THREE.Color('white');
function addLight(...pos) {
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(...pos);
scene.add(light);
}
addLight(-1, 2, 4);
addLight( 1, 2, 2);
const shape = new THREE.Shape();
shape.moveTo(-1, -1);
shape.lineTo( 0, -1);
shape.lineTo( 0, -2);
shape.lineTo( 2, 0);
shape.lineTo( 0, 2);
shape.lineTo( 0, 1);
shape.lineTo(-1, 1);
const extrudeSettings = {
depth: 1,
bevelEnabled: false,
};
const geometry = new THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
geometry.applyMatrix(new THREE.Matrix4().makeRotationY(Math.PI * -0.5));
const arrows = [];
const spread = 5;
for (let z = -3; z <= 3 ; ++z) {
for (let x = -3; x <= 3; ++x) {
const material = new THREE.MeshPhongMaterial({
color: new THREE.Color().setHSL(Math.abs(Math.atan2(x, z)) / Math.PI, 1, 0.5),
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
arrows.push(mesh);
mesh.position.set(x * spread, 0, z * spread);
}
}
const geometry2 = new THREE.SphereBufferGeometry();
const sphere = new THREE.Mesh(geometry2, new THREE.MeshPhongMaterial({color:'red'}));
const base = new THREE.Object3D();
scene.add(base)
base.position.y = 10;
const base2 = new THREE.Object3D();
base.add(base2);
base2.position.z = 15;
const base3 = new THREE.Object3D();
base2.add(base3);
base3.position.z = 5;
base3.add(sphere);
sphere.position.y = 5;
function resizeRendererToDisplaySize(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(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
base.rotation.y = time;
base2.rotation.y = time * 0.77;
base3.rotation.z = time * 2.33;
const temp = new THREE.Vector3();
for (const arrow of arrows) {
sphere.getWorldPosition(temp);
arrow.lookAt(temp);
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/js/controls/OrbitControls.js"></script>
maybe you mean not rotate in X? In that case get the target's position in a temp Vector3, the set the Y so it matches the y of the thing you want to aim, then call lookAt.
// Three.js - Responsive
// from https://threejsfundamentals.org/threejs/threejs-responsive.html
'use strict';
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 250;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(20, 10, 20);
const controls = new THREE.OrbitControls(camera, canvas);
controls.target.set(0, 0, 0);
controls.update();
const scene = new THREE.Scene();
scene.background = new THREE.Color('white');
function addLight(...pos) {
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(...pos);
scene.add(light);
}
addLight(-1, 2, 4);
addLight( 1, 2, 2);
const shape = new THREE.Shape();
shape.moveTo(-1, -1);
shape.lineTo( 0, -1);
shape.lineTo( 0, -2);
shape.lineTo( 2, 0);
shape.lineTo( 0, 2);
shape.lineTo( 0, 1);
shape.lineTo(-1, 1);
const extrudeSettings = {
depth: 1,
bevelEnabled: false,
};
const geometry = new THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
geometry.applyMatrix(new THREE.Matrix4().makeRotationY(Math.PI * -0.5));
const arrows = [];
const spread = 5;
for (let z = -3; z <= 3 ; ++z) {
for (let x = -3; x <= 3; ++x) {
const material = new THREE.MeshPhongMaterial({
color: new THREE.Color().setHSL(Math.abs(Math.atan2(x, z)) / Math.PI, 1, 0.5),
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
arrows.push(mesh);
mesh.position.set(x * spread, 0, z * spread);
}
}
const geometry2 = new THREE.SphereBufferGeometry();
const sphere = new THREE.Mesh(geometry2, new THREE.MeshPhongMaterial({color:'red'}));
const base = new THREE.Object3D();
scene.add(base)
base.position.y = 10;
const base2 = new THREE.Object3D();
base.add(base2);
base2.position.z = 15;
const base3 = new THREE.Object3D();
base2.add(base3);
base3.position.z = 5;
base3.add(sphere);
sphere.position.y = 5;
function resizeRendererToDisplaySize(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(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
base.rotation.y = time;
base2.rotation.y = time * 0.77;
base3.rotation.z = time * 2.33;
const temp = new THREE.Vector3();
for (const arrow of arrows) {
sphere.getWorldPosition(temp);
temp.y = arrow.position.y
arrow.lookAt(temp);
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/js/controls/OrbitControls.js"></script>
I'm looking for a way to wrap text around sphere in babylon or threejs. And i'm open-minded for changing javascript technology
I would look at an example of generating text. I’d then generate each letter separately recording their individual widths and use those to compute the total width across the string I want to display
I could then parent each mesh to an Object3D and set that Object3D’s rotation y to
widthSoFar = 0;
for each letter
obj3d.rotation.y = widthSoFar / totalWidth * Math.PI * 2;
widthSoFar += widthOfCurrentLetter;
And set the letter’s position.z to some radius which would put the letters around a circle.
What radius?
circumference = 2 * PI * radius
so
radius = circumference / (2 * PI)
We know the circumference we need, it’s the totalWidth of the string.
You might find this tutorial helpful in understanding how to use scene graph nodes (like the Object3D node) to organize a scene to meet your needs.
'use strict';
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 40;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 70;
const scene = new THREE.Scene();
scene.background = new THREE.Color('black');
function addLight(...pos) {
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(...pos);
scene.add(light);
}
addLight(-4, 4, 4);
addLight(5, -4, 4);
const lettersTilt = new THREE.Object3D();
scene.add(lettersTilt);
lettersTilt.rotation.set(
THREE.Math.degToRad(-15),
0,
THREE.Math.degToRad(-15));
const lettersBase = new THREE.Object3D();
lettersTilt.add(lettersBase);
{
const letterMaterial = new THREE.MeshPhongMaterial({
color: 'red',
});
const loader = new THREE.FontLoader();
loader.load('https://threejsfundamentals.org/threejs/resources/threejs/fonts/helvetiker_regular.typeface.json', (font) => {
const spaceSize = 1.0;
let totalWidth = 0;
let maxHeight = 0;
const letterGeometries = {
' ': { width: spaceSize, height: 0 }, // prepopulate space ' '
};
const size = new THREE.Vector3();
const str = 'threejs fundamentals ';
const letterInfos = str.split('').map((letter, ndx) => {
if (!letterGeometries[letter]) {
const geometry = new THREE.TextBufferGeometry(letter, {
font: font,
size: 3.0,
height: .2,
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 0.5,
bevelSize: .3,
bevelSegments: 5,
});
geometry.computeBoundingBox();
geometry.boundingBox.getSize(size);
letterGeometries[letter] = {
geometry,
width: size.x / 2, // no idea why size.x is double size
height: size.y,
};
}
const {geometry, width, height} = letterGeometries[letter];
const mesh = geometry
? new THREE.Mesh(geometry, letterMaterial)
: null;
totalWidth += width;
maxHeight = Math.max(maxHeight, height);
return {
mesh,
width,
};
});
let t = 0;
const radius = totalWidth / Math.PI;
for (const {mesh, width} of letterInfos) {
if (mesh) {
const offset = new THREE.Object3D();
lettersBase.add(offset);
offset.add(mesh);
offset.rotation.y = t / totalWidth * Math.PI * 2;
mesh.position.z = radius;
mesh.position.y = -maxHeight / 2;
}
t += width;
}
{
const geo = new THREE.SphereBufferGeometry(radius - 1, 32, 24);
const mat = new THREE.MeshPhongMaterial({
color: 'cyan',
});
const mesh = new THREE.Mesh(geo, mat);
scene.add(mesh);
}
camera.position.z = radius * 3;
});
}
function resizeRendererToDisplaySize(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(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
lettersBase.rotation.y = time * -0.5;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/three.min.js"></script>
I create a scene, and it is all fine without using OrbitControls.
When I use OrbitControls, I found that my camera's position and rotation has changed, and I can't modify it.
Can somebody tell me how to set a default position and rotation of the camera with OrbitControls.
Thanks!
OrbitControls require a target. Set the target such that you get the same view.
camera.position.set(1, 8, 7);
const controls = new THREE.OrbitControls(camera, canvas);
controls.target.set(0, 3, 0);
controls.update();
'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 = 500;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(1, 8, 7);
const controls = new THREE.OrbitControls(camera, canvas);
controls.target.set(0, 3, 0);
controls.update();
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
{
const boxWidth = 1;
const boxHeight = 10;
const boxDepth = 1;
const geometry = new THREE.BoxBufferGeometry(boxWidth, boxHeight, boxDepth);
const material = new THREE.MeshPhongMaterial({color: 'red'});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cube.position.y = .5;
}
{
const geometry = new THREE.PlaneBufferGeometry(10, 10);
const material = new THREE.MeshPhongMaterial({color: 'gray'});
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
plane.rotation.x = Math.PI * -0.5;
}
function resizeRendererToDisplaySize(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(time) {
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/js/controls/OrbitControls.js"></script>
Or you could compute a target based on the current camera view. The OrbitControls orbit around the target so you'd need to choose a distance from the camera as your target
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(1, 2, 5);
camera.rotation.set(.1, .2, 0);
// get the direction of the camera
const direction = new THREE.Vector3();
camera.getWorldDirection(direction);
const controls = new THREE.OrbitControls(camera, canvas);
// point the target from the camera in the
// target direction
camera.getWorldPosition(controls.target);
controls.target.addScaledVector(direction, 5);
controls.update();
Note the 5 in addScaledVector means the target will be 5 units in front of the camera in the direction the camera was facing. Whether 5 is the right distance is up to you. In my sample scene the camera started at z = 5 so 5 units in front of the camera seemed like a reasonable place to put the target
'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 = 500;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(1, 2, 5);
camera.rotation.set(.1, .2, 0);
// compute a target direction
const direction = new THREE.Vector3();
camera.getWorldDirection(direction);
const controls = new THREE.OrbitControls(camera, canvas);
// point the target from the camera in the
// target direction
camera.getWorldPosition(controls.target);
controls.target.addScaledVector(direction, 5);
controls.update();
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
{
const boxWidth = 1;
const boxHeight = 10;
const boxDepth = 1;
const geometry = new THREE.BoxBufferGeometry(boxWidth, boxHeight, boxDepth);
const material = new THREE.MeshPhongMaterial({color: 'red'});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cube.position.y = .5;
}
{
const geometry = new THREE.PlaneBufferGeometry(10, 10);
const material = new THREE.MeshPhongMaterial({color: 'gray'});
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
plane.rotation.x = Math.PI * -0.5;
}
function resizeRendererToDisplaySize(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(time) {
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/js/controls/OrbitControls.js"></script>
I'm trying to make the direction flow and air flow like this example: https://animagraffs.com/supercharger-vs-turbo/
As I can see there's only the texture is moving
I don't understand how to make the air flowing to correct direction on the pipe and surface like that.
Any idea is much appreciated!
(I'm thinking they might use some uv unwrap technique combine with animate the UV offset of texture)
It looks like you can just adjust a texture's UV offset
texture.offset.x = someAnimatedValue;
texture.offset.y = someAnimatedValue;
As for making the pipe itself, pretty much any 3d modeling package (blender, maya, 3d studio max, etc...) will let you extrude a line along a path. So to make the pipe you make a circle and then extrude it along the path. Similarly you can put a wall/fence down the center of a pipe by extruding a line down the same curve. The default UV coordinates will be correct for scrolling.
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
document.body.appendChild(renderer.domElement);
// make a texture with an arrow
const ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = 64;
ctx.canvas.height = 64;
ctx.fillStyle = "rgba(0,0,255,0.5)";
ctx.fillRect(0, 0, 64, 64);
ctx.translate(32, 32);
ctx.rotate(Math.PI * .5);
ctx.fillStyle = "rgb(0,255,255)";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "48px sans-serif";
ctx.fillText("➡︎", 0, 0);
const texture = new THREE.CanvasTexture(ctx.canvas);
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.x = 4;
texture.repeat.y = 9;
const radiusTop = 1;
const radiusBottom = 1;
const height = 5;
const radiusSegments = 20;
const heightSegments = 2;
const openEnded = true;
const geometry = new THREE.CylinderBufferGeometry(
radiusTop, radiusBottom, height, radiusSegments, heightSegments, openEnded);
const material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide,
depthWrite: false,
depthTest: false,
transparent: true,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
mesh.rotation.z = Math.PI * .5;
function render(time) {
time *= 0.001;
resize();
const cameraSpeed = time * 0.3;
const cameraRadius = 5;
camera.position.x = Math.cos(cameraSpeed) * cameraRadius;
camera.position.y = 1;
camera.position.z = Math.sin(cameraSpeed) * cameraRadius;
camera.lookAt(mesh.position);
texture.offset.y = (time * 3 % 1);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
function resize() {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
if (canvas.width !== width || canvas.height !== height) {
renderer.setSize(width, height, false);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.min.js"></script>
Note that looking at the original diagram I'd stay they aren't animating a pipe, they are animating a strip that's inside the pipe at has an arrow texture being scrolled on it. So, in you modeling package, after creating the pipes extrude a line down the path.
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
document.body.appendChild(renderer.domElement);
// make a texture with an arrow
const ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = 64;
ctx.canvas.height = 64;
ctx.translate(32, 32);
ctx.rotate(Math.PI * .5);
ctx.fillStyle = "rgb(0,255,255)";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "48px sans-serif";
ctx.fillText("➡︎", 0, 0);
const texture = new THREE.CanvasTexture(ctx.canvas);
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.x = 1;
texture.repeat.y = 5;
const radiusTop = 1;
const radiusBottom = 1;
const height = 5;
const radiusSegments = 20;
const heightSegments = 2;
const openEnded = true;
const geometry = new THREE.CylinderBufferGeometry(
radiusTop, radiusBottom, height, radiusSegments, heightSegments, openEnded);
const material = new THREE.MeshBasicMaterial({
color: 0x4040FF,
opacity: 0.5,
side: THREE.DoubleSide,
depthWrite: false,
depthTest: false,
transparent: true,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
mesh.rotation.z = Math.PI * .5;
const stripGeo = new THREE.PlaneBufferGeometry(radiusTop * 1.7, height);
const stripMat = new THREE.MeshBasicMaterial({
map: texture,
opacity: 0.5,
side: THREE.DoubleSide,
depthWrite: false,
depthTest: false,
transparent: true,
});
const stripMesh = new THREE.Mesh(stripGeo, stripMat);
scene.add(stripMesh);
stripMesh.rotation.z = Math.PI * .5;
function render(time) {
time *= 0.001;
resize();
const cameraSpeed = time * 0.3;
const cameraRadius = 5;
camera.position.x = Math.cos(cameraSpeed) * cameraRadius;
camera.position.y = 1;
camera.position.z = Math.sin(cameraSpeed) * cameraRadius;
camera.lookAt(mesh.position);
texture.offset.y = (time * 3 % 1);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
function resize() {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
if (canvas.width !== width || canvas.height !== height) {
renderer.setSize(width, height, false);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.min.js"></script>
PS: I'm too lazy to deal with the sorting issues but dealing with that should be a separate quesiton