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);