Skip to content

shiny7star/sea-and-sky-object

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Three.js-Ocean-Scene

This project is an example of lightweight procedural skybox and ocean shaders for browsers using the Three.js library. It is mainly designed for mobile, so don't expect too much in terms of graphics, but expect performance (I get a constant 120 FPS on a 2021 mid-range mobile at full resolution).

day sunset moon_rising
Daytime Sunset Moon rising

I made a little demo so you can check how it runs and looks. It supports touch input too. Or you can watch the video.

How it works

Skybox

The skybox is basically a cube around the player generated with 3 gradients:

  • Density gradient, which is used to give a brighter color at horizon
  • Luminosity gradient, which is used to estimate how much light reaches a given fragment
  • Twilight gradient, which is used to roughly simulate Rayleigh scattering by multiplying the density and luminosity gradients, offering beautiful dawns and dusks
density density_luminosity luminosity
Density gradient Twilight gradient Luminosity gradient
color
Twilight gradient with color

The sun is drawn by raising the luminosity gradient to some power and multiplying the result so that we get a glow around the sun. The moon is essentially the same thing, but with the inversed luminosity gradient, a higher power and higher multiplier so that the glow is more subtle.

sun moon
The sun The moon

The stars are generated by picking random 3d unit vectors mapped to a cube grid alongside with a random offset, size, and color. These vales are packed inside a texture and sent to the gpu. On the gpu, we map the corresponding cube grid values and compute the stars in a similar way to how the sun and moon were calculated. The moon and the stars are only drawn if the sky luminosity is low enough.

stars stars_unclamped
Stars Stars with unclamped distance

Water

The water is generated using 3 materials:

  • Surface material which is used to render the waviness effect
  • Water volume material which is used to avoid skybox rendering underwater
  • Object material which is used to render textured geometry

The entire ocean geometry is a giant box that stays centered with the player camera on the xz plane. The ocean surface has a side length of only 4000m to avoid float precision issues. Because the ocean is only 12 vertices and 12 triangles, it is very cheap geometry-wise. It is made from 2 meshes: the surface plane and the volume box, hence the need for surface and volume materials.

The ocean surface seems wavy even though it is geometrically completely flat, thanks to 2 scrolling different normal maps generated with Blender's ocean modifier (tutorial). In an earlier iteration, I also implemented parallax mapping which improved the waviness efect considerably, but I found that it drops the performance way too much on mobile.

Sea floor

The sea floor is procedurally generated using Perlin noise. I got heavily inspired by this video to generate sea floor height. Also, for performance reasons, it is divided into tiles (or chunks) and only tiles close enough to the player are visible.

How to edit

I highly recommend using Visual Studio Code to edit projects like this one. If you have never used Three.js before, I recommend installing it with Node.js using the npm install three command in the terminal of your project folder, inside VS Code. This will ensure you get the latest version of Three.js and that the TypeScript file (responsible for coding suggestions and tooltips) can get linked to your project. To link the TS file with the project put this in the head of your index.html:

<script type="importmap">
    {
        "imports":
        {
            "three": "./path/to/three.module.js"
        }
    }
</script>

If you have done it properly, you shold get coding suggestions when you write something three.js-related in your scripts.

You can then import Three.js in your JavaScript files using:

import * as THREE from "three";

Or:

import { first, second, third } from "three";

To run the project you will need an HTTP server, and the Live Server extension will help you create a local server directly from VS Code.

Coding shaders with code highlights improves readability and workflow, so I highly recommend using Shader languages support for VS Code together with Comment tagged templates. Here is an example of how to use them:

export const vertexShader = 
/*glsl*/`
    varying vec2 _uv;

    void main()
    {
        _uv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
`;

export const fragmentShader = 
/*glsl*/`
    varying vec2 _uv;

    void main() 
    {
        gl_FragColor = vec4(_uv, 0.5, 1.0);
    }
`;

Conclusion

I made this project because I wanted to make my own game, but quickly realized how difficult it is to do that by myself. I posted it here because I think it has reached a point which makes it worth sharing.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages