low-Poly Water Surface Effect for Compatibility with R155

Hello engineers!
I’m trying to create a low-poly water surface effect.
As I want to make it compatible with version R155, I’m currently attempting to modify this code from version R106.

This is the version I referenced, written by someone else in r106.

.
.
.
.
In the version I’m trying
Although I can see the water surface, it doesn’t behave as I expected it to. I’d appreciate it if you could take a look and see if I made any mistakes in my modifications.

However, up until now, I haven’t encountered any error messages. Despite the fact that the water surface is visible, it doesn’t behave as I anticipated. Could you please take a look and see if I made any mistakes in my modifications?

Furthermore, I’ve transformed the code into ES6 syntax, but since I’m not very familiar with ES5, I can’t rule out the possibility that I misunderstood something from the start.

my:
https://gotoo.co/demo/elizabeth/gotoo5th3D/lowPolyWater/

code:

import * as THREE from '../js/three/build/three.module.js';
import * as BufferGeometryUtils from '../js/three/examples/jsm/utils/BufferGeometryUtilsLocal.js';

class ThreeScene {
  constructor() {
    this.scene = new THREE.Scene();
    this.scene.fog = new THREE.Fog(0xf7d9aa, 100, 950);
    // this.Ocean = new Ocean();
    this.seaGeom = new THREE.PlaneGeometry(500, 500, 20, 20);
    this.waves = [];/* 先製造一個海浪的數據 */
    this.seaMesh;


    this.height = window.innerHeight;
    this.width = window.innerWidth;

    this.aspectRatio = this.width / this.height;
    this.fieldOfView = 60;
    this.nearPlane = 1;
    this.farPlane = 10000;
    this.camera = new THREE.PerspectiveCamera(
      this.fieldOfView,
      this.aspectRatio,
      this.nearPlane,
      this.farPlane
    );

    this.camera.position.x = 0;
    this.camera.position.z = 250;
    this.camera.position.y = 250;

    this.renderer = new THREE.WebGLRenderer({ alpha: true });
    this.renderer.setSize(this.WIDTH, this.HEIGHT);
    this.renderer.shadowMap.enabled = true;

    this.container = document.getElementById('world');
    this.container.appendChild(this.renderer.domElement);
  }
  init() {
    window.addEventListener('resize', this.onWindowResize(this), false);
    this.creatWaves();
    this.createLights();
    this.createOcean();
  }

  onWindowResize(that) {
    window.addEventListener('resize', function (e) {
      that.camera.aspect = window.innerWidth / window.innerHeight;
      that.camera.updateProjectionMatrix();
      that.renderer.setSize(window.innerWidth, window.innerHeight);
      that.windowWidth = window.innerWidth;
      that.windowHeight = window.innerHeight;
    });
  }

  createLights() {
    this.hemisphereLight = new THREE.HemisphereLight(0xaaaaaa, 0x000000, 0.9);
    this.shadowLight = new THREE.DirectionalLight(0xffffff, 0.9);
    this.shadowLight.position.set(-150, 350, 350);
    this.shadowLight.castShadow = true;
    this.shadowLight.shadow.camera.left = -400;
    this.shadowLight.shadow.camera.right = 400;
    this.shadowLight.shadow.camera.top = 400;
    this.shadowLight.shadow.camera.bottom = -400;
    this.shadowLight.shadow.camera.near = 1;
    this.shadowLight.shadow.camera.far = 1000;
    this.shadowLight.shadow.mapSize.width = 2048;
    this.shadowLight.shadow.mapSize.height = 2048;
    this.scene.add(this.hemisphereLight);
    this.scene.add(this.shadowLight);
  }


  creatWaves() {
    this.seaGeom.rotateX(-Math.PI / 2);
    BufferGeometryUtils.mergeVertices(this.seaGeom);
    this.seaGeom.vertices = this.seaGeom.attributes.position.array;

    let l = this.seaGeom.vertices.length;

    for (let i = 0; i < l; i += 3) {
      const v1 = this.seaGeom.vertices[i];
      const v2 = this.seaGeom.vertices[i + 1];
      const v3 = this.seaGeom.vertices[i + 2];

      this.waves.push({
        x: v1,
        y: v2,
        z: v3,
        ang: Math.random() * Math.PI * 2,
        speed: 0.016 + Math.random() * 0.032,
      });
    }

    let mat = new THREE.MeshPhongMaterial({
      transparent: true,
      color: 0x68c3c0,
      // flatShading: THREE.FlatShading //??
    });

    this.seaMesh = new THREE.Mesh(this.seaGeom, mat);//生成海洋元素
    this.seaMesh.receiveShadow = true;

    // console.log(this.waves);
  }
  moveWaves() {
    const verts = this.seaMesh.geometry.vertices;
    const l = verts.length / 3;// i < l

    for (let i = 0; i < l; i += 3) {
      const vprops = this.waves[i];
      let v1 = verts[i];
      let v2 = verts[i + 1];

      v1 = vprops.x + Math.cos(vprops.ang);
      v2 = vprops.y + Math.sin(vprops.ang) * 2;

      vprops.ang += vprops.speed;
    }

    this.seaMesh.geometry.verticesNeedUpdate = true;
  }
  createOcean() {
    this.seaMesh.position.y = 200;
    this.seaMesh.position.z = -35;
    this.scene.add(this.seaMesh);
  }
}


