Creating realistic water reflections in WebGL with ThreeJS

I am currently experimenting with creating a realistic water surface in WebGL using Three.js. My initial approach involves starting with a simple mirror effect and then adding displacement to achieve basic ripple effects.

Here's what I have learned so far: Typically, reflections are achieved by rendering a scene flipped vertically along the y-axis onto a Frame Buffer Object (FBO) using the water plane as a culling plane. This FBO is then applied as a texture to the water plane. By utilizing a displacement map or noise texture, the image can be distorted to create a realistic water effect.

The challenges I'm facing: Firstly, I am unable to find a straightforward method to flip the scene in ThreeJS. While OpenGL allows for flipping using glScale with -1 on the Y-axis, this functionality seems missing in WebGL based on GLES. Although there is a scale parameter for geometry in ThreeJS, there isn't one for scenes. One potential workaround could involve modifying .matrixWorldInverse in the Camera, but I'm unsure how to proceed with that. Any suggestions?

The second obstacle pertains to clipping/culling planes. The traditional glClipPlane method is no longer supported in modern OpenGL versions, including WebGL. I've heard about implementing this in the vertex shader, but in ThreeJS, I only know how to apply shaders as materials rather than during FBO rendering.

Lastly, accurately rendering the FBO onto the water plane with correct texture coordinates involves projecting from the camera position, which presents another challenge.

Information on this topic is scarce online. Only a few WebGL reflection examples exist, and the closest reference I found utilized an "Oblique View Frustum" technique for culling. Is manual coding the best solution nowadays instead of relying on built-in functions? Also, cube reflections provided in ThreeJS aren't suitable for a flat plane, despite my attempts.

If anyone could provide a simplified example demonstrating the process, I would greatly appreciate it.

Answer №1

Be sure to take a look at this fascinating three.js example.

Delivered as is, directly from the source:

water = new THREE.Water( renderer, camera, scene, {
    textureWidth: 512, 
    textureHeight: 512,
    waterNormals: waterNormals,
    alpha:  1.0,
    sunDirection: light.position.clone().normalize(),
    sunColor: 0xffffff,
    waterColor: 0x001e0f,
    distortionScale: 50.0,
} );


mirrorMesh = new THREE.Mesh(
    new THREE.PlaneBufferGeometry( parameters.width * 500, parameters.height * 500 ),
    water.material
);

mirrorMesh.add( water );
mirrorMesh.rotation.x = - Math.PI * 0.5;
scene.add( mirrorMesh );

This certainly resembles an ocean to me :)

Answer №2

If you're interested, check out this presentation on WebGL water effects at

Also, feel free to explore this helpful fiddle at jsfiddle.net/ahmedadel/44tjE

Answer №3

Focusing solely on the scaling aspect of your inquiry, it's worth noting that the Object3D is equipped with a makeScale method for manipulating matrices.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

uib-datepicker-popup allowing for changing minimum mode dynamically

Is there a way to dynamically set the minMode of an Angular Bootstrap Datepicker? I managed to achieve this using the following code: <input type="text" ng-model="myDate" uib-datepicker-popup="{{datepickerFormat}}" datepicker-options="{& ...

What is causing the TypeScript error in the MUI Autocomplete example?

I am attempting to implement a MUI Autocomplete component (v5.11) using the example shown in this link: import * as React from 'react'; import TextField from '@mui/material/TextField'; import Autocomplete from '@mui/material/Autoco ...

Teaching the render engine: The process of ReactJS guiding the render engine through updating the DOM component

I recently learned that ReactJS utilizes Virtual DOM to efficiently compare and update only the necessary parts of the actual DOM node, rather than refreshing all nodes. It's interesting how React instructs the render engine to target specific nodes f ...

Pick and modify a particular component in ReactJS

