【问题标题】:'plus' or 'x' shaped wave in threejsThreejs中的“加号”或“X”形波浪
【发布时间】:2020-07-19 09:20:47
【问题描述】:

我用 THREE.js 制作了这个场景。 (请参阅帖子中附加的代码 sn-p) 或https://codepen.io/farisk/pen/jOPgKGQ

目前它是基于“distance()”函数中的公式辐射圆形波

这样:

return Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2));

我想知道是否可以修改公式,以便我可以以某种方式获得“X”或“加号 (+)”形状的波?

var once = false;

class App {
  init() {
    this.stats = new Stats();
    this.stats.showPanel(0);
    document.body.querySelector('.stats').appendChild(this.stats.domElement);

    this.backgroundColor = 0x000000;
    this.ambientLightColor = 0xffffff;
    this.spotLightColor = 0xff9999;
    // this.boxColor = 0x1a63ed;
    this.boxColor = 0xffffff;
    this.angle = 0;
    this.gridSize = 30;
    this.ratio = 1.3
    this.col = this.gridSize*this.ratio;
    this.row = this.gridSize;
    this.velocity = .05;
    this.boxes = [];

    this.amplitude = -7;
    this.frequency = 0;
    this.waveLength = 242;

    this.scene = new THREE.Scene();
    this.scene.background = new THREE.Color(this.backgroundColor);

    this.camera = new THREE.PerspectiveCamera(2, window.innerWidth / window.innerHeight, 1, 10000);
    this.camera.position.set(0, 800, 0);

    this.addRenderer();

    document.body.appendChild(this.renderer.domElement);

    this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);

    this.addAmbientLight();

    this.addDirectionalLight();

    this.addFloor();

    this.addBoxes(this.scene);

    this.addGUIControls();

    this.animate();

