javascript - 在 Three.js 中模拟空气流经管道

标签 javascript three.js

我正在尝试使方向流动和空气流动,如下例所示:https://animagraffs.com/supercharger-vs-turbo/

enter image description here

正如我所见,只有纹理在移动

我不明白如何让空气在管道和表面上流向正确的方向。

非常感谢任何想法!

(我认为他们可能会使用一些 uv 展开技术并结合动画纹理的 UV 偏移)

最佳答案

看起来你可以调整纹理的 UV 偏移

texture.offset.x = someAnimatedValue;
texture.offset.y = someAnimatedValue;

至于制作管道本身,几乎任何 3D 建模包(blender、maya、3d studio max 等...)都可以让您extrude a line along a path 。因此,要制作管道,您需要制作一个圆圈,然后沿着路径挤压它。同样,您可以通过沿着同一条曲线挤出一条线,在管道的中心放置一堵墙/栅栏。默认 UV 坐标对于滚动来说是正确的。

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>

请注意,查看原始图表,我认为他们不是对管道进行动画处理,而是对管道内部的 strip 进行动画处理,其上有一个滚动的箭头纹理。因此,在建模包中,创建管道后,沿着路径挤出一条线。

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:我懒得处理排序问题,但处理这个问题应该是一个单独的问题

关于javascript - 在 Three.js 中模拟空气流经管道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43432263/

相关文章:

javascript - 在 for 循环中解决 setTimeout 函数内的 promise

javascript - Three.js 使用 Ray 检测 2 个移动物体之间的碰撞

javascript - Collada 装载机 : Is there anyway to get several meshes from the scene object?

javascript - 使用 Google Chrome 时启用 scrollX 或 scrollY 时数据表中出现重复的空标题

javascript - JQUERY PTTIMESELECT Timepicker 插件需要 Angular js 指令

Javascript/HTML 语法格式错误

javascript - 如何选择 Canvas 上的绘图?

javascript - Three.js Mesh 没有使用 AnimationHandler 进行动画处理

javascript - three.js:从简单 Material 切换到多开运行时

javascript - 如何使用 three.js 在运行时绘制线段