From d8f1daac2546c66d28d2e36a28c013ce56bddb4e Mon Sep 17 00:00:00 2001 From: GefullteTaubenbrust2 <72752000+GefullteTaubenbrust2@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:14:27 +0200 Subject: [PATCH] Visual Effects Vol. 1 (#14610) This PR adds a variety of effects to enhance the visual experience. "soft" clouds look Tinted shadows Crude water reflections (sky and sun) and waves Translucent foliage Node specular highlights Adjusted fog color (more saturated where the fog is lighter) Minor changes to volumetric lighting (crudely simulates the effect of depth) Co-authored-by: sfan5 --- builtin/settingtypes.txt | 22 +++ .../shaders/nodes_shader/opengl_fragment.glsl | 135 +++++++++++++++++- .../shaders/nodes_shader/opengl_vertex.glsl | 2 + .../object_shader/opengl_fragment.glsl | 11 +- .../volumetric_light/opengl_fragment.glsl | 4 +- doc/lua_api.md | 5 + src/client/clientevent.h | 1 + src/client/clientmap.cpp | 18 ++- src/client/clouds.cpp | 117 ++++++++++----- src/client/clouds.h | 8 ++ src/client/content_mapblock.cpp | 22 ++- src/client/content_mapblock.h | 1 + src/client/game.cpp | 11 +- src/client/shader.cpp | 9 ++ src/client/shadows/dynamicshadowsrender.h | 3 + src/client/shadows/shadowsshadercallbacks.cpp | 3 + src/client/shadows/shadowsshadercallbacks.h | 1 + src/defaultsettings.cpp | 4 + src/lighting.h | 3 + src/network/clientpackethandler.cpp | 8 ++ src/network/networkprotocol.h | 5 +- src/script/lua_api/l_object.cpp | 10 ++ src/server.cpp | 4 +- src/skyparams.h | 2 + 24 files changed, 356 insertions(+), 53 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 54a639a89..d13f25695 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -403,6 +403,11 @@ enable_clouds (Clouds) bool true # Requires: enable_clouds enable_3d_clouds (3D clouds) bool true +# Use smooth cloud shading. +# +# Requires: enable_3d_clouds, enable_clouds +soft_clouds (Soft clouds) bool false + [**Filtering and Antialiasing] # Use mipmaps when scaling textures. May slightly increase performance, @@ -505,6 +510,11 @@ water_wave_length (Waving liquids wavelength) float 20.0 0.1 # Requires: shaders, enable_waving_water water_wave_speed (Waving liquids wave speed) float 5.0 +# When enabled, liquid reflections are simulated. +# +# Requires: shaders, enable_waving_water, enable_dynamic_shadows +enable_water_reflections (Liquid reflections) bool false + [**Dynamic shadows] # Set to true to enable Shadow Mapping. @@ -661,6 +671,18 @@ bloom_radius (Bloom Radius) float 1 0.1 8 # Requires: shaders, enable_post_processing, enable_bloom enable_volumetric_lighting (Volumetric lighting) bool false +[**Other Effects] + +# Simulate translucency when looking at foliage in the sunlight. +# +# Requires: shaders, enable_dynamic_shadows +enable_translucent_foliage (Translucent foliage) bool false + +# Apply specular shading to nodes. +# +# Requires: shaders, enable_dynamic_shadows +enable_node_specular (Node specular) bool false + [*Audio] # Volume of all sounds. diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index 46977b147..c560a8505 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -1,3 +1,7 @@ +#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_OPAQUE || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_BASIC || MATERIAL_TYPE == TILE_MATERIAL_LIQUID_TRANSPARENT) +#define MATERIAL_WAVING_LIQUID 1 +#endif + uniform sampler2D baseTexture; uniform vec3 dayLight; @@ -7,6 +11,7 @@ uniform float fogShadingParameter; // The cameraOffset is the current center of the visible world. uniform highp vec3 cameraOffset; +uniform vec3 cameraPosition; uniform float animationTimer; #ifdef ENABLE_DYNAMIC_SHADOWS // shadow texture @@ -20,6 +25,7 @@ uniform float animationTimer; uniform vec4 CameraPos; uniform float xyPerspectiveBias0; uniform float xyPerspectiveBias1; + uniform vec3 shadow_tint; varying float adj_shadow_strength; varying float cosLight; @@ -47,6 +53,49 @@ varying highp vec3 eyeVec; varying float nightRatio; #ifdef ENABLE_DYNAMIC_SHADOWS +#if (defined(MATERIAL_WAVING_LIQUID) && defined(ENABLE_WATER_REFLECTIONS) && ENABLE_WAVING_WATER) +vec4 perm(vec4 x) +{ + return mod(((x * 34.0) + 1.0) * x, 289.0); +} + +// Corresponding gradient of snoise +vec3 gnoise(vec3 p){ + vec3 a = floor(p); + vec3 d = p - a; + vec3 dd = 6.0 * d * (1.0 - d); + d = d * d * (3.0 - 2.0 * d); + + vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0); + vec4 k1 = perm(b.xyxy); + vec4 k2 = perm(k1.xyxy + b.zzww); + + vec4 c = k2 + a.zzzz; + vec4 k3 = perm(c); + vec4 k4 = perm(c + 1.0); + + vec4 o1 = fract(k3 * (1.0 / 41.0)); + vec4 o2 = fract(k4 * (1.0 / 41.0)); + + vec4 o3 = o2 * d.z + o1 * (1.0 - d.z); + vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x); + + vec4 dz1 = (o2 - o1) * dd.z; + vec2 dz2 = dz1.yw * d.x + dz1.xz * (1.0 - d.x); + + vec2 dx = (o3.yw - o3.xz) * dd.x; + + return vec3( + dx.y * d.y + dx.x * (1. - d.y), + (o4.y - o4.x) * dd.y, + dz2.y * d.y + dz2.x * (1. - d.y) + ); +} + +vec2 wave_noise(vec3 p, float off) { + return (gnoise(p + vec3(0.0, 0.0, off)) * 0.4 + gnoise(2.0 * p + vec3(0.0, off, off)) * 0.2 + gnoise(3.0 * p + vec3(0.0, off, off)) * 0.225 + gnoise(4.0 * p + vec3(-off, off, 0.0)) * 0.2).xz; +} +#endif // assuming near is always 1.0 float getLinearDepth() @@ -66,6 +115,14 @@ float mtsmoothstep(in float edge0, in float edge1, in float x) return t * t * (3.0 - 2.0 * t); } +float shadowCutoff(float x) { + #if defined(ENABLE_TRANSLUCENT_FOLIAGE) && MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES + return mtsmoothstep(0.0, 0.002, x); + #else + return step(0.0, x); + #endif +} + #ifdef COLORED_SHADOWS // c_precision of 128 fits within 7 base-10 digits @@ -92,10 +149,10 @@ vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDist { vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba; - float visibility = step(0.0, realDistance - texDepth.r); + float visibility = shadowCutoff(realDistance - texDepth.r); vec4 result = vec4(visibility, vec3(0.0,0.0,0.0));//unpackColor(texDepth.g)); if (visibility < 0.1) { - visibility = step(0.0, realDistance - texDepth.b); + visibility = shadowCutoff(realDistance - texDepth.b); result = vec4(visibility, unpackColor(texDepth.a)); } return result; @@ -106,7 +163,7 @@ vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDist float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) { float texDepth = texture2D(shadowsampler, smTexCoord.xy).r; - float visibility = step(0.0, realDistance - texDepth); + float visibility = shadowCutoff(realDistance - texDepth); return visibility; } @@ -378,6 +435,9 @@ void main(void) vec4 col = vec4(color.rgb * varColor.rgb, 1.0); #ifdef ENABLE_DYNAMIC_SHADOWS + // Fragment normal, can differ from vNormal which is derived from vertex normals. + vec3 fNormal = vNormal; + if (f_shadow_strength > 0.0) { float shadow_int = 0.0; vec3 shadow_color = vec3(0.0, 0.0, 0.0); @@ -414,12 +474,19 @@ void main(void) // Power ratio was measured on torches in MTG (brightness = 14). float adjusted_night_ratio = pow(max(0.0, nightRatio), 0.6); + float shadow_uncorrected = shadow_int; + // Apply self-shadowing when light falls at a narrow angle to the surface // Cosine of the cut-off angle. const float self_shadow_cutoff_cosine = 0.035; if (f_normal_length != 0 && cosLight < self_shadow_cutoff_cosine) { shadow_int = max(shadow_int, 1 - clamp(cosLight, 0.0, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine); shadow_color = mix(vec3(0.0), shadow_color, min(cosLight, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine); + +#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES || MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS) + // Prevents foliage from becoming insanely bright outside the shadow map. + shadow_uncorrected = mix(shadow_int, shadow_uncorrected, clamp(distance_rate * 4.0 - 3.0, 0.0, 1.0)); +#endif } shadow_int *= f_adj_shadow_strength; @@ -428,8 +495,60 @@ void main(void) col.rgb = adjusted_night_ratio * col.rgb + // artificial light (1.0 - adjusted_night_ratio) * ( // natural light - col.rgb * (1.0 - shadow_int * (1.0 - shadow_color)) + // filtered texture color + col.rgb * (1.0 - shadow_int * (1.0 - shadow_color) * (1.0 - shadow_tint)) + // filtered texture color dayLight * shadow_color * shadow_int); // reflected filtered sunlight/moonlight + + + vec3 reflect_ray = -normalize(v_LightDirection - fNormal * dot(v_LightDirection, fNormal) * 2.0); + + vec3 viewVec = normalize(worldPosition + cameraOffset - cameraPosition); + + // Water reflections +#if (defined(MATERIAL_WAVING_LIQUID) && defined(ENABLE_WATER_REFLECTIONS) && ENABLE_WAVING_WATER) + vec3 wavePos = worldPosition * vec3(2.0, 0.0, 2.0); + float off = animationTimer * WATER_WAVE_SPEED * 10.0; + wavePos.x /= WATER_WAVE_LENGTH * 3.0; + wavePos.z /= WATER_WAVE_LENGTH * 2.0; + + // This is an analogous method to the bumpmap, except we get the gradient information directly from gnoise. + vec2 gradient = wave_noise(wavePos, off); + fNormal = normalize(normalize(fNormal) + vec3(gradient.x, 0., gradient.y) * WATER_WAVE_HEIGHT * abs(fNormal.y) * 0.25); + reflect_ray = -normalize(v_LightDirection - fNormal * dot(v_LightDirection, fNormal) * 2.0); + float fresnel_factor = dot(fNormal, viewVec); + + float brightness_factor = 1.0 - adjusted_night_ratio; + + // A little trig hack. We go from the dot product of viewVec and normal to the dot product of viewVec and tangent to apply a fresnel effect. + fresnel_factor = clamp(pow(1.0 - fresnel_factor * fresnel_factor, 8.0), 0.0, 1.0) * 0.8 + 0.2; + col.rgb *= 0.5; + vec3 reflection_color = mix(vec3(max(fogColor.r, max(fogColor.g, fogColor.b))), fogColor.rgb, f_shadow_strength); + + // Sky reflection + col.rgb += reflection_color * pow(fresnel_factor, 2.0) * 0.5 * brightness_factor; + vec3 water_reflect_color = 12.0 * dayLight * fresnel_factor * mtsmoothstep(0.85, 0.9, pow(clamp(dot(reflect_ray, viewVec), 0.0, 1.0), 32.0)) * max(1.0 - shadow_uncorrected, 0.0); + + // This line exists to prevent ridiculously bright reflection colors. + water_reflect_color /= clamp(max(water_reflect_color.r, max(water_reflect_color.g, water_reflect_color.b)) * 0.375, 1.0, 400.0); + col.rgb += water_reflect_color * f_adj_shadow_strength * brightness_factor; +#endif + +#if (defined(ENABLE_NODE_SPECULAR) && !defined(MATERIAL_WAVING_LIQUID)) + // Apply specular to blocks. + if (dot(v_LightDirection, vNormal) < 0.0) { + float intensity = 2.0 * (1.0 - (base.r * varColor.r)); + const float specular_exponent = 5.0; + const float fresnel_exponent = 4.0; + + col.rgb += + intensity * dayLight * (1.0 - nightRatio) * (1.0 - shadow_uncorrected) * f_adj_shadow_strength * + pow(max(dot(reflect_ray, viewVec), 0.0), fresnel_exponent) * pow(1.0 - abs(dot(viewVec, fNormal)), specular_exponent); + } +#endif + +#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES) && defined(ENABLE_TRANSLUCENT_FOLIAGE) + // Simulate translucent foliage. + col.rgb += 4.0 * dayLight * base.rgb * normalize(base.rgb * varColor.rgb * varColor.rgb) * f_adj_shadow_strength * pow(max(-dot(v_LightDirection, viewVec), 0.0), 4.0) * max(1.0 - shadow_uncorrected, 0.0); +#endif } #endif @@ -444,7 +563,13 @@ void main(void) // Note: clarity = (1 - fogginess) float clarity = clamp(fogShadingParameter - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); - col = mix(fogColor, col, clarity); + float fogColorMax = max(max(fogColor.r, fogColor.g), fogColor.b); + // Prevent zero division. + if (fogColorMax < 0.0000001) fogColorMax = 1.0; + // For high clarity (light fog) we tint the fog color. + // For this to not make the fog color artificially dark we need to normalize using the + // fog color's brightest value. We then blend our base color with this to make the fog. + col = mix(fogColor * pow(fogColor / fogColorMax, vec4(2.0 * clarity)), col, clarity); col = vec4(col.rgb, base.a); gl_FragData[0] = col; diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl index d96164d76..ba48189a5 100644 --- a/client/shaders/nodes_shader/opengl_vertex.glsl +++ b/client/shaders/nodes_shader/opengl_vertex.glsl @@ -256,7 +256,9 @@ void main(void) z_bias *= pFactor * pFactor / f_textureresolution / f_shadowfar; shadow_position = applyPerspectiveDistortion(m_ShadowViewProj * mWorld * (shadow_pos + vec4(normalOffsetScale * nNormal, 0.0))).xyz; +#if !defined(ENABLE_TRANSLUCENT_FOLIAGE) || MATERIAL_TYPE != TILE_MATERIAL_WAVING_LEAVES shadow_position.z -= z_bias; +#endif perspective_factor = pFactor; if (f_timeofday < 0.2) { diff --git a/client/shaders/object_shader/opengl_fragment.glsl b/client/shaders/object_shader/opengl_fragment.glsl index 2b8af3fa9..db72d7f7d 100644 --- a/client/shaders/object_shader/opengl_fragment.glsl +++ b/client/shaders/object_shader/opengl_fragment.glsl @@ -20,6 +20,7 @@ uniform float animationTimer; uniform vec4 CameraPos; uniform float xyPerspectiveBias0; uniform float xyPerspectiveBias1; + uniform vec3 shadow_tint; varying float adj_shadow_strength; varying float cosLight; @@ -432,7 +433,7 @@ void main(void) col.rgb = adjusted_night_ratio * col.rgb + // artificial light (1.0 - adjusted_night_ratio) * ( // natural light - col.rgb * (1.0 - shadow_int * (1.0 - shadow_color)) + // filtered texture color + col.rgb * (1.0 - shadow_int * (1.0 - shadow_color) * (1.0 - shadow_tint)) + // filtered texture color dayLight * shadow_color * shadow_int); // reflected filtered sunlight/moonlight } #endif @@ -448,7 +449,13 @@ void main(void) // Note: clarity = (1 - fogginess) float clarity = clamp(fogShadingParameter - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); - col = mix(fogColor, col, clarity); + float fogColorMax = max(max(fogColor.r, fogColor.g), fogColor.b); + // Prevent zero division. + if (fogColorMax < 0.0000001) fogColorMax = 1.0; + // For high clarity (light fog) we tint the fog color. + // For this to not make the fog color artificially dark we need to normalize using the + // fog color's brightest value. We then blend our base color with this to make the fog. + col = mix(fogColor * pow(fogColor / fogColorMax, vec4(2.0 * clarity)), col, clarity); col = vec4(col.rgb, base.a); gl_FragData[0] = col; diff --git a/client/shaders/volumetric_light/opengl_fragment.glsl b/client/shaders/volumetric_light/opengl_fragment.glsl index 9ed5fa9ba..001f59465 100644 --- a/client/shaders/volumetric_light/opengl_fragment.glsl +++ b/client/shaders/volumetric_light/opengl_fragment.glsl @@ -46,7 +46,9 @@ float sampleVolumetricLight(vec2 uv, vec3 lightVec, float rawDepth) if (min(samplepos.x, samplepos.y) > 0. && max(samplepos.x, samplepos.y) < 1.) result += texture2D(depthmap, samplepos).r < 1. ? 0.0 : 1.0; } - return result / samples; + // We use the depth map to approximate the effect of depth on the light intensity. + // The exponent was chosen based on aesthetic preference. + return result / samples * pow(texture2D(depthmap, uv).r, 128.0); } vec3 getDirectLightScatteringAtGround(vec3 v_LightDirection) diff --git a/doc/lua_api.md b/doc/lua_api.md index b06809aee..e7cc8a07f 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -8518,6 +8518,8 @@ child will follow movement and rotation of that bone. if set to zero the clouds are rendered flat. * `speed`: 2D cloud speed + direction in nodes per second (default `{x=0, z=-2}`). + * `shadow`: shadow color, applied to the base of the cloud + (default `#cccccc`). * `get_clouds()`: returns a table with the current cloud parameters as in `set_clouds`. * `override_day_night_ratio(ratio or nil)` @@ -8565,6 +8567,9 @@ child will follow movement and rotation of that bone. * `shadows` is a table that controls ambient shadows * `intensity` sets the intensity of the shadows from 0 (no shadows, default) to 1 (blackness) * This value has no effect on clients who have the "Dynamic Shadows" shader disabled. + * `tint` tints the shadows with the provided color, with RGB values ranging from 0 to 255. + (default `{r=0, g=0, b=0}`) + * This value has no effect on clients who have the "Dynamic Shadows" shader disabled. * `exposure` is a table that controls automatic exposure. The basic exposure factor equation is `e = 2^exposure_correction / clamp(luminance, 2^luminance_min, 2^luminance_max)` * `luminance_min` set the lower luminance boundary to use in the calculation (default: `-3.0`) diff --git a/src/client/clientevent.h b/src/client/clientevent.h index a53c007dd..acd375a4e 100644 --- a/src/client/clientevent.h +++ b/src/client/clientevent.h @@ -137,6 +137,7 @@ struct ClientEvent f32 density; u32 color_bright; u32 color_ambient; + u32 color_shadow; f32 height; f32 thickness; f32 speed_x; diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 3cc3564bc..d608ae2f6 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -1209,11 +1209,22 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver, // Render all mesh buffers in order drawcall_count += draw_order.size(); + bool translucent_foliage = g_settings->getBool("enable_translucent_foliage"); + + video::E_MATERIAL_TYPE leaves_material = video::EMT_SOLID; + + // For translucent leaves, we want to use backface culling instead of frontface. + if (translucent_foliage) { + // this is the material leaves would use, compare to nodedef.cpp + auto* shdsrc = m_client->getShaderSource(); + const u32 leaves_shader = shdsrc->getShader("nodes_shader", TILE_MATERIAL_WAVING_LEAVES, NDT_ALLFACES); + leaves_material = shdsrc->getShaderInfo(leaves_shader).material; + } + for (auto &descriptor : draw_order) { if (!descriptor.m_reuse_material) { // override some material properties video::SMaterial local_material = descriptor.getMaterial(); - local_material.MaterialType = material.MaterialType; // do not override culling if the original material renders both back // and front faces in solid mode (e.g. plantlike) // Transparent plants would still render shadows only from one side, @@ -1222,6 +1233,11 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver, local_material.BackfaceCulling = material.BackfaceCulling; local_material.FrontfaceCulling = material.FrontfaceCulling; } + if (local_material.MaterialType == leaves_material && translucent_foliage) { + local_material.BackfaceCulling = true; + local_material.FrontfaceCulling = false; + } + local_material.MaterialType = material.MaterialType; local_material.BlendOperation = material.BlendOperation; driver->setMaterial(local_material); ++material_swaps; diff --git a/src/client/clouds.cpp b/src/client/clouds.cpp index a7154c2c7..96926f3e0 100644 --- a/src/client/clouds.cpp +++ b/src/client/clouds.cpp @@ -65,6 +65,8 @@ Clouds::Clouds(scene::ISceneManager* mgr, IShaderSource *ssrc, readSettings(); g_settings->registerChangedCallback("enable_3d_clouds", &cloud_3d_setting_changed, this); + g_settings->registerChangedCallback("soft_clouds", + &cloud_3d_setting_changed, this); updateBox(); @@ -76,6 +78,8 @@ Clouds::~Clouds() { g_settings->deregisterChangedCallback("enable_3d_clouds", &cloud_3d_setting_changed, this); + g_settings->deregisterChangedCallback("soft_clouds", + &cloud_3d_setting_changed, this); } void Clouds::OnRegisterSceneNode() @@ -141,15 +145,18 @@ void Clouds::updateMesh() // shader mixes the base color, set via ColorParam c_top_f = c_side_1_f = c_side_2_f = c_bottom_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); } - c_side_1_f.r *= 0.95f; - c_side_1_f.g *= 0.95f; - c_side_1_f.b *= 0.95f; - c_side_2_f.r *= 0.90f; - c_side_2_f.g *= 0.90f; - c_side_2_f.b *= 0.90f; - c_bottom_f.r *= 0.80f; - c_bottom_f.g *= 0.80f; - c_bottom_f.b *= 0.80f; + video::SColorf shadow = m_params.color_shadow; + + c_side_1_f.r *= shadow.r * 0.25f + 0.75f; + c_side_1_f.g *= shadow.g * 0.25f + 0.75f; + c_side_1_f.b *= shadow.b * 0.25f + 0.75f; + c_side_2_f.r *= shadow.r * 0.5f + 0.5f; + c_side_2_f.g *= shadow.g * 0.5f + 0.5f; + c_side_2_f.b *= shadow.b * 0.5f + 0.5f; + c_bottom_f.r *= shadow.r; + c_bottom_f.g *= shadow.g; + c_bottom_f.b *= shadow.b; + video::SColor c_top = c_top_f.toSColor(); video::SColor c_side_1 = c_side_1_f.toSColor(); video::SColor c_side_2 = c_side_2_f.toSColor(); @@ -221,13 +228,14 @@ void Clouds::updateMesh() const f32 ry = is3D() ? m_params.thickness * BS : 0.0f; const f32 rz = cloud_size / 2; - for(u32 i = 0; i < num_faces_to_draw; i++) + bool soft_clouds_enabled = g_settings->getBool("soft_clouds"); + for (u32 i = 0; i < num_faces_to_draw; i++) { - switch(i) + switch (i) { case 0: // top - for (video::S3DVertex &vertex : v) { - vertex.Normal.set(0,1,0); + for (video::S3DVertex& vertex : v) { + vertex.Normal.set(0, 1, 0); } v[0].Pos.set(-rx, ry,-rz); v[1].Pos.set(-rx, ry, rz); @@ -237,12 +245,20 @@ void Clouds::updateMesh() case 1: // back if (INAREA(xi, zi - 1, m_cloud_radius_i)) { u32 j = GETINDEX(xi, zi - 1, m_cloud_radius_i); - if(grid[j]) + if (grid[j]) continue; } - for (video::S3DVertex &vertex : v) { - vertex.Color = c_side_1; - vertex.Normal.set(0,0,-1); + if (soft_clouds_enabled) { + for (video::S3DVertex& vertex : v) { + vertex.Normal.set(0, 0, -1); + } + v[2].Color = c_bottom; + v[3].Color = c_bottom; + } else { + for (video::S3DVertex& vertex : v) { + vertex.Color = c_side_1; + vertex.Normal.set(0, 0, -1); + } } v[0].Pos.set(-rx, ry,-rz); v[1].Pos.set( rx, ry,-rz); @@ -251,28 +267,45 @@ void Clouds::updateMesh() break; case 2: //right if (INAREA(xi + 1, zi, m_cloud_radius_i)) { - u32 j = GETINDEX(xi+1, zi, m_cloud_radius_i); - if(grid[j]) + u32 j = GETINDEX(xi + 1, zi, m_cloud_radius_i); + if (grid[j]) continue; } - for (video::S3DVertex &vertex : v) { - vertex.Color = c_side_2; - vertex.Normal.set(1,0,0); + if (soft_clouds_enabled) { + for (video::S3DVertex& vertex : v) { + vertex.Normal.set(1, 0, 0); + } + v[2].Color = c_bottom; + v[3].Color = c_bottom; } - v[0].Pos.set( rx, ry,-rz); - v[1].Pos.set( rx, ry, rz); - v[2].Pos.set( rx, 0, rz); - v[3].Pos.set( rx, 0,-rz); + else { + for (video::S3DVertex& vertex : v) { + vertex.Color = c_side_2; + vertex.Normal.set(1, 0, 0); + } + } + v[0].Pos.set(rx, ry,-rz); + v[1].Pos.set(rx, ry, rz); + v[2].Pos.set(rx, 0, rz); + v[3].Pos.set(rx, 0,-rz); break; case 3: // front if (INAREA(xi, zi + 1, m_cloud_radius_i)) { u32 j = GETINDEX(xi, zi + 1, m_cloud_radius_i); - if(grid[j]) + if (grid[j]) continue; } - for (video::S3DVertex &vertex : v) { - vertex.Color = c_side_1; - vertex.Normal.set(0,0,-1); + if (soft_clouds_enabled) { + for (video::S3DVertex& vertex : v) { + vertex.Normal.set(0, 0, -1); + } + v[2].Color = c_bottom; + v[3].Color = c_bottom; + } else { + for (video::S3DVertex& vertex : v) { + vertex.Color = c_side_1; + vertex.Normal.set(0, 0, -1); + } } v[0].Pos.set( rx, ry, rz); v[1].Pos.set(-rx, ry, rz); @@ -280,14 +313,22 @@ void Clouds::updateMesh() v[3].Pos.set( rx, 0, rz); break; case 4: // left - if (INAREA(xi-1, zi, m_cloud_radius_i)) { - u32 j = GETINDEX(xi-1, zi, m_cloud_radius_i); - if(grid[j]) + if (INAREA(xi - 1, zi, m_cloud_radius_i)) { + u32 j = GETINDEX(xi - 1, zi, m_cloud_radius_i); + if (grid[j]) continue; } - for (video::S3DVertex &vertex : v) { - vertex.Color = c_side_2; - vertex.Normal.set(-1,0,0); + if (soft_clouds_enabled) { + for (video::S3DVertex& vertex : v) { + vertex.Normal.set(-1, 0, 0); + } + v[2].Color = c_bottom; + v[3].Color = c_bottom; + } else { + for (video::S3DVertex& vertex : v) { + vertex.Color = c_side_2; + vertex.Normal.set(-1, 0, 0); + } } v[0].Pos.set(-rx, ry, rz); v[1].Pos.set(-rx, ry,-rz); @@ -295,9 +336,9 @@ void Clouds::updateMesh() v[3].Pos.set(-rx, 0, rz); break; case 5: // bottom - for (video::S3DVertex &vertex : v) { + for (video::S3DVertex& vertex : v) { vertex.Color = c_bottom; - vertex.Normal.set(0,-1,0); + vertex.Normal.set(0, -1, 0); } v[0].Pos.set( rx, 0, rz); v[1].Pos.set(-rx, 0, rz); diff --git a/src/client/clouds.h b/src/client/clouds.h index acd4b0cfb..7193c03f9 100644 --- a/src/client/clouds.h +++ b/src/client/clouds.h @@ -109,6 +109,14 @@ public: m_params.color_ambient = color_ambient; } + void setColorShadow(video::SColor color_shadow) + { + if (m_params.color_shadow == color_shadow) + return; + m_params.color_shadow = color_shadow; + invalidateMesh(); + } + void setHeight(float height) { if (m_params.height == height) diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index f7852ad1c..2a1352139 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -83,7 +83,8 @@ MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector meshmanip(mm), blockpos_nodes(data->m_blockpos * MAP_BLOCKSIZE), enable_mesh_cache(g_settings->getBool("enable_mesh_cache") && - !data->m_smooth_lighting) // Mesh cache is not supported with smooth lighting + !data->m_smooth_lighting), // Mesh cache is not supported with smooth lighting + smooth_liquids(g_settings->getBool("enable_water_reflections")) { } @@ -717,7 +718,7 @@ void MapblockMeshGenerator::drawLiquidSides() if (data->m_smooth_lighting) cur_node.color = blendLightColor(pos); pos += cur_node.origin; - vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, cur_node.color, vertex.u, v); + vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, face.dir.X, face.dir.Y, face.dir.Z, cur_node.color, vertex.u, v); }; collector->append(cur_liquid.tile, vertices, 4, quad_indices, 6); } @@ -740,6 +741,19 @@ void MapblockMeshGenerator::drawLiquidTop() for (int i = 0; i < 4; i++) { int u = corner_resolve[i][0]; int w = corner_resolve[i][1]; + + if (smooth_liquids) { + int x = vertices[i].Pos.X > 0; + int z = vertices[i].Pos.Z > 0; + + f32 dx = 0.5f * (cur_liquid.neighbors[z][x].level - cur_liquid.neighbors[z][x + 1].level + + cur_liquid.neighbors[z + 1][x].level - cur_liquid.neighbors[z + 1][x + 1].level); + f32 dz = 0.5f * (cur_liquid.neighbors[z][x].level - cur_liquid.neighbors[z + 1][x].level + + cur_liquid.neighbors[z][x + 1].level - cur_liquid.neighbors[z + 1][x + 1].level); + + vertices[i].Normal = v3f(dx, 1., dz).normalize(); + } + vertices[i].Pos.Y += cur_liquid.corner_levels[w][u] * BS; if (data->m_smooth_lighting) vertices[i].Color = blendLightColor(vertices[i].Pos); @@ -779,6 +793,10 @@ void MapblockMeshGenerator::drawLiquidTop() vertex.TCoords += tcoord_center; vertex.TCoords += tcoord_translate; + + if (!smooth_liquids) { + vertex.Normal = v3f(dx, 1., dz).normalize(); + } } std::swap(vertices[0].TCoords, vertices[2].TCoords); diff --git a/src/client/content_mapblock.h b/src/client/content_mapblock.h index 730330a03..1c3060c83 100644 --- a/src/client/content_mapblock.h +++ b/src/client/content_mapblock.h @@ -134,6 +134,7 @@ private: f32 corner_levels[2][2]; }; LiquidData cur_liquid; + bool smooth_liquids = false; void prepareLiquidNodeDrawing(); void getLiquidNeighborhood(); diff --git a/src/client/game.cpp b/src/client/game.cpp index 33475f338..ed593abba 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -386,6 +386,8 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter CachedPixelShaderSetting m_minimap_yaw{"yawVec"}; CachedPixelShaderSetting m_camera_offset_pixel{"cameraOffset"}; CachedVertexShaderSetting m_camera_offset_vertex{"cameraOffset"}; + CachedPixelShaderSetting m_camera_position_pixel{ "cameraPosition" }; + CachedVertexShaderSetting m_camera_position_vertex{ "cameraPosition" }; CachedPixelShaderSetting m_texture0{"texture0"}; CachedPixelShaderSetting m_texture1{"texture1"}; CachedPixelShaderSetting m_texture2{"texture2"}; @@ -492,6 +494,10 @@ public: m_camera_offset_pixel.set(offset, services); m_camera_offset_vertex.set(offset, services); + v3f camera_position = m_client->getCamera()->getPosition(); + m_camera_position_pixel.set(camera_position, services); + m_camera_position_pixel.set(camera_position, services); + SamplerLayer_t tex_id; tex_id = 0; m_texture0.set(&tex_id, services); @@ -3184,6 +3190,7 @@ void Game::handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation * clouds->setDensity(event->cloud_params.density); clouds->setColorBright(video::SColor(event->cloud_params.color_bright)); clouds->setColorAmbient(video::SColor(event->cloud_params.color_ambient)); + clouds->setColorShadow(video::SColor(event->cloud_params.color_shadow)); clouds->setHeight(event->cloud_params.height); clouds->setThickness(event->cloud_params.thickness); clouds->setSpeed(v2f(event->cloud_params.speed_x, event->cloud_params.speed_y)); @@ -4315,7 +4322,9 @@ void Game::updateShadows() float timeoftheday = getWickedTimeOfDay(in_timeofday); bool is_day = timeoftheday > 0.25 && timeoftheday < 0.75; bool is_shadow_visible = is_day ? sky->getSunVisible() : sky->getMoonVisible(); - shadow->setShadowIntensity(is_shadow_visible ? client->getEnv().getLocalPlayer()->getLighting().shadow_intensity : 0.0f); + const auto &lighting = client->getEnv().getLocalPlayer()->getLighting(); + shadow->setShadowIntensity(is_shadow_visible ? lighting.shadow_intensity : 0.0f); + shadow->setShadowTint(lighting.shadow_tint); timeoftheday = std::fmod(timeoftheday + 0.75f, 0.5f) + 0.25f; const float offset_constant = 10000.0f; diff --git a/src/client/shader.cpp b/src/client/shader.cpp index f43bc27dc..ad37ea4c1 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -689,6 +689,15 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, if (g_settings->getBool("shadow_poisson_filter")) shaders_header << "#define POISSON_FILTER 1\n"; + if (g_settings->getBool("enable_water_reflections")) + shaders_header << "#define ENABLE_WATER_REFLECTIONS 1\n"; + + if (g_settings->getBool("enable_translucent_foliage")) + shaders_header << "#define ENABLE_TRANSLUCENT_FOLIAGE 1\n"; + + if (g_settings->getBool("enable_node_specular")) + shaders_header << "#define ENABLE_NODE_SPECULAR 1\n"; + s32 shadow_filter = g_settings->getS32("shadow_filters"); shaders_header << "#define SHADOW_FILTER " << shadow_filter << "\n"; diff --git a/src/client/shadows/dynamicshadowsrender.h b/src/client/shadows/dynamicshadowsrender.h index c4ffb39e2..d4437be68 100644 --- a/src/client/shadows/dynamicshadowsrender.h +++ b/src/client/shadows/dynamicshadowsrender.h @@ -94,9 +94,11 @@ public: bool is_active() const { return m_shadows_enabled && shadowMapTextureFinal != nullptr; } void setTimeOfDay(float isDay) { m_time_day = isDay; }; void setShadowIntensity(float shadow_intensity); + void setShadowTint(video::SColor shadow_tint) { m_shadow_tint = shadow_tint; } s32 getShadowSamples() const { return m_shadow_samples; } float getShadowStrength() const { return m_shadows_enabled ? m_shadow_strength : 0.0f; } + video::SColor getShadowTint() const { return m_shadow_tint; } float getTimeOfDay() const { return m_time_day; } f32 getPerspectiveBiasXY() { return m_perspective_bias_xy; } @@ -131,6 +133,7 @@ private: std::vector m_shadow_node_array; float m_shadow_strength; + video::SColor m_shadow_tint{ 255, 0, 0, 0 }; float m_shadow_strength_gamma; float m_shadow_map_max_distance; float m_shadow_map_texture_size; diff --git a/src/client/shadows/shadowsshadercallbacks.cpp b/src/client/shadows/shadowsshadercallbacks.cpp index 32d3e36be..fc4f73186 100644 --- a/src/client/shadows/shadowsshadercallbacks.cpp +++ b/src/client/shadows/shadowsshadercallbacks.cpp @@ -40,6 +40,9 @@ void ShadowConstantSetter::onSetConstants(video::IMaterialRendererServices *serv f32 ShadowStrength = shadow->getShadowStrength(); m_shadow_strength.set(&ShadowStrength, services); + video::SColor ShadowTint = shadow->getShadowTint(); + m_shadow_tint.set(ShadowTint, services); + f32 timeOfDay = shadow->getTimeOfDay(); m_time_of_day.set(&timeOfDay, services); diff --git a/src/client/shadows/shadowsshadercallbacks.h b/src/client/shadows/shadowsshadercallbacks.h index af83f021e..beac25d72 100644 --- a/src/client/shadows/shadowsshadercallbacks.h +++ b/src/client/shadows/shadowsshadercallbacks.h @@ -31,6 +31,7 @@ class ShadowConstantSetter : public IShaderConstantSetter CachedPixelShaderSetting m_light_direction{"v_LightDirection"}; CachedPixelShaderSetting m_texture_res{"f_textureresolution"}; CachedPixelShaderSetting m_shadow_strength{"f_shadow_strength"}; + CachedPixelShaderSetting m_shadow_tint{ "shadow_tint" }; CachedPixelShaderSetting m_time_of_day{"f_timeofday"}; CachedPixelShaderSetting m_shadowfar{"f_shadowfar"}; CachedPixelShaderSetting m_camera_pos{"CameraPos"}; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 8e2411df5..12946b06d 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -275,6 +275,7 @@ void set_default_settings() settings->setDefault("view_bobbing_amount", "1.0"); settings->setDefault("fall_bobbing_amount", "0.03"); settings->setDefault("enable_3d_clouds", "true"); + settings->setDefault("soft_clouds", "false"); settings->setDefault("cloud_radius", "12"); settings->setDefault("menu_clouds", "true"); settings->setDefault("translucent_liquids", "true"); @@ -335,6 +336,9 @@ void set_default_settings() settings->setDefault("bloom_intensity", "0.05"); settings->setDefault("bloom_radius", "1"); settings->setDefault("enable_volumetric_lighting", "false"); + settings->setDefault("enable_water_reflections", "false"); + settings->setDefault("enable_translucent_foliage", "false"); + settings->setDefault("enable_node_specular", "false"); // Effects Shadows settings->setDefault("enable_dynamic_shadows", "false"); diff --git a/src/lighting.h b/src/lighting.h index 262a48b5d..20f434112 100644 --- a/src/lighting.h +++ b/src/lighting.h @@ -18,7 +18,9 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #pragma once +#include "SColor.h" +using namespace irr; /** * Parameters for automatic exposure compensation @@ -54,4 +56,5 @@ struct Lighting float shadow_intensity {0.0f}; float saturation {1.0f}; float volumetric_light_strength {0.0f}; + video::SColor shadow_tint; }; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index a1bffbb93..5b7c2f527 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1476,6 +1476,7 @@ void Client::handleCommand_CloudParams(NetworkPacket* pkt) f32 density; video::SColor color_bright; video::SColor color_ambient; + video::SColor color_shadow = video::SColor(255, 204, 204, 204); f32 height; f32 thickness; v2f speed; @@ -1483,6 +1484,10 @@ void Client::handleCommand_CloudParams(NetworkPacket* pkt) *pkt >> density >> color_bright >> color_ambient >> height >> thickness >> speed; + if (pkt->getRemainingBytes() >= 4) { + *pkt >> color_shadow; + } + ClientEvent *event = new ClientEvent(); event->type = CE_CLOUD_PARAMS; event->cloud_params.density = density; @@ -1491,6 +1496,7 @@ void Client::handleCommand_CloudParams(NetworkPacket* pkt) // we avoid using new() and delete() for no good reason event->cloud_params.color_bright = color_bright.color; event->cloud_params.color_ambient = color_ambient.color; + event->cloud_params.color_shadow = color_shadow.color; event->cloud_params.height = height; event->cloud_params.thickness = thickness; // same here: deconstruct to skip constructor @@ -1821,4 +1827,6 @@ void Client::handleCommand_SetLighting(NetworkPacket *pkt) } if (pkt->getRemainingBytes() >= 4) *pkt >> lighting.volumetric_light_strength; + if (pkt->getRemainingBytes() >= 4) + *pkt >> lighting.shadow_tint; } diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 5af9859ae..85931c71b 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -229,10 +229,13 @@ with this program; if not, write to the Free Software Foundation, Inc., [bump for 5.9.1] PROTOCOL VERSION 46: Move default hotbar from client-side C++ to server-side builtin Lua + Add shadow tint to Lighting packets + Add shadow color to CloudParam packets [scheduled bump for 5.10.0] */ #define LATEST_PROTOCOL_VERSION 46 + #define LATEST_PROTOCOL_VERSION_STRING TOSTRING(LATEST_PROTOCOL_VERSION) // Server's supported network protocol range @@ -1176,4 +1179,4 @@ enum InteractAction : u8 INTERACT_PLACE, // 3: place block or item (to abovesurface) INTERACT_USE, // 4: use item INTERACT_ACTIVATE // 5: rightclick air ("activate") -}; +}; \ No newline at end of file diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index db7212897..d6d608aab 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -2448,6 +2448,10 @@ int ObjectRef::l_set_clouds(lua_State *L) if (!lua_isnil(L, -1)) read_color(L, -1, &cloud_params.color_ambient); lua_pop(L, 1); + lua_getfield(L, 2, "shadow"); + if (!lua_isnil(L, -1)) + read_color(L, -1, &cloud_params.color_shadow); + lua_pop(L, 1); cloud_params.height = getfloatfield_default(L, 2, "height", cloud_params.height); cloud_params.thickness = getfloatfield_default(L, 2, "thickness", cloud_params.thickness); @@ -2483,6 +2487,8 @@ int ObjectRef::l_get_clouds(lua_State *L) lua_setfield(L, -2, "color"); push_ARGB8(L, cloud_params.color_ambient); lua_setfield(L, -2, "ambient"); + push_ARGB8(L, cloud_params.color_shadow); + lua_setfield(L, -2, "shadow"); lua_pushnumber(L, cloud_params.height); lua_setfield(L, -2, "height"); lua_pushnumber(L, cloud_params.thickness); @@ -2611,6 +2617,8 @@ int ObjectRef::l_set_lighting(lua_State *L) lua_getfield(L, 2, "shadows"); if (lua_istable(L, -1)) { getfloatfield(L, -1, "intensity", lighting.shadow_intensity); + lua_getfield(L, -1, "tint"); + read_color(L, -1, &lighting.shadow_tint); } lua_pop(L, 1); // shadows @@ -2654,6 +2662,8 @@ int ObjectRef::l_get_lighting(lua_State *L) lua_newtable(L); // "shadows" lua_pushnumber(L, lighting.shadow_intensity); lua_setfield(L, -2, "intensity"); + push_ARGB8(L, lighting.shadow_tint); + lua_setfield(L, -2, "tint"); lua_setfield(L, -2, "shadows"); lua_pushnumber(L, lighting.saturation); lua_setfield(L, -2, "saturation"); diff --git a/src/server.cpp b/src/server.cpp index 6421fc175..4be3aa26a 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1843,7 +1843,7 @@ void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms) { NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id); pkt << params.density << params.color_bright << params.color_ambient - << params.height << params.thickness << params.speed; + << params.height << params.thickness << params.speed << params.color_shadow; Send(&pkt); } @@ -1873,7 +1873,7 @@ void Server::SendSetLighting(session_t peer_id, const Lighting &lighting) << lighting.exposure.speed_bright_dark << lighting.exposure.center_weight_power; - pkt << lighting.volumetric_light_strength; + pkt << lighting.volumetric_light_strength << lighting.shadow_tint; Send(&pkt); } diff --git a/src/skyparams.h b/src/skyparams.h index 2ff918f36..52a15810b 100644 --- a/src/skyparams.h +++ b/src/skyparams.h @@ -81,6 +81,7 @@ struct CloudParams float density; video::SColor color_bright; video::SColor color_ambient; + video::SColor color_shadow; float thickness; float height; v2f speed; @@ -160,6 +161,7 @@ public: clouds.density = 0.4f; clouds.color_bright = video::SColor(229, 240, 240, 255); clouds.color_ambient = video::SColor(255, 0, 0, 0); + clouds.color_shadow = video::SColor(255, 204, 204, 204); clouds.thickness = 16.0f; clouds.height = 120; clouds.speed = v2f(0.0f, -2.0f);