1
0
Fork 0

WS30: Improved shoreline rendering

This commit is contained in:
Stuart Buchanan 2024-02-05 21:08:49 +00:00
parent c032a41cd6
commit 467edf2bb6
4 changed files with 103 additions and 79 deletions

View file

@ -35,14 +35,6 @@
<wrap-t>repeat</wrap-t> <wrap-t>repeat</wrap-t>
<internal-format>normalized</internal-format> <internal-format>normalized</internal-format>
</texture> </texture>
<texture n="8">
<image>Textures/Terrain/sand6.png</image>
<type>2d</type>
<filter>nearest</filter>
<wrap-s>repeat</wrap-s>
<wrap-t>repeat</wrap-t>
<internal-format>normalized</internal-format>
</texture>
<texture n="10"> <texture n="10">
<image>Textures/Terrain/snow3.png</image> <image>Textures/Terrain/snow3.png</image>
<type>2d</type> <type>2d</type>
@ -859,11 +851,6 @@
<name>coastline</name> <name>coastline</name>
<type>sampler-2d</type> <type>sampler-2d</type>
<value type="int">7</value> <value type="int">7</value>
</uniform>
<uniform>
<name>sand</name>
<type>sampler-2d</type>
<value type="int">8</value>
</uniform> </uniform>
<uniform> <uniform>
<name>swatch_size</name> <name>swatch_size</name>
@ -1289,11 +1276,6 @@
<type>sampler-2d</type> <type>sampler-2d</type>
<value type="int">7</value> <value type="int">7</value>
</uniform> </uniform>
<uniform>
<name>sand</name>
<type>sampler-2d</type>
<value type="int">8</value>
</uniform>
<uniform> <uniform>
<name>swatch_size</name> <name>swatch_size</name>
<type>int</type> <type>int</type>

View file