    window.addEventListener('resize', this.onResize.bind(this));
  }

  addDirectionalLight() {
    this.directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    this.directionalLight.castShadow = true;
    this.directionalLight.position.set(0, 1, 0);

    this.directionalLight.shadow.camera.far = 10000;
    this.directionalLight.shadow.camera.near = -100;

    this.directionalLight.shadow.camera.left = -40;
    this.directionalLight.shadow.camera.right = 40;
    this.directionalLight.shadow.camera.top = 20;
    this.directionalLight.shadow.camera.bottom = -20;
    this.directionalLight.shadow.camera.zoom = 1;
    this.directionalLight.shadow.camera.needsUpdate = true;

    const targetObject = new THREE.Object3D();
    targetObject.position.set(-50, -82, 40);
    this.directionalLight.target = targetObject;

    this.scene.add(this.directionalLight);
    this.scene.add(this.directionalLight.target);
  }

  addGUIControls() {
    this.gui = new dat.GUI();
    this.gui.add(this, 'amplitude', -10, 10);
    this.gui.add(this, 'velocity', 0, .5);
    this.gui.add(this, 'waveLength', 100, 500);
    this.controller = this.gui.add(this, 'gridSize', 24, 150);

    this.controller.onFinishChange((value) => {
      this.gridSize = Math.floor(value);

      this.clearScene();

      this.col = this.gridSize*this.ratio;
      this.row = this.gridSize;

      this.addBoxes(this.scene);
    });
  }

  addRenderer() {
    this.renderer = new THREE.WebGLRenderer({ antialias: true });
    this.renderer.shadowMap.enabled = true;
    this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    this.renderer.setSize(window.innerWidth, window.innerHeight);
  }

  addAmbientLight() {
    const light = new THREE.AmbientLight(this.ambientLightColor, .5);
    this.scene.add(light);
  }

  addSpotLight() {
    this.spotLight = new THREE.SpotLight(this.spotLightColor);
    this.spotLight.position.set(100, 250, 150);
    this.spotLight.castShadow = true;
    this.scene.add(this.spotLight);
  }

  clearScene() {
    this.scene.remove(this.mesh);

    this.boxes = [];
  }

  addBoxes(scene) {
    const size = 0.05;
    const height = 20;
    const material = new THREE.MeshLambertMaterial({
      color: this.boxColor,
    });

    const geometry = new THREE.BoxBufferGeometry(size, height, size);
    geometry.translate( 0, 2.5, 0 );
    this.mesh = this.getBox(geometry, material, this.row * this.col);
    this.scene.add(this.mesh);

    let ii = 0;

    for (let i = 0; i < this.col; i++) {
      this.boxes[i] = [];

      for (let j = 0; j < this.row; j++) {
        const pivot = new THREE.Object3D();
        this.boxes[i][j] = pivot;

        pivot.scale.set(1, 0.001, 1);
        // pivot.position.set(i - this.gridSize*this.ratio * .5, height * .5, j - this.gridSize * .5);
        pivot.position.set(i - this.gridSize*this.ratio * .5, height * 0, j - this.gridSize * .5);

        pivot.updateMatrix();
        this.mesh.setMatrixAt(ii++, pivot.matrix);
      }
    }

    this.mesh.instanceMatrix.needsUpdate = true;
  }

  drawWave() {
    let ii= 0;

    for (let i = 0; i < this.col; i++) {
      for (let j = 0; j < this.row; j++) {
        const distance = this.distance(j, i, this.row * .5, this.col * .5);

        const offset = this.map(distance, 0, this.waveLength, -100, 100);
        const angle = this.angle + offset ;
        if (!once) {
          console.log(this.boxes)
          once = true
        }
        this.boxes[i][j].scale.y = this.map(Math.sin(angle), -1, -this.amplitude, 0.001, 1);
        this.boxes[i][j].rotation.z = this.map(Math.sin(angle), -1, -this.amplitude, 0.001, 1);

        this.boxes[i][j].updateMatrix();
        this.mesh.setMatrixAt(ii++, this.boxes[i][j].matrix);
      }
    }

    this.mesh.instanceMatrix.needsUpdate = true;

    this.angle -= this.velocity;
  }

  distance(x1, y1, x2, y2) {
    // return Math.sin(x1 - x2)
    return Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2));
  }

  map(value, start1, stop1, start2, stop2) {
    return (value - start1) / (stop1 - start1) * (stop2 - start2) + start2
  }

  addFloor() {
    const planeGeometry = new THREE.PlaneBufferGeometry(10, 500);
    const planeMaterial = new THREE.ShadowMaterial({ opacity:1 });

    this.floor = new THREE.Mesh(planeGeometry, planeMaterial);

    planeGeometry.rotateX(- Math.PI / 2);

    this.floor.position.y = 2;
    this.floor.receiveShadow = true;

    this.scene.add(this.floor);
  }

  getBox(geometry, material, count) {
    const mesh = new THREE.InstancedMesh(geometry, material, count);
    mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
    mesh.castShadow = true;
    mesh.receiveShadow = true;

    return mesh;
  }

  addGrid() {
    const size = this.col;
    const divisions = size;
    const gridHelper = new THREE.GridHelper(size, divisions);

    gridHelper.position.set(0, 0, 0);
    gridHelper.material.opacity = 0;
    gridHelper.material.transparent = true;

    this.scene.add(gridHelper);
  }

  animate() {
    this.stats.begin();

    this.drawWave();

    this.controls.update();

    this.renderer.render(this.scene, this.camera);

    this.stats.end();

    requestAnimationFrame(this.animate.bind(this));
  }

  onResize() {
    const ww = window.innerWidth;
    const wh = window.innerHeight;

    this.camera.aspect = ww / wh;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(ww, wh);
  }
}

new App().init();
html {
  font-family: sans-serif;
}

* {
  box-sizing: border-box;
}

body {
  background: black;
  color: #fff;
  font-family: sans-serif;
  overflow: hidden;
  cursor: -webkit-grab;
  cursor: -moz-grab;
  padding: 0;
  margin: 0;
}

