In Minecraft when you look at water the deeper you view the darker it gets. Does anyone know how to code something like that?
Minecraft with effect
similar game without effect
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It only takes a minute to sign up.
Sign up to join this communityIn Minecraft when you look at water the deeper you view the darker it gets. Does anyone know how to code something like that?
Minecraft with effect
similar game without effect
There are essentially two different approaches for lighting water based on depth:
Minecraft uses voxel-based lighting, wich works by propagating light to adjacent cubes, lowering the brightness depending on the block type. Dark oceans are a side effect of this system.
Water blocks sunlight and reduces the light by 3 levels per block (instead of the default 1 level), wich means the brightness in an ocean for each distance from the surface is:
0 (surface): 15 (direct sunlight)
1: 12
2: 9
3: 6
4: 3
5 and below: 0 (darkness)
Source: Minecraft Wiki - Light
In games with a traditional lighting model, this effect can be created by measuring the amount of water that is between the light source and the ocean floor. The light is then faded based on this distance. There are a few methods for doing this:
If you have a flat surface, you can easily calculate the distance the light travels in the water if you pass the surface normal away from body of water and the dot product of this normal and a surface position
into the geometry shader.
The effective water distance is
where is the position of the vertex and
is the angle between the light direction beneath the surface and the water's surface normal towards the body of water.
At sunset, only reaches a bit less than 50° because the light is refracted when entering the water.
Here's a blog post with a good explanation: The Digital Camera: Total Internal Reflection
Another post with more details: The Digital Camera: Snell’s Law of Refraction
If you're using a heightmap on a surface parallel to the water, becomes
.
The right factor equals 1 if the sun is directly above the water surface.
With a point light, you have to calculate for each vertex based on the relative position to the light source.
With a fixed water level or a fixed light direction, parts of the equation are constant and shouldn't be calculated in the shader for performance reasons.
If you render the water surface to a separate depth map (as seen from the light source), you can use that depth texture to calculate the distance the light travels in the water before hitting the surface.
To do this, you project each vertex into the light source's view projection in the vertex shader and do the texture lookup in the pixel shader.
If the surface is relatively flat, you should use a refracted light origin for better results.
*You can determine the amount of water in front of the nearest solid surface by counting the depth from the light's POV as follows:
The result texture now contains the amount of water in front of the light in light-view-space, so the value must be transformed back before you use it. This method works to calculate the directional light (minus refraction), but will lead to incorrect ambient light if the surfaces are very irregular and there's a large amount of air between to bodies of water affecting the same fragments.
The pros and cons are the same as for normal shadow mapping, except that you need one more buffer while calculating depth and the performance is worse because you have to draw more geometry.
Ray tracing is by far the most accurate but also the most expensive solution for rendering transparent volumes. There are two ways of doing this: 1. Tracing from the ocean floor towards the surface and 2. Tracing from the light source towards the water. Multiple rays are needed for each point on the floor to calculate the brightness.
There are a few more things to take into account when rendering water:
Light in water is scattered again while travelling to the observer, so you should blend it towards a solid color.
If the observer is submerged, you can just render fog based on the final result of the depth buffer. The fog color, but not its density should change with the observer's distance from the surface! (Minecraft only uses this part of the effect.)
If the observer looks at the water from above, you need to calculate the fog based on the depth difference between the surface and the geometry under water. The fog colour should get slightly darker with larger depth differences, but should only change to the point where the fog is fully opaque.
The fog colour should also depend on the view direction for each pixel, so it's slightly darker when looking down in both cases.
If you use a seamless tiling 3D-Texture instead of a decal for fake caustics, you can avoid stretching on vertical surfaces. The strength of scattered light near the surface varies in three dimensions, so using a 2D-Texture usually produces stretching somewhere in the scene. You can model changing light angles by projecting the vertex positions of the floor into a different coordinate system.
Another possibility is to calculate the light density based on the surface position in the light's coordinate system, although that would most likely cost some performance.
The caustics should fade faster than the diffuse light with increasing depth.
Colours are scattered differently, so the light colour should change with increasing depth. This also prevents abrupt edges where, for example, a beach intersects the water surface.
Because of refraction, the light hits the ocean floor much steeper than it normally would. The Wikipedia article about Snell's law has formulae for angles and vectors.
I believe that the sky lighting effect in Minecraft is straight down - things get shaded by what is above them no matter where the sun is. Then local lighting from torches, etc. is applied with a dropoff effect - the farther away from the light source, the less light a cube gets.
If done this way, each layer of water would cumulatively shadow the layer below it, so each becomes progressively darker. Tree foliage provides shade like this, however it is not cumulative. You get the same shade under a tree whether it's 1 or 100 foliage cubes.
One clue that this is the method being used is that water doesn't get darker when farther away from the viewer - only as you go down. Yes, the fog effect does kick in at distance, but not the water dark effect.
So the basic formula for calculating lighting would be something like this in pseudo-code...
light_on_cube = 1.0
for each cube above target cube, from lowest to highest {
if cube being examined is tree foliage
light_on_cube = 0.5
else if cube being examined is water
light_on_cube = light_on_cube - 0.1
else if cube being examined is solid
light_on_cube = 0
}
This isn't perfect for calculating lighting under overhangs or in caves, as it would be pitch dark under an overhang using this method. But one could add in both local light sources (torches, fires, etc.) as well as treating sun lit blocks as light sources. Something like this might do it...
The idea here is that if a cube is lit by the sun or a torch, the cube next to it is going to also be lit in some way. And the farther away you are from that lit cube, the less light there will be. It's sort of a kludge way to estimate diffuse lighting but I think (?) it would work.
Perhaps I'm misunderstanding the question, but why can't you just change the colour of the blocks depending on their depth?
If you have the depth d (in blocks, starting at 0) then a reasonable equation for brightness would be:
L = (1-m) e-kd+ m
Code: L = (1.0 - m) * exp(-k * d) + m;
k controls how fast it gets darker (higher = faster). A reasonable value would be 0.5.
m is the minimum brightness you want.
L varies from 0 to 1.
If you don't know how to change the colour of the blocks in whatever graphics API you are using then please ask that as a separate question (stating what API you use, and whether or not you are using shaders).
e^-kd
bit is just an exponential decay, which is a standard function for things that gradually tend toward zero over some value (depth). The multiplication by (1-m)
and addition of m
are just to scale and offset the decay so that it ends up at a minimum of m
but still starts at 1
. en.wikipedia.org/wiki/Exponential_decay
\$\endgroup\$
Aug 21, 2011 at 0:19