diff --git a/res/scripts/hud.lua b/res/scripts/hud.lua index 79fe0e42..e8cc137d 100644 --- a/res/scripts/hud.lua +++ b/res/scripts/hud.lua @@ -1,3 +1,25 @@ +local function configure_SSAO() + local slot = gfx.posteffects.index("core:default") + gfx.posteffects.set_effect(slot, "ssao") + gfx.posteffects.set_intensity(slot, 1.0) + + -- Generating random SSAO samples + local buffer = Bytearray(0) + for i = 0, 63 do + local x = math.random() * 2.0 - 1.0 + local y = math.random() * 2.0 - 1.0 + local z = math.random() + local len = math.sqrt(x * x + y * y + z * z) + if len > 0 then + x = x / len + y = y / len + z = z / len + end + Bytearray.append(buffer, byteutil.pack("fff", x, y, z)) + end + gfx.posteffects.set_array(slot, "u_ssaoSamples", Bytearray_as_string(buffer)) +end + function on_hud_open() input.add_callback("player.pick", function () if hud.is_paused() or hud.is_inventory_open() then @@ -55,8 +77,4 @@ function on_hud_open() player.set_vel(pid, 0, 1, 0) end end) - - local slot = gfx.posteffects.index("core:default") - gfx.posteffects.set_effect(slot, "ssao") - gfx.posteffects.set_intensity(slot, 1.0) end diff --git a/res/shaders/effects/ssao.glsl b/res/shaders/effects/ssao.glsl index f0894b49..3446ecdb 100644 --- a/res/shaders/effects/ssao.glsl +++ b/res/shaders/effects/ssao.glsl @@ -1,8 +1,7 @@ -uniform vec3 u_ssaoSamples[64]; - -int kernelSize = 16; -float radius = 0.25; -float bias = 0.025; +#param vec3 u_ssaoSamples[64] +#param int u_kernelSize = 16 +#param float u_radius = 0.25 +#param float u_bias = 0.025 vec4 effect() { vec2 noiseScale = u_screenSize / 4.0; @@ -17,9 +16,9 @@ vec4 effect() { mat3 tbn = mat3(tangent, bitangent, normal); float occlusion = 0.0; - for (int i = 0; i < kernelSize; i++) { + for (int i = 0; i < u_kernelSize; i++) { vec3 samplePos = tbn * u_ssaoSamples[i]; - samplePos = position + samplePos * radius; + samplePos = position + samplePos * u_radius; vec4 offset = vec4(samplePos, 1.0); offset = u_projection * offset; @@ -27,10 +26,10 @@ vec4 effect() { offset.xyz = offset.xyz * 0.5 + 0.5; float sampleDepth = texture(u_position, offset.xy).z; - float rangeCheck = smoothstep(0.0, 1.0, radius / abs(position.z - sampleDepth)); - occlusion += (sampleDepth >= samplePos.z + bias ? 1.0 : 0.0) * rangeCheck; + float rangeCheck = smoothstep(0.0, 1.0, u_radius / abs(position.z - sampleDepth)); + occlusion += (sampleDepth >= samplePos.z + u_bias ? 1.0 : 0.0) * rangeCheck; } - occlusion = min(1.0, 1.05 - (occlusion / kernelSize)); + occlusion = min(1.0, 1.05 - (occlusion / u_kernelSize)); float z = -position.z * 0.02; z = max(0.0, 1.0 - z); diff --git a/res/shaders/lib/shadows.glsl b/res/shaders/lib/shadows.glsl index 59d874e3..e21afc22 100644 --- a/res/shaders/lib/shadows.glsl +++ b/res/shaders/lib/shadows.glsl @@ -1,10 +1,14 @@ #ifndef SHADOWS_GLSL_ #define SHADOWS_GLSL_ +#include + uniform sampler2DShadow u_shadows[2]; uniform mat4 u_shadowsMatrix[2]; uniform float u_dayTime; uniform int u_shadowsRes; +uniform float u_shadowsOpacity; +uniform float u_shadowsSoftness; float calc_shadow() { if (!u_enableShadows) { @@ -12,24 +16,25 @@ float calc_shadow() { } float step = 1.0 / float(u_shadowsRes); - float s = pow(abs(cos(u_dayTime * 6.283185)), 0.5); // 2*PI precomputed - vec3 normalOffset = a_realnormal * (a_distance > 128.0 ? 0.2 : 0.04); - int shadowIdx = a_distance > 128.0 ? 1 : 0; + float s = pow(abs(cos(u_dayTime * PI2)), 0.25) * u_shadowsOpacity; + vec3 normalOffset = a_realnormal * (a_distance > 64.0 ? 0.2 : 0.04); + int shadowIdx = a_distance > 64.0 ? 1 : 0; vec4 mpos = u_shadowsMatrix[shadowIdx] * vec4(a_modelpos.xyz + normalOffset, 1.0); vec3 projCoords = mpos.xyz / mpos.w; projCoords = projCoords * 0.5 + 0.5; - projCoords.z -= 0.0001; + projCoords.z -= 0.00001; float shadow = 0.0; if (dot(a_realnormal, u_sunDir) < 0.0) { - for (int y = -1; y <= 1; y++) { - for (int x = -1; x <= 1; x++) { - vec3 offset = vec3(x, y, -(abs(x) + abs(y))) * step; + // 5x5 kernel + for (int y = -2; y <= 2; y++) { + for (int x = -2; x <= 2; x++) { + vec3 offset = vec3(x, y, -(abs(x) + abs(y)) * 0.1) * step * 2.0 * u_shadowsSoftness; shadow += texture(u_shadows[shadowIdx], projCoords + offset); } } - shadow /= 9.0; + shadow /= 25.0; } else { shadow = 0.5; } diff --git a/src/coders/GLSLExtension.cpp b/src/coders/GLSLExtension.cpp index 47948bd9..f6543092 100644 --- a/src/coders/GLSLExtension.cpp +++ b/src/coders/GLSLExtension.cpp @@ -98,6 +98,8 @@ inline void source_line(std::stringstream& ss, uint linenum) { static Value default_value_for(Type type) { switch (type) { + case Type::INT: + return 0; case Type::FLOAT: return 0.0f; case Type::VEC2: @@ -183,6 +185,8 @@ public: Value parseDefaultValue(Type type, const std::string& name) { switch (type) { + case Type::INT: + return static_cast(parseNumber(1).asInteger()); case Type::FLOAT: return static_cast(parseNumber(1).asNumber()); case Type::VEC2: @@ -212,8 +216,22 @@ public: if (params.find(paramName) != params.end()) { throw error("duplicating param " + util::quote(paramName)); } + skipWhitespace(false); - ss << "uniform " << typeName << " " << paramName << ";\n"; + int start = pos; + + ss << "uniform " << typeName << " " << paramName; + + bool array = false; + if (peekNoJump() == '[') { + skip(1); + array = true; + readUntil(']'); + skip(1); + ss << source.substr(start, pos - start + 1); + } + + ss << ";\n"; auto defValue = default_value_for(type); // Parse default value @@ -225,7 +243,7 @@ public: skipLine(); - params[paramName] = PostEffect::Param(type, std::move(defValue)); + params[paramName] = PostEffect::Param(type, std::move(defValue), array); return false; } diff --git a/src/graphics/core/PostEffect.cpp b/src/graphics/core/PostEffect.cpp index 584a9b43..47f2ac9f 100644 --- a/src/graphics/core/PostEffect.cpp +++ b/src/graphics/core/PostEffect.cpp @@ -2,11 +2,14 @@ #include "Shader.hpp" #include "data/dv_util.hpp" +#include "debug/Logger.hpp" + +static debug::Logger logger("post-effect"); PostEffect::Param::Param() : type(Type::FLOAT) {} -PostEffect::Param::Param(Type type, Value defValue) - : type(type), defValue(defValue), value(defValue) { +PostEffect::Param::Param(Type type, Value defValue, bool array) + : type(type), defValue(defValue), value(defValue), array(array) { } PostEffect::PostEffect( @@ -24,21 +27,53 @@ Shader& PostEffect::use() { if (!param.dirty) { continue; } - switch (param.type) { - case Param::Type::FLOAT: - shader->uniform1f(name, std::get(param.value)); - break; - case Param::Type::VEC2: - shader->uniform2f(name, std::get(param.value)); - break; - case Param::Type::VEC3: - shader->uniform3f(name, std::get(param.value)); - break; - case Param::Type::VEC4: - shader->uniform4f(name, std::get(param.value)); - break; - default: - assert(false); + if (param.array) { + const auto& found = arrayValues.find(name); + if (found == arrayValues.end()) { + continue; + } + size_t size = found->second.size(); + auto ibuffer = reinterpret_cast(found->second.data()); + auto fbuffer = reinterpret_cast(found->second.data()); + switch (param.type) { + case Param::Type::INT: + shader->uniform1v(name, size / sizeof(int), ibuffer); + break; + case Param::Type::FLOAT: + shader->uniform1v(name, size / sizeof(float), fbuffer); + break; + case Param::Type::VEC2: + shader->uniform2v(name, size / sizeof(glm::vec2), fbuffer); + break; + case Param::Type::VEC3: + shader->uniform3v(name, size / sizeof(glm::vec3), fbuffer); + break; + case Param::Type::VEC4: + shader->uniform4v(name, size / sizeof(glm::vec4), fbuffer); + break; + default: + assert(false); + } + } else { + switch (param.type) { + case Param::Type::INT: + shader->uniform1i(name, std::get(param.value)); + break; + case Param::Type::FLOAT: + shader->uniform1f(name, std::get(param.value)); + break; + case Param::Type::VEC2: + shader->uniform2f(name, std::get(param.value)); + break; + case Param::Type::VEC3: + shader->uniform3f(name, std::get(param.value)); + break; + case Param::Type::VEC4: + shader->uniform4f(name, std::get(param.value)); + break; + default: + assert(false); + } } param.dirty = false; } @@ -67,6 +102,9 @@ void PostEffect::setParam(const std::string& name, const dv::value& value) { } auto& param = found->second; switch (param.type) { + case Param::Type::INT: + param.value = static_cast(value.asInteger()); + break; case Param::Type::FLOAT: param.value = static_cast(value.asNumber()); break; @@ -82,3 +120,20 @@ void PostEffect::setParam(const std::string& name, const dv::value& value) { } param.dirty = true; } + +void PostEffect::setArray(const std::string& name, std::vector&& values) { + const auto& found = params.find(name); + if (found == params.end()) { + return; + } + auto& param = found->second; + if (!param.array) { + logger.warning() << "set_array is used on non-array effect parameter"; + if (!values.empty()) { + setParam(name, values[0]); + } + return; + } + param.dirty = true; + arrayValues[name] = std::move(values); +} diff --git a/src/graphics/core/PostEffect.hpp b/src/graphics/core/PostEffect.hpp index 3448e05c..6dac6748 100644 --- a/src/graphics/core/PostEffect.hpp +++ b/src/graphics/core/PostEffect.hpp @@ -1,11 +1,13 @@ #pragma once +#include #include #include #include #include #include +#include "typedefs.hpp" #include "data/dv_fwd.hpp" #include "util/EnumMetadata.hpp" @@ -14,24 +16,26 @@ class Shader; class PostEffect { public: struct Param { - enum class Type { FLOAT, VEC2, VEC3, VEC4 }; + enum class Type { INT, FLOAT, VEC2, VEC3, VEC4 }; VC_ENUM_METADATA(Type) + {"int", Type::INT}, {"float", Type::FLOAT}, {"vec2", Type::VEC2}, {"vec3", Type::VEC3}, {"vec4", Type::VEC4}, VC_ENUM_END - using Value = std::variant; + using Value = std::variant; Type type; Value defValue; Value value; + bool array = false; bool dirty = true; Param(); - Param(Type type, Value defValue); + Param(Type type, Value defValue, bool array); }; PostEffect( @@ -49,6 +53,8 @@ public: void setParam(const std::string& name, const dv::value& value); + void setArray(const std::string& name, std::vector&& values); + bool isAdvanced() const { return advanced; } @@ -60,5 +66,6 @@ private: bool advanced = false; std::shared_ptr shader; std::unordered_map params; + std::unordered_map> arrayValues; float intensity = 0.0f; }; diff --git a/src/graphics/core/PostProcessing.cpp b/src/graphics/core/PostProcessing.cpp index b5922f72..13244c89 100644 --- a/src/graphics/core/PostProcessing.cpp +++ b/src/graphics/core/PostProcessing.cpp @@ -43,20 +43,6 @@ PostProcessing::PostProcessing(size_t effectSlotsCount) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glBindTexture(GL_TEXTURE_2D, 0); - - std::uniform_real_distribution randomFloats(0.0, 1.0); - std::default_random_engine generator; - for (unsigned int i = 0; i < 64; ++i) - { - glm::vec3 sample( - randomFloats(generator) * 2.0 - 1.0, - randomFloats(generator) * 2.0 - 1.0, - randomFloats(generator) - ); - sample = glm::normalize(sample); - sample *= randomFloats(generator); - ssaoKernel.push_back(sample); - } } PostProcessing::~PostProcessing() = default; @@ -90,21 +76,13 @@ void PostProcessing::refreshFbos(uint width, uint height) { void PostProcessing::configureEffect( const DrawContext& context, + PostEffect& effect, Shader& shader, float timer, const Camera& camera, uint shadowMap ) { const auto& viewport = context.getViewport(); - - bool ssaoConfigured = false; - if (!ssaoConfigured) { - for (unsigned int i = 0; i < 64; ++i) { - auto name = "u_ssaoSamples["+ std::to_string(i) + "]"; - shader.uniform3f(name, ssaoKernel[i]); - } - ssaoConfigured = true; - } shader.uniform1i("u_screen", 0); if (gbuffer) { shader.uniform1i("u_position", 1); @@ -156,7 +134,7 @@ void PostProcessing::render( if (totalPasses == 0) { auto& effect = assets.require("default"); auto& shader = effect.use(); - configureEffect(context, shader, timer, camera, shadowMap); + configureEffect(context, effect, shader, timer, camera, shadowMap); quadMesh->draw(); return; } @@ -170,7 +148,7 @@ void PostProcessing::render( continue; } auto& shader = effect->use(); - configureEffect(context, shader, timer, camera, shadowMap); + configureEffect(context, *effect, shader, timer, camera, shadowMap); if (currentPass > 1) { fbo->getTexture()->bind(); diff --git a/src/graphics/core/PostProcessing.hpp b/src/graphics/core/PostProcessing.hpp index 51e23ece..dc0779ed 100644 --- a/src/graphics/core/PostProcessing.hpp +++ b/src/graphics/core/PostProcessing.hpp @@ -58,6 +58,7 @@ public: private: void configureEffect( const DrawContext& context, + PostEffect& effect, Shader& shader, float timer, const Camera& camera, @@ -73,7 +74,5 @@ private: std::unique_ptr> quadMesh; std::vector> effectSlots; std::unique_ptr gbuffer; - - std::vector ssaoKernel; uint noiseTexture; }; diff --git a/src/graphics/core/Shader.cpp b/src/graphics/core/Shader.cpp index 626d747b..df02a5c0 100644 --- a/src/graphics/core/Shader.cpp +++ b/src/graphics/core/Shader.cpp @@ -76,6 +76,25 @@ void Shader::uniform4f(const std::string& name, const glm::vec4& xyzw) { glUniform4f(getUniformLocation(name), xyzw.x, xyzw.y, xyzw.z, xyzw.w); } +void Shader::uniform1v(const std::string& name, int length, const int* v) { + glUniform1iv(getUniformLocation(name), length, v); +} + +void Shader::uniform1v(const std::string& name, int length, const float* v) { + glUniform1fv(getUniformLocation(name), length, v); +} + +void Shader::uniform2v(const std::string& name, int length, const float* v) { + glUniform2fv(getUniformLocation(name), length, v); +} + +void Shader::uniform3v(const std::string& name, int length, const float* v) { + glUniform3fv(getUniformLocation(name), length, v); +} + +void Shader::uniform4v(const std::string& name, int length, const float* v) { + glUniform4fv(getUniformLocation(name), length, v); +} inline auto shader_deleter = [](GLuint* shader) { glDeleteShader(*shader); diff --git a/src/graphics/core/Shader.hpp b/src/graphics/core/Shader.hpp index 18a14da7..d50ccb46 100644 --- a/src/graphics/core/Shader.hpp +++ b/src/graphics/core/Shader.hpp @@ -32,6 +32,12 @@ public: void uniform3f(const std::string& name, const glm::vec3& xyz); void uniform4f(const std::string& name, const glm::vec4& xyzw); + void uniform1v(const std::string& name, int length, const int* v); + void uniform1v(const std::string& name, int length, const float* v); + void uniform2v(const std::string& name, int length, const float* v); + void uniform3v(const std::string& name, int length, const float* v); + void uniform4v(const std::string& name, int length, const float* v); + /// @brief Create shader program using vertex and fragment shaders source. /// @param vertexFile vertex shader file name /// @param fragmentFile fragment shader file name diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index d742ec3f..193cb2d9 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -131,10 +131,14 @@ void WorldRenderer::setupWorldShader( shader.uniform1i("u_enableShadows", shadows); if (shadows) { + const auto& worldInfo = level.getWorld()->getInfo(); + float cloudsIntensity = glm::max(worldInfo.fog, weather.clouds()); shader.uniformMatrix("u_shadowsMatrix[0]", shadowCamera.getProjView()); shader.uniformMatrix("u_shadowsMatrix[1]", wideShadowCamera.getProjView()); shader.uniform3f("u_sunDir", shadowCamera.front); shader.uniform1i("u_shadowsRes", shadowMap->getResolution()); + shader.uniform1f("u_shadowsOpacity", 1.0f - cloudsIntensity); // TODO: make it configurable + shader.uniform1f("u_shadowsSoftness", 1.0f + cloudsIntensity * 4); // TODO: make it configurable glActiveTexture(GL_TEXTURE4); shader.uniform1i("u_shadows[0]", 4); @@ -359,7 +363,7 @@ void WorldRenderer::generateShadowsMap( const auto& settings = engine.getSettings(); int resolution = shadowMap.getResolution(); float shadowMapScale = - 0.2f / (1 << glm::max(0L, settings.graphics.shadowsQuality.get())) * + 0.16f / (1 << glm::max(0L, settings.graphics.shadowsQuality.get())) * scale; float shadowMapSize = resolution * shadowMapScale; @@ -383,9 +387,9 @@ void WorldRenderer::generateShadowsMap( ); shadowCamera.updateVectors(); - shadowCamera.position -= shadowCamera.front * 300.0f; - shadowCamera.position += shadowCamera.up * 10.0f; - shadowCamera.position += camera.front * 100.0f; + shadowCamera.position -= shadowCamera.front * 500.0f; + shadowCamera.position += shadowCamera.up * 0.0f; + shadowCamera.position += camera.front * 0.0f; auto view = shadowCamera.getView(); @@ -445,9 +449,7 @@ void WorldRenderer::draw( const auto& worldInfo = world->getInfo(); - float sqrtT = glm::sqrt(weather.t); - float clouds = weather.b.clouds * sqrtT + - weather.a.clouds * (1.0f - sqrtT); + float clouds = weather.clouds(); clouds = glm::max(worldInfo.fog, clouds); float mie = 1.0f + glm::max(worldInfo.fog, clouds * 0.5f) * 2.0f; diff --git a/src/logic/scripting/lua/libs/libposteffects.cpp b/src/logic/scripting/lua/libs/libposteffects.cpp index ccdb34aa..5e3284bf 100644 --- a/src/logic/scripting/lua/libs/libposteffects.cpp +++ b/src/logic/scripting/lua/libs/libposteffects.cpp @@ -57,6 +57,22 @@ static int l_set_params(lua::State* L) { return 0; } +static int l_set_array(lua::State* L) { + size_t index = static_cast(lua::tointeger(L, 1)); + auto key = lua::require_string(L, 2); + auto data = lua::require_lstring(L, 3); + auto effect = post_processing->getEffect(index); + if (effect == nullptr) { + return 0; + } + std::vector buffer( + reinterpret_cast(data.begin()), + reinterpret_cast(data.end()) + ); + effect->setArray(key, std::move(buffer)); + return 0; +} + const luaL_Reg posteffectslib[] = { {"index", lua::wrap}, {"set_effect", lua::wrap}, @@ -64,5 +80,6 @@ const luaL_Reg posteffectslib[] = { {"set_intensity", lua::wrap}, {"is_active", lua::wrap}, {"set_params", lua::wrap}, + {"set_array", lua::wrap}, {NULL, NULL} }; diff --git a/src/logic/scripting/lua/lua_extensions.cpp b/src/logic/scripting/lua/lua_extensions.cpp index 399b6095..b9c25fc6 100644 --- a/src/logic/scripting/lua/lua_extensions.cpp +++ b/src/logic/scripting/lua/lua_extensions.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "libs/api_lua.hpp" #include "debug/Logger.hpp" @@ -149,6 +150,15 @@ int l_debug_print(lua::State* L) { return 0; } +namespace { + std::normal_distribution randomFloats(0.0f, 1.0f); + std::default_random_engine generator; +} + +static int l_math_normal_random(lua::State* L) { + return lua::pushnumber(L, randomFloats(generator)); +} + void initialize_libs_extends(lua::State* L) { if (lua::getglobal(L, "debug")) { lua::pushcfunction(L, lua::wrap); @@ -163,6 +173,12 @@ void initialize_libs_extends(lua::State* L) { lua::pushcfunction(L, lua::wrap); lua::setfield(L, "print"); + lua::pop(L); + } + if (lua::getglobal(L, "math")) { + lua::pushcfunction(L, lua::wrap); + lua::setfield(L, "normal_random"); + lua::pop(L); } } diff --git a/src/world/Weather.hpp b/src/world/Weather.hpp index fde642b7..6d8af0bb 100644 --- a/src/world/Weather.hpp +++ b/src/world/Weather.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include "presets/WeatherPreset.hpp" @@ -45,6 +46,11 @@ struct Weather : Serializable { return b.thunderRate * t + a.thunderRate * (1.0f - t); } + float clouds() const { + float sqrtT = glm::sqrt(t); + return b.clouds * sqrtT + a.clouds * (1.0f - sqrtT); + } + dv::value serialize() const override; void deserialize(const dv::value& src) override; };