@ -29,17 +29,13 @@
// End of test phase controls // End of test phase controls
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
// Constants controlling the transition from water
// to terrain depending on the terrain normal
// A normal of 1.0 is completely horizontal.
const float WATER_START = 0.995; // Deeper water.
const float WATER_BEACH_TO_WATER = 0.99; // Transition point between the shoreline and the shallow water
const float WATER_STEEP_TO_BEACH = 0.985; // The transition point between the shoreline and the land
const float WATER_STEEP = 0.98; // Anything with less than this value is considered not water or shoreline
// written by Thorsten Renk, Oct 2011, based on default.frag // written by Thorsten Renk, Oct 2011, based on default.frag
// Ambient term comes in gl_Color.rgb. // Ambient term comes in gl_Color.rgb.
@ -101,8 +97,8 @@ uniform vec3 fg_modelOffset;
// Coastline texture - generated from VPBTechnique // Coastline texture - generated from VPBTechnique
uniform sampler2D coastline; uniform sampler2D coastline;
// Sand texture // Index into the material definition for shorelines
uniform sampler2D sand; uniform int fg_shoreAtlasIndex;
const float EarthRadius = 5800000.0; const float EarthRadius = 5800000.0;
const float terminator_width = 200000.0; const float terminator_width = 200000.0;
@ -321,7 +317,8 @@ float noise_2000m = Noise3D(worldPos.xyz, 2000.0);
// Mix factor of base textures for 2 neighbour landclass(es) // Mix factor of base textures for 2 neighbour landclass(es)
vec4 mfact; vec4 mfact;
bool water = false; bool water_lc = false;
bool mix_water_texel = false;
// Partial derivatives of s and t of ground texture coords for this fragment, // Partial derivatives of s and t of ground texture coords for this fragment,
// with respect to window (screen space) x and y axes. // with respect to window (screen space) x and y axes.
@ -332,35 +329,37 @@ float noise_2000m = Noise3D(worldPos.xyz, 2000.0);
get_landclass_id(tile_coord, dxdy_gc, lc, lc_n, num_unique_neighbors, mfact); get_landclass_id(tile_coord, dxdy_gc, lc, lc_n, num_unique_neighbors, mfact);
get_material(lc, ground_tex_coord, dxdy_gc, mat_shininess, mat_ambient, mat_diffuse, mat_specular, dxdy, st); get_material(lc, ground_tex_coord, dxdy_gc, mat_shininess, mat_ambient, mat_diffuse, mat_specular, dxdy, st);
vec4 coast = texture2D(coastline, tile_coord);
if (fg_photoScenery) { if (fg_photoScenery) {
texel = texture(landclass, vec2(gl_TexCoord[0].s, 1.0 - gl_TexCoord[0].t)); texel = texture(landclass, vec2(gl_TexCoord[0].s, 1.0 - gl_TexCoord[0].t));
water = (texture(coastline, vec2(tile_coord.s, tile_coord.t)).r > 0.1); water_lc = (texture(coastline, vec2(tile_coord.s, tile_coord.t)).r > 0.1);
} else if (coast.g > 0.1) {
texel = lookup_ground_texture_array(0, tile_coord, lc, dxdy);
water = texture(landclass, vec2(tile_coord.s, tile_coord.t)).z > 0.9;
} else { } else {
// Lookup the base texture texel for this fragment and any neighbors, with mixing // Lookup the base texture texel for this fragment and any neighbors, with mixing
texel = get_mixed_texel(0, ground_tex_coord, lc, num_unique_neighbors, lc_n, mfact, dxdy_gc); texel = get_mixed_texel(0, ground_tex_coord, lc, num_unique_neighbors, lc_n, mfact, dxdy_gc);
water = texture(landclass, vec2(tile_coord.s, tile_coord.t)).z > 0.9; water_lc = texture(landclass, vec2(tile_coord.s, tile_coord.t)).z > 0.9;
} }
float steep = 0.9; float steepness_modifier = texture2D(coastline, tile_coord).g * 0.1;
float steepToBeach = 0.93; bool water = water_lc || texture2D(coastline, tile_coord).b > 0.05;
float beachToWater = 0.95;
float waterStart = 0.97;
if ((water_shader == 1) && ((coast.b > 0.05) || (water && steepness < (waterStart + 0.02)))) { if ((water_shader == 1) && (water && (steepness + steepness_modifier)< WATER_START)) {
// This is a water fragment, so calculate the fragment color procedurally, but mix in the steep and beach // This is a water fragment, so calculate the fragment color procedurally, but mix in the steep and beach
vec4 steep_texel = lookup_ground_texture_array(2, ground_tex_coord, lc, dxdy_gc); // Uses the same index as the gradient texture, which it is if (water_lc) {
vec4 beach_texel = texture2D(sand, ground_tex_coord); // Use the dot texture, which is overloaded to be the beach texture lc = fg_shoreAtlasIndex;
texel = mix(steep_texel, beach_texel, smoothstep(steep, steepToBeach, steepness)); get_material(lc, ground_tex_coord, dxdy_gc, mat_shininess, mat_ambient, mat_diffuse, mat_specular, dxdy, st);
fragColor = mix(texel, generateWaterTexel(), smoothstep(beachToWater,waterStart,steepness)); }
fragColor.rgb += getClusteredLightsContribution(eyePos.xyz, n, fragColor.rgb); vec4 steep_texel = lookup_ground_texture_array(0, ground_tex_coord, lc, dxdy_gc); // Uses the same index as the gradient texture, which it is
} else if ((water_shader == 1) && water) { vec4 beach_texel = lookup_ground_texture_array(0, ground_tex_coord, fg_shoreAtlasIndex, dxdy_gc); // Use the shore texture
texel = mix(steep_texel, beach_texel, smoothstep(WATER_STEEP, WATER_STEEP_TO_BEACH, steepness + steepness_modifier));
// Flag that we need to mix in a water texel later if appropriate.
mix_water_texel = (steepness + steepness_modifier > WATER_BEACH_TO_WATER);
water = false;
}
if ((water_shader == 1) && water) {
fragColor = generateWaterTexel(); fragColor = generateWaterTexel();
fragColor.rgb += getClusteredLightsContribution(eyePos.xyz, n, fragColor.rgb); fragColor.rgb += getClusteredLightsContribution(eyePos.xyz, n, fragColor.rgb);
} else { } else {
@ -602,6 +601,9 @@ float noise_2000m = Noise3D(worldPos.xyz, 2000.0);
color = clamp(color, 0.0, 1.0); color = clamp(color, 0.0, 1.0);
fragColor = color * texel + specular; fragColor = color * texel + specular;
if (mix_water_texel) { fragColor = mix(fragColor, generateWaterTexel(), smoothstep(WATER_BEACH_TO_WATER, WATER_START, steepness + steepness_modifier)); }
fragColor.rgb += getClusteredLightsContribution(eyePos.xyz, n, texel.rgb); fragColor.rgb += getClusteredLightsContribution(eyePos.xyz, n, texel.rgb);
} }

