diff --git a/res/content/base/blocks/torch.json b/res/content/base/blocks/torch.json index cf26bfec..e9a976f0 100644 --- a/res/content/base/blocks/torch.json +++ b/res/content/base/blocks/torch.json @@ -7,7 +7,7 @@ "torch_side", "torch_side" ], - "emission": [13, 13, 12], + "emission": [13, 10, 2], "model": "aabb", "hitbox": [0.4375, 0.0, 0.4375, 0.125, 0.5, 0.125], "light-passing": true, diff --git a/res/content/base/preload.json b/res/content/base/preload.json index 760ad2c9..24d54a42 100644 --- a/res/content/base/preload.json +++ b/res/content/base/preload.json @@ -14,6 +14,7 @@ ], "textures": [ "misc/moon", + "misc/moon_flare", "misc/sun", "gui/crosshair" ] diff --git a/res/layouts/pages/settings_graphics.xml.lua b/res/layouts/pages/settings_graphics.xml.lua index c4503e52..99ba6609 100644 --- a/res/layouts/pages/settings_graphics.xml.lua +++ b/res/layouts/pages/settings_graphics.xml.lua @@ -42,4 +42,7 @@ function on_open() create_setting("graphics.gamma", "Gamma", 0.05, "", "graphics.gamma.tooltip") create_checkbox("graphics.backlight", "Backlight", "graphics.backlight.tooltip") create_checkbox("graphics.dense-render", "Dense blocks render", "graphics.dense-render.tooltip") + create_checkbox("graphics.advanced-render", "Advanced render", "graphics.advanced-render.tooltip") + create_checkbox("graphics.ssao", "SSAO", "graphics.ssao.tooltip") + create_setting("graphics.shadows-quality", "Shadows quality", 1) end diff --git a/res/preload.json b/res/preload.json index cac23425..eb41f40d 100644 --- a/res/preload.json +++ b/res/preload.json @@ -6,10 +6,19 @@ "lines", "entity", "background", - "skybox_gen" + "skybox_gen", + "shadows" ], "post-effects": [ - "default" + "default", + { + "name": "ssao", + "advanced": true + }, + { + "name": "deferred_lighting", + "advanced": true + } ], "textures": [ "gui/menubg", diff --git a/res/resources.json b/res/resources.json index 93922266..0311f790 100644 --- a/res/resources.json +++ b/res/resources.json @@ -4,5 +4,8 @@ "third-person-front", "third-person-back", "cinematic" + ], + "post-effect-slot": [ + "default" ] } diff --git a/res/scripts/hud.lua b/res/scripts/hud.lua index 093d43c3..3fd1375c 100644 --- a/res/scripts/hud.lua +++ b/res/scripts/hud.lua @@ -1,3 +1,27 @@ +local function configure_SSAO() + -- Temporary using slot to configure built-in SSAO effect + local slot = gfx.posteffects.index("core:default") + gfx.posteffects.set_effect(slot, "ssao") + + -- 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() * 2.0 + 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)) + -- SSAO effect configured, so 'core:default' slot may be reused now + -- for test purposes +end + function on_hud_open() input.add_callback("player.pick", function () if hud.is_paused() or hud.is_inventory_open() then @@ -55,4 +79,6 @@ function on_hud_open() player.set_vel(pid, 0, 1, 0) end end) + + configure_SSAO() end diff --git a/res/shaders/background.glslf b/res/shaders/background.glslf index f7ff07c8..ea4079ba 100644 --- a/res/shaders/background.glslf +++ b/res/shaders/background.glslf @@ -1,9 +1,14 @@ in vec3 v_coord; -out vec4 f_color; +layout (location = 0) out vec4 f_color; +layout (location = 1) out vec4 f_position; +layout (location = 2) out vec4 f_normal; -uniform samplerCube u_cubemap; +uniform mat4 u_view; +uniform samplerCube u_skybox; void main(){ - vec3 dir = normalize(v_coord); - f_color = texture(u_cubemap, dir); + vec3 dir = normalize(v_coord) * 1e6; + f_position = u_view * vec4(dir, 1.0); + f_normal = vec4(0.0, 0.0, 1.0, 1.0); + f_color = texture(u_skybox, dir); } diff --git a/res/shaders/background.glslv b/res/shaders/background.glslv index 4de0d5b8..a3dfa198 100644 --- a/res/shaders/background.glslv +++ b/res/shaders/background.glslv @@ -7,6 +7,6 @@ uniform float u_ar; uniform float u_zoom; void main(){ - v_coord = (vec4(v_position*vec2(u_ar, 1.0f)*u_zoom, -1.0, 1.0) * u_view).xyz; - gl_Position = vec4(v_position, 0.0, 1.0); + v_coord = (vec4(v_position * vec2(u_ar, 1.0f) * u_zoom, -1.0, 1.0) * u_view).xyz; + gl_Position = vec4(v_position, 1.0 - 1e-6, 1.0); } diff --git a/res/shaders/effect.glslf b/res/shaders/effect.glslf index a6325cca..2b6307ef 100644 --- a/res/shaders/effect.glslf +++ b/res/shaders/effect.glslf @@ -2,8 +2,22 @@ in vec2 v_uv; out vec4 f_color; uniform sampler2D u_screen; +uniform sampler2D u_position; +uniform sampler2D u_normal; +uniform sampler2D u_emission; +uniform sampler2D u_noise; +uniform sampler2D u_ssao; +uniform samplerCube u_skybox; + uniform ivec2 u_screenSize; uniform float u_intensity; +uniform float u_timer; +uniform bool u_enableShadows; +uniform mat4 u_projection; +uniform mat4 u_view; +uniform mat4 u_inverseView; +uniform vec3 u_sunDir; +uniform vec3 u_cameraPos; #include <__effect__> diff --git a/res/shaders/effects/deferred_lighting.glsl b/res/shaders/effects/deferred_lighting.glsl new file mode 100644 index 00000000..f8f10401 --- /dev/null +++ b/res/shaders/effects/deferred_lighting.glsl @@ -0,0 +1,35 @@ +#include +#include + +vec4 effect() { + vec4 pos = texture(u_position, v_uv); + float light = 1.0; + +#ifdef ENABLE_SSAO + light = 0.0; + float z = pos.z; + for (int y = -2; y <= 2; y++) { + for (int x = -2; x <= 2; x++) { + vec2 offset = vec2(x, y) / u_screenSize; + light += texture(u_ssao, v_uv + offset * 2.0).r; + } + } + light /= 24.0; +#endif // ENABLE_SSAO + + vec4 modelpos = u_inverseView * pos; + vec3 normal = transpose(mat3(u_view)) * texture(u_normal, v_uv).xyz; + vec3 dir = modelpos.xyz - u_cameraPos; + + float emission = texture(u_emission, v_uv).r; + +#ifdef ENABLE_SHADOWS + light *= calc_shadow(modelpos, normal, length(pos)); +#endif + + light = max(light, emission); + + vec3 fogColor = texture(u_skybox, dir).rgb; + float fog = calc_fog(length(u_view * vec4((modelpos.xyz - u_cameraPos) * FOG_POS_SCALE, 0.0)) / 256.0); + return vec4(mix(texture(u_screen, v_uv).rgb * mix(1.0, light, 1.0), fogColor, fog), 1.0); +} diff --git a/res/shaders/effects/ssao.glsl b/res/shaders/effects/ssao.glsl new file mode 100644 index 00000000..508d74e7 --- /dev/null +++ b/res/shaders/effects/ssao.glsl @@ -0,0 +1,37 @@ +#param vec3 u_ssaoSamples[64] +#param int u_kernelSize = 16 +#param float u_radius = 0.4 +#param float u_bias = 0.006 + +vec4 effect() { + vec2 noiseScale = u_screenSize / 4.0; + + vec3 position = texture(u_position, v_uv).xyz; + vec3 normal = texture(u_normal, v_uv).xyz; + vec3 randomVec = normalize(texture(u_noise, v_uv * noiseScale).xyz); + + vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal)); + vec3 bitangent = cross(normal, tangent); + mat3 tbn = mat3(tangent, bitangent, normal); + + float occlusion = 0.0; + for (int i = 0; i < u_kernelSize; i++) { + vec3 samplePos = tbn * u_ssaoSamples[i]; + samplePos = position + samplePos * u_radius; + + vec4 offset = vec4(samplePos, 1.0); + offset = u_projection * offset; + offset.xyz /= offset.w; + offset.xyz = offset.xyz * 0.5 + 0.5; + + float sampleDepth = texture(u_position, offset.xy).z; + 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 / u_kernelSize)); + occlusion = max(occlusion, texture(u_emission, v_uv).r); + + float z = -position.z * 0.01; + z = max(0.0, 1.0 - z); + return vec4(mix(1.0, occlusion, z), 0.0, 0.0, 1.0); +} diff --git a/res/shaders/entity.glslf b/res/shaders/entity.glslf index e7a8a754..4d4fadba 100644 --- a/res/shaders/entity.glslf +++ b/res/shaders/entity.glslf @@ -1,23 +1,45 @@ -in vec4 a_color; +layout (location = 0) out vec4 f_color; +layout (location = 1) out vec4 f_position; +layout (location = 2) out vec4 f_normal; +layout (location = 3) out vec4 f_emission; + +in float a_distance; +in float a_fog; in vec2 a_texCoord; in vec3 a_dir; -in float a_fog; -out vec4 f_color; +in vec3 a_normal; +in vec3 a_position; +in vec3 a_realnormal; +in vec4 a_color; +in vec4 a_modelpos; +in float a_emission; uniform sampler2D u_texture0; -uniform samplerCube u_cubemap; +uniform samplerCube u_skybox; uniform vec3 u_fogColor; uniform float u_fogFactor; uniform float u_fogCurve; uniform bool u_alphaClip; +uniform vec3 u_sunDir; + +#include void main() { - vec3 fogColor = texture(u_cubemap, a_dir).rgb; - vec4 tex_color = texture(u_texture0, a_texCoord); - float alpha = a_color.a * tex_color.a; + vec4 texColor = texture(u_texture0, a_texCoord); + float alpha = a_color.a * texColor.a; // anyway it's any alpha-test alternative required - if (alpha < (u_alphaClip ? 0.5f : 0.15f)) + if (alpha < (u_alphaClip ? 0.5f : 0.15f)) { discard; - f_color = mix(a_color * tex_color, vec4(fogColor,1.0), a_fog); + } + f_color = a_color * texColor; + +#ifndef ADVANCED_RENDER + vec3 fogColor = texture(u_skybox, a_dir).rgb; + f_color = mix(f_color, vec4(fogColor, 1.0), a_fog); +#endif + f_color.a = alpha; + f_position = vec4(a_position, 1.0); + f_normal = vec4(a_normal, 1.0); + f_emission = vec4(vec3(a_emission), 1.0); } diff --git a/res/shaders/entity.glslv b/res/shaders/entity.glslv index 3d916e9a..d7b6d3e0 100644 --- a/res/shaders/entity.glslv +++ b/res/shaders/entity.glslv @@ -4,11 +4,18 @@ layout (location = 0) in vec3 v_position; layout (location = 1) in vec2 v_texCoord; layout (location = 2) in vec3 v_color; layout (location = 3) in vec4 v_light; +layout (location = 4) in vec4 v_normal; -out vec4 a_color; -out vec2 a_texCoord; +out float a_distance; out float a_fog; +out vec2 a_texCoord; out vec3 a_dir; +out vec3 a_normal; +out vec3 a_position; +out vec3 a_realnormal; +out vec4 a_color; +out vec4 a_modelpos; +out float a_emission; uniform mat4 u_model; uniform mat4 u_proj; @@ -16,36 +23,40 @@ uniform mat4 u_view; uniform vec3 u_cameraPos; uniform float u_gamma; uniform float u_opacity; -uniform float u_fogFactor; -uniform float u_fogCurve; -uniform float u_weatherFogOpacity; -uniform float u_weatherFogDencity; -uniform float u_weatherFogCurve; -uniform samplerCube u_cubemap; +uniform float u_timer; +uniform samplerCube u_skybox; uniform vec3 u_torchlightColor; uniform float u_torchlightDistance; +#include +#include + void main() { - vec4 modelpos = u_model * vec4(v_position, 1.0); - vec3 pos3d = modelpos.xyz - u_cameraPos; - modelpos.xyz = apply_planet_curvature(modelpos.xyz, pos3d); + a_modelpos = u_model * vec4(v_position, 1.0); + vec3 pos3d = a_modelpos.xyz - u_cameraPos; + a_modelpos.xyz = apply_planet_curvature(a_modelpos.xyz, pos3d); + + a_realnormal = v_normal.xyz * 2.0 - 1.0; + a_normal = calc_screen_normal(a_realnormal); vec3 light = v_light.rgb; - float torchlight = max(0.0, 1.0-distance(u_cameraPos, modelpos.xyz) / - u_torchlightDistance); + float torchlight = calc_torch_light(a_realnormal, a_modelpos.xyz); light += torchlight * u_torchlightColor; - a_color = vec4(pow(light, vec3(u_gamma)),1.0f); + a_color = vec4(pow(light, vec3(u_gamma)), 1.0f); a_texCoord = v_texCoord; - a_dir = modelpos.xyz - u_cameraPos; - vec3 skyLightColor = pick_sky_color(u_cubemap); - a_color.rgb = max(a_color.rgb, skyLightColor.rgb*v_light.a) * v_color; + a_dir = a_modelpos.xyz - u_cameraPos; + vec3 skyLightColor = pick_sky_color(u_skybox); + a_color.rgb = max(a_color.rgb, skyLightColor.rgb * v_light.a) * v_color; a_color.a = u_opacity; - float dist = length(u_view * u_model * vec4(pos3d * FOG_POS_SCALE, 0.0)); - float depth = (dist / 256.0); - a_fog = min(1.0, max(pow(depth * u_fogFactor, u_fogCurve), - min(pow(depth * u_weatherFogDencity, u_weatherFogCurve), u_weatherFogOpacity))); - gl_Position = u_proj * u_view * modelpos; + mat4 viewmodel = u_view * u_model; + a_distance = length(viewmodel * vec4(pos3d, 0.0)); + a_fog = calc_fog(length(viewmodel * vec4(pos3d * FOG_POS_SCALE, 0.0)) / 256.0); + a_emission = v_normal.w; + + vec4 viewmodelpos = u_view * a_modelpos; + a_position = viewmodelpos.xyz; + gl_Position = u_proj * viewmodelpos; } diff --git a/res/shaders/lib/fog.glsl b/res/shaders/lib/fog.glsl new file mode 100644 index 00000000..e5ff18bb --- /dev/null +++ b/res/shaders/lib/fog.glsl @@ -0,0 +1,19 @@ +#ifndef FOG_GLSL_ +#define FOG_GLSL_ + +uniform float u_fogFactor; +uniform float u_fogCurve; +uniform float u_weatherFogOpacity; +uniform float u_weatherFogDencity; +uniform float u_weatherFogCurve; + +float calc_fog(float depth) { + return min( + 1.0, + max(pow(depth * u_fogFactor, u_fogCurve), + min(pow(depth * u_weatherFogDencity, u_weatherFogCurve), + u_weatherFogOpacity)) + ); +} + +#endif // FOG_GLSL_ diff --git a/res/shaders/lib/lighting.glsl b/res/shaders/lib/lighting.glsl new file mode 100644 index 00000000..c9e14920 --- /dev/null +++ b/res/shaders/lib/lighting.glsl @@ -0,0 +1,13 @@ +#ifndef LIGHTING_GLSL_ +#define LIGHTING_GLSL_ + +float calc_torch_light(vec3 normal, vec3 modelpos) { + return max(0.0, 1.0 - distance(u_cameraPos, modelpos) / u_torchlightDistance) + * max(0.0, -dot(normal, normalize(modelpos - u_cameraPos))); +} + +vec3 calc_screen_normal(vec3 normal) { + return transpose(inverse(mat3(u_view * u_model))) * normal; +} + +#endif // LIGHTING_GLSL_ diff --git a/res/shaders/lib/shadows.glsl b/res/shaders/lib/shadows.glsl new file mode 100644 index 00000000..db37cdaa --- /dev/null +++ b/res/shaders/lib/shadows.glsl @@ -0,0 +1,47 @@ +#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(vec4 modelPos, vec3 realnormal, float distance) { +#ifdef ENABLE_SHADOWS + float step = 1.0 / float(u_shadowsRes); + float s = pow(abs(cos(u_dayTime * PI2)), 0.25) * u_shadowsOpacity; + vec3 normalOffset = realnormal * (distance > 64.0 ? 0.2 : 0.04); + int shadowIdx = distance > 80.0 ? 1 : 0; + + vec4 mpos = u_shadowsMatrix[shadowIdx] * vec4(modelPos.xyz + normalOffset, 1.0); + vec3 projCoords = mpos.xyz / mpos.w; + projCoords = projCoords * 0.5 + 0.5; + projCoords.z -= 0.00001 / u_shadowsRes; + if (shadowIdx > 0) { + projCoords.z -= 0.001; + } + + float shadow = 0.0; + if (dot(realnormal, u_sunDir) < 0.0) { + // 3x3 kernel + for (int y = -1; y <= 1; y++) { + for (int x = -1; x <= 1; x++) { + vec3 offset = vec3(x, y, -(abs(x) + abs(y)) * 0.8) * step * 1.0 * u_shadowsSoftness; + shadow += texture(u_shadows[shadowIdx], projCoords + offset); + } + } + shadow /= 9.0; + } else { + shadow = 0.5; + } + return 0.5 * (1.0 + s * shadow); +#else + return 1.0; +#endif +} + +#endif // SHADOWS_GLSL_ diff --git a/res/shaders/main.glslf b/res/shaders/main.glslf index d856acc2..f7336d9a 100644 --- a/res/shaders/main.glslf +++ b/res/shaders/main.glslf @@ -1,20 +1,34 @@ -in vec4 a_color; -in vec2 a_texCoord; +layout (location = 0) out vec4 f_color; +layout (location = 1) out vec4 f_position; +layout (location = 2) out vec4 f_normal; +layout (location = 3) out vec4 f_emission; + +in float a_distance; in float a_fog; +in vec2 a_texCoord; in vec3 a_dir; -out vec4 f_color; +in vec3 a_normal; +in vec3 a_position; +in vec3 a_realnormal; +in vec3 a_skyLight; +in vec4 a_modelpos; +in vec4 a_torchLight; +in float a_emission; uniform sampler2D u_texture0; -uniform samplerCube u_cubemap; +uniform samplerCube u_skybox; +uniform vec3 u_sunDir; + +// flags uniform bool u_alphaClip; uniform bool u_debugLights; +uniform bool u_debugNormals; + +#include void main() { - vec3 fogColor = texture(u_cubemap, a_dir).rgb; - vec4 tex_color = texture(u_texture0, a_texCoord); - if (u_debugLights) - tex_color.rgb = vec3(1.0); - float alpha = a_color.a * tex_color.a; + vec4 texColor = texture(u_texture0, a_texCoord); + float alpha = texColor.a; if (u_alphaClip) { if (alpha < 0.2f) discard; @@ -23,6 +37,20 @@ void main() { if (alpha < 0.002f) discard; } - f_color = mix(a_color * tex_color, vec4(fogColor,1.0), a_fog); + if (u_debugLights) + texColor.rgb = u_debugNormals ? (a_normal * 0.5 + 0.5) : vec3(1.0); + else if (u_debugNormals) { + texColor.rgb *= a_normal * 0.5 + 0.5; + } + f_color = texColor; + f_color.rgb *= min(vec3(1.0), a_torchLight.rgb + a_skyLight); + +#ifndef ADVANCED_RENDER + vec3 fogColor = texture(u_skybox, a_dir).rgb; + f_color = mix(f_color, vec4(fogColor, 1.0), a_fog); +#endif f_color.a = alpha; + f_position = vec4(a_position, 1.0); + f_normal = vec4(a_normal, 1.0); + f_emission = vec4(vec3(a_emission), 1.0); } diff --git a/res/shaders/main.glslv b/res/shaders/main.glslv index 24ec6cae..8fd38d20 100644 --- a/res/shaders/main.glslv +++ b/res/shaders/main.glslv @@ -3,48 +3,58 @@ layout (location = 0) in vec3 v_position; layout (location = 1) in vec2 v_texCoord; layout (location = 2) in vec4 v_light; +layout (location = 3) in vec4 v_normal; -out vec4 a_color; -out vec2 a_texCoord; out float a_distance; out float a_fog; +out vec2 a_texCoord; out vec3 a_dir; +out vec3 a_normal; +out vec3 a_position; +out vec3 a_realnormal; +out vec4 a_torchLight; +out vec3 a_skyLight; +out vec4 a_modelpos; +out float a_emission; uniform mat4 u_model; uniform mat4 u_proj; uniform mat4 u_view; uniform vec3 u_cameraPos; uniform float u_gamma; -uniform float u_fogFactor; -uniform float u_fogCurve; -uniform float u_weatherFogOpacity; -uniform float u_weatherFogDencity; -uniform float u_weatherFogCurve; uniform float u_timer; -uniform samplerCube u_cubemap; +uniform samplerCube u_skybox; uniform vec3 u_torchlightColor; uniform float u_torchlightDistance; +#include +#include + void main() { - vec4 modelpos = u_model * vec4(v_position, 1.0f); - vec3 pos3d = modelpos.xyz-u_cameraPos; - modelpos.xyz = apply_planet_curvature(modelpos.xyz, pos3d); + a_modelpos = u_model * vec4(v_position, 1.0f); + vec3 pos3d = a_modelpos.xyz - u_cameraPos; + a_modelpos.xyz = apply_planet_curvature(a_modelpos.xyz, pos3d); + + a_realnormal = v_normal.xyz * 2.0 - 1.0; + a_normal = calc_screen_normal(a_realnormal); vec3 light = v_light.rgb; - float torchlight = max(0.0, 1.0-distance(u_cameraPos, modelpos.xyz) / - u_torchlightDistance); - light += torchlight * u_torchlightColor; - a_color = vec4(pow(light, vec3(u_gamma)),1.0f); + float torchlight = calc_torch_light(a_realnormal, a_modelpos.xyz); + a_torchLight = vec4(pow(light + torchlight * u_torchlightColor, vec3(u_gamma)), 1.0f); + a_texCoord = v_texCoord; - a_dir = modelpos.xyz - u_cameraPos; - vec3 skyLightColor = pick_sky_color(u_cubemap); - a_color.rgb = max(a_color.rgb, skyLightColor.rgb*v_light.a); + a_dir = a_modelpos.xyz - u_cameraPos; + vec3 skyLightColor = pick_sky_color(u_skybox); + a_skyLight = skyLightColor.rgb*v_light.a; - a_distance = length(u_view * u_model * vec4(pos3d * FOG_POS_SCALE, 0.0)); - float depth = (a_distance / 256.0); - a_fog = min(1.0, max(pow(depth * u_fogFactor, u_fogCurve), - min(pow(depth * u_weatherFogDencity, u_weatherFogCurve), u_weatherFogOpacity))); - gl_Position = u_proj * u_view * modelpos; + mat4 viewmodel = u_view * u_model; + a_distance = length(viewmodel * vec4(pos3d, 0.0)); + a_fog = calc_fog(length(viewmodel * vec4(pos3d * FOG_POS_SCALE, 0.0)) / 256.0); + a_emission = v_normal.w; + + vec4 viewmodelpos = u_view * a_modelpos; + a_position = viewmodelpos.xyz; + gl_Position = u_proj * viewmodelpos; } diff --git a/res/shaders/shadows.glslf b/res/shaders/shadows.glslf new file mode 100644 index 00000000..53d6f5d2 --- /dev/null +++ b/res/shaders/shadows.glslf @@ -0,0 +1,11 @@ +in vec2 a_texCoord; + +uniform sampler2D u_texture0; + +void main() { + vec4 tex_color = texture(u_texture0, a_texCoord); + if (tex_color.a < 0.5) { + discard; + } + // depth will be written anyway +} diff --git a/res/shaders/shadows.glslv b/res/shaders/shadows.glslv new file mode 100644 index 00000000..4d4094ac --- /dev/null +++ b/res/shaders/shadows.glslv @@ -0,0 +1,17 @@ +#include + +layout (location = 0) in vec3 v_position; +layout (location = 1) in vec2 v_texCoord; +layout (location = 2) in vec4 v_light; +layout (location = 3) in vec4 v_normal; + +out vec2 a_texCoord; + +uniform mat4 u_model; +uniform mat4 u_proj; +uniform mat4 u_view; + +void main() { + a_texCoord = v_texCoord; + gl_Position = u_proj * u_view * u_model * vec4(v_position, 1.0f); +} diff --git a/res/shaders/skybox_gen.glslf b/res/shaders/skybox_gen.glslf index a3e2cdeb..481203f3 100644 --- a/res/shaders/skybox_gen.glslf +++ b/res/shaders/skybox_gen.glslf @@ -268,7 +268,7 @@ void main() { camera_vector, // the camera vector (ray direction of this pixel) 1e12f, // max dist, essentially the scene depth vec3(0.0f), // scene color, the color of the current pixel being rendered - u_lightDir, // light direction + vec3(u_lightDir.x, pow(u_lightDir.y, 3.0), u_lightDir.z), // light direction vec3(40.0*fog), // light intensity, 40 looks nice PLANET_POS, // position of the planet PLANET_RADIUS, // radius of the planet in meters diff --git a/res/texts/ru_RU.txt b/res/texts/ru_RU.txt index 1836981d..d2d7c63e 100644 --- a/res/texts/ru_RU.txt +++ b/res/texts/ru_RU.txt @@ -98,6 +98,8 @@ settings.V-Sync=Вертикальная Синхронизация settings.Key=Кнопка settings.Controls Search Mode=Поиск по привязанной кнопки управления settings.Limit Background FPS=Ограничить фоновую частоту кадров +settings.Advanced render=Продвинутый рендер +settings.Shadows quality=Качество теней # Управление chunks.reload=Перезагрузить Чанки diff --git a/res/textures/misc/moon_flare.png b/res/textures/misc/moon_flare.png new file mode 100644 index 00000000..57304f3b Binary files /dev/null and b/res/textures/misc/moon_flare.png differ diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index 80c1186d..fd399ecd 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -147,14 +147,12 @@ void AssetsLoader::processPreload( add(tag, path, name); return; } + std::shared_ptr config = nullptr; map.at("path").get(path); switch (tag) { case AssetType::SOUND: { bool keepPCM = false; - add(tag, - path, - name, - std::make_shared(map.at("keep-pcm").get(keepPCM))); + config = std::make_shared(map.at("keep-pcm").get(keepPCM)); break; } case AssetType::ATLAS: { @@ -164,13 +162,19 @@ void AssetsLoader::processPreload( if (typeName == "separate") { type = AtlasType::SEPARATE; } - add(tag, path, name, std::make_shared(type)); + config = std::make_shared(type); + break; + } + case AssetType::POST_EFFECT: { + bool advanced = false; + map.at("advanced").get(advanced); + config = std::make_shared(advanced); break; } default: - add(tag, path, name); break; } + add(tag, path, name, std::move(config)); } void AssetsLoader::processPreloadList(AssetType tag, const dv::value& list) { diff --git a/src/assets/AssetsLoader.hpp b/src/assets/AssetsLoader.hpp index e3c137ba..e35a78ce 100644 --- a/src/assets/AssetsLoader.hpp +++ b/src/assets/AssetsLoader.hpp @@ -40,8 +40,7 @@ struct LayoutCfg : AssetCfg { struct SoundCfg : AssetCfg { bool keepPCM; - SoundCfg(bool keepPCM) : keepPCM(keepPCM) { - } + SoundCfg(bool keepPCM) : keepPCM(keepPCM) {} }; enum class AtlasType { @@ -51,8 +50,13 @@ enum class AtlasType { struct AtlasCfg : AssetCfg { AtlasType type; - AtlasCfg(AtlasType type) : type(type) { - } + AtlasCfg(AtlasType type) : type(type) {} +}; + +struct PostEffectCfg : AssetCfg { + bool advanced; + + PostEffectCfg(bool advanced) : advanced(advanced) {} }; using aloader_func = std::function< diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index db77e354..78d89a0b 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -76,6 +76,14 @@ static auto process_program(const ResPaths& paths, const std::string& filename) return std::make_pair(vertex, fragment); } +static auto read_program(const ResPaths& paths, const std::string& filename) { + io::path vertexFile = paths.find(filename + ".glslv"); + io::path fragmentFile = paths.find(filename + ".glslf"); + return std::make_pair( + io::read_string(vertexFile), io::read_string(fragmentFile) + ); +} + assetload::postfunc assetload::shader( AssetsLoader*, const ResPaths& paths, @@ -83,21 +91,18 @@ assetload::postfunc assetload::shader( const std::string& name, const std::shared_ptr& ) { - auto [vertex, fragment] = process_program(paths, filename); + auto result = read_program(paths, filename); + auto vertex = result.first; + auto fragment = result.second; io::path vertexFile = paths.find(filename + ".glslv"); io::path fragmentFile = paths.find(filename + ".glslf"); - std::string vertexSource = std::move(vertex.code); - std::string fragmentSource = std::move(fragment.code); - return [=](auto assets) { assets->store( Shader::create( - vertexFile.string(), - fragmentFile.string(), - vertexSource, - fragmentSource + {vertexFile.string(), vertex}, + {fragmentFile.string(), fragment} ), name ); @@ -127,13 +132,16 @@ assetload::postfunc assetload::posteffect( return [=](auto assets) { auto program = Shader::create( - effectFile.string(), - effectFile.string(), - vertexSource, - fragmentSource + {effectFile.string(), vertexSource}, + {effectFile.string(), fragmentSource} ); + bool advanced = false; + if (settings) { + advanced = dynamic_cast(settings.get())->advanced; + } assets->store( - std::make_shared(std::move(program), params), name + std::make_shared(advanced, std::move(program), params), + name ); }; } diff --git a/src/coders/GLSLExtension.cpp b/src/coders/GLSLExtension.cpp index 47948bd9..8105b09e 100644 --- a/src/coders/GLSLExtension.cpp +++ b/src/coders/GLSLExtension.cpp @@ -76,6 +76,14 @@ void GLSLExtension::undefine(const std::string& name) { } } +void GLSLExtension::setDefined(const std::string& name, bool defined) { + if (defined) { + define(name, "TRUE"); + } else { + undefine(name); + } +} + inline std::runtime_error parsing_error( const io::path& file, uint linenum, const std::string& message ) { @@ -98,6 +106,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: @@ -156,7 +166,6 @@ public: } bool processVersionDirective() { - parsing_warning(filename, line, "removed #version directive"); source_line(ss, line); skipLine(); return false; @@ -183,6 +192,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 +223,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 +250,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/coders/GLSLExtension.hpp b/src/coders/GLSLExtension.hpp index 2d795d2c..b61ca512 100644 --- a/src/coders/GLSLExtension.hpp +++ b/src/coders/GLSLExtension.hpp @@ -22,6 +22,7 @@ public: void define(const std::string& name, std::string value); void undefine(const std::string& name); + void setDefined(const std::string& name, bool defined); void addHeader(const std::string& name, ProcessingResult header); const ProcessingResult& getHeader(const std::string& name) const; diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index ced38093..74003b2b 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -178,6 +178,8 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player) uicamera = std::make_unique(glm::vec3(), 1); uicamera->perspective = false; uicamera->flipped = true; + uicamera->near = -1.0f; + uicamera->far = 1.0f; debugPanel = create_debug_panel( engine, frontend.getLevel(), player, allowDebugCheats diff --git a/src/frontend/screens/MenuScreen.cpp b/src/frontend/screens/MenuScreen.cpp index f7e091ba..be114ed3 100644 --- a/src/frontend/screens/MenuScreen.cpp +++ b/src/frontend/screens/MenuScreen.cpp @@ -22,6 +22,8 @@ MenuScreen::MenuScreen(Engine& engine) : Screen(engine) { uicamera = std::make_unique(glm::vec3(), engine.getWindow().getSize().y); uicamera->perspective = false; + uicamera->near = -1.0f; + uicamera->far = 1.0f; uicamera->flipped = true; } diff --git a/src/graphics/core/Batch2D.cpp b/src/graphics/core/Batch2D.cpp index 19dfb7c4..b39866ce 100644 --- a/src/graphics/core/Batch2D.cpp +++ b/src/graphics/core/Batch2D.cpp @@ -60,17 +60,17 @@ void Batch2D::vertex( index++; } -void Batch2D::texture(const Texture* new_texture){ - if (currentTexture == new_texture) { +void Batch2D::texture(const Texture* newTexture){ + if (currentTexture == newTexture) { return; } flush(); - currentTexture = new_texture; - if (new_texture == nullptr) { + currentTexture = newTexture; + if (newTexture == nullptr) { blank->bind(); region = blank->getUVRegion(); } else { - new_texture->bind(); + newTexture->bind(); region = currentTexture->getUVRegion(); } } diff --git a/src/graphics/core/Batch3D.cpp b/src/graphics/core/Batch3D.cpp index 327bb690..8f222a51 100644 --- a/src/graphics/core/Batch3D.cpp +++ b/src/graphics/core/Batch3D.cpp @@ -13,8 +13,6 @@ namespace { Batch3D::Batch3D(size_t capacity) : capacity(capacity) { - - buffer = std::make_unique(capacity); mesh = std::make_unique>(buffer.get(), 0); index = 0; diff --git a/src/graphics/core/DrawContext.cpp b/src/graphics/core/DrawContext.cpp index 6bb0115d..fc37fd46 100644 --- a/src/graphics/core/DrawContext.cpp +++ b/src/graphics/core/DrawContext.cpp @@ -100,7 +100,7 @@ void DrawContext::setViewport(const glm::uvec2& viewport) { glViewport(0, 0, viewport.x, viewport.y); } -void DrawContext::setFramebuffer(Framebuffer* fbo) { +void DrawContext::setFramebuffer(Bindable* fbo) { if (this->fbo == fbo) return; this->fbo = fbo; diff --git a/src/graphics/core/DrawContext.hpp b/src/graphics/core/DrawContext.hpp index d9c61c19..b14bf71b 100644 --- a/src/graphics/core/DrawContext.hpp +++ b/src/graphics/core/DrawContext.hpp @@ -16,7 +16,7 @@ class DrawContext { glm::uvec2 viewport; Batch2D* g2d; Flushable* flushable = nullptr; - Framebuffer* fbo = nullptr; + Bindable* fbo = nullptr; bool depthMask = true; bool depthTest = false; bool cullFace = false; @@ -37,7 +37,7 @@ public: [[nodiscard]] DrawContext sub(Flushable* flushable=nullptr) const; void setViewport(const glm::uvec2& viewport); - void setFramebuffer(Framebuffer* fbo); + void setFramebuffer(Bindable* fbo); void setDepthMask(bool flag); void setDepthTest(bool flag); void setCullFace(bool flag); diff --git a/src/graphics/core/Framebuffer.cpp b/src/graphics/core/Framebuffer.cpp index b0a452f4..bb360b54 100644 --- a/src/graphics/core/Framebuffer.cpp +++ b/src/graphics/core/Framebuffer.cpp @@ -2,6 +2,9 @@ #include #include "Texture.hpp" +#include "debug/Logger.hpp" + +static debug::Logger logger("gl-framebuffer"); Framebuffer::Framebuffer(uint fbo, uint depth, std::unique_ptr texture) : fbo(fbo), depth(depth), texture(std::move(texture)) @@ -38,12 +41,17 @@ Framebuffer::Framebuffer(uint width, uint height, bool alpha) // Setup color attachment (texture) texture = create_texture(width, height, format); - + // Setup depth attachment glGenRenderbuffers(1, &depth); glBindRenderbuffer(GL_RENDERBUFFER, depth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + logger.error() << "framebuffer is not complete!"; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); } @@ -67,11 +75,12 @@ void Framebuffer::resize(uint width, uint height) { this->width = width; this->height = height; + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, depth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); glBindRenderbuffer(GL_RENDERBUFFER, 0); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); texture = create_texture(width, height, format); glBindFramebuffer(GL_FRAMEBUFFER, 0); } @@ -87,3 +96,7 @@ uint Framebuffer::getWidth() const { uint Framebuffer::getHeight() const { return height; } + +uint Framebuffer::getFBO() const { + return fbo; +} diff --git a/src/graphics/core/Framebuffer.hpp b/src/graphics/core/Framebuffer.hpp index 795ca2c4..e48e97df 100644 --- a/src/graphics/core/Framebuffer.hpp +++ b/src/graphics/core/Framebuffer.hpp @@ -1,12 +1,13 @@ #pragma once #include "typedefs.hpp" +#include "commons.hpp" #include class Texture; -class Framebuffer { +class Framebuffer : public Bindable { uint fbo; uint depth; uint width; @@ -19,10 +20,10 @@ public: ~Framebuffer(); /// @brief Use framebuffer - void bind(); + void bind() override; /// @brief Stop using framebuffer - void unbind(); + void unbind() override; /// @brief Update framebuffer texture size /// @param width new width @@ -36,4 +37,6 @@ public: uint getWidth() const; /// @brief Get framebuffer height uint getHeight() const; + + uint getFBO() const; }; diff --git a/src/graphics/core/GBuffer.cpp b/src/graphics/core/GBuffer.cpp new file mode 100644 index 00000000..d466f1e9 --- /dev/null +++ b/src/graphics/core/GBuffer.cpp @@ -0,0 +1,273 @@ +#include "GBuffer.hpp" + +#include + +#include "debug/Logger.hpp" + +using namespace advanced_pipeline; + +static debug::Logger logger("gl-gbuffer"); + +// TODO: REFACTOR + +void GBuffer::createColorBuffer() { + if (colorBuffer == 0) + glGenTextures(1, &colorBuffer); + glBindTexture(GL_TEXTURE_2D, colorBuffer); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGB, + width, + height, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + nullptr + ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBuffer, 0 + ); +} + +void GBuffer::createPositionsBuffer() { + if (positionsBuffer == 0) + glGenTextures(1, &positionsBuffer); + glBindTexture(GL_TEXTURE_2D, positionsBuffer); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA16F, + width, + height, + 0, + GL_RGBA, + GL_FLOAT, + nullptr + ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, positionsBuffer, 0 + ); +} + +void GBuffer::createNormalsBuffer() { + if (normalsBuffer == 0) + glGenTextures(1, &normalsBuffer); + glBindTexture(GL_TEXTURE_2D, normalsBuffer); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA16F, + width, + height, + 0, + GL_RGB, + GL_FLOAT, + nullptr + ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, normalsBuffer, 0 + ); +} + +void GBuffer::createEmissionBuffer() { + if (emissionBuffer == 0) + glGenTextures(1, &emissionBuffer); + glBindTexture(GL_TEXTURE_2D, emissionBuffer); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_R8, + width, + height, + 0, + GL_RED, + GL_FLOAT, + nullptr + ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, emissionBuffer, 0 + ); +} + +void GBuffer::createDepthBuffer() { + if (depthBuffer == 0) + glGenRenderbuffers(1, &depthBuffer); + glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); + glFramebufferRenderbuffer( + GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer + ); +} + +void GBuffer::createSSAOBuffer() { + if (ssaoBuffer == 0) + glGenTextures(1, &ssaoBuffer); + glBindTexture(GL_TEXTURE_2D, ssaoBuffer); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_R16F, + width, + height, + 0, + GL_RED, + GL_FLOAT, + nullptr + ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +GBuffer::GBuffer(uint width, uint height) : width(width), height(height) { + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + createColorBuffer(); + createPositionsBuffer(); + createNormalsBuffer(); + createEmissionBuffer(); + + GLenum attachments[4] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3, + }; + glDrawBuffers(4, attachments); + + createDepthBuffer(); + + int status; + if ((status = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) { + logger.error() << "gbuffer is not complete! (" << status << ")"; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glGenFramebuffers(1, &ssaoFbo); + glBindFramebuffer(GL_FRAMEBUFFER, ssaoFbo); + createSSAOBuffer(); + glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ssaoBuffer, 0 + ); + GLenum ssaoAttachments[1] = {GL_COLOR_ATTACHMENT0}; + glDrawBuffers(1, ssaoAttachments); + + + if ((status = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) { + logger.error() << "SSAO framebuffer is not complete! (" << status << ")"; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +GBuffer::~GBuffer() { + glDeleteTextures(1, &colorBuffer); + glDeleteTextures(1, &positionsBuffer); + glDeleteTextures(1, &normalsBuffer); + glDeleteTextures(1, &emissionBuffer); + glDeleteTextures(1, &ssaoBuffer); + glDeleteRenderbuffers(1, &depthBuffer); + glDeleteFramebuffers(1, &fbo); + glDeleteFramebuffers(1, &ssaoFbo); +} + +void GBuffer::bind() { + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glClear(GL_COLOR_BUFFER_BIT); +} + +void GBuffer::bindSSAO() const { + glBindFramebuffer(GL_FRAMEBUFFER, ssaoFbo); +} + +void GBuffer::unbind() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void GBuffer::bindBuffers() const { + glActiveTexture(GL_TEXTURE0 + TARGET_EMISSION); + glBindTexture(GL_TEXTURE_2D, emissionBuffer); + + glActiveTexture(GL_TEXTURE0 + TARGET_NORMALS); + glBindTexture(GL_TEXTURE_2D, normalsBuffer); + + glActiveTexture(GL_TEXTURE0 + TARGET_POSITIONS); + glBindTexture(GL_TEXTURE_2D, positionsBuffer); + + glActiveTexture(GL_TEXTURE0 + TARGET_COLOR); + glBindTexture(GL_TEXTURE_2D, colorBuffer); + + if (TARGET_COLOR != 0) + glActiveTexture(GL_TEXTURE0); +} + +void GBuffer::bindSSAOBuffer() const { + glBindTexture(GL_TEXTURE_2D, ssaoBuffer); +} + +void GBuffer::bindDepthBuffer(int drawFbo) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, drawFbo); + glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, + GL_DEPTH_BUFFER_BIT, GL_NEAREST); +} + +void GBuffer::resize(uint width, uint height) { + if (this->width == width && this->height == height) { + return; + } + this->width = width; + this->height = height; + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + createDepthBuffer(); + createColorBuffer(); + createPositionsBuffer(); + createNormalsBuffer(); + createEmissionBuffer(); + + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, ssaoFbo); + createSSAOBuffer(); + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +std::unique_ptr GBuffer::toImage() const { + auto data = std::make_unique(width * height * 3); + glBindTexture(GL_TEXTURE_2D, colorBuffer); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, data.get()); + glBindTexture(GL_TEXTURE_2D, 0); + return std::make_unique( + ImageFormat::rgb888, width, height, std::move(data) + ); +} + +uint GBuffer::getWidth() const { + return width; +} + +uint GBuffer::getHeight() const { + return height; +} diff --git a/src/graphics/core/GBuffer.hpp b/src/graphics/core/GBuffer.hpp new file mode 100644 index 00000000..ae433c87 --- /dev/null +++ b/src/graphics/core/GBuffer.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include "typedefs.hpp" +#include "commons.hpp" +#include "ImageData.hpp" + +class GBuffer : public Bindable { +public: + GBuffer(uint width, uint height); + ~GBuffer() override; + + void bind() override; + void bindSSAO() const; + void unbind() override; + + void bindBuffers() const; + void bindSSAOBuffer() const; + + void bindDepthBuffer(int drawFbo); + + void resize(uint width, uint height); + + uint getWidth() const; + uint getHeight() const; + + std::unique_ptr toImage() const; +private: + uint width; + uint height; + + uint fbo; + uint colorBuffer = 0; + uint positionsBuffer = 0; + uint normalsBuffer = 0; + uint emissionBuffer = 0; + uint depthBuffer = 0; + uint ssaoFbo = 0; + uint ssaoBuffer = 0; + + void createColorBuffer(); + void createPositionsBuffer(); + void createNormalsBuffer(); + void createEmissionBuffer(); + void createDepthBuffer(); + void createSSAOBuffer(); +}; diff --git a/src/graphics/core/PostEffect.cpp b/src/graphics/core/PostEffect.cpp index d634c636..b0c34571 100644 --- a/src/graphics/core/PostEffect.cpp +++ b/src/graphics/core/PostEffect.cpp @@ -2,18 +2,81 @@ #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( + bool advanced, std::shared_ptr shader, std::unordered_map params ) - : shader(std::move(shader)), params(std::move(params)) { + : advanced(advanced), shader(std::move(shader)), params(std::move(params)) { +} + +static void apply_uniform_value( + const PostEffect::Param& param, + Shader& shader, + const std::string& name +) { + using Type = PostEffect::Param::Type; + switch (param.type) { + case Type::INT: + shader.uniform1i(name, std::get(param.value)); + break; + case Type::FLOAT: + shader.uniform1f(name, std::get(param.value)); + break; + case Type::VEC2: + shader.uniform2f(name, std::get(param.value)); + break; + case Type::VEC3: + shader.uniform3f(name, std::get(param.value)); + break; + case Type::VEC4: + shader.uniform4f(name, std::get(param.value)); + break; + default: + assert(false); + } +} + +static void apply_uniform_array( + const PostEffect::Param& param, + Shader& shader, + const std::string& name, + const std::vector& values +) { + size_t size = values.size(); + auto ibuffer = reinterpret_cast(values.data()); + auto fbuffer = reinterpret_cast(values.data()); + + using Type = PostEffect::Param::Type; + switch (param.type) { + case Type::INT: + shader.uniform1v(name, size / sizeof(int), ibuffer); + break; + case Type::FLOAT: + shader.uniform1v(name, size / sizeof(float), fbuffer); + break; + case Type::VEC2: + shader.uniform2v(name, size / sizeof(glm::vec2), fbuffer); + break; + case Type::VEC3: + shader.uniform3v(name, size / sizeof(glm::vec3), fbuffer); + break; + case Type::VEC4: + shader.uniform4v(name, size / sizeof(glm::vec4), fbuffer); + break; + default: + assert(false); + } } Shader& PostEffect::use() { @@ -23,27 +86,24 @@ 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; + } + apply_uniform_array(param, *shader, name, found->second); + } else { + apply_uniform_value(param, *shader, name); } param.dirty = false; } return *shader; } +Shader& PostEffect::getShader() { + return *shader; +} + float PostEffect::getIntensity() const { return intensity; } @@ -66,6 +126,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; @@ -81,3 +144,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 9c4bd84f..a29fd404 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,27 +16,30 @@ 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( + bool advanced, std::shared_ptr shader, std::unordered_map params ); @@ -43,16 +48,26 @@ public: Shader& use(); + Shader& getShader(); + float getIntensity() const; void setIntensity(float value); void setParam(const std::string& name, const dv::value& value); + void setArray(const std::string& name, std::vector&& values); + + bool isAdvanced() const { + return advanced; + } + bool isActive() { return intensity > 1e-4f; } 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 2968cb4d..ec491c52 100644 --- a/src/graphics/core/PostProcessing.cpp +++ b/src/graphics/core/PostProcessing.cpp @@ -1,58 +1,200 @@ #include "PostProcessing.hpp" #include "Mesh.hpp" #include "Shader.hpp" +#include "GBuffer.hpp" #include "Texture.hpp" #include "Framebuffer.hpp" #include "DrawContext.hpp" #include "PostEffect.hpp" #include "assets/Assets.hpp" +#include "window/Camera.hpp" #include +#include + +// TODO: REFACTOR WHOLE RENDER ENGINE + +using namespace advanced_pipeline; PostProcessing::PostProcessing(size_t effectSlotsCount) : effectSlots(effectSlotsCount) { // Fullscreen quad mesh bulding - PostProcessingVertex meshData[]{ - {{-1.0f, -1.0f}}, - {{-1.0f, 1.0f}}, - {{1.0f, 1.0f}}, - {{-1.0f, -1.0f}}, - {{1.0f, 1.0f}}, - {{1.0f, -1.0f}}, + PostProcessingVertex meshData[] { + {{-1.0f, -1.0f}}, + {{-1.0f, 1.0f}}, + {{1.0f, 1.0f}}, + {{-1.0f, -1.0f}}, + {{1.0f, 1.0f}}, + {{1.0f, -1.0f}}, }; quadMesh = std::make_unique>(meshData, 6); + + std::vector ssaoNoise; + for (unsigned int i = 0; i < 16; i++) + { + glm::vec3 noise( + (rand() / static_cast(RAND_MAX)) * 2.0 - 1.0, + (rand() / static_cast(RAND_MAX)) * 2.0 - 1.0, + 0.0f); + ssaoNoise.push_back(noise); + } + glGenTextures(1, &noiseTexture); + glBindTexture(GL_TEXTURE_2D, noiseTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, 4, 4, 0, GL_RGB, GL_FLOAT, ssaoNoise.data()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + 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); } PostProcessing::~PostProcessing() = default; -void PostProcessing::use(DrawContext& context) { +void PostProcessing::use(DrawContext& context, bool gbufferPipeline) { const auto& vp = context.getViewport(); - if (fbo) { - fbo->resize(vp.x, vp.y); - fboSecond->resize(vp.x, vp.y); + + if (gbufferPipeline) { + if (gbuffer == nullptr) { + gbuffer = std::make_unique(vp.x, vp.y); + } else { + gbuffer->resize(vp.x, vp.y); + } + context.setFramebuffer(gbuffer.get()); } else { - fbo = std::make_unique(vp.x, vp.y); - fboSecond = std::make_unique(vp.x, vp.y); + gbuffer.reset(); + refreshFbos(vp.x, vp.y); + context.setFramebuffer(fbo.get()); + } +} + +void PostProcessing::refreshFbos(uint width, uint height) { + if (fbo) { + fbo->resize(width, height); + fboSecond->resize(width, height); + } else { + fbo = std::make_unique(width, height); + fboSecond = std::make_unique(width, height); + } +} + +void PostProcessing::bindDepthBuffer() { + if (gbuffer) { + gbuffer->bindDepthBuffer(fbo->getFBO()); + } +} + +void PostProcessing::configureEffect( + const DrawContext& context, + PostEffect& effect, + Shader& shader, + float timer, + const Camera& camera +) { + const auto& viewport = context.getViewport(); + shader.uniform1i("u_screen", TARGET_COLOR); + shader.uniform1i("u_skybox", TARGET_SKYBOX); + if (gbuffer) { + shader.uniform1i("u_position", TARGET_POSITIONS); + shader.uniform1i("u_normal", TARGET_NORMALS); + shader.uniform1i("u_emission", TARGET_EMISSION); + } + shader.uniform1i("u_noise", TARGET_SSAO); // used in SSAO pass + shader.uniform1i("u_ssao", TARGET_SSAO); + shader.uniform2i("u_screenSize", viewport); + shader.uniform3f("u_cameraPos", camera.position); + shader.uniform1f("u_timer", timer); + shader.uniformMatrix("u_projection", camera.getProjection()); + shader.uniformMatrix("u_view", camera.getView()); + shader.uniformMatrix("u_inverseView", glm::inverse(camera.getView())); +} + +void PostProcessing::renderDeferredShading( + const DrawContext& context, + const Assets& assets, + float timer, + const Camera& camera +) { + if (gbuffer == nullptr) { + throw std::runtime_error("gbuffer is not initialized"); + } + // Generating ssao + gbuffer->bindBuffers(); + + glActiveTexture(GL_TEXTURE0 + TARGET_SSAO); + glBindTexture(GL_TEXTURE_2D, noiseTexture); + + glActiveTexture(GL_TEXTURE0); + + auto& ssaoEffect = assets.require("ssao"); + auto& shader = ssaoEffect.use(); + configureEffect( + context, + ssaoEffect, + shader, + timer, + camera + ); + gbuffer->bindSSAO(); + quadMesh->draw(); + gbuffer->unbind(); + + { + auto viewport = context.getViewport(); + refreshFbos(viewport.x, viewport.y); + + auto ctx = context.sub(); + ctx.setFramebuffer(fbo.get()); + + glActiveTexture(GL_TEXTURE0 + TARGET_SSAO); + gbuffer->bindSSAOBuffer(); + + glActiveTexture(GL_TEXTURE0); + + gbuffer->bindBuffers(); + + auto& effect = assets.require("deferred_lighting"); + auto& shader = effect.use(); + configureEffect( + context, + effect, + shader, + timer, + camera + ); + quadMesh->draw(); } - context.setFramebuffer(fbo.get()); } void PostProcessing::render( - const DrawContext& context, const Assets& assets, float timer + const DrawContext& context, + const Assets& assets, + float timer, + const Camera& camera ) { if (fbo == nullptr) { throw std::runtime_error("'use(...)' was never called"); } int totalPasses = 0; for (const auto& effect : effectSlots) { - totalPasses += (effect != nullptr && effect->isActive()); + totalPasses += + (effect != nullptr && effect->isActive() && + !(effect->isAdvanced() && gbuffer == nullptr)); } + const auto& vp = context.getViewport(); + refreshFbos(vp.x, vp.y); + + glActiveTexture(GL_TEXTURE0); + fbo->getTexture()->bind(); + if (totalPasses == 0) { + // replace 'default' blit shader with glBlitFramebuffer? auto& effect = assets.require("default"); - effect.use(); - fbo->getTexture()->bind(); + auto& shader = effect.use(); + configureEffect( + context, effect, shader, timer, camera + ); quadMesh->draw(); return; } @@ -62,17 +204,26 @@ void PostProcessing::render( if (effect == nullptr || !effect->isActive()) { continue; } + if (effect->isAdvanced() && gbuffer == nullptr) { + continue; + } auto& shader = effect->use(); + configureEffect( + context, + *effect, + shader, + timer, + camera + ); - const auto& viewport = context.getViewport(); - shader.uniform1i("u_screen", 0); - shader.uniform2i("u_screenSize", viewport); - shader.uniform1f("u_timer", timer); + if (currentPass > 1) { + fbo->getTexture()->bind(); + } - fbo->getTexture()->bind(); if (currentPass < totalPasses) { fboSecond->bind(); } + quadMesh->draw(); if (currentPass < totalPasses) { fboSecond->unbind(); diff --git a/src/graphics/core/PostProcessing.hpp b/src/graphics/core/PostProcessing.hpp index c6b3ae14..f9d50b75 100644 --- a/src/graphics/core/PostProcessing.hpp +++ b/src/graphics/core/PostProcessing.hpp @@ -11,6 +11,9 @@ class Framebuffer; class DrawContext; class ImageData; class PostEffect; +class Camera; +class GBuffer; +class Shader; struct PostProcessingVertex { glm::vec2 position; @@ -24,25 +27,31 @@ struct PostProcessingVertex { /// @attention Current implementation does not support multiple render passes /// for multiple effects. Will be implemented in v0.21 class PostProcessing { - /// @brief Main framebuffer (lasy field) - std::unique_ptr fbo; - std::unique_ptr fboSecond; - /// @brief Fullscreen quad mesh as the post-processing canvas - std::unique_ptr> quadMesh; - std::vector> effectSlots; public: PostProcessing(size_t effectSlotsCount); ~PostProcessing(); /// @brief Prepare and bind framebuffer /// @param context graphics context will be modified - void use(DrawContext& context); + void use(DrawContext& context, bool gbufferPipeline); + + void renderDeferredShading( + const DrawContext& context, + const Assets& assets, + float timer, + const Camera& camera + ); /// @brief Render fullscreen quad using the passed shader /// with framebuffer texture bound /// @param context graphics context /// @throws std::runtime_error if use(...) wasn't called before - void render(const DrawContext& context, const Assets& assets, float timer); + void render( + const DrawContext& context, + const Assets& assets, + float timer, + const Camera& camera + ); void setEffect(size_t slot, std::shared_ptr effect); @@ -52,4 +61,24 @@ public: std::unique_ptr toImage(); Framebuffer* getFramebuffer() const; + void bindDepthBuffer(); +private: + void configureEffect( + const DrawContext& context, + PostEffect& effect, + Shader& shader, + float timer, + const Camera& camera + ); + + void refreshFbos(uint width, uint height); + + /// @brief Main framebuffer (lasy field) + std::unique_ptr fbo; + std::unique_ptr fboSecond; + /// @brief Fullscreen quad mesh as the post-processing canvas + std::unique_ptr> quadMesh; + std::vector> effectSlots; + std::unique_ptr gbuffer; + uint noiseTexture; }; diff --git a/src/graphics/core/Shader.cpp b/src/graphics/core/Shader.cpp index 626d747b..da6ea342 100644 --- a/src/graphics/core/Shader.cpp +++ b/src/graphics/core/Shader.cpp @@ -12,20 +12,25 @@ #include #include "coders/GLSLExtension.hpp" +#include "debug/Logger.hpp" + +static debug::Logger logger("gl-shader"); namespace fs = std::filesystem; GLSLExtension* Shader::preprocessor = new GLSLExtension(); Shader* Shader::used = nullptr; -Shader::Shader(uint id) : id(id){ -} +Shader::Shader(uint id, Source&& vertexSource, Source&& fragmentSource) + : id(id), + vertexSource(std::move(vertexSource)), + fragmentSource(std::move(fragmentSource)) {} -Shader::~Shader(){ +Shader::~Shader() { glDeleteProgram(id); } -void Shader::use(){ +void Shader::use() { used = this; glUseProgram(id); } @@ -40,8 +45,16 @@ uint Shader::getUniformLocation(const std::string& name) { return found->second; } -void Shader::uniformMatrix(const std::string& name, const glm::mat4& matrix){ - glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix)); +void Shader::uniformMatrix(const std::string& name, const glm::mat4& matrix) { + glUniformMatrix4fv( + getUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix) + ); +} + +void Shader::uniformMatrix(const std::string& name, const glm::mat3& matrix) { + glUniformMatrix3fv( + getUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix) + ); } void Shader::uniform1i(const std::string& name, int x){ @@ -76,8 +89,27 @@ 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); +} -inline auto shader_deleter = [](GLuint* shader) { +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); +} + +static inline auto shader_deleter = [](GLuint* shader) { glDeleteShader(*shader); delete shader; }; @@ -93,45 +125,73 @@ glshader compile_shader(GLenum type, const GLchar* source, const std::string& fi glShaderSource(shader, 1, &source, nullptr); glCompileShader(shader); glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - if (!success){ + if (!success) { GLchar infoLog[GL_LOG_LEN]; glGetShaderInfoLog(shader, GL_LOG_LEN, nullptr, infoLog); glDeleteShader(shader); throw std::runtime_error( - "vertex shader compilation failed ("+file+"):\n"+std::string(infoLog) + "vertex shader compilation failed (" + file + "):\n" + + std::string(infoLog) ); - } + } return glshader(new GLuint(shader), shader_deleter); //-V508 } -std::unique_ptr Shader::create( - const std::string& vertexFile, - const std::string& fragmentFile, - const std::string& vertexCode, - const std::string& fragmentCode +static GLuint compile_program( + const Shader::Source& vertexSource, const Shader::Source& fragmentSource ) { + auto& preprocessor = *Shader::preprocessor; + + auto vertexCode = std::move( + preprocessor.process(vertexSource.file, vertexSource.code).code + ); + auto fragmentCode = std::move( + preprocessor.process(fragmentSource.file, fragmentSource.code).code + ); + const GLchar* vCode = vertexCode.c_str(); const GLchar* fCode = fragmentCode.c_str(); - glshader vertex = compile_shader(GL_VERTEX_SHADER, vCode, vertexFile); - glshader fragment = compile_shader(GL_FRAGMENT_SHADER, fCode, fragmentFile); + glshader vertex = + compile_shader(GL_VERTEX_SHADER, vCode, vertexSource.file); + glshader fragment = + compile_shader(GL_FRAGMENT_SHADER, fCode, fragmentSource.file); // Shader Program GLint success; - GLuint id = glCreateProgram(); - glAttachShader(id, *vertex); - glAttachShader(id, *fragment); - glLinkProgram(id); + GLuint program = glCreateProgram(); + glAttachShader(program, *vertex); + glAttachShader(program, *fragment); + glLinkProgram(program); - glGetProgramiv(id, GL_LINK_STATUS, &success); - if (!success){ + glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (!success) { GLchar infoLog[GL_LOG_LEN]; - glGetProgramInfoLog(id, GL_LOG_LEN, nullptr, infoLog); + glGetProgramInfoLog(program, GL_LOG_LEN, nullptr, infoLog); throw std::runtime_error( - "shader program linking failed:\n"+std::string(infoLog) + "shader program linking failed:\n" + std::string(infoLog) ); } - return std::make_unique(id); + return program; +} + +void Shader::recompile() { + GLuint newProgram = compile_program(vertexSource, fragmentSource); + glDeleteProgram(id); + id = newProgram; + uniformLocations.clear(); + logger.info() << "shader " << id << " has been recompiled"; +} + +std::unique_ptr Shader::create( + Source&& vertexSource, Source&& fragmentSource +) { + return std::make_unique( + compile_program(vertexSource, fragmentSource), + std::move(vertexSource), + std::move(fragmentSource) + ); } Shader& Shader::getUsed() { diff --git a/src/graphics/core/Shader.hpp b/src/graphics/core/Shader.hpp index 18a14da7..4808172e 100644 --- a/src/graphics/core/Shader.hpp +++ b/src/graphics/core/Shader.hpp @@ -10,19 +10,30 @@ class GLSLExtension; class Shader { +public: + struct Source { + std::string file; + std::string code; + }; +private: static Shader* used; uint id; std::unordered_map uniformLocations; + // source code used for re-compiling shaders after updating defines + Source vertexSource; + Source fragmentSource; + uint getUniformLocation(const std::string& name); public: static GLSLExtension* preprocessor; - Shader(uint id); + Shader(uint id, Source&& vertexSource, Source&& fragmentSource); ~Shader(); void use(); void uniformMatrix(const std::string&, const glm::mat4& matrix); + void uniformMatrix(const std::string&, const glm::mat3& matrix); void uniform1i(const std::string& name, int x); void uniform1f(const std::string& name, float x); void uniform2f(const std::string& name, float x, float y); @@ -32,17 +43,19 @@ 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 Re-preprocess source code and re-compile shader program + void recompile(); + /// @brief Create shader program using vertex and fragment shaders source. - /// @param vertexFile vertex shader file name - /// @param fragmentFile fragment shader file name - /// @param vertexSource vertex shader source code - /// @param fragmentSource fragment shader source code /// @return linked shader program containing vertex and fragment shaders static std::unique_ptr create( - const std::string& vertexFile, - const std::string& fragmentFile, - const std::string& vertexSource, - const std::string& fragmentSource + Source&& vertexSource, Source&& fragmentSource ); static Shader& getUsed(); diff --git a/src/graphics/core/ShadowMap.cpp b/src/graphics/core/ShadowMap.cpp new file mode 100644 index 00000000..e5032d8b --- /dev/null +++ b/src/graphics/core/ShadowMap.cpp @@ -0,0 +1,47 @@ +#include "ShadowMap.hpp" + +#include + +ShadowMap::ShadowMap(int resolution) : resolution(resolution) { + glGenTextures(1, &depthMap); + glBindTexture(GL_TEXTURE_2D, depthMap); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, + resolution, resolution, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + float border[4] {1.0f, 1.0f, 1.0f, 1.0f}; + glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_BORDER_COLOR, border); + + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0); + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +ShadowMap::~ShadowMap() { + glDeleteFramebuffers(1, &fbo); + glDeleteTextures(1, &depthMap); +} + +void ShadowMap::bind() { + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glClear(GL_DEPTH_BUFFER_BIT); +} + +void ShadowMap::unbind() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +uint ShadowMap::getDepthMap() const { + return depthMap; +} + +int ShadowMap::getResolution() const { + return resolution; +} diff --git a/src/graphics/core/ShadowMap.hpp b/src/graphics/core/ShadowMap.hpp new file mode 100644 index 00000000..828c51aa --- /dev/null +++ b/src/graphics/core/ShadowMap.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "typedefs.hpp" + +class ShadowMap { +public: + ShadowMap(int resolution); + ~ShadowMap(); + + void bind(); + void unbind(); + uint getDepthMap() const; + int getResolution() const; +private: + uint fbo; + uint depthMap; + int resolution; +}; diff --git a/src/graphics/core/commons.hpp b/src/graphics/core/commons.hpp index 7c26b75b..8785cec1 100644 --- a/src/graphics/core/commons.hpp +++ b/src/graphics/core/commons.hpp @@ -50,6 +50,17 @@ enum class CursorShape { LAST=NOT_ALLOWED }; +namespace advanced_pipeline { + inline constexpr int TARGET_COLOR = 0; + inline constexpr int TARGET_SKYBOX = 1; + inline constexpr int TARGET_POSITIONS = 2; + inline constexpr int TARGET_NORMALS = 3; + inline constexpr int TARGET_EMISSION = 4; + inline constexpr int TARGET_SSAO = 5; + inline constexpr int TARGET_SHADOWS0 = 6; + inline constexpr int TARGET_SHADOWS1 = 7; +} + VC_ENUM_METADATA(CursorShape) {"arrow", CursorShape::ARROW}, {"text", CursorShape::TEXT}, @@ -69,3 +80,11 @@ public: virtual void flush() = 0; }; + +class Bindable { +public: + virtual ~Bindable() = default; + + virtual void bind() = 0; + virtual void unbind() = 0; +}; diff --git a/src/graphics/render/BlocksRenderer.cpp b/src/graphics/render/BlocksRenderer.cpp index 3baeb0e4..2fedd15b 100644 --- a/src/graphics/render/BlocksRenderer.cpp +++ b/src/graphics/render/BlocksRenderer.cpp @@ -39,17 +39,27 @@ BlocksRenderer::~BlocksRenderer() { /// Basic vertex add method void BlocksRenderer::vertex( - const glm::vec3& coord, float u, float v, const glm::vec4& light + const glm::vec3& coord, + float u, + float v, + const glm::vec4& light, + const glm::vec3& normal, + float emission ) { - vertexBuffer[vertexCount].position = coord; vertexBuffer[vertexCount].uv = {u,v}; + vertexBuffer[vertexCount].normal[0] = static_cast(normal.r * 127 + 128); + vertexBuffer[vertexCount].normal[1] = static_cast(normal.g * 127 + 128); + vertexBuffer[vertexCount].normal[2] = static_cast(normal.b * 127 + 128); + vertexBuffer[vertexCount].normal[3] = static_cast(emission * 255); + vertexBuffer[vertexCount].color[0] = static_cast(light.r * 255); vertexBuffer[vertexCount].color[1] = static_cast(light.g * 255); vertexBuffer[vertexCount].color[2] = static_cast(light.b * 255); vertexBuffer[vertexCount].color[3] = static_cast(light.a * 255); + vertexCount++; } @@ -82,10 +92,10 @@ void BlocksRenderer::face( auto Y = axisY * h; auto Z = axisZ * d; float s = 0.5f; - vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, lights[0] * tint); - vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, lights[1] * tint); - vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, lights[2] * tint); - vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, lights[3] * tint); + vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, lights[0] * tint, axisZ, 0); + vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, lights[1] * tint, axisZ, 0); + vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, lights[2] * tint, axisZ, 0); + vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, lights[3] * tint, axisZ, 0); index(0, 1, 3, 1, 2, 3); } @@ -103,7 +113,7 @@ void BlocksRenderer::vertexAO( axisX, axisY ); - vertex(coord, u, v, light * tint); + vertex(coord, u, v, light * tint, axisZ, 0.0f); } void BlocksRenderer::faceAO( @@ -134,11 +144,12 @@ void BlocksRenderer::faceAO( vertexAO(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, axisX, axisY, axisZ); vertexAO(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, axisX, axisY, axisZ); } else { + auto axisZ = glm::normalize(Z); glm::vec4 tint(1.0f); - vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint); - vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint); - vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint); - vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint); + vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint, axisZ, 1); + vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint, axisZ, 1); + vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, axisZ, 1); + vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, axisZ, 1); } index(0, 1, 2, 0, 2, 3); } @@ -163,10 +174,10 @@ void BlocksRenderer::face( d = (1.0f - DIRECTIONAL_LIGHT_FACTOR) + d * DIRECTIONAL_LIGHT_FACTOR; tint *= d; } - vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint); - vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint); - vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint); - vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint); + vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint, Z, lights ? 0 : 1); + vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint, Z, lights ? 0 : 1); + vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, Z, lights ? 0 : 1); + vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, Z, lights ? 0 : 1); index(0, 1, 2, 0, 2, 3); } @@ -198,14 +209,16 @@ void BlocksRenderer::blockXSprite( const float w = size.x / 1.41f; const glm::vec4 tint (0.8f); - face({x + xs, y, z + zs}, w, size.y, 0, {-1, 0, 1}, {0, 1, 0}, glm::vec3(), + glm::vec3 n(0.0f, 1.0f, 0.0f); + + face({x + xs, y, z + zs}, w, size.y, 0, {-1, 0, 1}, {0, 1, 0}, n, texface1, lights2, tint); - face({x + xs, y, z + zs}, w, size.y, 0, {1, 0, 1}, {0, 1, 0}, glm::vec3(), + face({x + xs, y, z + zs}, w, size.y, 0, {1, 0, 1}, {0, 1, 0}, n, texface1, lights1, tint); - face({x + xs, y, z + zs}, w, size.y, 0, {-1, 0, -1}, {0, 1, 0}, glm::vec3(), + face({x + xs, y, z + zs}, w, size.y, 0, {-1, 0, -1}, {0, 1, 0}, n, texface2, lights2, tint); - face({x + xs, y, z + zs}, w, size.y, 0, {1, 0, -1}, {0, 1, 0}, glm::vec3(), + face({x + xs, y, z + zs}, w, size.y, 0, {1, 0, -1}, {0, 1, 0}, n, texface2, lights1, tint); } @@ -337,7 +350,9 @@ void BlocksRenderer::blockCustomModel( coord + vcoord.x * X + vcoord.y * Y + vcoord.z * Z, vertex.uv.x, vertex.uv.y, - glm::vec4(d, d, d, d) * aoColor + glm::vec4(d, d, d, d) * aoColor, + n, + 0.0f ); indexBuffer[indexCount++] = vertexOffset++; } diff --git a/src/graphics/render/BlocksRenderer.hpp b/src/graphics/render/BlocksRenderer.hpp index 1edcbfcc..b017a3f3 100644 --- a/src/graphics/render/BlocksRenderer.hpp +++ b/src/graphics/render/BlocksRenderer.hpp @@ -44,7 +44,14 @@ class BlocksRenderer { SortingMeshData sortingMesh; - void vertex(const glm::vec3& coord, float u, float v, const glm::vec4& light); + void vertex( + const glm::vec3& coord, + float u, + float v, + const glm::vec4& light, + const glm::vec3& normal, + float emission + ); void index(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f); void vertexAO( diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index 199d818b..284ebb43 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -143,7 +143,7 @@ void ChunksRenderer::update() { } const Mesh* ChunksRenderer::retrieveChunk( - size_t index, const Camera& camera, Shader& shader, bool culling + size_t index, const Camera& camera, bool culling ) { auto chunk = chunks.getChunks()[index]; if (chunk == nullptr) { @@ -182,13 +182,39 @@ const Mesh* ChunksRenderer::retrieveChunk( return mesh; } +void ChunksRenderer::drawChunksShadowsPass( + const Camera& camera, Shader& shader +) { + const auto& atlas = assets.require("blocks"); + + atlas.getTexture()->bind(); + + for (const auto& chunk : chunks.getChunks()) { + if (chunk == nullptr) { + continue; + } + const auto& found = meshes.find({chunk->x, chunk->z}); + if (found == meshes.end()) { + continue; + } + auto mesh = found->second.mesh.get(); + if (mesh) { + glm::vec3 coord( + chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f + ); + glm::mat4 model = glm::translate(glm::mat4(1.0f), coord); + shader.uniformMatrix("u_model", model); + mesh->draw(); + } + } +} + void ChunksRenderer::drawChunks( const Camera& camera, Shader& shader ) { const auto& atlas = assets.require("blocks"); atlas.getTexture()->bind(); - update(); // [warning] this whole method is not thread-safe for chunks @@ -219,7 +245,7 @@ void ChunksRenderer::drawChunks( // TODO: minimize draw calls number for (int i = indices.size()-1; i >= 0; i--) { auto& chunk = chunks.getChunks()[indices[i].index]; - auto mesh = retrieveChunk(indices[i].index, camera, shader, culling); + auto mesh = retrieveChunk(indices[i].index, camera, culling); if (mesh) { glm::vec3 coord( diff --git a/src/graphics/render/ChunksRenderer.hpp b/src/graphics/render/ChunksRenderer.hpp index aa9833af..fcc8b07d 100644 --- a/src/graphics/render/ChunksRenderer.hpp +++ b/src/graphics/render/ChunksRenderer.hpp @@ -50,7 +50,7 @@ class ChunksRenderer { std::vector indices; util::ThreadPool, RendererResult> threadPool; const Mesh* retrieveChunk( - size_t index, const Camera& camera, Shader& shader, bool culling + size_t index, const Camera& camera, bool culling ); public: ChunksRenderer( @@ -72,6 +72,9 @@ public: const Mesh* getOrRender( const std::shared_ptr& chunk, bool important ); + + void drawChunksShadowsPass(const Camera& camera, Shader& shader); + void drawChunks(const Camera& camera, Shader& shader); void drawSortedMeshes(const Camera& camera, Shader& shader); diff --git a/src/graphics/render/MainBatch.cpp b/src/graphics/render/MainBatch.cpp index 34976819..f05b715d 100644 --- a/src/graphics/render/MainBatch.cpp +++ b/src/graphics/render/MainBatch.cpp @@ -94,39 +94,39 @@ void MainBatch::cube( const glm::vec3 Z(0.0f, 0.0f, 1.0f); quad( - coord + Z * size.z * 0.5f, - X, Y, glm::vec2(size.x, size.y), - (shading ? do_tint(0.8) * tint : tint), - glm::vec3(1.0f), texfaces[5] + coord + Z * size.z * 0.5f, + X, Y, Z, glm::vec2(size.x, size.y), + (shading ? do_tint(0.8) * tint : tint), + glm::vec3(1.0f), texfaces[5] ); quad( - coord - Z * size.z * 0.5f, - -X, Y, glm::vec2(size.x, size.y), - (shading ? do_tint(0.9f) * tint : tint), - glm::vec3(1.0f), texfaces[4] + coord - Z * size.z * 0.5f, + -X, Y, -Z, glm::vec2(size.x, size.y), + (shading ? do_tint(0.9f) * tint : tint), + glm::vec3(1.0f), texfaces[4] ); quad( - coord + Y * size.y * 0.5f, - -X, Z, glm::vec2(size.x, size.z), - (shading ? do_tint(1.0f) * tint : tint), - glm::vec3(1.0f), texfaces[3] + coord + Y * size.y * 0.5f, + -X, Z, Y, glm::vec2(size.x, size.z), + (shading ? do_tint(1.0f) * tint : tint), + glm::vec3(1.0f), texfaces[3] ); quad( - coord - Y * size.y * 0.5f, - X, Z, glm::vec2(size.x, size.z), - (shading ? do_tint(0.7f) * tint : tint), - glm::vec3(1.0f), texfaces[2] + coord - Y * size.y * 0.5f, + X, Z, -Y, glm::vec2(size.x, size.z), + (shading ? do_tint(0.7f) * tint : tint), + glm::vec3(1.0f), texfaces[2] ); quad( - coord + X * size.x * 0.5f, - -Z, Y, glm::vec2(size.z, size.y), - (shading ? do_tint(0.8f) * tint : tint), - glm::vec3(1.0f), texfaces[1] + coord + X * size.x * 0.5f, + -Z, Y, X, glm::vec2(size.z, size.y), + (shading ? do_tint(0.8f) * tint : tint), + glm::vec3(1.0f), texfaces[1] ); quad( - coord - X * size.x * 0.5f, - Z, Y, glm::vec2(size.z, size.y), - (shading ? do_tint(0.9f) * tint : tint), - glm::vec3(1.0f), texfaces[1] + coord - X * size.x * 0.5f, + Z, Y, -X, glm::vec2(size.z, size.y), + (shading ? do_tint(0.9f) * tint : tint), + glm::vec3(1.0f), texfaces[1] ); } diff --git a/src/graphics/render/MainBatch.hpp b/src/graphics/render/MainBatch.hpp index 2ca1d3d0..fae9f904 100644 --- a/src/graphics/render/MainBatch.hpp +++ b/src/graphics/render/MainBatch.hpp @@ -18,13 +18,15 @@ struct MainBatchVertex { glm::vec3 position; glm::vec2 uv; glm::vec3 tint; - std::array color; + std::array color; + std::array normal; static constexpr VertexAttribute ATTRIBUTES[] = { {VertexAttribute::Type::FLOAT, false, 3}, {VertexAttribute::Type::FLOAT, false, 2}, {VertexAttribute::Type::FLOAT, false, 3}, {VertexAttribute::Type::UNSIGNED_BYTE, true, 4}, + {VertexAttribute::Type::UNSIGNED_BYTE, true, 4}, {{}, 0}}; }; @@ -61,7 +63,9 @@ public: const glm::vec3& pos, const glm::vec2& uv, const glm::vec4& light, - const glm::vec3& tint + const glm::vec3& tint, + const glm::vec3& normal, + float emission ) { MainBatchVertex* buffer = this->buffer.get(); buffer[index].position = pos; @@ -72,6 +76,11 @@ public: buffer[index].color[1] = static_cast(light.g * 255); buffer[index].color[2] = static_cast(light.b * 255); buffer[index].color[3] = static_cast(light.a * 255); + + buffer[index].normal[0] = static_cast(normal.x * 128 + 127); + buffer[index].normal[1] = static_cast(normal.y * 128 + 127); + buffer[index].normal[2] = static_cast(normal.z * 128 + 127); + buffer[index].normal[3] = static_cast(emission * 255); index++; } @@ -79,48 +88,62 @@ public: const glm::vec3& pos, const glm::vec3& right, const glm::vec3& up, + const glm::vec3& normal, const glm::vec2& size, const glm::vec4& light, const glm::vec3& tint, - const UVRegion& subregion + const UVRegion& subregion, + float emission = 0.0f ) { prepare(6); vertex( pos - right * size.x * 0.5f - up * size.y * 0.5f, {subregion.u1, subregion.v1}, light, - tint + tint, + normal, + emission ); vertex( pos + right * size.x * 0.5f - up * size.y * 0.5f, {subregion.u2, subregion.v1}, light, - tint + tint, + normal, + emission ); vertex( pos + right * size.x * 0.5f + up * size.y * 0.5f, {subregion.u2, subregion.v2}, light, - tint + tint, + normal, + emission ); vertex( pos - right * size.x * 0.5f - up * size.y * 0.5f, {subregion.u1, subregion.v1}, light, - tint + tint, + normal, + emission ); vertex( pos + right * size.x * 0.5f + up * size.y * 0.5f, {subregion.u2, subregion.v2}, light, - tint + tint, + normal, + emission ); vertex( pos - right * size.x * 0.5f + up * size.y * 0.5f, {subregion.u1, subregion.v2}, light, - tint + tint, + normal, + emission ); } diff --git a/src/graphics/render/ModelBatch.cpp b/src/graphics/render/ModelBatch.cpp index 08deb7bd..61123232 100644 --- a/src/graphics/render/ModelBatch.cpp +++ b/src/graphics/render/ModelBatch.cpp @@ -57,12 +57,14 @@ ModelBatch::ModelBatch( ModelBatch::~ModelBatch() = default; -void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix, - const glm::mat3& rotation, glm::vec3 tint, - const texture_names_map* varTextures, - bool backlight) { - - +void ModelBatch::draw( + const model::Mesh& mesh, + const glm::mat4& matrix, + const glm::mat3& rotation, + glm::vec3 tint, + const texture_names_map* varTextures, + bool backlight +) { setTexture(mesh.texture, varTextures); size_t vcount = mesh.vertices.size(); const auto& vertexData = mesh.vertices.data(); @@ -78,12 +80,19 @@ void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix, for (size_t j = 0; j < 3; j++) { const auto vert = vertexData[i * 3 + j]; float d = 1.0f; + auto norm = rotation * vert.normal; if (mesh.lighting) { - auto norm = rotation * vert.normal; d = glm::dot(norm, SUN_VECTOR); d = 0.8f + d * 0.2f; } - batch->vertex(matrix * glm::vec4(vert.coord, 1.0f), vert.uv, lights*d, tint); + batch->vertex( + matrix * glm::vec4(vert.coord, 1.0f), + vert.uv, + lights * d, + tint, + norm, + mesh.lighting ? 0.0f : 1.0f + ); } } } diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index 4c6a913e..4788d0f4 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -117,7 +117,6 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { light *= 0.9f + (particle.random % 100) * 0.001f; } - glm::vec3 localRight = right; glm::vec3 localUp = preset.globalUpVector ? glm::vec3(0, 1, 0) : up; float angle = particle.angle; @@ -134,10 +133,12 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { particle.position, localRight, localUp, + -camera.front, preset.size * scale, light, glm::vec3(1.0f), - particle.region + particle.region, + preset.lighting ? 0.0f : 1.0f ); if (particle.lifetime <= 0.0f) { iter = vec.erase(iter); diff --git a/src/graphics/render/PrecipitationRenderer.cpp b/src/graphics/render/PrecipitationRenderer.cpp index 60f2c026..42dc3115 100644 --- a/src/graphics/render/PrecipitationRenderer.cpp +++ b/src/graphics/render/PrecipitationRenderer.cpp @@ -146,6 +146,7 @@ void PrecipitationRenderer::render( pos, face.right, {0, 1, 0}, + glm::cross(glm::vec3(0, 1, 0), face.right), FACE_SIZE, light_at(chunks, pos.x, y, pos.z), glm::vec3(1.0f), diff --git a/src/graphics/render/Skybox.cpp b/src/graphics/render/Skybox.cpp index d78b3917..eb90feef 100644 --- a/src/graphics/render/Skybox.cpp +++ b/src/graphics/render/Skybox.cpp @@ -15,11 +15,10 @@ #include #include #include +#include #include -#ifndef M_PI -#define M_PI 3.141592 -#endif // M_PI +using namespace advanced_pipeline; const int STARS_COUNT = 3000; const int STARS_SEED = 632; @@ -46,18 +45,28 @@ Skybox::Skybox(uint size, Shader& shader) mesh = std::make_unique>(vertices, 6); - sprites.push_back(skysprite { + sprites.push_back(SkySprite { "misc/moon", - glm::pi()*0.5f, + glm::pi() * 0.5f, 4.0f, - false + false, + glm::pi() * 0.25f, + }); + + sprites.push_back(SkySprite { + "misc/moon_flare", + glm::pi() * 0.5f, + 0.5f, + false, + glm::pi() * 0.25f, }); - sprites.push_back(skysprite { + sprites.push_back(SkySprite { "misc/sun", - glm::pi()*1.5f, + glm::pi() * 1.5f, 4.0f, - true + true, + glm::pi() * 0.25f, }); } @@ -69,9 +78,11 @@ void Skybox::drawBackground( auto backShader = assets.get("background"); backShader->use(); backShader->uniformMatrix("u_view", camera.getView(false)); - backShader->uniform1f("u_zoom", camera.zoom*camera.getFov()/(M_PI*0.5f)); + backShader->uniform1f( + "u_zoom", camera.zoom * camera.getFov() / glm::half_pi() + ); backShader->uniform1f("u_ar", float(width)/float(height)); - backShader->uniform1i("u_cubemap", 1); + backShader->uniform1i("u_skybox", 1); bind(); mesh->draw(); unbind(); @@ -80,20 +91,29 @@ void Skybox::drawBackground( void Skybox::drawStars(float angle, float opacity) { batch3d->texture(nullptr); random.setSeed(STARS_SEED); + + glm::mat4 rotation = glm::rotate( + glm::mat4(1.0f), + -angle + glm::pi() * 0.5f, + glm::vec3(0, 0, -1) + ); + rotation = glm::rotate(rotation, sunAltitude, glm::vec3(1, 0, 0)); + + float depth = 1e3; for (int i = 0; i < STARS_COUNT; i++) { float rx = (random.randFloat()) - 0.5f; float ry = (random.randFloat()) - 0.5f; - float z = (random.randFloat()) - 0.5f; - float x = rx * std::sin(angle) + ry * -std::cos(angle); - float y = rx * std::cos(angle) + ry * std::sin(angle); + float rz = (random.randFloat()) - 0.5f; + + glm::vec3 pos = glm::vec4(rx, ry, rz, 1) * rotation; float sopacity = random.randFloat(); - if (y < 0.0f) + if (pos.y < 0.0f) continue; - sopacity *= (0.2f+std::sqrt(std::cos(angle))*0.5f) - 0.05f; + sopacity *= (0.2f + std::sqrt(std::cos(angle)) * 0.5f) - 0.05f; glm::vec4 tint (1,1,1, sopacity * opacity); - batch3d->point(glm::vec3(x, y, z), tint); + batch3d->point(pos * depth, tint); } batch3d->flushPoints(); } @@ -107,6 +127,8 @@ void Skybox::draw( { const glm::uvec2& viewport = pctx.getViewport(); + glActiveTexture(GL_TEXTURE0); + drawBackground(camera, assets, viewport.x, viewport.y); DrawContext ctx = pctx.sub(); @@ -119,24 +141,32 @@ void Skybox::draw( batch3d->begin(); float angle = daytime * glm::pi() * 2.0f; - float opacity = glm::pow(1.0f-fog, 7.0f); + float opacity = glm::pow(1.0f - fog, 7.0f); + float depthScale = 1e3; for (auto& sprite : sprites) { batch3d->texture(assets.get(sprite.texture)); - float sangle = daytime * glm::pi()*2.0 + sprite.phase; - float distance = sprite.distance; + float sangle = daytime * glm::pi() * 2.0 + sprite.phase; + float distance = sprite.distance * depthScale; - glm::vec3 pos(-std::cos(sangle)*distance, std::sin(sangle)*distance, 0); - glm::vec3 up(-std::sin(-sangle), std::cos(-sangle), 0.0f); + glm::mat4 rotation = glm::rotate( + glm::mat4(1.0f), + -sangle + glm::pi() * 0.5f, + glm::vec3(0, 0, -1) + ); + rotation = glm::rotate(rotation, sprite.altitude, glm::vec3(1, 0, 0)); + glm::vec3 pos = glm::vec4(0, distance, 0, 1) * rotation; + glm::vec3 up = glm::vec4(depthScale, 0, 0, 1) * rotation; + glm::vec3 right = glm::vec4(0, 0, depthScale, 1) * rotation; glm::vec4 tint (1,1,1, opacity); if (!sprite.emissive) { - tint *= 0.6f+std::cos(angle)*0.4; + tint *= 0.6f + std::cos(angle)*0.4; } - batch3d->sprite(pos, glm::vec3(0, 0, 1), + batch3d->sprite(pos, right, up, 1, 1, UVRegion(), tint); } - + batch3d->flush(); drawStars(angle, opacity); } @@ -153,12 +183,22 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality) assert(cubemap != nullptr); ready = true; - glActiveTexture(GL_TEXTURE1); + glActiveTexture(GL_TEXTURE0 + TARGET_SKYBOX); cubemap->bind(); shader.use(); - t *= glm::pi()*2.0f; + t *= glm::two_pi(); lightDir = glm::normalize(glm::vec3(sin(t), -cos(t), 0.0f)); + + float sunAngle = glm::radians((t / glm::two_pi() - 0.25f) * 360.0f); + float x = -glm::cos(sunAngle + glm::pi() * 0.5f) * glm::radians(sunAltitude); + float y = sunAngle - glm::pi() * 0.5f; + float z = glm::radians(0.0f); + rotation = glm::rotate(glm::mat4(1.0f), y, glm::vec3(0, 1, 0)); + rotation = glm::rotate(rotation, x, glm::vec3(1, 0, 0)); + rotation = glm::rotate(rotation, z, glm::vec3(0, 0, 1)); + lightDir = glm::vec3(rotation * glm::vec4(0, 0, -1, 1)); + shader.uniform1i("u_quality", quality); shader.uniform1f("u_mie", mie); shader.uniform1f("u_fog", mie - 1.0f); @@ -223,13 +263,13 @@ void Skybox::refreshFace(uint face, Cubemap* cubemap) { } void Skybox::bind() const { - glActiveTexture(GL_TEXTURE1); + glActiveTexture(GL_TEXTURE0 + TARGET_SKYBOX); fbo->getTexture()->bind(); glActiveTexture(GL_TEXTURE0); } void Skybox::unbind() const { - glActiveTexture(GL_TEXTURE1); + glActiveTexture(GL_TEXTURE0 + TARGET_SKYBOX); fbo->getTexture()->unbind(); glActiveTexture(GL_TEXTURE0); } diff --git a/src/graphics/render/Skybox.hpp b/src/graphics/render/Skybox.hpp index f469b155..f2b81876 100644 --- a/src/graphics/render/Skybox.hpp +++ b/src/graphics/render/Skybox.hpp @@ -25,11 +25,12 @@ struct SkyboxVertex { {{}, 0}}; }; -struct skysprite { +struct SkySprite { std::string texture; float phase; float distance; bool emissive; + float altitude; }; class Skybox { @@ -42,11 +43,13 @@ class Skybox { std::unique_ptr> mesh; std::unique_ptr batch3d; - std::vector sprites; + std::vector sprites; int frameid = 0; float prevMie = -1.0f; float prevT = -1.0f; + float sunAltitude = 45.0f; + glm::mat4 rotation; void drawStars(float angle, float opacity); void drawBackground( diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 20640a78..1e77272b 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -11,6 +11,7 @@ #include "assets/assets_util.hpp" #include "content/Content.hpp" #include "engine/Engine.hpp" +#include "coders/GLSLExtension.hpp" #include "frontend/LevelFrontend.hpp" #include "frontend/ContentGfxCache.hpp" #include "items/Inventory.hpp" @@ -26,7 +27,6 @@ #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" -#include "window/Camera.hpp" #include "window/Window.hpp" #include "world/Level.hpp" #include "world/LevelEvents.hpp" @@ -38,9 +38,12 @@ #include "graphics/core/LineBatch.hpp" #include "graphics/core/Mesh.hpp" #include "graphics/core/PostProcessing.hpp" +#include "graphics/core/Framebuffer.hpp" #include "graphics/core/Shader.hpp" #include "graphics/core/Texture.hpp" #include "graphics/core/Font.hpp" +#include "graphics/core/ShadowMap.hpp" +#include "graphics/core/GBuffer.hpp" #include "BlockWrapsRenderer.hpp" #include "ParticlesRenderer.hpp" #include "PrecipitationRenderer.hpp" @@ -52,8 +55,12 @@ #include "Emitter.hpp" #include "TextNote.hpp" +using namespace advanced_pipeline; + inline constexpr size_t BATCH3D_CAPACITY = 4096; inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000; +inline constexpr GLenum TEXTURE_MAIN = GL_TEXTURE0; +inline constexpr int MIN_SHADOW_MAP_RES = 512; bool WorldRenderer::showChunkBorders = false; bool WorldRenderer::showEntitiesDebug = false; @@ -119,13 +126,37 @@ void WorldRenderer::setupWorldShader( shader.uniform1f("u_fogFactor", fogFactor); shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get()); shader.uniform1i("u_debugLights", lightsDebug); + shader.uniform1i("u_debugNormals", false); shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity()); shader.uniform1f("u_weatherFogDencity", weather.fogDencity()); shader.uniform1f("u_weatherFogCurve", weather.fogCurve()); shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime); shader.uniform2f("u_lightDir", skybox->getLightDir()); shader.uniform3f("u_cameraPos", camera.position); - shader.uniform1i("u_cubemap", 1); + shader.uniform1i("u_skybox", 1); + shader.uniform1i("u_enableShadows", shadows); + + if (shadows) { + const auto& worldInfo = level.getWorld()->getInfo(); + float cloudsIntensity = glm::max(worldInfo.fog, weather.clouds()); + shader.uniform1i("u_screen", 0); + 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_TEXTURE0 + TARGET_SHADOWS0); + shader.uniform1i("u_shadows[0]", TARGET_SHADOWS0); + glBindTexture(GL_TEXTURE_2D, shadowMap->getDepthMap()); + + glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS1); + shader.uniform1i("u_shadows[1]", TARGET_SHADOWS1); + glBindTexture(GL_TEXTURE_2D, wideShadowMap->getDepthMap()); + + glActiveTexture(TEXTURE_MAIN); + } auto indices = level.content.getIndices(); // Light emission when an emissive item is chosen @@ -195,22 +226,6 @@ void WorldRenderer::renderLevel( if (!pause) { scripting::on_frontend_render(); } - - setupWorldShader(entityShader, camera, settings, fogFactor); - - std::array weatherInstances {&weather.a, &weather.b}; - for (const auto& weather : weatherInstances) { - float maxIntensity = weather->fall.maxIntensity; - float zero = weather->fall.minOpacity; - float one = weather->fall.maxOpacity; - float t = (weather->intensity * (one - zero)) * maxIntensity + zero; - entityShader.uniform1i("u_alphaClip", weather->fall.opaque); - entityShader.uniform1f("u_opacity", weather->fall.opaque ? t * t : t); - if (weather->intensity > 1.e-3f && !weather->fall.texture.empty()) { - precipitation->render(camera, pause ? 0.0f : delta, *weather); - } - } - skybox->unbind(); } @@ -232,7 +247,7 @@ void WorldRenderer::renderBlockSelection() { const glm::vec3 center = glm::vec3(pos) + hitbox.center(); const glm::vec3 size = hitbox.size(); lineBatch->box( - center, size + glm::vec3(0.01), glm::vec4(0.f, 0.f, 0.f, 0.5f) + center, size + glm::vec3(0.01), glm::vec4(0.f, 0.f, 0.f, 1.0f) ); if (debug) { lineBatch->line( @@ -324,6 +339,79 @@ void WorldRenderer::renderHands( skybox->unbind(); } +void WorldRenderer::generateShadowsMap( + const Camera& camera, + const DrawContext& pctx, + ShadowMap& shadowMap, + Camera& shadowCamera, + float scale +) { + auto& shadowsShader = assets.require("shadows"); + + auto world = level.getWorld(); + const auto& worldInfo = world->getInfo(); + + const auto& settings = engine.getSettings(); + int resolution = shadowMap.getResolution(); + int quality = settings.graphics.shadowsQuality.get(); + float shadowMapScale = 0.32f / (1 << glm::max(0, quality)) * scale; + float shadowMapSize = resolution * shadowMapScale; + + glm::vec3 basePos = glm::floor(camera.position / 4.0f) * 4.0f; + glm::vec3 prevPos = shadowCamera.position; + shadowCamera = Camera( + glm::distance2(prevPos, basePos) > 25.0f ? basePos : prevPos, + shadowMapSize + ); + shadowCamera.near = 0.1f; + shadowCamera.far = 1000.0f; + shadowCamera.perspective = false; + shadowCamera.setAspectRatio(1.0f); + + float t = worldInfo.daytime - 0.25f; + if (t < 0.0f) { + t += 1.0f; + } + t = fmod(t, 0.5f); + + float sunCycleStep = 1.0f / 500.0f; + float sunAngle = glm::radians( + 90.0f - + ((static_cast(t / sunCycleStep)) * sunCycleStep + 0.25f) * 360.0f + ); + float sunAltitude = glm::pi() * 0.25f; + shadowCamera.rotate( + -glm::cos(sunAngle + glm::pi() * 0.5f) * sunAltitude, + sunAngle - glm::pi() * 0.5f, + glm::radians(0.0f) + ); + + shadowCamera.position -= shadowCamera.front * 500.0f; + shadowCamera.position += shadowCamera.up * 0.0f; + shadowCamera.position += camera.front * 0.0f; + + auto view = shadowCamera.getView(); + + auto currentPos = shadowCamera.position; + auto topRight = shadowCamera.right + shadowCamera.up; + auto min = view * glm::vec4(currentPos - topRight * shadowMapSize * 0.5f, 1.0f); + auto max = view * glm::vec4(currentPos + topRight * shadowMapSize * 0.5f, 1.0f); + + shadowCamera.setProjection(glm::ortho(min.x, max.x, min.y, max.y, 0.1f, 1000.0f)); + + { + frustumCulling->update(shadowCamera.getProjView()); + auto sctx = pctx.sub(); + sctx.setDepthTest(true); + sctx.setCullFace(true); + sctx.setViewport({resolution, resolution}); + shadowMap.bind(); + setupWorldShader(shadowsShader, shadowCamera, settings, 0.0f); + chunks->drawChunksShadowsPass(shadowCamera, shadowsShader); + shadowMap.unbind(); + } +} + void WorldRenderer::draw( const DrawContext& pctx, Camera& camera, @@ -332,6 +420,7 @@ void WorldRenderer::draw( float uiDelta, PostProcessing& postProcessing ) { + // TODO: REFACTOR WHOLE RENDER ENGINE float delta = uiDelta * !pause; timer += delta; weather.update(delta); @@ -341,29 +430,75 @@ void WorldRenderer::draw( const auto& vp = pctx.getViewport(); camera.setAspectRatio(vp.x / static_cast(vp.y)); + auto& mainShader = assets.require("main"); + auto& entityShader = assets.require("entity"); + auto& deferredShader = assets.require("deferred_lighting").getShader(); const auto& settings = engine.getSettings(); + + gbufferPipeline = settings.graphics.advancedRender.get(); + int shadowsQuality = settings.graphics.shadowsQuality.get() * gbufferPipeline; + int resolution = MIN_SHADOW_MAP_RES << shadowsQuality; + if (shadowsQuality > 0 && !shadows) { + shadowMap = std::make_unique(resolution); + wideShadowMap = std::make_unique(resolution); + shadows = true; + } else if (shadowsQuality == 0 && shadows) { + shadowMap.reset(); + wideShadowMap.reset(); + shadows = false; + } + + CompileTimeShaderSettings currentSettings { + gbufferPipeline, + shadows, + settings.graphics.ssao.get() && gbufferPipeline + }; + if ( + prevCTShaderSettings.advancedRender != currentSettings.advancedRender || + prevCTShaderSettings.shadows != currentSettings.shadows || + prevCTShaderSettings.ssao != currentSettings.ssao + ) { + Shader::preprocessor->setDefined("ENABLE_SHADOWS", currentSettings.shadows); + Shader::preprocessor->setDefined("ENABLE_SSAO", currentSettings.ssao); + Shader::preprocessor->setDefined("ADVANCED_RENDER", currentSettings.advancedRender); + mainShader.recompile(); + entityShader.recompile(); + deferredShader.recompile(); + prevCTShaderSettings = currentSettings; + } + + if (shadows && shadowMap->getResolution() != resolution) { + shadowMap = std::make_unique(resolution); + wideShadowMap = std::make_unique(resolution); + } + 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; skybox->refresh(pctx, worldInfo.daytime, mie, 4); - const auto& assets = *engine.getAssets(); - auto& linesShader = assets.require("lines"); + chunks->update(); + static int frameid = 0; + if (shadows) { + if (frameid % 2 == 0) { + generateShadowsMap(camera, pctx, *shadowMap, shadowCamera, 1.0f); + } else { + generateShadowsMap(camera, pctx, *wideShadowMap, wideShadowCamera, 3.0f); + } + } + frameid++; + + auto& linesShader = assets.require("lines"); /* World render scope with diegetic HUD included */ { DrawContext wctx = pctx.sub(); - postProcessing.use(wctx); + postProcessing.use(wctx, gbufferPipeline); display::clearDepth(); - // Drawing background sky plane - skybox->draw(pctx, camera, assets, worldInfo.daytime, clouds); - /* Actually world render with depth buffer on */ { DrawContext ctx = wctx.sub(); ctx.setDepthTest(true); @@ -376,19 +511,59 @@ void WorldRenderer::draw( ctx, camera, *lineBatch, linesShader, showChunkBorders ); } - if (player.currentCamera == player.fpCamera) { - renderHands(camera, delta); - } } } - { - DrawContext ctx = wctx.sub(); - texts->render(ctx, camera, settings, hudVisible, true); - } - renderBlockOverlay(wctx); + texts->render(pctx, camera, settings, hudVisible, true); } + skybox->bind(); + float fogFactor = + 15.0f / static_cast(settings.chunks.loadDistance.get() - 2); + if (gbufferPipeline) { + deferredShader.use(); + setupWorldShader(deferredShader, camera, settings, fogFactor); + postProcessing.renderDeferredShading(pctx, assets, timer, camera); + } + { + DrawContext ctx = pctx.sub(); + ctx.setDepthTest(true); + if (gbufferPipeline) { + postProcessing.bindDepthBuffer(); + } else { + postProcessing.getFramebuffer()->bind(); + } + // Drawing background sky plane + skybox->draw(ctx, camera, assets, worldInfo.daytime, clouds); - postProcessing.render(pctx, assets, timer); + entityShader.use(); + setupWorldShader(entityShader, camera, settings, fogFactor); + + std::array weatherInstances {&weather.a, &weather.b}; + for (const auto& weather : weatherInstances) { + float maxIntensity = weather->fall.maxIntensity; + float zero = weather->fall.minOpacity; + float one = weather->fall.maxOpacity; + float t = (weather->intensity * (one - zero)) * maxIntensity + zero; + entityShader.uniform1i("u_alphaClip", weather->fall.opaque); + entityShader.uniform1f("u_opacity", weather->fall.opaque ? t * t : t); + if (weather->intensity > 1.e-3f && !weather->fall.texture.empty()) { + precipitation->render(camera, pause ? 0.0f : delta, *weather); + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + postProcessing.render(pctx, assets, timer, camera); + + skybox->unbind(); + if (player.currentCamera == player.fpCamera) { + DrawContext ctx = pctx.sub(); + ctx.setDepthTest(true); + ctx.setCullFace(true); + renderHands(camera, delta); + } + renderBlockOverlay(pctx); + + glActiveTexture(GL_TEXTURE0); } void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) { diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index fbfd4998..5d33f8cd 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -9,6 +9,7 @@ #include "presets/WeatherPreset.hpp" #include "world/Weather.hpp" +#include "window/Camera.hpp" class Level; class Player; @@ -30,8 +31,16 @@ class PostProcessing; class DrawContext; class ModelBatch; class Assets; +class ShadowMap; +class GBuffer; struct EngineSettings; +struct CompileTimeShaderSettings { + bool advancedRender = false; + bool shadows = false; + bool ssao = false; +}; + class WorldRenderer { Engine& engine; const Level& level; @@ -44,11 +53,19 @@ class WorldRenderer { std::unique_ptr guides; std::unique_ptr chunks; std::unique_ptr skybox; + std::unique_ptr shadowMap; + std::unique_ptr wideShadowMap; Weather weather {}; + Camera shadowCamera; + Camera wideShadowCamera; float timer = 0.0f; bool debug = false; bool lightsDebug = false; + bool gbufferPipeline = false; + bool shadows = false; + + CompileTimeShaderSettings prevCTShaderSettings {}; /// @brief Render block selection lines void renderBlockSelection(); @@ -70,6 +87,14 @@ class WorldRenderer { const EngineSettings& settings, float fogFactor ); + + void generateShadowsMap( + const Camera& camera, + const DrawContext& pctx, + ShadowMap& shadowMap, + Camera& shadowCamera, + float scale + ); public: std::unique_ptr particles; std::unique_ptr texts; diff --git a/src/graphics/render/commons.hpp b/src/graphics/render/commons.hpp index cfc2f16c..905e34cc 100644 --- a/src/graphics/render/commons.hpp +++ b/src/graphics/render/commons.hpp @@ -14,11 +14,13 @@ struct ChunkVertex { glm::vec3 position; glm::vec2 uv; std::array color; + std::array normal; static constexpr VertexAttribute ATTRIBUTES[] = { {VertexAttribute::Type::FLOAT, false, 3}, {VertexAttribute::Type::FLOAT, false, 2}, {VertexAttribute::Type::UNSIGNED_BYTE, true, 4}, + {VertexAttribute::Type::UNSIGNED_BYTE, true, 4}, {{}, 0}}; }; diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index dc379a68..0cea8a66 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -38,6 +38,8 @@ GUI::GUI(Engine& engine) std::make_unique(glm::vec3(), engine.getWindow().getSize().y); uicamera->perspective = false; uicamera->flipped = true; + uicamera->near = -1.0f; + uicamera->far = 1.0f; menu = std::make_shared(*this); menu->setId("menu"); diff --git a/src/io/settings_io.cpp b/src/io/settings_io.cpp index 39df07f0..000e965b 100644 --- a/src/io/settings_io.cpp +++ b/src/io/settings_io.cpp @@ -74,6 +74,9 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) { builder.add("chunk-max-vertices", &settings.graphics.chunkMaxVertices); builder.add("chunk-max-vertices-dense", &settings.graphics.chunkMaxVerticesDense); builder.add("chunk-max-renderers", &settings.graphics.chunkMaxRenderers); + builder.add("advanced-render", &settings.graphics.advancedRender); + builder.add("ssao", &settings.graphics.ssao); + builder.add("shadows-quality", &settings.graphics.shadowsQuality); builder.section("ui"); builder.add("language", &settings.ui.language); diff --git a/src/logic/scripting/lua/libs/libposteffects.cpp b/src/logic/scripting/lua/libs/libposteffects.cpp index ccdb34aa..f4e1e252 100644 --- a/src/logic/scripting/lua/libs/libposteffects.cpp +++ b/src/logic/scripting/lua/libs/libposteffects.cpp @@ -17,7 +17,10 @@ static int l_set_effect(lua::State* L) { size_t index = static_cast(lua::tointeger(L, 1)); auto name = lua::require_string(L, 2); auto& assets = *engine->getAssets(); - auto effect = std::make_shared(assets.require(name)); + auto effect = assets.getShared(name); + if (effect == nullptr) { + throw std::runtime_error(std::string("post-effect '") + name + "' not found"); + } post_processing->setEffect(index, std::move(effect)); return 0; } @@ -57,6 +60,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.data()), + reinterpret_cast(data.data() + data.size()) + ); + effect->setArray(key, std::move(buffer)); + return 0; +} + const luaL_Reg posteffectslib[] = { {"index", lua::wrap}, {"set_effect", lua::wrap}, @@ -64,5 +83,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/settings.hpp b/src/settings.hpp index 53ebf5f9..e8826800 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -75,6 +75,12 @@ struct GraphicsSettings { IntegerSetting chunkMaxVerticesDense {800'000, 0, 8'000'000}; /// @brief Limit of chunk renderers count IntegerSetting chunkMaxRenderers {6, -4, 32}; + /// @brief Advanced render pipeline + FlagSetting advancedRender {true}; + /// @brief Screen space ambient occlusion + FlagSetting ssao {true}; + /// @brief Shadows quality + IntegerSetting shadowsQuality {0, 0, 3}; }; struct DebugSettings { diff --git a/src/window/Camera.cpp b/src/window/Camera.cpp index 14939740..fb23676d 100644 --- a/src/window/Camera.cpp +++ b/src/window/Camera.cpp @@ -28,12 +28,15 @@ void Camera::rotate(float x, float y, float z) { } glm::mat4 Camera::getProjection() const { + if (projset) { + return projection; + } if (perspective) { return glm::perspective(fov * zoom, ar, near, far); } else if (flipped) { - return glm::ortho(0.0f, fov * ar, fov, 0.0f); + return glm::ortho(0.0f, fov * ar, fov, 0.0f, near, far); } else { - return glm::ortho(0.0f, fov * ar, 0.0f, fov); + return glm::ortho(0.0f, fov * ar, 0.0f, fov, near, far); } } @@ -45,7 +48,8 @@ glm::mat4 Camera::getView(bool pos) const { if (perspective) { return glm::lookAt(camera_pos, camera_pos + front, up); } else { - return glm::translate(glm::mat4(1.0f), camera_pos); + return glm::lookAt(camera_pos, camera_pos + front, up); + //return glm::translate(glm::mat4(1.0f), camera_pos); } } @@ -61,6 +65,11 @@ float Camera::getFov() const { return fov; } +void Camera::setProjection(const glm::mat4& matrix) { + projection = matrix; + projset = true; +} + float Camera::getAspectRatio() const { return ar; } diff --git a/src/window/Camera.hpp b/src/window/Camera.hpp index ba4a5331..677e0ede 100644 --- a/src/window/Camera.hpp +++ b/src/window/Camera.hpp @@ -19,7 +19,10 @@ public: bool perspective = true; bool flipped = false; float near = 0.05f; - float far = 1500.0f; + float far = 1e4f; + + bool projset = false; + glm::mat4 projection; Camera() { updateVectors(); @@ -36,6 +39,8 @@ public: void setFov(float fov); float getFov() const; + void setProjection(const glm::mat4& matrix); + float getAspectRatio() const; void setAspectRatio(float ar); }; diff --git a/src/window/detail/GLFWWindow.cpp b/src/window/detail/GLFWWindow.cpp index 3d7ac26e..9a99405d 100644 --- a/src/window/detail/GLFWWindow.cpp +++ b/src/window/detail/GLFWWindow.cpp @@ -519,6 +519,7 @@ public: } std::unique_ptr takeScreenshot() override { + glBindFramebuffer(GL_FRAMEBUFFER, 0); auto data = std::make_unique(size.x * size.y * 3); glPixelStorei(GL_PACK_ALIGNMENT, 1); glReadPixels(0, 0, size.x, size.y, GL_RGB, GL_UNSIGNED_BYTE, data.get()); 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; };