Currently, I am working on a project in ReactJS where I am attempting to toggle the colors of two buttons. While I have successfully set the active state property of the selected button, I am facing challenges with changing the style of another button (cal ...

Employing jQuery to populate information into an input field, yet encountering an issue where the data is not being successfully transmitted to

On my page, there is a form with an input box <input name="bid_price" id="bid_price" class="form-control"> If I manually enter a value, it gets posted successfully But when I try to insert a value using jQuery, it doesn't get posted, even tho ...

Dynamically changing click events in VueJS can be achieved using a combination of

Once component B has finished loading, I trigger an "emit" event to notify component A using EventBus. When A receives the event with a value of "on", it updates a specific data value to true. This value is stored in a computed property and can only be a ...

Div trigger malfunctioning

I am working with an html file that includes a button trigger using jQuery. My goal is to close a specific div when the button is clicked. Although I have attempted to use the following jQuery code in the button click event, the div is not closing. Can an ...

We encountered an issue while trying to utilize the react-awesome-query-builder npm package within our next.js project, specifically struggling to include multiple

I am attempting to create a query builder similar to the demo using the provided documentation ... when I add more fields in the config, new options appear in the dropdown ... I tried using multiple Builder components to create more fields and even experim ...

Exploring TypeScript's type discrimination with objects

When working with TypeScript, type discrimination is a powerful concept: https://www.typescriptlang.org/play#example/discriminate-types But is it possible to achieve something like this? type A = {id: "a", value: boolean}; type B = {id: "b ...

Organize a list in AngularJS by breaking it down based on its headers and displaying them in

As someone who is new to angularJs, I am looking to convert an app to angularJs but I have encountered a roadblock: The custom accordion markup I am working with is as follows: <div class="accord_main_wrap" ng-controller="catController"> <di ...

Deleting a page reference from the page history stack in Angular 4

I am working on my angular web application and I am looking for a way to remove a specific page reference from the angular page history stack. For example, if I navigate from the login page to the dashboard page, I want to remove the login page reference f ...

The masonry plugin overlays images on top of each other

I have gone through similar questions but haven't found anything that applies to my specific case. Following an ajax call, I am adding li elements to a ul $.ajax({ url: 'do_load_gallery.php?load=all' }).done(function(result) { ...

Ember: Trigger a Function on Input Field Blur with Parameter

Currently, I am in the process of developing a basic Todo App to get familiar with EmberJS version 2.14. One feature I aim to integrate is manual inline editing where users can double-click on a todo item text span to activate an input field for editing. T ...

I am sending an AJAX request to a remote server in order to retrieve access records

Currently, I am attempting to retrieve data by sending an ajax request to a remote server controller from my current remote page. Below is the code for my first remote view page: <?php include 'header.php'; ?> <script src="/assets/js/ ...

Expandable Table displaying all sections upon clicking with Shopify linklists

My current setup includes a sidebar menu that iterates through Shopify linklists to display the parent and child collection titles and URLs. However, I am encountering an issue where clicking on one of the anchor tags opens all anchor links due to non-uniq ...

Perform the default event prior to executing the custom click function

I need a way to have the default event of a button execute before running my custom function. Here's my current code: $('#Button').click( function (){ sideMenuCheck(); }); I want to ensure that this runs only after the default event o ...

I am unable to produce sound by clicking on the drum machine

My goal is to develop a basic react drum machine project that I discovered on freecodecamp. The objective is to display 9 drumpads and trigger sound upon clicking. Update: I have successfully rendered all the keys but I am facing issues with the function ...

Converting a 'div' element into a dropdown menu with CSS and Jquery

I am facing a challenge where I have a collection of buttons enclosed in a div that I want to resemble a dropdown: <div id = "group"> <label> Group: </ label> <div value =" 1hour "> 1h < / div> <div value =" 2hou ...

Troubleshooting: Node.js Express Server GET Handler Failing to Function

Recently, I've been attempting to build a GET request handler in Express.js. Here's the snippet of code I've put together: // include necessary files and packages const express = require('./data.json'); var app = express(); var m ...

What could be causing the failure of this web component using shadow DOM when the HTML code includes multiple instances of the component?

Recently, a question was raised on this platform regarding the use of Selenium to access the shadow DOM. Curious about this topic, I delved into the MDN article Using shadow DOM and decided to replicate the code on my local machine. Surprisingly, it worked ...