在Three.js模拟流经管道的空气

时间:2017-04-15 23:32:38

标签: javascript three.js

我正试图像这个例子那样制作方向流和气流:https://animagraffs.com/supercharger-vs-turbo/

enter image description here

我可以看到只有纹理在移动

我不明白如何使空气像管那样在管道和表面上正确流动。

非常感谢任何想法!

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

1 个答案:

答案 0 :(得分:6)

看起来您可以调整纹理的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>

请注意,查看原始图表时我会留下它们不是动画管道,它们会为管道内的条带设置动画,并在其上滚动箭头纹理。因此,在建模包中,在创建管道后沿路径挤出一条线。

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