View file

@ -528,6 +528,13 @@ int read_landclass_id(in vec2 tile_coord)
return lc; return lc;
} }
// Landclass sources: texture or random
ivec2 read_landclass_id_and_water(in vec2 tile_coord)
{
int lc = (int(texture2D(landclass, tile_coord.st).g * 255.0 + 0.5));
return ivec2(lc, (texture2D(landclass, tile_coord.st).b > 0.9) ? 1 : 0);
}
int read_landclass_id_non_pixelated(in vec2 tile_coord, int read_landclass_id_non_pixelated(in vec2 tile_coord,
const in float landclass_texel_size_m) const in float landclass_texel_size_m)
@ -991,8 +998,8 @@ if ( (enable_large_scale_transition_search == 1) &&
for (int i=1;i<=n;i++) { for (int i=1;i<=n;i++) {
vec2 c = c0+float(i)*dir; vec2 c = c0+float(i)*dir;
int v = read_landclass_id(c); ivec2 v = read_landclass_id_and_water(c);
if ((v != lc) && (mi[0] > n)) {l[0] = v; mi[0] = i; } if ((v[0] != lc) && (v[1] != 1) && (mi[0] > n)) {l[0] = v[0]; mi[0] = i; }
} }
@ -1001,8 +1008,8 @@ if ( (enable_large_scale_transition_search == 1) &&
for (int i=1;i<=n;i++) for (int i=1;i<=n;i++)
{ {
vec2 c = c0+float(i)*dir; vec2 c = c0+float(i)*dir;
int v = read_landclass_id(c); ivec2 v = read_landclass_id_and_water(c);
if ((v != lc) && (mi[1] > n)) {l[1] = v; mi[1] = i; } if ((v[0] != lc) && (v[1] != 1) && (mi[1] > n)) {l[1] = v[0]; mi[1] = i; }
} }
@ -1011,8 +1018,8 @@ if ( (enable_large_scale_transition_search == 1) &&
for (int i=1;i<=n;i++) for (int i=1;i<=n;i++)
{ {
vec2 c = c0+float(i)*dir; vec2 c = c0+float(i)*dir;
int v = read_landclass_id(c); ivec2 v = read_landclass_id_and_water(c);
if ((v != lc) && (mi[2] > n)) {l[2] = v; mi[2] = i; } if ((v[0] != lc) && (v[1] != 1) && (mi[2] > n)) {l[2] = v[0]; mi[2] = i; }
} }
@ -1021,8 +1028,8 @@ if ( (enable_large_scale_transition_search == 1) &&
for (int i=1;i<=n;i++) for (int i=1;i<=n;i++)
{ {
vec2 c = c0+float(i)*dir; vec2 c = c0+float(i)*dir;
int v = read_landclass_id(c); ivec2 v = read_landclass_id_and_water(c);
if ((v != lc) && (mi[3] > n)) {l[3] = v; mi[3] = i; } if ((v[0] != lc) && (v[1] != 1) && (mi[3] > n)) {l[3] = v[0]; mi[3] = i; }
} }

View file

@ -28,6 +28,15 @@ const int randomise_texture_lookups = 0;
// Use built-in water shader. Use for testing impact of ws30-water.frag // Use built-in water shader. Use for testing impact of ws30-water.frag
const int water_shader = 1; const int water_shader = 1;
// Constants controlling the transition from water
// to terrain depending on the terrain normal
// A normal of 1.0 is completely horizontal.
const float WATER_START = 0.995; // Deeper water.
const float WATER_BEACH_TO_WATER = 0.99; // Transition point between the shoreline and the shallow water
const float WATER_STEEP_TO_BEACH = 0.985; // The transition point between the shoreline and the land
const float WATER_STEEP = 0.98; // Anything with less than this value is considered not water or shoreline
// //
// End of test phase controls // End of test phase controls
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
@ -103,6 +112,9 @@ uniform vec4 fg_materialParams1[128];
uniform vec4 fg_materialParams2[128]; uniform vec4 fg_materialParams2[128];
uniform vec4 fg_materialParams3[128]; uniform vec4 fg_materialParams3[128];
// Index into the material definition for shorelines
uniform int fg_shoreAtlasIndex;
// Coastline texture - generated from VPBTechnique // Coastline texture - generated from VPBTechnique
uniform sampler2D coastline; uniform sampler2D coastline;
@ -417,7 +429,8 @@ void main()
// Mix factor of base textures for 2 neighbour landclass(es) // Mix factor of base textures for 2 neighbour landclass(es)
vec4 mfact; vec4 mfact;
bool water = false; bool water_lc = false;
bool mix_water_texel = false;
// Partial derivatives of s and t of ground texture coords for this fragment, // Partial derivatives of s and t of ground texture coords for this fragment,
// with respect to window (screen space) x and y axes. // with respect to window (screen space) x and y axes.
@ -428,24 +441,20 @@ void main()
get_landclass_id(tile_coord, dxdy_gc, lc, lc_n, num_unique_neighbors, mfact); get_landclass_id(tile_coord, dxdy_gc, lc, lc_n, num_unique_neighbors, mfact);
get_material(lc, ground_tex_coord, dxdy_gc, mat_shininess, mat_ambient, mat_diffuse, mat_specular, dxdy, st); get_material(lc, ground_tex_coord, dxdy_gc, mat_shininess, mat_ambient, mat_diffuse, mat_specular, dxdy, st);
vec4 coast = texture2D(coastline, tile_coord);
if (fg_photoScenery) { if (fg_photoScenery) {
// The photoscenery orthophotos are stored in the landclass texture // The photoscenery orthophotos are stored in the landclass texture
// and use normalised tile coordinates // and use normalised tile coordinates
texel = texture(landclass, vec2(tile_coord.s, 1.0 - tile_coord.t)); texel = texture(landclass, vec2(tile_coord.s, 1.0 - tile_coord.t));
water = (texture(coastline, vec2(tile_coord.s, tile_coord.t)).r > 0.1); water_lc = (texture(coastline, vec2(tile_coord.s, tile_coord.t)).r > 0.1);
// Do not attempt any mixing // Do not attempt any mixing
flag = 0; flag = 0;
mix_flag = 0; mix_flag = 0;
} else if (coast.g > 0.1) {
texel = lookup_ground_texture_array(0, tile_coord, lc, dxdy);
water = texture(landclass, vec2(tile_coord.s, tile_coord.t)).z > 0.9;
} else { } else {
// Lookup the base texture texel for this fragment and any neighbors, with mixing // Lookup the base texture texel for this fragment and any neighbors, with mixing
texel = get_mixed_texel(0, ground_tex_coord, lc, num_unique_neighbors, lc_n, mfact, dxdy_gc); texel = get_mixed_texel(0, ground_tex_coord, lc, num_unique_neighbors, lc_n, mfact, dxdy_gc);
water = texture(landclass, vec2(tile_coord.s, tile_coord.t)).z > 0.9; water_lc = texture(landclass, vec2(tile_coord.s, tile_coord.t)).b > 0.5;
} }
vec4 color = gl_Color * mat_ambient; vec4 color = gl_Color * mat_ambient;
@ -455,20 +464,41 @@ void main()
//vec4 green = vec4(0.0, 0.5, 0.0, 0.0); //vec4 green = vec4(0.0, 0.5, 0.0, 0.0);
//texel = mix(texel, green, (mfact[2])); //texel = mix(texel, green, (mfact[2]));
float steep = 0.9; // The coastline BLUE channel provides a higher detail level for waters. The coastline G channel provides a
float steepToBeach = 0.93; // steepness modified that is used so that rivers and lakes are displayed with water on more angled surfaces. Otherwise
float beachToWater = 0.95; // rivers tend to just be sand, as they flow downhill.
float waterStart = 0.97; float steepness_modifier = texture2D(coastline, tile_coord).g * 0.1;
bool water = water_lc || texture2D(coastline, tile_coord).b > 0.05;
if (water && (steepness + steepness_modifier < WATER_START)) {
// For water surfaces that are simply too steep to be plausible we look for an adjacent landclass and mix it with
// a possible shoreline
if (water_lc) {
if (lc == lc_n[0]) {
// Default to the shore material definition
lc = fg_shoreAtlasIndex;
} else {
lc = lc_n[0];
}
}
if ((coast.b > 0.05) || (water && steepness < (waterStart + 0.02))) { get_material(lc, ground_tex_coord, dxdy_gc, mat_shininess, mat_ambient, mat_diffuse, mat_specular, dxdy, st);
float waterline_min_steepness = fg_materialParams3[lc].y; vec4 color = gl_Color * mat_ambient;
float waterline_max_steepness = fg_materialParams3[lc].z; color.a = 1.0;
vec4 steep_texel = lookup_ground_texture_array(2, ground_tex_coord, lc, dxdy_gc); // Uses the same index as the gradient texture, which it is
vec4 beach_texel = texture2D(sand, ground_tex_coord); // Use the dot texture, which is overloaded to be the beach texture vec4 steep_texel = lookup_ground_texture_array(0, ground_tex_coord, lc, dxdy); // look up the secondary texture
texel = mix(steep_texel, beach_texel, smoothstep(steep, steepToBeach, steepness)); vec4 beach_texel = lookup_ground_texture_array(0, ground_tex_coord, fg_shoreAtlasIndex, dxdy); // Use the shore texture
fragColor = mix(texel, generateWaterTexel(), smoothstep(beachToWater,waterStart,steepness)); texel = mix(steep_texel, beach_texel, smoothstep(WATER_STEEP, WATER_STEEP_TO_BEACH, steepness + steepness_modifier));
fragColor.rgb += getClusteredLightsContribution(ecPosition.xyz, n, fragColor.rgb);
} else if (water) { // Flag that we need to mix in a water texel later if appropriate.
mix_water_texel = (steepness + steepness_modifier > WATER_BEACH_TO_WATER);
water = false;
}
// Test code to view the coastline rasters in-sim
//texel.r = texture2D(coastline, tile_coord).b;
//water = false;
if (water) {
fragColor = generateWaterTexel(); fragColor = generateWaterTexel();
fragColor.rgb += getClusteredLightsContribution(ecPosition.xyz, n, fragColor.rgb); fragColor.rgb += getClusteredLightsContribution(ecPosition.xyz, n, fragColor.rgb);
} else { } else {
@ -833,6 +863,9 @@ void main()
color.rgb += secondary_light * light_distance_fading(dist); color.rgb += secondary_light * light_distance_fading(dist);
fragColor = color * texel + specular; fragColor = color * texel + specular;
if (mix_water_texel) { fragColor = mix(fragColor, generateWaterTexel(), smoothstep(WATER_BEACH_TO_WATER, WATER_START, steepness + steepness_modifier)); }
fragColor.rgb += getClusteredLightsContribution(ecPosition.xyz, n, texel.rgb); fragColor.rgb += getClusteredLightsContribution(ecPosition.xyz, n, texel.rgb);
} }