canvas {
  width: 100%;
  height: 100%;
}

.stats {
  opacity: 1;
  z-index: 10;
  position: absolute;
}

.dg.ac {
  position: absolute;
  z-index: 10 !important;
}
  <main>
     <div class="stats"></div>
  </main>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
  <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.2/dat.gui.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>

【问题讨论】:

  • sin(2 alpha)^2 调制幅度怎么样?
  • @mikuszefski 嗯,你能指出我如何实现它吗?
  • 好吧 sin( 2 alpha ) = 2 sin( alpha) cos(alpha) 那就是 2 y/sqrt(x^2+y2) * x/sqrt(x^2+y^ 2) = 2 x * y/(x^2+y^2) 然后你取它的平方来调制你的幅度。因此 cos( 2 alpha) ^2 提供了另一个方向。 cos(2 alpha) = cos( alpha )^2 - sin(alpha)^2 最终给出 (x^2-y^2)/(x^2+y^2) 并且你希望它的平方来调制幅度

标签: javascript math three.js


【解决方案1】:

“加号”“X”

“加号” 是最容易实现的。您只需要距中心线的两个距离(水平和垂直)。

const yd = j - this.row / 2; // distance from horizontal
const xd = i - this.col / 2; // distance from vertical

获取每个距离的绝对值的最小值

const distance = Math.min(Math.abs(yd), Math.abs(xd));

然后您可以像在原始文件中一样使用该距离。

实施

如果您将函数 draw 更改为以下内容,这将创建我认为您正在寻找的加号。

  drawWave() {
    var ii= 0, x, y;
    for (x = 0; x < this.col; x++) {
      const bRow = this.boxes[x];
      for (y = 0; y < this.row; y++) {
        const yd = y - this.row / 2;
        const xd = x - this.col / 2;
        const distance = Math.min(Math.abs(yd), Math.abs(xd));
        const angle = this.angle + this.map(distance, 0, this.waveLength, -100, 100);
        const size = this.map(Math.sin(angle), -1, -this.amplitude, 0.001, 1);

        bRow[y].scale.y = size;
        bRow[y].rotation.z = size;

        bRow[y].updateMatrix();
        this.mesh.setMatrixAt(ii++, bRow[y].matrix);
      }
    }
    this.mesh.instanceMatrix.needsUpdate = true;
    this.angle -= this.velocity;
  }

十字架

如果您想要一个十字,您只需将 x 和 y 旋转 45 度。下一个函数就是这样做的。

  drawWave() {
    const xAx = Math.cos(Math.PI / 4);  // Axis 45 deg CW
    const xAy = Math.sin(Math.PI / 4);

    var ii= 0, x, y;
    for (x = 0; x < this.col; x++) {
      const bRow = this.boxes[x];
      for (y = 0; y < this.row; y++) {
        const xx = x - this.col / 2;
        const yy = y - this.row / 2;

        const xd = xx * xAx - yy * xAy;  // rotate
        const yd = xx * xAy + yy * xAx;

        const distance = Math.min(Math.abs(yd), Math.abs(xd));
        const angle = this.angle + this.map(distance, 0, this.waveLength, -100, 100);
        const size = this.map(Math.sin(angle), -1, -this.amplitude, 0.001, 1);

        bRow[y].scale.y = size;
        bRow[y].rotation.z = size;

        bRow[y].updateMatrix();
        this.mesh.setMatrixAt(ii++, bRow[y].matrix);
      }
    }
    this.mesh.instanceMatrix.needsUpdate = true;
    this.angle -= this.velocity;
  }

如果你在顶点着色器中实现以上所有逻辑,效率会高得多

【讨论】:

    猜你喜欢
    • 2019-07-07
    • 2019-04-09
    • 1970-01-01
    • 2017-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-04
    • 2013-03-29
    相关资源
    最近更新 更多