window.addEventListener('load', function () {
  const app = new ThreeScene();
  app.init();
  animate();

  function animate() {
    requestAnimationFrame(animate);
    app.moveWaves();
    app.renderer.render(app.scene, app.camera);
  }
});

Try it like so: three.js dev template - module - JSFiddle - Code Playground

Code:

import * as THREE from 'three';
import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';

var scene, camera, fieldOfView, aspectRatio, nearPlane, farPlane, HEIGHT, WIDTH, renderer;

function createScene() {
  HEIGHT = window.innerHeight;
  WIDTH = window.innerWidth;

  scene = new THREE.Scene();

  scene.fog = new THREE.Fog(0xf7d9aa, 100, 950);

  aspectRatio = WIDTH / HEIGHT;
  fieldOfView = 60;
  nearPlane = 1;
  farPlane = 10000;
  camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, nearPlane, farPlane);

  camera.position.x = 0;
  camera.position.z = 250;
  camera.position.y = 250;

  renderer = new THREE.WebGLRenderer();
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(WIDTH, HEIGHT);

  renderer.shadowMap.enabled = true;

  document.body.appendChild(renderer.domElement);

  window.addEventListener('resize', handleWindowResize, false);
}

function handleWindowResize() {
  HEIGHT = window.innerHeight;
  WIDTH = window.innerWidth;
  renderer.setSize(WIDTH, HEIGHT);
  camera.aspect = WIDTH / HEIGHT;
  camera.updateProjectionMatrix();
}

var hemisphereLight, shadowLight;

function createLights() {
  hemisphereLight = new THREE.HemisphereLight(0xaaaaaa, 0x000000, 3);

  shadowLight = new THREE.DirectionalLight(0xffffff, 3);

  shadowLight.position.set(-150, 350, 350);

  shadowLight.castShadow = true;

  shadowLight.shadow.camera.left = -400;
  shadowLight.shadow.camera.right = 400;
  shadowLight.shadow.camera.top = 400;
  shadowLight.shadow.camera.bottom = -400;
  shadowLight.shadow.camera.near = 1;
  shadowLight.shadow.camera.far = 1000;

  shadowLight.shadow.mapSize.width = 2048;
  shadowLight.shadow.mapSize.height = 2048;

  scene.add(hemisphereLight);
  scene.add(shadowLight);
}

const Ocean = function() {
  let geom = new THREE.PlaneGeometry(350, 250, 10, 10);

  geom.applyMatrix4(new THREE.Matrix4().makeRotationX(-Math.PI / 2));
  geom.deleteAttribute('normal');
  geom.deleteAttribute('uv');

  const geometry = BufferGeometryUtils.mergeVertices(geom);

  const positionAttribute = geometry.getAttribute('position');
  positionAttribute.setUsage(THREE.DynamicDrawUsage);

  this.waves = [];
  const v = new THREE.Vector3();

  for (var i = 0; i < positionAttribute.count; i++) {
    v.fromBufferAttribute(positionAttribute, i);

    this.waves.push({
      y: v.y,
      x: v.x,
      z: v.z,
      ang: Math.random() * Math.PI * 2,
      speed: 0.016 + Math.random() * 0.032
    });
  }

  let mat = new THREE.MeshPhongMaterial({
    color: 0x68c3c0,
    transparent: true,
    // opacity: .8,
    flatShading: true,
  });

  this.mesh = new THREE.Mesh(geometry, mat);

  this.mesh.receiveShadow = true;

}

Ocean.prototype.moveWaves = function() {

  const positionAttribute = this.mesh.geometry.getAttribute('position');

  const v = new THREE.Vector3();

  for (var i = 0; i < positionAttribute.count; i++) {
    v.fromBufferAttribute(positionAttribute, i);

    const vprops = this.waves[i];

    v.x = vprops.x + Math.cos(vprops.ang);
    v.y = vprops.y + Math.sin(vprops.ang) * 2;

    vprops.ang += vprops.speed;

    positionAttribute.setXY(i, v.x, v.y);
  }

  positionAttribute.needsUpdate = true;

}

var ocean;

function createOcean() {
  ocean = new Ocean();

  ocean.mesh.position.y = 200;
  ocean.mesh.position.z = -35;

  scene.add(ocean.mesh);
}

function loop() {
  ocean.moveWaves();

  renderer.render(scene, camera);

  requestAnimationFrame(loop);
}

function init() {
  createScene();

  createLights();

  createOcean();

  loop();
}

window.addEventListener('load', init, false);
1 Like

Oh my goodness :flushed: :flushed:
Thank you so much!!
I just tried adding it to my original file and it worked!!

But this also made me realize that there are many methods and processes that I haven’t learned before.
The tutorials I’ve watched previously seem to not align well with the current approach.

If it’s convenient for you, could I ask for some advice on which examples provided by Three.js are more recommended to study?
Or any examples related to identification?

The examples at three.js examples usually reflect the latest API. In general, they are the most up-to-date code resource.

The water displacement effect wasn’t actually that hard to migrate since the only major difference is how geometry data are handled.

1 Like

Oh, I understand, thanks for the suggestion!
I’m going to take a look at the geometry again!