diff --git a/client/shaders/clouds_merge/opengl_fragment.glsl b/client/shaders/clouds_merge/opengl_fragment.glsl new file mode 100644 index 000000000..c88f0d95a --- /dev/null +++ b/client/shaders/clouds_merge/opengl_fragment.glsl @@ -0,0 +1,44 @@ +#define cloudsTexture texture0 +#define sceneTexture texture1 + +uniform sampler2D cloudsTexture; +uniform sampler2D sceneTexture; +uniform vec2 texelSize0; + +uniform vec3 dayLight; + +varying vec2 screenspaceCoordinate; + +vec4 sampleClouds(vec2 uv) { + vec4 cloudsKey = texture2D(cloudsTexture, uv); + + const vec3 darkColor = vec3(0.05, 0.1, 0.2); + const vec3 auroraDark = vec3(0., 0.5, 0.5); + const vec3 auroraBright = vec3(0., 0.5, .0); + + return vec4( + mix(auroraDark, auroraBright, cloudsKey.b) * cloudsKey.b * max(0., 1. - cloudsKey.r) + + cloudsKey.r * (darkColor * max(0., 1. - cloudsKey.g) + dayLight * cloudsKey.g), + cloudsKey.r); +} + +vec4 getClouds(vec2 uv) { + return + sampleClouds(uv - texelSize0 * vec2(-1.0, -1.0)) / 9.0 + + sampleClouds(uv - texelSize0 * vec2( 0.0, -1.0)) / 9.0 + + sampleClouds(uv - texelSize0 * vec2( 1.0, -1.0)) / 9.0 + + sampleClouds(uv - texelSize0 * vec2(-1.0, 0.0)) / 9.0 + + sampleClouds(uv - texelSize0 * vec2( 0.0, 0.0)) / 9.0 + + sampleClouds(uv - texelSize0 * vec2( 1.0, 0.0)) / 9.0 + + sampleClouds(uv - texelSize0 * vec2(-1.0, 1.0)) / 9.0 + + sampleClouds(uv - texelSize0 * vec2( 0.0, 1.0)) / 9.0 + + sampleClouds(uv - texelSize0 * vec2( 1.0, 1.0)) / 9.0; +} + +void main(void) +{ + vec4 cloudsColor = getClouds(screenspaceCoordinate * 0.5 + 0.5); + vec4 sceneColor = texture2D(sceneTexture, screenspaceCoordinate * 0.5 + 0.5); + + gl_FragColor = vec4(sceneColor.rgb * (1. - cloudsColor.a) + cloudsColor.rgb, 1.); +} diff --git a/client/shaders/clouds_merge/opengl_vertex.glsl b/client/shaders/clouds_merge/opengl_vertex.glsl new file mode 100644 index 000000000..5487edeb6 --- /dev/null +++ b/client/shaders/clouds_merge/opengl_vertex.glsl @@ -0,0 +1,7 @@ +varying vec2 screenspaceCoordinate; + +void main(void) +{ + screenspaceCoordinate = inVertexPosition.xy; + gl_Position = inVertexPosition; +} diff --git a/client/shaders/coarse_noise_shader/opengl_fragment.glsl b/client/shaders/coarse_noise_shader/opengl_fragment.glsl new file mode 100644 index 000000000..c0cfecc9a --- /dev/null +++ b/client/shaders/coarse_noise_shader/opengl_fragment.glsl @@ -0,0 +1,17 @@ +uniform float cloudDensity; + +// Pseudorandom number generator +float rand(vec2 n) { + return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); +} + +// More random pseudorandom number generator; +float noise(vec2 p){ + vec2 p2 = p + vec2(rand(p), rand(p.yx)); + return rand(p2); +} + +void main(void) +{ + gl_FragColor = vec4(vec3(step(noise(floor(gl_FragCoord.xy * 0.25)), cloudDensity)), 1.); +} diff --git a/client/shaders/coarse_noise_shader/opengl_vertex.glsl b/client/shaders/coarse_noise_shader/opengl_vertex.glsl new file mode 100644 index 000000000..9182a208a --- /dev/null +++ b/client/shaders/coarse_noise_shader/opengl_vertex.glsl @@ -0,0 +1,4 @@ +void main(void) +{ + gl_Position = inVertexPosition; +} diff --git a/client/shaders/noise_shader/opengl_fragment.glsl b/client/shaders/noise_shader/opengl_fragment.glsl new file mode 100644 index 000000000..3a8081b8f --- /dev/null +++ b/client/shaders/noise_shader/opengl_fragment.glsl @@ -0,0 +1,15 @@ +// Pseudorandom number generator +float rand(vec2 n) { + return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); +} + +// More random pseudorandom number generator; +float noise(vec2 p){ + vec2 p2 = p + vec2(rand(p), rand(p.yx)); + return rand(p2); +} + +void main(void) +{ + gl_FragColor = vec4(vec3(noise(gl_FragCoord.xy)), 1.); +} diff --git a/client/shaders/noise_shader/opengl_vertex.glsl b/client/shaders/noise_shader/opengl_vertex.glsl new file mode 100644 index 000000000..9182a208a --- /dev/null +++ b/client/shaders/noise_shader/opengl_vertex.glsl @@ -0,0 +1,4 @@ +void main(void) +{ + gl_Position = inVertexPosition; +} diff --git a/client/shaders/second_stage/opengl_fragment.glsl b/client/shaders/second_stage/opengl_fragment.glsl index 46b0d1e31..0c39d1e94 100644 --- a/client/shaders/second_stage/opengl_fragment.glsl +++ b/client/shaders/second_stage/opengl_fragment.glsl @@ -163,7 +163,7 @@ void main(void) // Filter out blue pixels, because the color grading tends to wash them out. float blue_factor = clamp((color.b - max(color.r, color.g)) / max(0.01, min(color.r, color.g)), 0.0, 1.0); - color.rgb = mix(color.rgb, pow(color.rgb * slope, power), pow(1. - blue_factor, 4.)); + color.rgb = mix(color.rgb, pow(color.rgb * slope, power), 1.); #endif color.rgb = applySaturation(color.rgb, saturation); } diff --git a/client/shaders/volumetric_clouds/opengl_fragment.glsl b/client/shaders/volumetric_clouds/opengl_fragment.glsl new file mode 100644 index 000000000..af24765be --- /dev/null +++ b/client/shaders/volumetric_clouds/opengl_fragment.glsl @@ -0,0 +1,182 @@ +#define depthmap texture0 +#define noiseTexture texture1 +#define noiseTextureCoarse texture2 + +#define ITERATIONS 50 +#define LIGHT_ITERATIONS 10 +#define LIGHT_DISTANCE 100. +#define AURORA_ITERATIONS 100 + +uniform sampler2D depthmap; +uniform sampler2D noiseTexture; +uniform sampler2D noiseTextureCoarse; + +uniform vec2 texelSize0; + +uniform float cloudHeight; +uniform float cloudThickness; +uniform float cloudDensity; + +varying vec3 relativePosition; +varying vec3 viewDirection; +uniform vec3 eyePosition; +uniform vec3 cameraOffset; +uniform vec3 cameraPosition; + +uniform mat4 mCameraView; +uniform mat4 mCameraProjInv; + +uniform float cameraNear; +uniform float cameraFar; + +varying vec2 screenspaceCoordinate; + +uniform float fogDistance; +uniform float fogShadingParameter; + +uniform vec3 v_LightDirection; + +uniform float animationTimer; + +// Derived From http://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf +// and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom) +// NOTE: `frag_coord` is in pixels (i.e. not normalized UV). +float screenSpaceDither(highp vec2 frag_coord) { + // Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, slightly modified for VR. + highp float dither = dot(vec2(171.0, 231.0), frag_coord); + dither = fract(dither / 103.0); + + return dither; +} + +// custom smoothstep implementation because it's not defined in glsl1.2 +// https://docs.gl/sl4/smoothstep +float mtsmoothstep(in float edge0, in float edge1, in float x) +{ + float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); + return t * t * (3.0 - 2.0 * t); +} + +float getDepth(vec2 screenspacePosition) { + float depth = texture2D(depthmap, screenspacePosition * 0.5 + 0.5).r; + return cameraNear * cameraFar / (cameraFar + depth * (cameraNear - cameraFar)); +} + +float getRawDepth(vec2 screenspacePosition) { + return texture2D(depthmap, screenspacePosition * 0.5 + 0.5).r; +} + +float noise(vec3 p){ + //p.y *= 1.; + float y = floor(p.y); + float f1 = texture2D(noiseTexture, p.xz / 256. + y * 0.2).r; + float f2 = texture2D(noiseTexture, p.xz / 256. + y * 0.2 + 0.2).r; + return mix(f1, f2, fract(p.y)); +} + +float fnoise(vec3 p) { + return noise(p * 4.) * 0.5 + noise(p * 8.) * 0.25; +} + +float fnoise3(vec3 p) { + return noise(p * 4.) * 0.5 + noise(p * 8.) * 0.25 + noise(p * 16.) * 0.125; +} + +float getAuroraDensity(vec3 position) { + float density = pow(max(0., 1. - 10. * abs(fnoise3(vec3(position.x * 0.25, animationTimer, position.z * 0.25)) - 0.5)), 4.); + return 1.0 * density * mtsmoothstep(0.0, 0.05, position.y - 1.) * pow(1. - mtsmoothstep(0.05, 2.0, position.y - 1.), 4.); +} + +float getDensity(vec3 position) { + float density = texture2D(noiseTextureCoarse, position.xz / 2560. / 16.).r * + mtsmoothstep(0.0, cloudThickness * 0.2, position.y - cloudHeight) * + (1.0 - mtsmoothstep(cloudThickness * 0.5, cloudThickness, position.y - cloudHeight)); + + density = max(0., density - 0.5 * fnoise(position * 0.005)); + + return 0.04 * density; +} + +float getBrightness(vec3 position, float bias) { + float density = 0.; + for (int i = 0; i < LIGHT_ITERATIONS; i++) { + vec3 rayPosition = position - v_LightDirection * LIGHT_DISTANCE * (float(i) + bias) / float(LIGHT_ITERATIONS); + + density += getDensity(rayPosition) * float(LIGHT_DISTANCE) / float(LIGHT_ITERATIONS); + } + return exp(-density); +} + +float blend(float A, float B, float alphaA, float alphaB) { + float alphaC = alphaA + (1. - alphaA) * alphaB; + return (alphaA * A + (1. - alphaA) * alphaB * B) / alphaC; +} + +void main(void) +{ + vec3 viewVec = normalize(relativePosition); + + vec3 position = cameraOffset + eyePosition; + + float depth = getDepth(screenspaceCoordinate) / normalize(viewDirection).z; + float bottomPlaneIntersect = clamp(min((cloudHeight - eyePosition.y) / viewVec.y, depth), 0., 4. * fogDistance); + float topPlaneIntersect = clamp(min((cloudHeight + cloudThickness - eyePosition.y) / viewVec.y, depth), 0., 4. * fogDistance); + + float startDepth = min(bottomPlaneIntersect, topPlaneIntersect); + float endDepth = max(bottomPlaneIntersect, topPlaneIntersect); + + float bias = screenSpaceDither(gl_FragCoord.xy + animationTimer * 2400.0); + + vec3 color = vec3(0.); + + float dx = (endDepth - startDepth) / float(ITERATIONS); + + float density = 0.; + + float auroraStartDepth = min(max(0., 1.0 / viewVec.y), 8.); + float auroraEndDepth = min(max(0., 3.0 / viewVec.y), 8.); + float rawDepth = getRawDepth(screenspaceCoordinate); + + if (auroraEndDepth - auroraStartDepth > 0.1 && rawDepth >= 1.0) { + for (int i = 0; i < ITERATIONS; i++) { + vec3 rayPosition = viewVec * (auroraStartDepth + (auroraEndDepth - auroraStartDepth) * (float(i) + bias) / float(ITERATIONS)); + + float localDensity = getAuroraDensity(rayPosition); + + localDensity *= 1.0 - mtsmoothstep(4.0, 8.0, length(rayPosition)); + + density += localDensity; + } + } + + color.b = density * (auroraEndDepth - auroraStartDepth) / float(AURORA_ITERATIONS); + + float sunlightContribution = 0.; + float alpha = 0.; + float outScatter = 2. * (dot(v_LightDirection, viewVec) * 0.5 + 0.5); + density = 0.; + + for (int i = 0; i < ITERATIONS; i++) { + vec3 rayPosition = eyePosition + viewVec * (startDepth + (endDepth - startDepth) * (float(i) + bias) / float(ITERATIONS)); + + float localDensity = getDensity(rayPosition) * dx; + + if (localDensity < 0.0001) continue; + + float clarity = clamp(fogShadingParameter - fogShadingParameter * length(rayPosition - eyePosition) / (4. * fogDistance), 0.0, 1.0); + float brightness = getBrightness(rayPosition, bias) * exp(-outScatter * localDensity); + sunlightContribution = blend(sunlightContribution, brightness, 1. - exp(-density), 1. - exp(-localDensity)); + alpha = blend(alpha, clarity, 1. - exp(-density), 1. - exp(-localDensity)); + + density += localDensity; + + if (density > 10.0) break; + } + + float forwardScatter = 1. + 4. * pow(min(dot(v_LightDirection, viewVec), 0.), 4.); + + color.r = (1. - exp(-density)) * alpha; + color.g = sunlightContribution * forwardScatter; + + gl_FragColor = vec4(color, 1.0); +} diff --git a/client/shaders/volumetric_clouds/opengl_vertex.glsl b/client/shaders/volumetric_clouds/opengl_vertex.glsl new file mode 100644 index 000000000..33ee9edf6 --- /dev/null +++ b/client/shaders/volumetric_clouds/opengl_vertex.glsl @@ -0,0 +1,17 @@ +uniform mat4 mCameraProjInv; +uniform mat4 mCameraView; +uniform vec3 eyePosition; + +varying vec3 relativePosition; +varying vec3 viewDirection; + +varying vec2 screenspaceCoordinate; + +void main(void) +{ + screenspaceCoordinate = inVertexPosition.xy; + vec4 p = mCameraProjInv * inVertexPosition; + viewDirection = p.xyz / p.w; + relativePosition = (p.xyz / p.w) * mat3(mCameraView); + gl_Position = inVertexPosition; +} diff --git a/src/client/clouds.cpp b/src/client/clouds.cpp index adfdbf842..81b4086c4 100644 --- a/src/client/clouds.cpp +++ b/src/client/clouds.cpp @@ -28,7 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include - // Menu clouds are created later class Clouds; Clouds *g_menuclouds = NULL; @@ -344,6 +343,7 @@ void Clouds::updateMesh() void Clouds::render() { +#if 0 if (m_params.density <= 0.0f) return; // no need to do anything @@ -394,13 +394,7 @@ void Clouds::render() // Restore fog settings driver->setFog(fog_color, fog_type, fog_start, fog_end, fog_density, fog_pixelfog, fog_rangefog); -} - -void Clouds::renderVolumetrics() { - video::IVideoDriver* driver = SceneManager->getVideoDriver(); - v2u32 ss = driver->getScreenSize(); - core::rect rect(0, 0, ss.X, ss.Y); - driver->draw2DRectangle(video::SColor(255, 255, 255, 255), rect); +#endif } void Clouds::step(float dtime) diff --git a/src/client/clouds.h b/src/client/clouds.h index 7676136b2..100f3fcbe 100644 --- a/src/client/clouds.h +++ b/src/client/clouds.h @@ -52,8 +52,6 @@ public: virtual void render(); - void renderVolumetrics(); - virtual const aabb3f &getBoundingBox() const { return m_box; @@ -93,6 +91,10 @@ public: invalidateMesh(); } + float getDensity() const { + return m_params.density; + } + void setColorBright(video::SColor color_bright) { m_params.color_bright = color_bright; @@ -112,6 +114,10 @@ public: invalidateMesh(); } + float getHeight() const { + return m_params.height; + } + void setSpeed(v2f speed) { m_params.speed = speed; @@ -126,6 +132,10 @@ public: invalidateMesh(); } + float getThickness() const { + return m_params.thickness; + } + bool isCameraInsideCloud() const { return m_camera_inside_cloud; } const video::SColor getColor() const { return m_color.toSColor(); } @@ -147,7 +157,7 @@ private: bool gridFilled(int x, int y) const; - video::SMaterial m_volume_material; + video::ITexture *m_density_texture = nullptr; video::SMaterial m_material; irr_ptr m_meshbuffer; diff --git a/src/client/game.cpp b/src/client/game.cpp index 9bd8fc359..252a59a0e 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -383,9 +383,18 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter m_animation_timer_delta_pixel{"animationTimerDelta"}; CachedPixelShaderSetting m_artificial_light{ "artificialLight" }; CachedPixelShaderSetting m_day_light{"dayLight"}; + CachedPixelShaderSetting m_eye_position_pixel{ "eyePosition" }; + CachedVertexShaderSetting m_eye_position_vertex{ "eyePosition" }; 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_projinv_vertex{"mCameraProjInv"}; + CachedPixelShaderSetting m_camera_projinv_pixel{"mCameraProjInv"}; + CachedVertexShaderSetting m_camera_view_vertex{"mCameraView"}; + CachedPixelShaderSetting m_camera_view_pixel{"mCameraView"}; + CachedPixelShaderSetting m_camera_near_pixel{"cameraNear"}; + CachedPixelShaderSetting m_camera_far_pixel{"cameraFar"}; CachedPixelShaderSetting m_texture0{"texture0"}; CachedPixelShaderSetting m_texture1{"texture1"}; CachedPixelShaderSetting m_texture2{"texture2"}; @@ -408,6 +417,9 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter float m_bloom_strength; CachedPixelShaderSetting m_bloom_radius_pixel{"bloomRadius"}; float m_bloom_radius; + CachedPixelShaderSetting m_cloud_height_pixel{"cloudHeight"}; + CachedPixelShaderSetting m_cloud_thickness_pixel{"cloudThickness"}; + CachedPixelShaderSetting m_cloud_density_pixel{"cloudDensity"}; CachedPixelShaderSetting m_saturation_pixel{"saturation"}; float m_gamma; CachedPixelShaderSetting m_gamma_pixel{"gamma"}; @@ -489,6 +501,10 @@ public: m_animation_timer_delta_vertex.set(&animation_timer_delta_f, services); m_animation_timer_delta_pixel.set(&animation_timer_delta_f, services); + v3f epos = m_client->getEnv().getLocalPlayer()->getEyePosition(); + m_eye_position_pixel.set(epos, services); + m_eye_position_vertex.set(epos, services); + if (m_client->getMinimap()) { v3f minimap_yaw = m_client->getMinimap()->getYawVec(); m_minimap_yaw.set(minimap_yaw, services); @@ -498,6 +514,24 @@ 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); + + core::matrix4 camera_proj = m_client->getCamera()->getCameraNode()->getProjectionMatrix(); + core::matrix4 camera_projinv; + camera_proj.getInverse(camera_projinv); + m_camera_projinv_vertex.set(camera_projinv, services); + m_camera_projinv_pixel.set(camera_projinv, services); + + core::matrix4 camera_view = m_client->getCamera()->getCameraNode()->getViewMatrix(); + m_camera_view_vertex.set(camera_view, services); + m_camera_view_pixel.set(camera_view, services); + + float camera_near = m_client->getCamera()->getCameraNode()->getNearValue(); + m_camera_near_pixel.set(&camera_near, services); + float camera_far = m_client->getCamera()->getCameraNode()->getFarValue(); + m_camera_far_pixel.set(&camera_far, services); + SamplerLayer_t tex_id; tex_id = 0; m_texture0.set(&tex_id, services); @@ -537,6 +571,17 @@ public: video::SColorf artificial_light = lighting.artificial_light_color; m_artificial_light.set(artificial_light, services); + // TODO: settings + Clouds* clouds = m_client->getClouds(); + if (m_client->getClouds()) { + float cloud_height = clouds->getHeight() * 10.0f; + m_cloud_height_pixel.set(&cloud_height, services); + float cloud_thickness = clouds->getThickness() * 10.0f; + m_cloud_thickness_pixel.set(&cloud_thickness, services); + float cloud_density = clouds->getDensity(); + m_cloud_density_pixel.set(&cloud_density, services); + } + if (m_volumetric_light_enabled) { // Map directional light to screen space auto camera_node = m_client->getCamera()->getCameraNode(); diff --git a/src/client/render/plain.cpp b/src/client/render/plain.cpp index fb8bcec70..60a732415 100644 --- a/src/client/render/plain.cpp +++ b/src/client/render/plain.cpp @@ -66,11 +66,6 @@ void DrawHUD::run(PipelineContext &context) context.client->getCamera()->drawNametags(); } context.device->getGUIEnvironment()->drawAll(); - - // TODO: proper settings - if (true && context.client->getClouds()) { - context.client->getClouds()->renderVolumetrics(); - } } diff --git a/src/client/render/secondstage.cpp b/src/client/render/secondstage.cpp index da6536b97..0a03011c8 100644 --- a/src/client/render/secondstage.cpp +++ b/src/client/render/secondstage.cpp @@ -25,6 +25,65 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/tile.h" #include "settings.h" +class NoiseStep : public RenderStep { +public: + NoiseStep(u32 shader_id, u8 texture_id) : + shader_id(shader_id), texture_id(texture_id) + { + material.UseMipMaps = false; + material.ZBuffer = true; + material.ZWriteEnable = video::EZW_ON; + } + + void setRenderSource(RenderSource* _source) override { + source = _source; + } + + void setRenderTarget(RenderTarget* _target) override { + target = _target; + } + + void reset(PipelineContext& context) override {} + + void run(PipelineContext& context) override { + video::ITexture* texture = source->getTexture(texture_id); + if (texture != last_texture) { + last_texture = texture; + + if (target) + target->activate(context); + + // attach the shader + material.MaterialType = context.client->getShaderSource()->getShaderInfo(shader_id).material; + + auto driver = context.device->getVideoDriver(); + + static const video::SColor color = video::SColor(0, 0, 0, 255); + static const video::S3DVertex vertices[4] = { + video::S3DVertex(1.0, -1.0, 0.0, 0.0, 0.0, -1.0, + color, 1.0, 0.0), + video::S3DVertex(-1.0, -1.0, 0.0, 0.0, 0.0, -1.0, + color, 0.0, 0.0), + video::S3DVertex(-1.0, 1.0, 0.0, 0.0, 0.0, -1.0, + color, 0.0, 1.0), + video::S3DVertex(1.0, 1.0, 0.0, 0.0, 0.0, -1.0, + color, 1.0, 1.0), + }; + static const u16 indices[6] = { 0, 1, 2, 2, 3, 0 }; + driver->setMaterial(material); + driver->drawVertexPrimitiveList(&vertices, 4, &indices, 2); + } + } + +private: + u32 shader_id; + u8 texture_id; + video::SMaterial material; + video::ITexture* last_texture = nullptr; + RenderSource* source{ nullptr }; + RenderTarget* target{ nullptr }; +}; + PostProcessingStep::PostProcessingStep(u32 _shader_id, const std::vector &_texture_map) : shader_id(_shader_id), texture_map(_texture_map) { @@ -65,7 +124,6 @@ void PostProcessingStep::run(PipelineContext &context) if (target) target->activate(context); - // attach the shader material.MaterialType = context.client->getShaderSource()->getShaderInfo(shader_id).material; auto driver = context.device->getVideoDriver(); @@ -96,6 +154,12 @@ void PostProcessingStep::setBilinearFilter(u8 index, bool value) material.TextureLayers[index].MagFilter = value ? video::ETMAGF_LINEAR : video::ETMAGF_NEAREST; } +void PostProcessingStep::setWrapRepeat(u8 index, bool value) { + assert(index < video::MATERIAL_MAX_TEXTURES); + material.TextureLayers[index].TextureWrapU = value ? video::ETC_REPEAT : video::ETC_CLAMP_TO_EDGE; + material.TextureLayers[index].TextureWrapV = value ? video::ETC_REPEAT : video::ETC_CLAMP_TO_EDGE; +} + RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep, v2f scale, Client *client) { auto buffer = pipeline->createOwned(); @@ -121,8 +185,13 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep static const u8 TEXTURE_EXPOSURE_2 = 4; static const u8 TEXTURE_FXAA = 5; static const u8 TEXTURE_VOLUME = 6; - static const u8 TEXTURE_SCALE_DOWN = 10; - static const u8 TEXTURE_SCALE_UP = 20; + static const u8 TEXTURE_CLOUDS_1 = 7; + static const u8 TEXTURE_CLOUDS_2 = 8; + static const u8 TEXTURE_CLOUD_DENSITY = 9; + static const u8 TEXTURE_NOISE = 10; + static const u8 TEXTURE_NOISE_COARSE = 11; + static const u8 TEXTURE_SCALE_DOWN = 20; + static const u8 TEXTURE_SCALE_UP = 30; // Super-sampling is simply rendering into a larger texture. // Downscaling is done by the final step when rendering to the screen. @@ -132,6 +201,8 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep const bool enable_ssaa = antialiasing == "ssaa"; const bool enable_fxaa = antialiasing == "fxaa"; const bool enable_volumetric_light = g_settings->getBool("enable_volumetric_lighting") && enable_bloom; + const bool enable_volumetric_clouds = true; + // TODO: Add clouds setting if (enable_ssaa) { u16 ssaa_scale = MYMAX(2, g_settings->getU16("fsaa")); @@ -152,10 +223,47 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep // Number of mipmap levels of the bloom downsampling texture const u8 MIPMAP_LEVELS = 4; - // post-processing stage u8 source = TEXTURE_COLOR; + + if (enable_volumetric_clouds) { + buffer->setTexture(TEXTURE_NOISE, core::dimension2du(256, 256), "noise", color_format); + + shader_id = client->getShaderSource()->getShader("noise_shader", TILE_MATERIAL_PLAIN, NDT_MESH); + RenderStep *noise_step = pipeline->addStep(shader_id, TEXTURE_NOISE); + noise_step->setRenderSource(buffer); + noise_step->setRenderTarget(pipeline->createOwned(buffer, TEXTURE_NOISE)); + + buffer->setTexture(TEXTURE_NOISE_COARSE, core::dimension2du(256, 256), "noise", color_format); + + shader_id = client->getShaderSource()->getShader("coarse_noise_shader", TILE_MATERIAL_PLAIN, NDT_MESH); + noise_step = pipeline->addStep(shader_id, std::vector()); + noise_step->setRenderTarget(pipeline->createOwned(buffer, TEXTURE_NOISE_COARSE)); + + buffer->setTexture(TEXTURE_CLOUDS_1, scale * 0.25f, "clouds_1", color_format, /*clear:*/ true); + buffer->setTexture(TEXTURE_CLOUDS_2, scale, "clouds_2", color_format); + buffer->setTexture(TEXTURE_CLOUD_DENSITY, scale, "cloud_density", color_format); + + shader_id = client->getShaderSource()->getShader("volumetric_clouds", TILE_MATERIAL_PLAIN, NDT_MESH); + PostProcessingStep *volumetric_clouds = pipeline->addStep(shader_id, std::vector { TEXTURE_DEPTH, TEXTURE_NOISE, TEXTURE_NOISE_COARSE }); + volumetric_clouds->setRenderSource(buffer); + volumetric_clouds->setRenderTarget(pipeline->createOwned(buffer, TEXTURE_CLOUDS_1)); + volumetric_clouds->setBilinearFilter(1, true); + volumetric_clouds->setBilinearFilter(2, true); + volumetric_clouds->setWrapRepeat(1, true); + volumetric_clouds->setWrapRepeat(2, true); + + source = TEXTURE_CLOUDS_1; + + shader_id = client->getShaderSource()->getShader("clouds_merge", TILE_MATERIAL_PLAIN, NDT_MESH); + PostProcessingStep* blend_clouds = pipeline->addStep(shader_id, std::vector { TEXTURE_CLOUDS_1, TEXTURE_COLOR }); + blend_clouds->setRenderSource(buffer); + blend_clouds->setRenderTarget(pipeline->createOwned(buffer, TEXTURE_CLOUDS_2)); + blend_clouds->setBilinearFilter(0, true); + + source = TEXTURE_CLOUDS_2; + } // common downsampling step for bloom or autoexposure if (enable_bloom || enable_auto_exposure) { @@ -173,7 +281,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep // get bright spots u32 shader_id = client->getShaderSource()->getShader("extract_bloom", TILE_MATERIAL_PLAIN, NDT_MESH); - RenderStep *extract_bloom = pipeline->addStep(shader_id, std::vector { source, TEXTURE_EXPOSURE_1 }); + auto extract_bloom = pipeline->addStep(shader_id, std::vector { source, TEXTURE_EXPOSURE_1 }); extract_bloom->setRenderSource(buffer); extract_bloom->setRenderTarget(pipeline->createOwned(buffer, TEXTURE_BLOOM)); source = TEXTURE_BLOOM; @@ -224,7 +332,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep } // FXAA - u8 final_stage_source = TEXTURE_COLOR; + u8 final_stage_source = TEXTURE_CLOUDS_2; if (enable_fxaa) { final_stage_source = TEXTURE_FXAA; diff --git a/src/client/render/secondstage.h b/src/client/render/secondstage.h index 9e3640927..04705f3ae 100644 --- a/src/client/render/secondstage.h +++ b/src/client/render/secondstage.h @@ -49,6 +49,9 @@ public: * @param value true to enable the bilinear filter, false to disable */ void setBilinearFilter(u8 index, bool value); + + void setWrapRepeat(u8 index, bool value); + private: u32 shader_id; std::vector texture_map;