forked from Mirrorlandia_minetest/minetest
Add dynamic exposure correction (#12959)
* Add uniform for frame delta time * Adjust exposure in logarithmic (EV) space * Add network support and LUA API * Add testing mod
This commit is contained in:
parent
2715cc8bf6
commit
6d45c243f8
@ -194,6 +194,7 @@ LOCAL_SRC_FILES := \
|
|||||||
../../src/itemdef.cpp \
|
../../src/itemdef.cpp \
|
||||||
../../src/itemstackmetadata.cpp \
|
../../src/itemstackmetadata.cpp \
|
||||||
../../src/light.cpp \
|
../../src/light.cpp \
|
||||||
|
../../src/lighting.cpp \
|
||||||
../../src/log.cpp \
|
../../src/log.cpp \
|
||||||
../../src/main.cpp \
|
../../src/main.cpp \
|
||||||
../../src/map.cpp \
|
../../src/map.cpp \
|
||||||
|
@ -450,12 +450,17 @@ shadow_sky_body_orbit_tilt (Sky Body Orbit Tilt) float 0.0 -60.0 60.0
|
|||||||
|
|
||||||
[**Post processing]
|
[**Post processing]
|
||||||
|
|
||||||
# Set the exposure compensation factor.
|
# Set the exposure compensation in EV units.
|
||||||
# This factor is applied to linear color value
|
# Value of 0.0 (default) means no exposure compensation.
|
||||||
# before all other post-processing effects.
|
# Range: from -1 to 1.0
|
||||||
# Value of 1.0 (default) means no exposure compensation.
|
exposure_compensation (Exposure compensation) float 0.0 -1.0 1.0
|
||||||
# Range: from 0.1 to 10.0
|
|
||||||
exposure_factor (Exposure Factor) float 1.0 0.1 10.0
|
# Enable automatic exposure correction
|
||||||
|
# When enabled, the post-processing engine will
|
||||||
|
# automatically adjust to the brightness of the scene,
|
||||||
|
# simulating the behavior of human eye.
|
||||||
|
enable_auto_exposure (Enable Automatic Exposure) bool false
|
||||||
|
|
||||||
|
|
||||||
[**Bloom]
|
[**Bloom]
|
||||||
|
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
#define rendered texture0
|
#define rendered texture0
|
||||||
|
|
||||||
|
struct ExposureParams {
|
||||||
|
float compensationFactor;
|
||||||
|
};
|
||||||
|
|
||||||
uniform sampler2D rendered;
|
uniform sampler2D rendered;
|
||||||
uniform mediump float exposureFactor;
|
|
||||||
uniform mediump float bloomStrength;
|
uniform mediump float bloomStrength;
|
||||||
|
uniform ExposureParams exposureParams;
|
||||||
|
|
||||||
#ifdef GL_ES
|
#ifdef GL_ES
|
||||||
varying mediump vec2 varTexCoord;
|
varying mediump vec2 varTexCoord;
|
||||||
@ -10,6 +14,7 @@ varying mediump vec2 varTexCoord;
|
|||||||
centroid varying vec2 varTexCoord;
|
centroid varying vec2 varTexCoord;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
varying float exposure;
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
@ -18,10 +23,6 @@ void main(void)
|
|||||||
// translate to linear colorspace (approximate)
|
// translate to linear colorspace (approximate)
|
||||||
color = pow(color, vec3(2.2));
|
color = pow(color, vec3(2.2));
|
||||||
|
|
||||||
// Scale colors by luminance to amplify bright colors
|
color *= pow(2., exposure) * exposureParams.compensationFactor * bloomStrength;
|
||||||
// in SDR textures.
|
|
||||||
float luminance = dot(color, vec3(0.213, 0.515, 0.072));
|
|
||||||
luminance *= luminance;
|
|
||||||
color *= luminance * exposureFactor * bloomStrength;
|
|
||||||
gl_FragColor = vec4(color, 1.0); // force full alpha to avoid holes in the image.
|
gl_FragColor = vec4(color, 1.0); // force full alpha to avoid holes in the image.
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
|
#define exposureMap texture1
|
||||||
|
|
||||||
|
uniform sampler2D exposureMap;
|
||||||
|
|
||||||
#ifdef GL_ES
|
#ifdef GL_ES
|
||||||
varying mediump vec2 varTexCoord;
|
varying mediump vec2 varTexCoord;
|
||||||
#else
|
#else
|
||||||
centroid varying vec2 varTexCoord;
|
centroid varying vec2 varTexCoord;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
varying float exposure;
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
|
exposure = texture2D(exposureMap, vec2(0.5)).r;
|
||||||
|
|
||||||
varTexCoord.st = inTexCoord0.st;
|
varTexCoord.st = inTexCoord0.st;
|
||||||
gl_Position = inVertexPosition;
|
gl_Position = inVertexPosition;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
#define rendered texture0
|
#define rendered texture0
|
||||||
#define bloom texture1
|
#define bloom texture1
|
||||||
|
|
||||||
|
struct ExposureParams {
|
||||||
|
float compensationFactor;
|
||||||
|
};
|
||||||
|
|
||||||
uniform sampler2D rendered;
|
uniform sampler2D rendered;
|
||||||
uniform sampler2D bloom;
|
uniform sampler2D bloom;
|
||||||
uniform mediump float exposureFactor;
|
|
||||||
|
uniform ExposureParams exposureParams;
|
||||||
uniform lowp float bloomIntensity;
|
uniform lowp float bloomIntensity;
|
||||||
uniform lowp float saturation;
|
uniform lowp float saturation;
|
||||||
|
|
||||||
@ -13,6 +18,8 @@ varying mediump vec2 varTexCoord;
|
|||||||
centroid varying vec2 varTexCoord;
|
centroid varying vec2 varTexCoord;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
varying float exposure;
|
||||||
|
|
||||||
#ifdef ENABLE_BLOOM
|
#ifdef ENABLE_BLOOM
|
||||||
|
|
||||||
vec4 applyBloom(vec4 color, vec2 uv)
|
vec4 applyBloom(vec4 color, vec2 uv)
|
||||||
@ -80,7 +87,7 @@ void main(void)
|
|||||||
if (uv.x > 0.5 || uv.y > 0.5)
|
if (uv.x > 0.5 || uv.y > 0.5)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
color.rgb *= exposureFactor;
|
color.rgb *= exposure * exposureParams.compensationFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,11 +1,24 @@
|
|||||||
|
#define exposureMap texture2
|
||||||
|
|
||||||
|
uniform sampler2D exposureMap;
|
||||||
|
|
||||||
#ifdef GL_ES
|
#ifdef GL_ES
|
||||||
varying mediump vec2 varTexCoord;
|
varying mediump vec2 varTexCoord;
|
||||||
#else
|
#else
|
||||||
centroid varying vec2 varTexCoord;
|
centroid varying vec2 varTexCoord;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
varying float exposure;
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
|
#ifdef ENABLE_AUTO_EXPOSURE
|
||||||
|
exposure = texture2D(exposureMap, vec2(0.5)).r;
|
||||||
|
exposure = pow(2., exposure);
|
||||||
|
#else
|
||||||
|
exposure = 1.0;
|
||||||
|
#endif
|
||||||
|
|
||||||
varTexCoord.st = inTexCoord0.st;
|
varTexCoord.st = inTexCoord0.st;
|
||||||
gl_Position = inVertexPosition;
|
gl_Position = inVertexPosition;
|
||||||
}
|
}
|
||||||
|
75
client/shaders/update_exposure/opengl_fragment.glsl
Normal file
75
client/shaders/update_exposure/opengl_fragment.glsl
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#define exposure texture0
|
||||||
|
#define screen texture1
|
||||||
|
|
||||||
|
struct ExposureParams {
|
||||||
|
float luminanceMin;
|
||||||
|
float luminanceMax;
|
||||||
|
float exposureCorrection;
|
||||||
|
float luminanceKey;
|
||||||
|
float speedDarkBright;
|
||||||
|
float speedBrightDark;
|
||||||
|
float centerWeightPower;
|
||||||
|
float compensationFactor;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform sampler2D exposure;
|
||||||
|
uniform sampler2D screen;
|
||||||
|
|
||||||
|
#ifdef ENABLE_BLOOM
|
||||||
|
uniform float bloomStrength;
|
||||||
|
#else
|
||||||
|
const float bloomStrength = 1.0;
|
||||||
|
#endif
|
||||||
|
uniform ExposureParams exposureParams;
|
||||||
|
uniform float animationTimerDelta;
|
||||||
|
|
||||||
|
|
||||||
|
const vec3 luminanceFactors = vec3(0.213, 0.715, 0.072);
|
||||||
|
|
||||||
|
float getLuminance(vec3 color)
|
||||||
|
{
|
||||||
|
return dot(color, luminanceFactors);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
float previousExposure = texture2D(exposure, vec2(0.5, 0.5)).r;
|
||||||
|
|
||||||
|
vec3 averageColor = vec3(0.);
|
||||||
|
float n = 0.;
|
||||||
|
|
||||||
|
// Scan the screen with center-weighting and sample average color
|
||||||
|
for (float _x = 0.1; _x < 0.9; _x += 0.17) {
|
||||||
|
float x = pow(_x, exposureParams.centerWeightPower);
|
||||||
|
for (float _y = 0.1; _y < 0.9; _y += 0.17) {
|
||||||
|
float y = pow(_y, exposureParams.centerWeightPower);
|
||||||
|
averageColor += texture2D(screen, vec2(0.5 + 0.5 * x, 0.5 + 0.5 * y)).rgb;
|
||||||
|
averageColor += texture2D(screen, vec2(0.5 + 0.5 * x, 0.5 - 0.5 * y)).rgb;
|
||||||
|
averageColor += texture2D(screen, vec2(0.5 - 0.5 * x, 0.5 + 0.5 * y)).rgb;
|
||||||
|
averageColor += texture2D(screen, vec2(0.5 - 0.5 * x, 0.5 - 0.5 * y)).rgb;
|
||||||
|
n += 4.;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float luminance = getLuminance(averageColor);
|
||||||
|
luminance /= n;
|
||||||
|
|
||||||
|
luminance /= pow(2., previousExposure) * bloomStrength * exposureParams.compensationFactor; // compensate for the configurable factors
|
||||||
|
|
||||||
|
luminance = clamp(luminance, exposureParams.luminanceMin, exposureParams.luminanceMax);
|
||||||
|
|
||||||
|
// From https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/course-notes-moving-frostbite-to-pbr-v2.pdf
|
||||||
|
// 1. EV100 = log2(luminance * S / K) where S = 100, K = 0.125 = log2(luminance) + 3
|
||||||
|
// 2. Lmax = 1.2 * 2 ^ (EV100 - EC)
|
||||||
|
// => Lmax = 1.2 * 2^3 * luminance / 2^EC = 9.6 * luminance / 2^EC
|
||||||
|
// 3. exposure = 1 / Lmax
|
||||||
|
// => exposure = 2^EC / (9.6 * luminance)
|
||||||
|
float wantedExposure = exposureParams.exposureCorrection - log(luminance)/0.693147180559945 - 3.263034405833794;
|
||||||
|
|
||||||
|
if (wantedExposure < previousExposure)
|
||||||
|
wantedExposure = mix(wantedExposure, previousExposure, exp(-animationTimerDelta * exposureParams.speedDarkBright)); // dark -> bright
|
||||||
|
else
|
||||||
|
wantedExposure = mix(wantedExposure, previousExposure, exp(-animationTimerDelta * exposureParams.speedBrightDark)); // bright -> dark
|
||||||
|
|
||||||
|
gl_FragColor = vec4(vec3(wantedExposure), 1.);
|
||||||
|
}
|
11
client/shaders/update_exposure/opengl_vertex.glsl
Normal file
11
client/shaders/update_exposure/opengl_vertex.glsl
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifdef GL_ES
|
||||||
|
varying mediump vec2 varTexCoord;
|
||||||
|
#else
|
||||||
|
centroid varying vec2 varTexCoord;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
varTexCoord.st = inTexCoord0.st;
|
||||||
|
gl_Position = inVertexPosition;
|
||||||
|
}
|
@ -7472,6 +7472,15 @@ child will follow movement and rotation of that bone.
|
|||||||
* `shadows` is a table that controls ambient shadows
|
* `shadows` is a table that controls ambient shadows
|
||||||
* `intensity` sets the intensity of the shadows from 0 (no shadows, default) to 1 (blackness)
|
* `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.
|
* 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
|
||||||
|
* `luminance_max` set the upper luminance boundary to use in the calculation
|
||||||
|
* `exposure_correction` correct observed exposure by the given EV value
|
||||||
|
* `speed_dark_bright` set the speed of adapting to bright light
|
||||||
|
* `speed_bright_dark` set the speed of adapting to dark scene
|
||||||
|
* `center_weight_power` set the power factor for center-weighted luminance measurement
|
||||||
|
|
||||||
* `get_lighting()`: returns the current state of lighting for the player.
|
* `get_lighting()`: returns the current state of lighting for the player.
|
||||||
* Result is a table with the same fields as `light_definition` in `set_lighting`.
|
* Result is a table with the same fields as `light_definition` in `set_lighting`.
|
||||||
* `respawn()`: Respawns the player using the same mechanism as the death screen,
|
* `respawn()`: Respawns the player using the same mechanism as the death screen,
|
||||||
|
140
games/devtest/mods/lighting/init.lua
Normal file
140
games/devtest/mods/lighting/init.lua
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
local lighting_sections = {
|
||||||
|
{n = "shadows", d = "Shadows",
|
||||||
|
entries = {
|
||||||
|
{ n = "intensity", d = "Shadow Intensity", min = 0, max = 1 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
n = "exposure", d = "Exposure",
|
||||||
|
entries = {
|
||||||
|
{n = "luminance_min", d = "Minimum Luminance", min = -10, max = 10},
|
||||||
|
{n = "luminance_max", d = "Maximum Luminance", min = -10, max = 10},
|
||||||
|
{n = "exposure_correction", d = "Exposure Correction", min = -10, max = 10},
|
||||||
|
{n = "speed_dark_bright", d = "Bright light adaptation speed", min = -10, max = 10, type="log2"},
|
||||||
|
{n = "speed_bright_dark", d = "Dark scene adaptation speed", min = -10, max = 10, type="log2"},
|
||||||
|
{n = "center_weight_power", d = "Power factor for center-weighting", min = 0.1, max = 10},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local function dump_lighting(lighting)
|
||||||
|
local result = "{\n"
|
||||||
|
local section_count = 0
|
||||||
|
for _,section in ipairs(lighting_sections) do
|
||||||
|
section_count = section_count + 1
|
||||||
|
|
||||||
|
local parameters = section.entries or {}
|
||||||
|
local state = lighting[section.n] or {}
|
||||||
|
|
||||||
|
result = result.." "..section.n.." = {\n"
|
||||||
|
|
||||||
|
local count = 0
|
||||||
|
for _,v in ipairs(parameters) do
|
||||||
|
count = count + 1
|
||||||
|
result = result.." "..v.n.." = "..(math.floor(state[v.n] * 1000)/1000)
|
||||||
|
if count < #parameters then
|
||||||
|
result = result..","
|
||||||
|
end
|
||||||
|
result = result.."\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
result = result.." }"
|
||||||
|
|
||||||
|
if section_count < #lighting_sections then
|
||||||
|
result = result..","
|
||||||
|
end
|
||||||
|
result = result.."\n"
|
||||||
|
end
|
||||||
|
result = result .."}"
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_chatcommand("set_lighting", {
|
||||||
|
params = "",
|
||||||
|
description = "Tune lighting parameters",
|
||||||
|
func = function(player_name, param)
|
||||||
|
local player = minetest.get_player_by_name(player_name);
|
||||||
|
if not player then return end
|
||||||
|
|
||||||
|
local lighting = player:get_lighting()
|
||||||
|
local exposure = lighting.exposure or {}
|
||||||
|
|
||||||
|
local form = {
|
||||||
|
"formspec_version[2]",
|
||||||
|
"size[15,30]",
|
||||||
|
"position[0.99,0.15]",
|
||||||
|
"anchor[1,0]",
|
||||||
|
"padding[0.05,0.1]",
|
||||||
|
"no_prepend[]"
|
||||||
|
};
|
||||||
|
|
||||||
|
local line = 1
|
||||||
|
for _,section in ipairs(lighting_sections) do
|
||||||
|
local parameters = section.entries or {}
|
||||||
|
local state = lighting[section.n] or {}
|
||||||
|
|
||||||
|
table.insert(form, "label[1,"..line..";"..section.d.."]")
|
||||||
|
line = line + 1
|
||||||
|
|
||||||
|
for _,v in ipairs(parameters) do
|
||||||
|
table.insert(form, "label[2,"..line..";"..v.d.."]")
|
||||||
|
table.insert(form, "scrollbaroptions[min=0;max=1000;smallstep=10;largestep=100;thumbsize=10]")
|
||||||
|
local value = state[v.n]
|
||||||
|
if v.type == "log2" then
|
||||||
|
value = math.log(value or 1) / math.log(2)
|
||||||
|
end
|
||||||
|
local sb_scale = math.floor(1000 * (math.max(v.min, value or 0) - v.min) / (v.max - v.min))
|
||||||
|
table.insert(form, "scrollbar[2,"..(line+0.7)..";12,1;horizontal;"..section.n.."."..v.n..";"..sb_scale.."]")
|
||||||
|
line = line + 2.7
|
||||||
|
end
|
||||||
|
|
||||||
|
line = line + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.show_formspec(player_name, "lighting", table.concat(form))
|
||||||
|
local debug_value = dump_lighting(lighting)
|
||||||
|
local debug_ui = player:hud_add({type="text", position={x=0.1, y=0.3}, scale={x=1,y=1}, alignment = {x=1, y=1}, text=debug_value, number=0xFFFFFF})
|
||||||
|
player:get_meta():set_int("lighting_hud", debug_ui)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
|
if formname ~= "lighting" then return end
|
||||||
|
|
||||||
|
if not player then return end
|
||||||
|
|
||||||
|
local hud_id = player:get_meta():get_int("lighting_hud")
|
||||||
|
|
||||||
|
if fields.quit then
|
||||||
|
player:hud_remove(hud_id)
|
||||||
|
player:get_meta():set_int("lighting_hud", -1)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local lighting = player:get_lighting()
|
||||||
|
for _,section in ipairs(lighting_sections) do
|
||||||
|
local parameters = section.entries or {}
|
||||||
|
|
||||||
|
local state = (lighting[section.n] or {})
|
||||||
|
lighting[section.n] = state
|
||||||
|
|
||||||
|
for _,v in ipairs(parameters) do
|
||||||
|
|
||||||
|
if fields[section.n.."."..v.n] then
|
||||||
|
local event = minetest.explode_scrollbar_event(fields[section.n.."."..v.n])
|
||||||
|
if event.type == "CHG" then
|
||||||
|
local value = v.min + (v.max - v.min) * (event.value / 1000);
|
||||||
|
if v.type == "log2" then
|
||||||
|
value = math.pow(2, value);
|
||||||
|
end
|
||||||
|
state[v.n] = value;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local debug_value = dump_lighting(lighting)
|
||||||
|
player:hud_change(hud_id, "text", debug_value)
|
||||||
|
|
||||||
|
player:set_lighting(lighting)
|
||||||
|
end)
|
2
games/devtest/mods/lighting/mod.conf
Normal file
2
games/devtest/mods/lighting/mod.conf
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
name = lighting
|
||||||
|
description = UI to control and debug lighting parameters
|
@ -210,20 +210,6 @@ minetest.register_chatcommand("dump_item", {
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
-- shadow control
|
|
||||||
minetest.register_on_joinplayer(function (player)
|
|
||||||
player:set_lighting({shadows={intensity = 0.33}})
|
|
||||||
end)
|
|
||||||
|
|
||||||
core.register_chatcommand("set_shadow", {
|
|
||||||
params = "<shadow_intensity>",
|
|
||||||
description = "Set shadow parameters of current player.",
|
|
||||||
func = function(player_name, param)
|
|
||||||
local shadow_intensity = tonumber(param)
|
|
||||||
minetest.get_player_by_name(player_name):set_lighting({shadows = { intensity = shadow_intensity} })
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
core.register_chatcommand("set_saturation", {
|
core.register_chatcommand("set_saturation", {
|
||||||
params = "<saturation>",
|
params = "<saturation>",
|
||||||
description = "Set the saturation for current player.",
|
description = "Set the saturation for current player.",
|
||||||
|
@ -375,6 +375,7 @@ set(common_SRCS
|
|||||||
itemdef.cpp
|
itemdef.cpp
|
||||||
itemstackmetadata.cpp
|
itemstackmetadata.cpp
|
||||||
light.cpp
|
light.cpp
|
||||||
|
lighting.cpp
|
||||||
log.cpp
|
log.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
map.cpp
|
map.cpp
|
||||||
|
@ -531,8 +531,13 @@ void ClientEnvironment::updateFrameTime(bool is_paused)
|
|||||||
{
|
{
|
||||||
// if paused, m_frame_time_pause_accumulator increases by dtime,
|
// if paused, m_frame_time_pause_accumulator increases by dtime,
|
||||||
// otherwise, m_frame_time increases by dtime
|
// otherwise, m_frame_time increases by dtime
|
||||||
if (is_paused)
|
if (is_paused) {
|
||||||
|
m_frame_dtime = 0;
|
||||||
m_frame_time_pause_accumulator = porting::getTimeMs() - m_frame_time;
|
m_frame_time_pause_accumulator = porting::getTimeMs() - m_frame_time;
|
||||||
else
|
}
|
||||||
m_frame_time = porting::getTimeMs() - m_frame_time_pause_accumulator;
|
else {
|
||||||
|
auto new_frame_time = porting::getTimeMs() - m_frame_time_pause_accumulator;
|
||||||
|
m_frame_dtime = new_frame_time - MYMAX(m_frame_time, m_frame_time_pause_accumulator);
|
||||||
|
m_frame_time = new_frame_time;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,6 +144,7 @@ public:
|
|||||||
|
|
||||||
void updateFrameTime(bool is_paused);
|
void updateFrameTime(bool is_paused);
|
||||||
u64 getFrameTime() const { return m_frame_time; }
|
u64 getFrameTime() const { return m_frame_time; }
|
||||||
|
u64 getFrameTimeDelta() const { return m_frame_dtime; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ClientMap *m_map;
|
ClientMap *m_map;
|
||||||
@ -158,5 +159,6 @@ private:
|
|||||||
std::list<std::string> m_player_names;
|
std::list<std::string> m_player_names;
|
||||||
v3s16 m_camera_offset;
|
v3s16 m_camera_offset;
|
||||||
u64 m_frame_time = 0;
|
u64 m_frame_time = 0;
|
||||||
|
u64 m_frame_dtime = 0;
|
||||||
u64 m_frame_time_pause_accumulator = 0;
|
u64 m_frame_time_pause_accumulator = 0;
|
||||||
};
|
};
|
||||||
|
@ -414,6 +414,8 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
|
|||||||
CachedPixelShaderSetting<float> m_fog_distance;
|
CachedPixelShaderSetting<float> m_fog_distance;
|
||||||
CachedVertexShaderSetting<float> m_animation_timer_vertex;
|
CachedVertexShaderSetting<float> m_animation_timer_vertex;
|
||||||
CachedPixelShaderSetting<float> m_animation_timer_pixel;
|
CachedPixelShaderSetting<float> m_animation_timer_pixel;
|
||||||
|
CachedVertexShaderSetting<float> m_animation_timer_delta_vertex;
|
||||||
|
CachedPixelShaderSetting<float> m_animation_timer_delta_pixel;
|
||||||
CachedPixelShaderSetting<float, 3> m_day_light;
|
CachedPixelShaderSetting<float, 3> m_day_light;
|
||||||
CachedPixelShaderSetting<float, 4> m_star_color;
|
CachedPixelShaderSetting<float, 4> m_star_color;
|
||||||
CachedPixelShaderSetting<float, 3> m_eye_position_pixel;
|
CachedPixelShaderSetting<float, 3> m_eye_position_pixel;
|
||||||
@ -427,8 +429,8 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
|
|||||||
CachedPixelShaderSetting<SamplerLayer_t> m_texture3;
|
CachedPixelShaderSetting<SamplerLayer_t> m_texture3;
|
||||||
CachedPixelShaderSetting<float, 2> m_texel_size0;
|
CachedPixelShaderSetting<float, 2> m_texel_size0;
|
||||||
std::array<float, 2> m_texel_size0_values;
|
std::array<float, 2> m_texel_size0_values;
|
||||||
CachedPixelShaderSetting<float> m_exposure_factor_pixel;
|
CachedStructPixelShaderSetting<float, 7> m_exposure_params_pixel;
|
||||||
float m_user_exposure_factor;
|
float m_user_exposure_compensation;
|
||||||
bool m_bloom_enabled;
|
bool m_bloom_enabled;
|
||||||
CachedPixelShaderSetting<float> m_bloom_intensity_pixel;
|
CachedPixelShaderSetting<float> m_bloom_intensity_pixel;
|
||||||
float m_bloom_intensity;
|
float m_bloom_intensity;
|
||||||
@ -443,8 +445,8 @@ public:
|
|||||||
{
|
{
|
||||||
if (name == "enable_fog")
|
if (name == "enable_fog")
|
||||||
m_fog_enabled = g_settings->getBool("enable_fog");
|
m_fog_enabled = g_settings->getBool("enable_fog");
|
||||||
if (name == "exposure_factor")
|
if (name == "exposure_compensation")
|
||||||
m_user_exposure_factor = g_settings->getFloat("exposure_factor", 0.1f, 10.0f);
|
m_user_exposure_compensation = g_settings->getFloat("exposure_compensation", -1.0f, 1.0f);
|
||||||
if (name == "bloom_intensity")
|
if (name == "bloom_intensity")
|
||||||
m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f);
|
m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f);
|
||||||
if (name == "bloom_strength_factor")
|
if (name == "bloom_strength_factor")
|
||||||
@ -470,6 +472,8 @@ public:
|
|||||||
m_fog_distance("fogDistance"),
|
m_fog_distance("fogDistance"),
|
||||||
m_animation_timer_vertex("animationTimer"),
|
m_animation_timer_vertex("animationTimer"),
|
||||||
m_animation_timer_pixel("animationTimer"),
|
m_animation_timer_pixel("animationTimer"),
|
||||||
|
m_animation_timer_delta_vertex("animationTimerDelta"),
|
||||||
|
m_animation_timer_delta_pixel("animationTimerDelta"),
|
||||||
m_day_light("dayLight"),
|
m_day_light("dayLight"),
|
||||||
m_star_color("starColor"),
|
m_star_color("starColor"),
|
||||||
m_eye_position_pixel("eyePosition"),
|
m_eye_position_pixel("eyePosition"),
|
||||||
@ -482,20 +486,24 @@ public:
|
|||||||
m_texture2("texture2"),
|
m_texture2("texture2"),
|
||||||
m_texture3("texture3"),
|
m_texture3("texture3"),
|
||||||
m_texel_size0("texelSize0"),
|
m_texel_size0("texelSize0"),
|
||||||
m_exposure_factor_pixel("exposureFactor"),
|
m_exposure_params_pixel("exposureParams",
|
||||||
|
std::array<const char*, 7> {
|
||||||
|
"luminanceMin", "luminanceMax", "exposureCorrection",
|
||||||
|
"speedDarkBright", "speedBrightDark", "centerWeightPower", "compensationFactor"
|
||||||
|
}),
|
||||||
m_bloom_intensity_pixel("bloomIntensity"),
|
m_bloom_intensity_pixel("bloomIntensity"),
|
||||||
m_bloom_strength_pixel("bloomStrength"),
|
m_bloom_strength_pixel("bloomStrength"),
|
||||||
m_bloom_radius_pixel("bloomRadius"),
|
m_bloom_radius_pixel("bloomRadius"),
|
||||||
m_saturation_pixel("saturation")
|
m_saturation_pixel("saturation")
|
||||||
{
|
{
|
||||||
g_settings->registerChangedCallback("enable_fog", settingsCallback, this);
|
g_settings->registerChangedCallback("enable_fog", settingsCallback, this);
|
||||||
g_settings->registerChangedCallback("exposure_factor", settingsCallback, this);
|
g_settings->registerChangedCallback("exposure_compensation", settingsCallback, this);
|
||||||
g_settings->registerChangedCallback("bloom_intensity", settingsCallback, this);
|
g_settings->registerChangedCallback("bloom_intensity", settingsCallback, this);
|
||||||
g_settings->registerChangedCallback("bloom_strength_factor", settingsCallback, this);
|
g_settings->registerChangedCallback("bloom_strength_factor", settingsCallback, this);
|
||||||
g_settings->registerChangedCallback("bloom_radius", settingsCallback, this);
|
g_settings->registerChangedCallback("bloom_radius", settingsCallback, this);
|
||||||
g_settings->registerChangedCallback("saturation", settingsCallback, this);
|
g_settings->registerChangedCallback("saturation", settingsCallback, this);
|
||||||
m_fog_enabled = g_settings->getBool("enable_fog");
|
m_fog_enabled = g_settings->getBool("enable_fog");
|
||||||
m_user_exposure_factor = g_settings->getFloat("exposure_factor", 0.1f, 10.0f);
|
m_user_exposure_compensation = g_settings->getFloat("exposure_compensation", -1.0f, 1.0f);
|
||||||
m_bloom_enabled = g_settings->getBool("enable_bloom");
|
m_bloom_enabled = g_settings->getBool("enable_bloom");
|
||||||
m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f);
|
m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f);
|
||||||
m_bloom_strength = RenderingEngine::BASE_BLOOM_STRENGTH * g_settings->getFloat("bloom_strength_factor", 0.1f, 10.0f);
|
m_bloom_strength = RenderingEngine::BASE_BLOOM_STRENGTH * g_settings->getFloat("bloom_strength_factor", 0.1f, 10.0f);
|
||||||
@ -546,6 +554,10 @@ public:
|
|||||||
m_animation_timer_vertex.set(&animation_timer_f, services);
|
m_animation_timer_vertex.set(&animation_timer_f, services);
|
||||||
m_animation_timer_pixel.set(&animation_timer_f, services);
|
m_animation_timer_pixel.set(&animation_timer_f, services);
|
||||||
|
|
||||||
|
float animation_timer_delta_f = (float)m_client->getEnv().getFrameTimeDelta() / 100000.f;
|
||||||
|
m_animation_timer_delta_vertex.set(&animation_timer_delta_f, services);
|
||||||
|
m_animation_timer_delta_pixel.set(&animation_timer_delta_f, services);
|
||||||
|
|
||||||
float eye_position_array[3];
|
float eye_position_array[3];
|
||||||
v3f epos = m_client->getEnv().getLocalPlayer()->getEyePosition();
|
v3f epos = m_client->getEnv().getLocalPlayer()->getEyePosition();
|
||||||
epos.getAs3Values(eye_position_array);
|
epos.getAs3Values(eye_position_array);
|
||||||
@ -577,10 +589,17 @@ public:
|
|||||||
|
|
||||||
m_texel_size0.set(m_texel_size0_values.data(), services);
|
m_texel_size0.set(m_texel_size0_values.data(), services);
|
||||||
|
|
||||||
float exposure_factor = m_user_exposure_factor;
|
const AutoExposure &exposure_params = m_client->getEnv().getLocalPlayer()->getLighting().exposure;
|
||||||
if (std::isnan(exposure_factor))
|
std::array<float, 7> exposure_buffer = {
|
||||||
exposure_factor = 1.0f;
|
std::pow(2.0f, exposure_params.luminance_min),
|
||||||
m_exposure_factor_pixel.set(&exposure_factor, services);
|
std::pow(2.0f, exposure_params.luminance_max),
|
||||||
|
exposure_params.exposure_correction,
|
||||||
|
exposure_params.speed_dark_bright,
|
||||||
|
exposure_params.speed_bright_dark,
|
||||||
|
exposure_params.center_weight_power,
|
||||||
|
powf(2.f, m_user_exposure_compensation)
|
||||||
|
};
|
||||||
|
m_exposure_params_pixel.set(exposure_buffer.data(), services);
|
||||||
|
|
||||||
if (m_bloom_enabled) {
|
if (m_bloom_enabled) {
|
||||||
m_bloom_intensity_pixel.set(&m_bloom_intensity, services);
|
m_bloom_intensity_pixel.set(&m_bloom_intensity, services);
|
||||||
|
@ -101,6 +101,16 @@ void TextureBuffer::reset(PipelineContext &context)
|
|||||||
RenderSource::reset(context);
|
RenderSource::reset(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextureBuffer::swapTextures(u8 texture_a, u8 texture_b)
|
||||||
|
{
|
||||||
|
assert(m_definitions[texture_a].valid && m_definitions[texture_b].valid);
|
||||||
|
|
||||||
|
video::ITexture *temp = m_textures[texture_a];
|
||||||
|
m_textures[texture_a] = m_textures[texture_b];
|
||||||
|
m_textures[texture_b] = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefinition& definition, PipelineContext &context)
|
bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefinition& definition, PipelineContext &context)
|
||||||
{
|
{
|
||||||
bool modify;
|
bool modify;
|
||||||
@ -230,6 +240,16 @@ void SetRenderTargetStep::run(PipelineContext &context)
|
|||||||
step->setRenderTarget(target);
|
step->setRenderTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SwapTexturesStep::SwapTexturesStep(TextureBuffer *_buffer, u8 _texture_a, u8 _texture_b)
|
||||||
|
: buffer(_buffer), texture_a(_texture_a), texture_b(_texture_b)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapTexturesStep::run(PipelineContext &context)
|
||||||
|
{
|
||||||
|
buffer->swapTextures(texture_a, texture_b);
|
||||||
|
}
|
||||||
|
|
||||||
RenderSource *RenderPipeline::getInput()
|
RenderSource *RenderPipeline::getInput()
|
||||||
{
|
{
|
||||||
return &m_input;
|
return &m_input;
|
||||||
|
@ -53,7 +53,7 @@ struct PipelineContext
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Base object that can be owned by RenderPipeline
|
* Base object that can be owned by RenderPipeline
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class RenderPipelineObject
|
class RenderPipelineObject
|
||||||
{
|
{
|
||||||
@ -74,7 +74,7 @@ public:
|
|||||||
virtual u8 getTextureCount() = 0;
|
virtual u8 getTextureCount() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a texture by index.
|
* Get a texture by index.
|
||||||
* Returns nullptr is the texture does not exist.
|
* Returns nullptr is the texture does not exist.
|
||||||
*/
|
*/
|
||||||
virtual video::ITexture *getTexture(u8 index) = 0;
|
virtual video::ITexture *getTexture(u8 index) = 0;
|
||||||
@ -119,7 +119,7 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure fixed-size texture for the specific index
|
* Configure fixed-size texture for the specific index
|
||||||
*
|
*
|
||||||
* @param index index of the texture
|
* @param index index of the texture
|
||||||
* @param size width and height of the texture in pixels
|
* @param size width and height of the texture in pixels
|
||||||
* @param height height of the texture in pixels
|
* @param height height of the texture in pixels
|
||||||
@ -130,7 +130,7 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure relative-size texture for the specific index
|
* Configure relative-size texture for the specific index
|
||||||
*
|
*
|
||||||
* @param index index of the texture
|
* @param index index of the texture
|
||||||
* @param scale_factor relation of the texture dimensions to the screen dimensions
|
* @param scale_factor relation of the texture dimensions to the screen dimensions
|
||||||
* @param name unique name of the texture
|
* @param name unique name of the texture
|
||||||
@ -141,6 +141,7 @@ public:
|
|||||||
virtual u8 getTextureCount() override { return m_textures.size(); }
|
virtual u8 getTextureCount() override { return m_textures.size(); }
|
||||||
virtual video::ITexture *getTexture(u8 index) override;
|
virtual video::ITexture *getTexture(u8 index) override;
|
||||||
virtual void reset(PipelineContext &context) override;
|
virtual void reset(PipelineContext &context) override;
|
||||||
|
void swapTextures(u8 texture_a, u8 texture_b);
|
||||||
private:
|
private:
|
||||||
static const u8 NO_DEPTH_TEXTURE = 255;
|
static const u8 NO_DEPTH_TEXTURE = 255;
|
||||||
|
|
||||||
@ -193,7 +194,7 @@ private:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows remapping texture indicies in another RenderSource.
|
* Allows remapping texture indicies in another RenderSource.
|
||||||
*
|
*
|
||||||
* @note all unmapped indexes are passed through to the underlying render source.
|
* @note all unmapped indexes are passed through to the underlying render source.
|
||||||
*/
|
*/
|
||||||
class RemappingSource : RenderSource
|
class RemappingSource : RenderSource
|
||||||
@ -205,7 +206,7 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps texture index to a different index in the dependent source.
|
* Maps texture index to a different index in the dependent source.
|
||||||
*
|
*
|
||||||
* @param index texture index as requested by the @see RenderStep.
|
* @param index texture index as requested by the @see RenderStep.
|
||||||
* @param target_index matching texture index in the underlying @see RenderSource.
|
* @param target_index matching texture index in the underlying @see RenderSource.
|
||||||
*/
|
*/
|
||||||
@ -250,7 +251,7 @@ public:
|
|||||||
virtual u8 getTextureCount() override;
|
virtual u8 getTextureCount() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a texture by index.
|
* Get a texture by index.
|
||||||
* Returns nullptr is the texture does not exist.
|
* Returns nullptr is the texture does not exist.
|
||||||
*/
|
*/
|
||||||
virtual video::ITexture *getTexture(u8 index) override;
|
virtual video::ITexture *getTexture(u8 index) override;
|
||||||
@ -288,14 +289,14 @@ class RenderStep : virtual public RenderPipelineObject
|
|||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Assigns render source to this step.
|
* Assigns render source to this step.
|
||||||
*
|
*
|
||||||
* @param source source of rendering information
|
* @param source source of rendering information
|
||||||
*/
|
*/
|
||||||
virtual void setRenderSource(RenderSource *source) = 0;
|
virtual void setRenderSource(RenderSource *source) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assigned render target to this step.
|
* Assigned render target to this step.
|
||||||
*
|
*
|
||||||
* @param target render target to send output to.
|
* @param target render target to send output to.
|
||||||
*/
|
*/
|
||||||
virtual void setRenderTarget(RenderTarget *target) = 0;
|
virtual void setRenderTarget(RenderTarget *target) = 0;
|
||||||
@ -319,7 +320,7 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Dynamically changes render target of another step.
|
* Dynamically changes render target of another step.
|
||||||
*
|
*
|
||||||
* This allows re-running parts of the pipeline with different outputs
|
* This allows re-running parts of the pipeline with different outputs
|
||||||
*/
|
*/
|
||||||
class SetRenderTargetStep : public TrivialRenderStep
|
class SetRenderTargetStep : public TrivialRenderStep
|
||||||
@ -332,9 +333,24 @@ private:
|
|||||||
RenderTarget *target;
|
RenderTarget *target;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swaps two textures in the texture buffer.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class SwapTexturesStep : public TrivialRenderStep
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SwapTexturesStep(TextureBuffer *buffer, u8 texture_a, u8 texture_b);
|
||||||
|
virtual void run(PipelineContext &context) override;
|
||||||
|
private:
|
||||||
|
TextureBuffer *buffer;
|
||||||
|
u8 texture_a;
|
||||||
|
u8 texture_b;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render Pipeline provides a flexible way to execute rendering steps in the engine.
|
* Render Pipeline provides a flexible way to execute rendering steps in the engine.
|
||||||
*
|
*
|
||||||
* RenderPipeline also implements @see RenderStep, allowing for nesting of the pipelines.
|
* RenderPipeline also implements @see RenderStep, allowing for nesting of the pipelines.
|
||||||
*/
|
*/
|
||||||
class RenderPipeline : public RenderStep
|
class RenderPipeline : public RenderStep
|
||||||
@ -342,7 +358,7 @@ class RenderPipeline : public RenderStep
|
|||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Add a step to the end of the pipeline
|
* Add a step to the end of the pipeline
|
||||||
*
|
*
|
||||||
* @param step reference to a @see RenderStep implementation.
|
* @param step reference to a @see RenderStep implementation.
|
||||||
*/
|
*/
|
||||||
RenderStep *addStep(RenderStep *step)
|
RenderStep *addStep(RenderStep *step)
|
||||||
@ -353,9 +369,9 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Capture ownership of a dynamically created @see RenderStep instance.
|
* Capture ownership of a dynamically created @see RenderStep instance.
|
||||||
*
|
*
|
||||||
* RenderPipeline will delete the instance when the pipeline is destroyed.
|
* RenderPipeline will delete the instance when the pipeline is destroyed.
|
||||||
*
|
*
|
||||||
* @param step reference to the instance.
|
* @param step reference to the instance.
|
||||||
* @return RenderStep* value of the 'step' parameter.
|
* @return RenderStep* value of the 'step' parameter.
|
||||||
*/
|
*/
|
||||||
|
@ -115,10 +115,14 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
|
|||||||
static const u8 TEXTURE_COLOR = 0;
|
static const u8 TEXTURE_COLOR = 0;
|
||||||
static const u8 TEXTURE_DEPTH = 1;
|
static const u8 TEXTURE_DEPTH = 1;
|
||||||
static const u8 TEXTURE_BLOOM = 2;
|
static const u8 TEXTURE_BLOOM = 2;
|
||||||
|
static const u8 TEXTURE_EXPOSURE_1 = 3;
|
||||||
|
static const u8 TEXTURE_EXPOSURE_2 = 4;
|
||||||
static const u8 TEXTURE_BLOOM_DOWN = 10;
|
static const u8 TEXTURE_BLOOM_DOWN = 10;
|
||||||
static const u8 TEXTURE_BLOOM_UP = 20;
|
static const u8 TEXTURE_BLOOM_UP = 20;
|
||||||
|
|
||||||
buffer->setTexture(TEXTURE_COLOR, scale, "3d_render", color_format);
|
buffer->setTexture(TEXTURE_COLOR, scale, "3d_render", color_format);
|
||||||
|
buffer->setTexture(TEXTURE_EXPOSURE_1, core::dimension2du(1,1), "exposure_1", color_format);
|
||||||
|
buffer->setTexture(TEXTURE_EXPOSURE_2, core::dimension2du(1,1), "exposure_2", color_format);
|
||||||
buffer->setTexture(TEXTURE_DEPTH, scale, "3d_depthmap", depth_format);
|
buffer->setTexture(TEXTURE_DEPTH, scale, "3d_depthmap", depth_format);
|
||||||
|
|
||||||
// attach buffer to the previous step
|
// attach buffer to the previous step
|
||||||
@ -127,30 +131,40 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
|
|||||||
// shared variables
|
// shared variables
|
||||||
u32 shader_id;
|
u32 shader_id;
|
||||||
|
|
||||||
|
// Number of mipmap levels of the bloom downsampling texture
|
||||||
|
const u8 MIPMAP_LEVELS = 4;
|
||||||
|
|
||||||
|
const bool enable_bloom = g_settings->getBool("enable_bloom");
|
||||||
|
const bool enable_auto_exposure = g_settings->getBool("enable_auto_exposure");
|
||||||
|
|
||||||
// post-processing stage
|
// post-processing stage
|
||||||
// set up bloom
|
|
||||||
if (g_settings->getBool("enable_bloom")) {
|
|
||||||
|
|
||||||
|
u8 source = TEXTURE_COLOR;
|
||||||
|
|
||||||
buffer->setTexture(TEXTURE_BLOOM, scale, "bloom", color_format);
|
// common downsampling step for bloom or autoexposure
|
||||||
|
if (enable_bloom || enable_auto_exposure) {
|
||||||
|
|
||||||
const u8 MIPMAP_LEVELS = 4;
|
|
||||||
v2f downscale = scale * 0.5;
|
v2f downscale = scale * 0.5;
|
||||||
for (u8 i = 0; i < MIPMAP_LEVELS; i++) {
|
for (u8 i = 0; i < MIPMAP_LEVELS; i++) {
|
||||||
buffer->setTexture(TEXTURE_BLOOM_DOWN + i, downscale, std::string("bloom_down") + std::to_string(i), color_format);
|
buffer->setTexture(TEXTURE_BLOOM_DOWN + i, downscale, std::string("downsample") + std::to_string(i), color_format);
|
||||||
buffer->setTexture(TEXTURE_BLOOM_UP + i, downscale, std::string("bloom_up") + std::to_string(i), color_format);
|
if (enable_bloom)
|
||||||
|
buffer->setTexture(TEXTURE_BLOOM_UP + i, downscale, std::string("upsample") + std::to_string(i), color_format);
|
||||||
downscale *= 0.5;
|
downscale *= 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get bright spots
|
if (enable_bloom) {
|
||||||
u32 shader_id = client->getShaderSource()->getShader("extract_bloom", TILE_MATERIAL_PLAIN, NDT_MESH);
|
buffer->setTexture(TEXTURE_BLOOM, scale, "bloom", color_format);
|
||||||
RenderStep *extract_bloom = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR });
|
|
||||||
extract_bloom->setRenderSource(buffer);
|
// get bright spots
|
||||||
extract_bloom->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM));
|
u32 shader_id = client->getShaderSource()->getShader("extract_bloom", TILE_MATERIAL_PLAIN, NDT_MESH);
|
||||||
|
RenderStep *extract_bloom = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR, TEXTURE_EXPOSURE_1 });
|
||||||
|
extract_bloom->setRenderSource(buffer);
|
||||||
|
extract_bloom->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM));
|
||||||
|
source = TEXTURE_BLOOM;
|
||||||
|
}
|
||||||
|
|
||||||
// downsample
|
// downsample
|
||||||
shader_id = client->getShaderSource()->getShader("bloom_downsample", TILE_MATERIAL_PLAIN, NDT_MESH);
|
shader_id = client->getShaderSource()->getShader("bloom_downsample", TILE_MATERIAL_PLAIN, NDT_MESH);
|
||||||
u8 source = TEXTURE_BLOOM;
|
|
||||||
for (u8 i = 0; i < MIPMAP_LEVELS; i++) {
|
for (u8 i = 0; i < MIPMAP_LEVELS; i++) {
|
||||||
auto step = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { source });
|
auto step = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { source });
|
||||||
step->setRenderSource(buffer);
|
step->setRenderSource(buffer);
|
||||||
@ -158,7 +172,9 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
|
|||||||
step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM_DOWN + i));
|
step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM_DOWN + i));
|
||||||
source = TEXTURE_BLOOM_DOWN + i;
|
source = TEXTURE_BLOOM_DOWN + i;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enable_bloom) {
|
||||||
// upsample
|
// upsample
|
||||||
shader_id = client->getShaderSource()->getShader("bloom_upsample", TILE_MATERIAL_PLAIN, NDT_MESH);
|
shader_id = client->getShaderSource()->getShader("bloom_upsample", TILE_MATERIAL_PLAIN, NDT_MESH);
|
||||||
for (u8 i = MIPMAP_LEVELS - 1; i > 0; i--) {
|
for (u8 i = MIPMAP_LEVELS - 1; i > 0; i--) {
|
||||||
@ -171,11 +187,24 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enable_auto_exposure) {
|
||||||
|
shader_id = client->getShaderSource()->getShader("update_exposure", TILE_MATERIAL_PLAIN, NDT_MESH);
|
||||||
|
auto update_exposure = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_EXPOSURE_1, u8(TEXTURE_BLOOM_DOWN + MIPMAP_LEVELS - 1) });
|
||||||
|
update_exposure->setBilinearFilter(1, true);
|
||||||
|
update_exposure->setRenderSource(buffer);
|
||||||
|
update_exposure->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_EXPOSURE_2));
|
||||||
|
}
|
||||||
|
|
||||||
// final post-processing
|
// final post-processing
|
||||||
shader_id = client->getShaderSource()->getShader("second_stage", TILE_MATERIAL_PLAIN, NDT_MESH);
|
shader_id = client->getShaderSource()->getShader("second_stage", TILE_MATERIAL_PLAIN, NDT_MESH);
|
||||||
PostProcessingStep *effect = pipeline->createOwned<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR, TEXTURE_BLOOM_UP });
|
PostProcessingStep *effect = pipeline->createOwned<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR, TEXTURE_BLOOM_UP, TEXTURE_EXPOSURE_2 });
|
||||||
pipeline->addStep(effect);
|
pipeline->addStep(effect);
|
||||||
effect->setBilinearFilter(1, true); // apply filter to the bloom
|
effect->setBilinearFilter(1, true); // apply filter to the bloom
|
||||||
effect->setRenderSource(buffer);
|
effect->setRenderSource(buffer);
|
||||||
|
|
||||||
|
if (enable_auto_exposure) {
|
||||||
|
pipeline->addStep<SwapTexturesStep>(buffer, TEXTURE_EXPOSURE_1, TEXTURE_EXPOSURE_2);
|
||||||
|
}
|
||||||
|
|
||||||
return effect;
|
return effect;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
RenderingEngine *RenderingEngine::s_singleton = nullptr;
|
RenderingEngine *RenderingEngine::s_singleton = nullptr;
|
||||||
const float RenderingEngine::BASE_BLOOM_STRENGTH = 8.0f;
|
const float RenderingEngine::BASE_BLOOM_STRENGTH = 1.0f;
|
||||||
|
|
||||||
|
|
||||||
static gui::GUISkin *createSkin(gui::IGUIEnvironment *environment,
|
static gui::GUISkin *createSkin(gui::IGUIEnvironment *environment,
|
||||||
|
@ -784,6 +784,9 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
|
|||||||
shaders_header << "#define ENABLE_BLOOM_DEBUG 1\n";
|
shaders_header << "#define ENABLE_BLOOM_DEBUG 1\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_settings->getBool("enable_auto_exposure"))
|
||||||
|
shaders_header << "#define ENABLE_AUTO_EXPOSURE 1\n";
|
||||||
|
|
||||||
shaders_header << "#line 0\n"; // reset the line counter for meaningful diagnostics
|
shaders_header << "#line 0\n"; // reset the line counter for meaningful diagnostics
|
||||||
|
|
||||||
std::string common_header = shaders_header.str();
|
std::string common_header = shaders_header.str();
|
||||||
|
@ -121,6 +121,43 @@ public:
|
|||||||
CachedShaderSetting<T, count, cache>(name, false){}
|
CachedShaderSetting<T, count, cache>(name, false){}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T, std::size_t count, bool cache, bool is_pixel>
|
||||||
|
class CachedStructShaderSetting {
|
||||||
|
const char *m_name;
|
||||||
|
T m_sent[count];
|
||||||
|
bool has_been_set = false;
|
||||||
|
std::array<const char*, count> m_fields;
|
||||||
|
public:
|
||||||
|
CachedStructShaderSetting(const char *name, std::array<const char*, count> &&fields) :
|
||||||
|
m_name(name), m_fields(std::move(fields))
|
||||||
|
{}
|
||||||
|
|
||||||
|
void set(const T value[count], video::IMaterialRendererServices *services)
|
||||||
|
{
|
||||||
|
if (cache && has_been_set && std::equal(m_sent, m_sent + count, value))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < count; i++) {
|
||||||
|
std::string uniform_name = std::string(m_name) + "." + m_fields[i];
|
||||||
|
|
||||||
|
if (is_pixel)
|
||||||
|
services->setPixelShaderConstant(services->getPixelShaderConstantID(uniform_name.c_str()), value + i, 1);
|
||||||
|
else
|
||||||
|
services->setVertexShaderConstant(services->getVertexShaderConstantID(uniform_name.c_str()), value + i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache) {
|
||||||
|
std::copy(value, value + count, m_sent);
|
||||||
|
has_been_set = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, std::size_t count, bool cache = true>
|
||||||
|
using CachedStructVertexShaderSetting = CachedStructShaderSetting<T, count, cache, false>;
|
||||||
|
|
||||||
|
template<typename T, std::size_t count, bool cache = true>
|
||||||
|
using CachedStructPixelShaderSetting = CachedStructShaderSetting<T, count, cache, true>;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ShaderSource creates and caches shaders.
|
ShaderSource creates and caches shaders.
|
||||||
|
@ -278,7 +278,8 @@ void set_default_settings()
|
|||||||
settings->setDefault("water_wave_speed", "5.0");
|
settings->setDefault("water_wave_speed", "5.0");
|
||||||
settings->setDefault("enable_waving_leaves", "false");
|
settings->setDefault("enable_waving_leaves", "false");
|
||||||
settings->setDefault("enable_waving_plants", "false");
|
settings->setDefault("enable_waving_plants", "false");
|
||||||
settings->setDefault("exposure_factor", "1.0");
|
settings->setDefault("exposure_compensation", "0.0");
|
||||||
|
settings->setDefault("enable_auto_exposure", "false");
|
||||||
settings->setDefault("enable_bloom", "false");
|
settings->setDefault("enable_bloom", "false");
|
||||||
settings->setDefault("enable_bloom_debug", "false");
|
settings->setDefault("enable_bloom_debug", "false");
|
||||||
settings->setDefault("bloom_strength_factor", "1.0");
|
settings->setDefault("bloom_strength_factor", "1.0");
|
||||||
|
29
src/lighting.cpp
Normal file
29
src/lighting.cpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
Minetest
|
||||||
|
Copyright (C) 2021 x2048, Dmitry Kostenko <codeforsmile@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lighting.h"
|
||||||
|
|
||||||
|
AutoExposure::AutoExposure()
|
||||||
|
: luminance_min(-3.f),
|
||||||
|
luminance_max(-3.f),
|
||||||
|
exposure_correction(0.0f),
|
||||||
|
speed_dark_bright(1000.f),
|
||||||
|
speed_bright_dark(1000.f),
|
||||||
|
center_weight_power(1.f)
|
||||||
|
{}
|
@ -19,10 +19,38 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameters for automatic exposure compensation
|
||||||
|
*
|
||||||
|
* Automatic exposure compensation uses the following equation:
|
||||||
|
*
|
||||||
|
* wanted_exposure = 2^exposure_correction / clamp(observed_luminance, 2^luminance_min, 2^luminance_max)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct AutoExposure
|
||||||
|
{
|
||||||
|
/// @brief Minimum boundary for computed luminance
|
||||||
|
float luminance_min;
|
||||||
|
/// @brief Maximum boundary for computed luminance
|
||||||
|
float luminance_max;
|
||||||
|
/// @brief Luminance bias. Higher values make the scene darker, can be negative.
|
||||||
|
float exposure_correction;
|
||||||
|
/// @brief Speed of transition from dark to bright scenes
|
||||||
|
float speed_dark_bright;
|
||||||
|
/// @brief Speed of transition from bright to dark scenes
|
||||||
|
float speed_bright_dark;
|
||||||
|
/// @brief Power value for center-weighted metering. Value of 1.0 measures entire screen uniformly
|
||||||
|
float center_weight_power;
|
||||||
|
|
||||||
|
AutoExposure();
|
||||||
|
};
|
||||||
|
|
||||||
/** Describes ambient light settings for a player
|
/** Describes ambient light settings for a player
|
||||||
*/
|
*/
|
||||||
struct Lighting
|
struct Lighting
|
||||||
{
|
{
|
||||||
|
AutoExposure exposure;
|
||||||
float shadow_intensity {0.0f};
|
float shadow_intensity {0.0f};
|
||||||
float saturation {1.0f};
|
float saturation {1.0f};
|
||||||
};
|
};
|
||||||
|
@ -1766,4 +1766,12 @@ void Client::handleCommand_SetLighting(NetworkPacket *pkt)
|
|||||||
*pkt >> lighting.shadow_intensity;
|
*pkt >> lighting.shadow_intensity;
|
||||||
if (pkt->getRemainingBytes() >= 4)
|
if (pkt->getRemainingBytes() >= 4)
|
||||||
*pkt >> lighting.saturation;
|
*pkt >> lighting.saturation;
|
||||||
|
if (pkt->getRemainingBytes() >= 24) {
|
||||||
|
*pkt >> lighting.exposure.luminance_min
|
||||||
|
>> lighting.exposure.luminance_max
|
||||||
|
>> lighting.exposure.exposure_correction
|
||||||
|
>> lighting.exposure.speed_dark_bright
|
||||||
|
>> lighting.exposure.speed_bright_dark
|
||||||
|
>> lighting.exposure.center_weight_power;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -831,6 +831,13 @@ enum ToClientCommand
|
|||||||
/*
|
/*
|
||||||
f32 shadow_intensity
|
f32 shadow_intensity
|
||||||
f32 saturation
|
f32 saturation
|
||||||
|
exposure parameters
|
||||||
|
f32 luminance_min
|
||||||
|
f32 luminance_max
|
||||||
|
f32 exposure_correction
|
||||||
|
f32 speed_dark_bright
|
||||||
|
f32 speed_bright_dark
|
||||||
|
f32 center_weight_power
|
||||||
*/
|
*/
|
||||||
|
|
||||||
TOCLIENT_NUM_MSG_TYPES = 0x64,
|
TOCLIENT_NUM_MSG_TYPES = 0x64,
|
||||||
|
@ -2297,8 +2297,20 @@ int ObjectRef::l_set_lighting(lua_State *L)
|
|||||||
getfloatfield(L, -1, "intensity", lighting.shadow_intensity);
|
getfloatfield(L, -1, "intensity", lighting.shadow_intensity);
|
||||||
}
|
}
|
||||||
lua_pop(L, 1); // shadows
|
lua_pop(L, 1); // shadows
|
||||||
|
|
||||||
getfloatfield(L, -1, "saturation", lighting.saturation);
|
getfloatfield(L, -1, "saturation", lighting.saturation);
|
||||||
|
|
||||||
|
lua_getfield(L, 2, "exposure");
|
||||||
|
if (lua_istable(L, -1)) {
|
||||||
|
lighting.exposure.luminance_min = getfloatfield_default(L, -1, "luminance_min", lighting.exposure.luminance_min);
|
||||||
|
lighting.exposure.luminance_max = getfloatfield_default(L, -1, "luminance_max", lighting.exposure.luminance_max);
|
||||||
|
lighting.exposure.exposure_correction = getfloatfield_default(L, -1, "exposure_correction", lighting.exposure.exposure_correction);
|
||||||
|
lighting.exposure.speed_dark_bright = getfloatfield_default(L, -1, "speed_dark_bright", lighting.exposure.speed_dark_bright);
|
||||||
|
lighting.exposure.speed_bright_dark = getfloatfield_default(L, -1, "speed_bright_dark", lighting.exposure.speed_bright_dark);
|
||||||
|
lighting.exposure.center_weight_power = getfloatfield_default(L, -1, "center_weight_power", lighting.exposure.center_weight_power);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1); // exposure
|
||||||
|
|
||||||
getServer(L)->setLighting(player, lighting);
|
getServer(L)->setLighting(player, lighting);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2321,6 +2333,20 @@ int ObjectRef::l_get_lighting(lua_State *L)
|
|||||||
lua_setfield(L, -2, "shadows");
|
lua_setfield(L, -2, "shadows");
|
||||||
lua_pushnumber(L, lighting.saturation);
|
lua_pushnumber(L, lighting.saturation);
|
||||||
lua_setfield(L, -2, "saturation");
|
lua_setfield(L, -2, "saturation");
|
||||||
|
lua_newtable(L); // "exposure"
|
||||||
|
lua_pushnumber(L, lighting.exposure.luminance_min);
|
||||||
|
lua_setfield(L, -2, "luminance_min");
|
||||||
|
lua_pushnumber(L, lighting.exposure.luminance_max);
|
||||||
|
lua_setfield(L, -2, "luminance_max");
|
||||||
|
lua_pushnumber(L, lighting.exposure.exposure_correction);
|
||||||
|
lua_setfield(L, -2, "exposure_correction");
|
||||||
|
lua_pushnumber(L, lighting.exposure.speed_dark_bright);
|
||||||
|
lua_setfield(L, -2, "speed_dark_bright");
|
||||||
|
lua_pushnumber(L, lighting.exposure.speed_bright_dark);
|
||||||
|
lua_setfield(L, -2, "speed_bright_dark");
|
||||||
|
lua_pushnumber(L, lighting.exposure.center_weight_power);
|
||||||
|
lua_setfield(L, -2, "center_weight_power");
|
||||||
|
lua_setfield(L, -2, "exposure");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1866,6 +1866,13 @@ void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
|
|||||||
pkt << lighting.shadow_intensity;
|
pkt << lighting.shadow_intensity;
|
||||||
pkt << lighting.saturation;
|
pkt << lighting.saturation;
|
||||||
|
|
||||||
|
pkt << lighting.exposure.luminance_min
|
||||||
|
<< lighting.exposure.luminance_max
|
||||||
|
<< lighting.exposure.exposure_correction
|
||||||
|
<< lighting.exposure.speed_dark_bright
|
||||||
|
<< lighting.exposure.speed_bright_dark
|
||||||
|
<< lighting.exposure.center_weight_power;
|
||||||
|
|
||||||
Send(&pkt);
|
Send(&pkt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user