Realistic
Realistic Integrated Creating Terrain Effects
Hello, this is Park Hyun Sang
This Shader VFX lecture
will focus on the Unreal Material Editor
Shaders and VFX are interdependent
and essential for creating seamless visual effects
The examples in this lecture
are designed to intentionally overlap
the previous Unreal VFX course
It ensures continuous
and efficient learning by reinforcing key concepts
Some of you may find the material repetitive
But let's focus on how identical data can be applied to achieve different effects
For our first lecture
we will depict a surface where water gradually rises from dry ground
The goal is to explore
how the shader programming process
can visually convey the transition from dry to wet conditions
Additionally, we’ll learn how to simulate
the visual effect of water rising from the bottom
and investigate the types of data required
for such an effect
Creating the WetFloor Material
Before starting
let’s set up the project environment
We’re using Unreal Engine version 5.4.4
and we’ll begin by creating a new project
Launch the Unreal Editor
Today is not just about
material, shader
or effect creation
We will be looking at how they interact
with each other in practical examples
Choose the GAMES template
From there, select the Third Person template
with Blueprint, Desktop/Maximum, and Starter Content
Name the project SHADERnVFX
Capitalize SHADER for the project name
and click Create
Once the project has been created
navigate to the Content folder
and create a new folder
Name the folder WetFlow
Within this folder, create a new material
and name it M_WetFloor
Open the material editor
Our objective here is
to work with tiled maps
while integrating concepts like color mixing
Start by adding a Texture Sample node
and selecting the Cobblestone texture
For now, get the diffuse map
I’ll start by connecting this to the base color
The texture on a flat surface
shows us the image exactly as it is
Now, let’s apply a common technique in shaders
which is Multiply operation
when we think of something
as soaked with water
we often associate it with the color appearing darker
Usually, in real life
when objects get wet
their colors typically darken
This happens because
of how light interacts with the surface, which I'll draw here
It's the ground
where stones are embedded unevenly
creating a rough surface like this
These rocks
have jagged surfaces
which scatter light
in all directions when it hits them
We call this diffuse reflection
The term diffuse reflects this concept
That's why the texture
we’re using here is named Cobblestone_D
That D comes from diffuse
Let's use blue here
When water is added
it forms a thin layer on the surface
reducing the scattering effect
The water absorbs or redirects it
This leaves less light reflected back to our eyes
It makes the object appear darker
Normally, scattered light reaches our eyes
and allows us to perceive the color
but with less light coming through
the object looks literally darker
By applying a Multiply operation
we can replicate wet texture
To understand Multiply further, let’s break it down
Assume the Red, Green, and Blue channels
of a texture have values of 0.5, 0.2, and 0.1, respectively
If we multiply these values by themselves
0.5 multiplied by 0.5, 0.2 by 0.2, and 0.1 by 0.1
the result is 0.25 for Red
0.04 for Green
and 0.01 for Blue, so the values decrease
Or another way to put this is
the value for Red channel
the original value
let's say this is value A and value B
These A and B are connected
So A is 0.5
which is 50%
Whatever is the value for Green, it reduces by the same amount
So according to this, it's 20%
And for Blue, only 10%
The greater the decrease in value
the more significant the darkening effect becomes
When the Red, Green, and Blue values are all at 1, or white
what is happening here?
It is getting closer to 0
This means it's getting closer to being black
If the Red channel decreases slightly
but the Blue channel decreases significantly
the texture will appear deeper
or more saturated with red tones
If the Blue channel were the highest value
of Red, Green, and Blue channels
the texture would appear more prominently blue
This illustrates how the Multiply function works
Multiply can also be used in grayscale textures
But when applied uniformly to color values
it effectively squares each channel
making the texture appear
darker and more saturated
which resembles the wet texture
Now, let’s define two states
One representing the original dry ground
labeled as A or Dry
and B representing the wet ground
This is clear enough, I hope
Now let's make these conditions coexist
We aim to show parts of the texture as dry A
and others as wet B
so that they coexist
To achieve this, we use a node
called Linear Interpolate
often referred to as Lerp
We assign the Dry texture to A
and the Wet texture to B
When connected directly to the base color
there’s no visible change
To make this clearer
let’s introduce an additional texture
We’ll bring in a Texture Sample node
and load a Noise Texture
specifically Noise_01
That's a Noise Texture
Since this is a grayscale image
any of the Red, Green, or Blue channels are identical
Let's just use the closest Red value
It has a blotchy appearance
but not too clearly
Now, use a Step node with the noise image
Connect the Red channel to the X
For Y, press Alt
to disconnect any connections
and set it to 0.5
So it is between 0 and 1, or black and white
In this grayscale range, below 0.5 is black
and above 0.5 is white
Like this, black and white
It's a split into two images
We still see some blotchy areas
If you’d like to emphasize the black areas further
lower the Y value to 0.3
o that 30% of the grayscale values turn black
and the rest remain white
So we get more areas
Similarly, setting it to 0.25
will expand the black regions further
You can also experiment with different noise textures
You can experiment
with Perlin noise textures as well
to customize it
Just make sure to adjust the Step threshold
You could do this
Using Lerp, we’ve mixed the colors effectively
The Lerp function essentially
blends A for black
and B for white based on the alpha value
Imagine a container
that can hypothetically
contain all the colors
Partially fill it with A
Like so, this is A
So this much is empty
Then it fills in B for the leftover space
This is called linear interpolation
Based on the value between 0 and 1
For example, let's remove this
Type in 0.3
And 0, and we get a value closer to A
And with 1, we get the B image
Of course, something closer to B, like 0.7
will mix more of B
So for an alpha value like this
you can think of it as the proportion for B
The percentage of B included in the overall blend
So 07. is 70%
In an image
1 is white and 0 is black
So making it into an image
instead of number values
will determine the areas for black and white
to show different images
Let's connect the output of the Step node to the alpha input
Next, we duplicate the Lerp node
keeping the alpha connection intact
For the dry state A
the Roughness value is set to 1
representing a very rough surface
To illustrate this, I’ll use the sphere preview
to explain how light interacts with the material
When the Roughness is 1
the surface scatters light diffusely
giving it a coarse texture
When Roughness is 0
the surface becomes smooth, reflecting light sharply
and appearing highly polished
This may confuse you a little
Here, A has a Roughness value close to 1
which is very dry in general
The wet state B has a Roughness value of 0
which is very smooth
Connecting the alpha value
to the roughness input
makes only the darker, wet areas
highly reflective
See how it reflects
only the darker area around it
Creating the Effect of Rising Water
Let's the Normal Map from the Cobblestone Texture
First, add a Texture Sample node
and select this Cobblestone one
and bring in the normal map
When the RGB output
of the texture is connected to the Normal input
the surface now has depth
Let's look at it from a distance
Hold the L key and using the left mouse button
to adjust the light direction
This makes the rock look three-dimensional
Now, let’s create a unique scenario
Imagine water gradually rising
from the bottom of the surface
Revisiting the earlier cross-section
envision a flat ground covered with scattered stones
Picture water starting at the lowest point
and slowly rising to cover the entire surface
At low, mid, and high levels
To accurately portray the physical properties of water
more intricate calculations are required
When considering light reflection or transmission
water is one of the materials that reflects light very effectively
Of course, it depends on the angle of the incoming light
but water typically
creates specular reflection
It reflects light directly back in the incoming direction
So it is slightly elevated compared to the original surface beneath it
For instance, as water rises higher
this orange part will look like the surface
If water rises even higher
than it will also rise up
And at some point, the water
will fill the surface, making it flat
At this point, light no longer interacts with the rocks directly
but reflects off the water’s surface instead
This reduces the sense of depth or texture in the scene
and the normal map will work differently
To achieve this effect
a single normal map is insufficient
Instead, we need to mix multiple normal map effect
So we need to mix
as we did before here
First, we’ll create a flat normal map
Let's begin with a flat normal
Normal maps generally provide
information about the inclination of a surface
but they don’t indicate height
So using a Constant3Vector node
set blue channel to 1
The actual Vector3 value is x, y, 0
The z value is 1, which is the basic normal value
This setup creates the most basic normal value
Thus, to create a flat normal
this orange surface
the blue channel
is set to 1
The length of this vector is
always normalized to 1 unit
If the surface tilts in any direction
the blue channel’s value decreases slightly
while the X and Y channel values increase
resulting in a vector that adjusts
to describe the tilt of the plane
It remains perpendicular to the surface
That's the vector size
with the length of 1
Normal maps work
by lowering the blue channel from its value of 1
The red channel
represents horizontal slopes
while the green channel represents vertical slopes
creating the illusion of depth and three-dimensionality
As a result, tilted surfaces
exhibit more colorful variations
while flat surfaces display uniform blue values
The cobblestone’s original normal map
now represents the dry state or A
The blue vector that we created
is the wet state or B
Now, let's mix these two
When mixing normal maps, it’s crucial
to think of the data as gradients rather than raw numbers
This means
the data in normal maps
the ones going upward
in any slope or angle
are all slopes
If we were to depict these slopes in 3D polygons
we’d require numerous vertices
to accurately model and connect surface details
However, the meshes we typically use
with this flat mesh
Basic mesh is always very flat
To simulate detailed surface variations
the slope information is stored as vector values in pixels
creating the normal map like this
While we’re using RGB texture data here
these values represent
spatial coordinates that include
X, Y, and Z
For mixing normal maps
the red channel encodes horizontal slopes
the green channel encodes vertical slopes
meaning for a flat surface
the information about this slope
The blue channel typically isn’t
directly used for calculations in shaders
The saved information is all according to the vector data
So to make this vector length 1
Red is not enough
So the blue channel holds vector data
that ensures the combined vector’s length equals 1
For our current effect
while the water appears to rise
the height remains unchanged
When water rises on a flat surface
the sloped vectors
that are in angle
soon align perpendicular to the surface
Thus, for mixing normal maps
the red and green channel data represent the slopes
These values about the slopes
are typically used in the mixing process
To isolate only the red and green values
search for the Component Mask node
Component Mask
This node allows you to extract red and green
In real-time shading
they often only bring in red and green values
So that's the default setting
Apply this mask to both vectors
to retain only their red and green components
One simple mixing method
is Multiply
which multiplies them
However, using a vector of 0, 0, 0 for multiplication
would be ineffective
If you are to do this
to add fine details
to the base normal map,like these textures
adjust the secondary vector slightly
This example demonstrates how Unreal processes
and mixes these vectors
After multiplication
Use Append Vector node
It only has red and green now
So this is Vector2
The multiplied result is also a Vector2
Since normal maps
require a Vector3 with X, Y, and Z
we append the blue channel value
from the original data to complete it
So bring in the blue value
This can be done with Multiply
or Add, which can
mix the two values in different ways
Add or Multiply
this mixes the two values
However, mixing blue values
may make the vector length longer or shorter than 1
So normalize the result
to ensure the final vector’s length is precisely 1
When you integrate the normalized vector into the normal map
you can observe an added layer of finer
rougher detail
like rocks are added
beneath the base stone texture
See how the surface texture differs?
Let's compare it with one that only has normal
Notice the transition to a rougher surface?
This is a typical method of mixing textures
You can enhance the detail by using Add
for stronger effects
The foundational layer is
often referred to as the base normal
while the additional layer
to that base is called the detail normal
So you begin with the base and then the details
The distinction lies in
where you source the blue channel value
That's the difference
In most cases, images are blended directly
However, if we do that here
Multiply results in 0
making it not meaningful
Instead, working with the raw vector
you should use Add
This directly uses Lerp from the beginning
within the Component Mask
For blending, the most commonly used nodes
are Multiply
the most commonly used
then Add
and finally Lerp
We're using Lerp for nuanced transition
By focusing on the alpha value
append it to a vector as the A
Remember
until you apply operations like Multiply, Add, or Lerp
the data remains a Vector2
It then becomes a Vector3 through an Append operation
followed by normalization to create a unit vector
That's what you need to remember
To visually inspect the blending
we could keep adding the value for Lerp
like 0.1, 0.2, and so on
We will get some checkered patterns
which is hard for us to check in real time
If we want
to see the values change smoothly
instead of checking it at material
we should create a material instance
Create a Scalar Parameter node
and name it LerpVal
Let's just keep it simple
If the default value is set to 0
any value less than 0 is black, and above that white
so it would be all white, or above 0
Since this has the data
let’s set the default value to 0.5 for now
and save it
Click Apply and Save
then go to the Content Browser window
For WetFloor
right-click on the WetFloor material
and create a Material Instance from it
Open it by double-clicking on it
With the default Step value set to 0.5
you’ll see the material applied with that configuration
Open the Global Scalar Parameter section
check LerpValue, and adjust its value
Now you can visually observe
smooth, continuous changes as the value shifts
As you can see now
we see the gradual change from 0 to 1
To observe smooth transitions like this
avoid directly changing
values in the material editor
Instead, create a Material Instance
and adjust the values there
The relationship between materials
and material instances is important to understand
A material is essentially akin to
a shader in other programs
which determines how colors are represented
The material created based on a shader
can be considered a Material Instance
It's easier for you to observe and adjust
value changes yourselves
When programming shaders
the computer must go through
a compilation process to recognize changes
Even with minor adjustments to numerical values
recompilation is required
recompiling it again
which makes it difficult to view continuous changes in real time
On the other hand, creating a Material Instance
means the material has already been fully compiled
So you can directly
update the result values
without going through the shader compilation process
his allows you to see results immediately in real time
Shader compilation can slow down your computer significantly
For lower-spec computers
anything more complex than this
even minor parameter adjustments
might cause delays
of one to three minutes
My computer uses an i9-3080
with 64GB RAM
The compilation process is shorter for better graphic cards
so I can check the changes even at the material level
Though, as observed earlier
when you adjust values in the Material Editor
the changes are not continuous
lagging a little in between
Therefore, if you can
when checking for the values
to see if your materials are properly mixed
it is recommended to use Material Instances
Let's see how the Normal Map changes on a flat surface
Currently, the material reflects more light
By increasing the Parameter Value
for flat state, press L and left click to see
The light is very flat
The highlights on the surface
appear very flat
If the Parameter Value is increased further
the stones appear more distinct
The light interaction weakens
due to Roughness
The shading becomes more defined
creating a sharper contrast between light and shadow
As water levels decrease
the shadows become even sharper and clearer
However, these sharper shadows
do not indicate any physical changes to the flat surface
The surface remains flat
but you can observe
how the light changes in tandem
with the variations in the Lerp Value
Finally, let’s enhance this effect
to make it even more realistic
To address the current issue
let’s assume that water is partially filling the gaps between the stones
In the current setup
some areas appear as though there is not enough water
as water has unnaturally pooled
in random spots as I marked here
Ideally, with this rough surface with the rocks
the water should gradually rise to fill the gaps
Right?
The water should gradually rise
until it covers the surface completely
But right now
for this surface
the water filling at different heights
some there, and some there
They are filling up in different places
due to the noise map pattern
rather than actual height differences being considered
To create a more realistic effect
we’ll start by moving the noise texture aside
In the Start Content folder under the Textures section
Let's go to our cobblestone
This isn't what this texture is for
but this is Cobblestone_M
intended for metallic use
Opening this texture
reveals a foggy, uneven pattern
This map has a unique characteristic
Let's draw the surface
Let’s assume that white represents higher elevations
and black represents lower ones
The darker it is, the lower
Shades of gray would
represent intermediate height
If we trace the levels from point A to point B
so the higher, the whiter
and it fluctuates repeatedly
to reach this state
the height starts low
increases, then decreases, and so on
Shades of gray would represent
intermediate heights that are pretty even
The higher it is, the whiter it gets
and it fluctuates
This is what we can visualize
This kind of texture is commonly
referred to as a Height Map
designed to represent elevation changes
Sometimes there isn't one
Height Map is designed to represent
elevation changes using black-and-white
but we don't have Height Map here
so we need something to replace this effect
If we adjust the gray areas in the texture
to be either black or white
the water effect
will appear more realistic as it fills up
Using the Cobblestone_M texture
we’ll extract its red channel
and multiply the two
Actually, this isn't enough, but let's just try connecting
This highlights only the white areas at 0.5
Lowering it to 0.1 will show more white
while raising it to 0.9 makes it closer to black
0.2 will make it more white
However, the mid-tones lack variation
so we apply a multiply operation
just like we did earlier for the base color
Let's try that
Now connect them
to visualize the difference better
So this is 0.2, and then 0.5
then 0.3, and then 0.1
Now, water fills up surface areas before the gap
To fix this, we’ll invert the values
using a OneMinus node
Search for OneMinus
And you get the node that says 1-x
Plug it in
and let's check at our instance
At LerpValue = 0
or, precisely, not LerpValue
but if Step is over 0, it is always white
We set the parameter for this baseline
So since over 0 is all white
the water filled in
Let's slowly drain the water
Let's keep it slow
The higher parts of the stones are exposed first
gradually revealing more of the upper sections
As the value increases, the effect moves downward
toward the base, like water draining
To refill the water
lowering the value slowly
though not accurate
will first fill the gaps and then
move upward toward the center of the stones
While not perfectly accurate
this mimics a realistic effect of water filling up
Now, for LerpValue
as we learned earlier
a value of 1 represents 100% of B
and a value of 0.7 represents 70% of B
That's what I told you
If the goal is to show 100% of B
the parameter currently uses a Step node that ensures
anything above 0 is white
The value that needs to go into the Alpha
and the parameter value
we set are inversely related
To handle this, using the OneMinus node
helps reverse the values
Remember, we used it here
Let's Apply the changes
Then, when it becomes 1
the b image is displayed exactly as the LerpValue is
replicating the scenario where the Alpha value is 1
When the its is 0.7, B is 70%
At 0.5, 0.3, and finally at 0
when the LerpValue reaches 0
it's the A image, which is the dry one
The OneMinus node essentially represents 1 - X
where X is the value we assign
For example
if we input 0, 0.3, 0.7, 1 into X
what would be the result?
It's 1-0
which is 1
1-0.3, which is 0.7
1-0.7
which is 0.3
And 1-1 is 0
This creates a natural reversal of the values
The OneMinus node is
frequently used for tasks
like color mixing
or building shaders
That was about basic textures
Now, using a different approach
for now, we only have one set of image
for the texture
So we’ll apply a Texture Coordinate node
to adjust the texture repetition
We have U Tiling and V Tiling here
Increasing the U Tiling value
increases the horizontal repetition of the image
So we say UV
for textures as in for X and Y axes
X is horizontal, and Y is vertical
If you increase the U Tiling value
the 3D mesh is currently a flat plan
By default, only one image is shown
but setting
U Tiling to 3 will
display three images side by side
Using this property
apply a Multiply operation to the Texture Coordinate node
and the multiplier value for B
is set as a Scalar Parameter
The parameter will be named Tilling
with a default value of 1
Let's restore U tiling value of
the Texture Coordinate to 1
This allows for
adjustments without modifying
the Texture Coordinate values
But for the Texture Coordinate
of the textures we use
the same texture in Cobblestone_M
and its normal map
should be applied in the same manner
to avoid mismatched repetitions between base color
If you remove it
as shown now, the normal map will
repeat only once by default
while the base color may repeat two or three times
That's why we do it this way
For noise textures, sometimes tiling values are
intentionally set differently to create more irregular patterns
Depending on the case, regardless of the tiling value
you can use a Multiply node to apply
only a fraction of the tiling value
If you want to repeat half of a three-tile sequence
you can multiply the tiling value by 0.5
resulting in 3 multiplied by 0.5
or 1.5 repetitions
Or, you could use 0.3333
to repeat only one-third
adjusting the Multiply value to control the repetition ratio
Of course, you can’t just plug it in directly
You need to connect the result of the Texture Coordinate and Multiply nodes before applying it
Once applied
in the material instance
let's adjust the tiling value
This allows you to represent the gradual filling of water
It shows that the textures are evenly applied based on the tiling frequency
So, in this session
we used basic nodes
like Lerp which is
essentially just addition and multiplication
So using Multiply and Add
we created a transition effect
that gradually fills water from dry ground to wet ground
Today, we utilized Multiply and Lerp
to create a gradual water-filling effect, transitioning
from dry to wet
Using only simple operations like addition and multiplication
we achieved natural visual effects
Let’s conclude by summarizing what we learned today
Thank you
WetFloor Material Creation
Material Creation Create a folder named WetFloor in the Content folder and generate a material named M_WetFloor
dd a Texture Sample node and connect T_CobbleStone_Pebble_D texture to it Extract RGB output and create a Multiply node
Connect RGB output to both A and B inputs of the Multiply node (Wet)
Combining Dry and Wet Ground Create a Lerp (Linear Interpolate) node Connect Lerp A with Texture (Dry), Lerp B with Multiply (Wet)
Add a Texture Sample, select T_Perlin_Noise_M Connect its R channel to Lerp Alpha Set Lerp A to 1 and Lerp B to 0, then connect the output to Roughnes
Water Rising Effect Connect and mix normal maps Create a Texture Sample and connect T_CobbleStone_Pebble_N
Connect the RGB channels of T_CobbleStone_Pebble_N to the Component Mask (set the Component Mask to RG)
Create a Constant3Vector node and input (0,0,1) Connect it to the Component Mask (R,G) Lerp A: The component mask connected to the texture
Lerp B: The component mask connected to the Constant3Vector Lerp Alpha: The Step output
Append Vector: Create a node to expand the Lerp data with R and G channels into RGB format Append A: Lerp output, B: Blue channel of normal texture
Connect the normalized result to the Normal input of the master node
Creating Material Instance Create a ScalarParameter node and name it LerpVal
Right-click M_WetFloor and generate Create Material Instance and open it
Check LerpVal in the Global Scalar Parameter Values section to observe continuous changes in the result
Height Map Effect Creation Import the T_Cobblestone_Pebble_M texture, and Multiply the R channel to enhance contrast
Use the OneMinus node to invert the Black and White values Connect the result of the multiplication with the noise texture
Import the T_Cobblestone_Pebble_M texture and Multiply the R channel to enhance contrast Use the OneMinus node to invert the Black and White values
Connect the result of the multiplication with the noise texture to the Step node
Water Filling Up Effect Tilemap Effect Creation Create a Texture Coordinate node Create a ScalarParameter node (tiling)
Multiply the two nodes and connect the result to the UV input of each texture (base color and normal)
Add Multiply to the tilling parameter and compute it with the result of the previous Multiply Connect it to the UV of the noise texture