«плюс» или «х» в форме волны в три раза - PullRequest
1 голос
/ 07 апреля 2020

У меня есть эта сцена, сделанная с ТРИ. js. (см. фрагмент кода, прикрепленный к сообщению) или https://codepen.io/farisk/pen/jOPgKGQ

В настоящее время это излучение круговой волны, основанное на формуле в функции 'distance()'

Таким образом:

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

Мне интересно, возможно ли изменить формулу, чтобы я мог каким-то образом получить волну в форме буквы «Х» или «Плюс» (+)?

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>

1 Ответ

0 голосов
/ 07 апреля 2020

«Плюс» или «Х»

«Плюс» проще всего реализовать. Вам просто нужно два расстояния от центральных линий (горизонтальное и вертикальное).

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;
  }

Было бы гораздо эффективнее, если бы вы реализовали все вышеописанные логи c в вершинном шейдере

...