SPACE
to play / pauseARROW RIGHT
orL
to go forwardARROW LEFT
orJ
to go backwardARROW UP
to increase volumeARROW DOWN
to decrease volumeF
to toggle fullscreenM
to toggle mute0 to 9
to go to the corresponding part of the videoSHIFT
+,
to decrease playback speedSHIFT
+.
or;
to increase playback speed
Others
Three.js documentation
GLSL functions
Shortcuts ⌨️
Unlock content 🔓
To get access to 93 hours of video, a members-only Discord server, subtitles, lesson resources, future updates and much more join us for only $95!
Want to learn more? 👋
That's the end of the free part 😔
To get access to 93 hours of video, a members-only Discord server and future updates, join us for only $95!
Introduction 00:00
Creating a realistic Earth might sound simple. Create a sphere, slap a nice Earth texture on it and we’re good to go, right?
Unfortunately, there is a lot more going on:
- Cities in the dark side are illuminated
- There are clouds
- The Sun’s reflection is mostly visible on the oceans, not on the continents
- The part between the day and the night called twilight looks red-ish
- The atmosphere creates a glow all around the Earth, feeling like a volume
We are not going for a physics-based rendering with complex scientific formulas, but that won’t prevent the final result from looking good and realistic.
Setup 02:09
The starter already contains the following:
- A well-subdivided sphere rotating slowly
- A basic shader is already included in the
src/shaders/earth/
folder with avertex.glsl
and afragment.glsl
- An instance of
lil-gui
with no tweak yet - The
vite-plugin-glsl
dependency to handle GLSL files OrbitControls
to rotate around- A
TextureLoader
instance
As for the shader itself:
- The
uv
is sent to the fragment asvUv
and displayed ongl_FragColor
- The
modelPosition
is sent to the fragment asvPosition
and used to calculate theviewDirection
variable (vector going from the camera to the fragment position) - The transformed
normal
is sent to the fragment asvNormal
and normalized again asnormal
to prevent grid artifacts
It’s quite a lot for a starter, but those are concepts we have already learned and practiced a few times in previous lessons.
Earth textures 05:08
In this lesson, we are going to use Earth textures. Yet, the code can easily be applied to other planets and even exotic or procedurally generated ones.
We are going to use the textures you can find on Solar System Scope. It’s a website providing various planet textures under CC Attribution 4.0 license, which requires providing appropriate credit. If you put the website live, make sure to show those credits somewhere on the page.
The Earth textures are already available in the static/earth/
folder.
The day.jpg
and the night.jpg
files are two separated textures. We are going to send both to the shader and mix between them according to the orientation of the sun. They have been encoded in sRGB.
The specularClouds.jpg
contains the specular texture (where it’s reflective) into the red channel and the clouds texture into the green channel. Combining data like this reduces the amount of memory we allocate to the GPU. The texture is encoded in linear.
All textures have been resized to a 4096x2048
resolution and compressed for the web.
Load
Let’s load all three textures.
At the beginning of the Earth
section, use the textureLoader
to load()
the following textures:
./earth/day.jpg
./earth/night.jpg
./earth/specularClouds.jpg
/**
* Earth
*/
// Textures
const earthDayTexture = textureLoader.load('./earth/day.jpg')
const earthNightTexture = textureLoader.load('./earth/night.jpg')
const earthSpecularCloudsTexture = textureLoader.load('./earth/specularClouds.jpg')
The day.jpg
and night.jpg
have been encoded in sRGB since they will be used for display and we need to inform Three.js of that.
Update their colorSpace
to THREE.SRGBColorSpace
:
// Textures
const earthDayTexture = textureLoader.load('./earth/day.jpg')
earthDayTexture.colorSpace = THREE.SRGBColorSpace
const earthNightTexture = textureLoader.load('./earth/night.jpg')
earthNightTexture.colorSpace = THREE.SRGBColorSpace
const earthSpecularCloudsTexture = textureLoader.load('./earth/specularClouds.jpg')
Send all three of them to the earthMaterial
as the following uniforms using the Uniform class:
uDayTexture
uNightTexture
uSpecularCloudsTexture
const earthMaterial = new THREE.ShaderMaterial({
// ...
uniforms:
{
uDayTexture: new THREE.Uniform(earthDayTexture),
uNightTexture: new THREE.Uniform(earthNightTexture),
uSpecularCloudsTexture: new THREE.Uniform(earthSpecularCloudsTexture)
}
})
In fragment.glsl
, retrieve all three uniforms as sampler2D
:
uniform sampler2D uDayTexture;
uniform sampler2D uNightTexture;
uniform sampler2D uSpecularCloudsTexture;
Day / night color 13:02
We want to show the uDayTexture
on the side of the earth facing the sun and the uNightTexture
on the other side.
First, set the color
to vec3(0.0)
:
void main()
{
// ...
vec3 color = vec3(0.0);
// ...
}
Pick the color
on the uDayTexture
using the texture()
method and the vUv
, then do the same with the uNightTexture
. We only need the rgb
channels and we can save those in vec3
variables:
void main()
{
// ...
// Day / night color
vec3 dayColor = texture(uDayTexture, vUv).rgb;
vec3 nightColor = texture(uNightTexture, vUv).rgb;
// Final color
// ...
}
Let’s try the dayColor
on the color
:
void main()
{
// ...
// Day / night color
vec3 dayColor = texture(uDayTexture, vUv).rgb;
vec3 nightColor = texture(uNightTexture, vUv).rgb;
color = dayColor;
// ...
}
How to use it 🤔
- Download the Starter pack or Final project
- Unzip it
- Open your terminal and go to the unzip folder
-
Run
npm install
to install dependencies
(if your terminal warns you about vulnerabilities, ignore it) -
Run
npm run dev
to launch the local server
(project should open on your default browser automatically) - Start coding
- The JS is located in src/script.js
- The HTML is located in src/index.html
- The CSS is located in src/style.css