# low-Poly Water Surface Effect for Compatibility with R155

**URL:** <https://discourse.threejs.org/t/low-poly-water-surface-effect-for-compatibility-with-r155/54755>\
**Category:** Questions\
**Tags:** water, ocean, animation, geometry\
**Created:** [August 8, 2023, 4:11am UTC](https://discourse.threejs.org/t/low-poly-water-surface-effect-for-compatibility-with-r155/54755 "2023-08-08T04:11:17Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![ginnychen19](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/ginnychen19/32/40109_2.png) [@ginnychen19](https://discourse.threejs.org/u/ginnychen19)\
**Post date:** [August 8, 2023, 4:11am UTC](https://discourse.threejs.org/t/low-poly-water-surface-effect-for-compatibility-with-r155/54755/1 "2023-08-08T04:11:17Z")

</div>

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.  
https://codepen.io/quintono/embed/preview/VwwdVbY?default-tabs=js%2Cresult&height=300&host=https%3A%2F%2Fcodepen.io&slug-hash=VwwdVbY  
.  
.  
.  
.  
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/](https://gotoo.co/demo/elizabeth/gotoo5th3D/lowPolyWater/)

code:

```javascript
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);
  }
});

```

---

<div class="post-metadata">

**Author:** ![Mugen87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mugen87/32/5645_2.png) [@Mugen87](https://discourse.threejs.org/u/Mugen87)\
**Post date:** [August 8, 2023, 9:16am UTC](https://discourse.threejs.org/t/low-poly-water-surface-effect-for-compatibility-with-r155/54755/2 "2023-08-08T09:16:44Z")

</div>

Try it like so: [three.js dev template - module - JSFiddle - Code Playground](https://jsfiddle.net/tzu3sv5L/1/)

Code:

```js
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);

```

---

<div class="post-metadata">

**Author:** ![ginnychen19](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/ginnychen19/32/40109_2.png) [@ginnychen19](https://discourse.threejs.org/u/ginnychen19)\
**Post date:** [August 8, 2023, 9:52am UTC](https://discourse.threejs.org/t/low-poly-water-surface-effect-for-compatibility-with-r155/54755/4 "2023-08-08T09:52:21Z")

</div>

Oh my goodness 😳 😳  
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?

---

<div class="post-metadata">

**Author:** ![Mugen87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mugen87/32/5645_2.png) [@Mugen87](https://discourse.threejs.org/u/Mugen87)\
**Post date:** [August 8, 2023, 9:56am UTC](https://discourse.threejs.org/t/low-poly-water-surface-effect-for-compatibility-with-r155/54755/5 "2023-08-08T09:56:55Z")

</div>

The examples at [three.js examples](https://threejs.org/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.

---

<div class="post-metadata">

**Author:** ![ginnychen19](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/ginnychen19/32/40109_2.png) [@ginnychen19](https://discourse.threejs.org/u/ginnychen19)\
**Post date:** [August 8, 2023, 10:05am UTC](https://discourse.threejs.org/t/low-poly-water-surface-effect-for-compatibility-with-r155/54755/6 "2023-08-08T10:05:34Z")

</div>

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