Merge pull request #535 from MihailRis/update-gfx-pipeline

Advanced graphics mode
This commit is contained in:
MihailRis 2025-07-13 00:42:37 +03:00 committed by GitHub
commit 976425a968
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
70 changed files with 1865 additions and 343 deletions

View File

@ -7,7 +7,7 @@
"torch_side", "torch_side",
"torch_side" "torch_side"
], ],
"emission": [13, 13, 12], "emission": [13, 10, 2],
"model": "aabb", "model": "aabb",
"hitbox": [0.4375, 0.0, 0.4375, 0.125, 0.5, 0.125], "hitbox": [0.4375, 0.0, 0.4375, 0.125, 0.5, 0.125],
"light-passing": true, "light-passing": true,

View File

@ -14,6 +14,7 @@
], ],
"textures": [ "textures": [
"misc/moon", "misc/moon",
"misc/moon_flare",
"misc/sun", "misc/sun",
"gui/crosshair" "gui/crosshair"
] ]

View File

@ -42,4 +42,7 @@ function on_open()
create_setting("graphics.gamma", "Gamma", 0.05, "", "graphics.gamma.tooltip") create_setting("graphics.gamma", "Gamma", 0.05, "", "graphics.gamma.tooltip")
create_checkbox("graphics.backlight", "Backlight", "graphics.backlight.tooltip") create_checkbox("graphics.backlight", "Backlight", "graphics.backlight.tooltip")
create_checkbox("graphics.dense-render", "Dense blocks render", "graphics.dense-render.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 end

View File

@ -6,10 +6,19 @@
"lines", "lines",
"entity", "entity",
"background", "background",
"skybox_gen" "skybox_gen",
"shadows"
], ],
"post-effects": [ "post-effects": [
"default" "default",
{
"name": "ssao",
"advanced": true
},
{
"name": "deferred_lighting",
"advanced": true
}
], ],
"textures": [ "textures": [
"gui/menubg", "gui/menubg",

View File

@ -4,5 +4,8 @@
"third-person-front", "third-person-front",
"third-person-back", "third-person-back",
"cinematic" "cinematic"
],
"post-effect-slot": [
"default"
] ]
} }

View File

@ -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() function on_hud_open()
input.add_callback("player.pick", function () input.add_callback("player.pick", function ()
if hud.is_paused() or hud.is_inventory_open() then 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) player.set_vel(pid, 0, 1, 0)
end end
end) end)
configure_SSAO()
end end

View File

@ -1,9 +1,14 @@
in vec3 v_coord; 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(){ void main(){
vec3 dir = normalize(v_coord); vec3 dir = normalize(v_coord) * 1e6;
f_color = texture(u_cubemap, dir); 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);
} }

View File

@ -7,6 +7,6 @@ uniform float u_ar;
uniform float u_zoom; uniform float u_zoom;
void main(){ void main(){
v_coord = (vec4(v_position*vec2(u_ar, 1.0f)*u_zoom, -1.0, 1.0) * u_view).xyz; 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); gl_Position = vec4(v_position, 1.0 - 1e-6, 1.0);
} }

View File

@ -2,8 +2,22 @@ in vec2 v_uv;
out vec4 f_color; out vec4 f_color;
uniform sampler2D u_screen; 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 ivec2 u_screenSize;
uniform float u_intensity; 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__> #include <__effect__>

View File

@ -0,0 +1,35 @@
#include <shadows>
#include <fog>
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);
}

View File

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

View File

@ -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 vec2 a_texCoord;
in vec3 a_dir; in vec3 a_dir;
in float a_fog; in vec3 a_normal;
out vec4 f_color; 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 sampler2D u_texture0;
uniform samplerCube u_cubemap; uniform samplerCube u_skybox;
uniform vec3 u_fogColor; uniform vec3 u_fogColor;
uniform float u_fogFactor; uniform float u_fogFactor;
uniform float u_fogCurve; uniform float u_fogCurve;
uniform bool u_alphaClip; uniform bool u_alphaClip;
uniform vec3 u_sunDir;
#include <shadows>
void main() { void main() {
vec3 fogColor = texture(u_cubemap, a_dir).rgb; vec4 texColor = texture(u_texture0, a_texCoord);
vec4 tex_color = texture(u_texture0, a_texCoord); float alpha = a_color.a * texColor.a;
float alpha = a_color.a * tex_color.a;
// anyway it's any alpha-test alternative required // 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; 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_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);
} }

View File

@ -4,11 +4,18 @@ layout (location = 0) in vec3 v_position;
layout (location = 1) in vec2 v_texCoord; layout (location = 1) in vec2 v_texCoord;
layout (location = 2) in vec3 v_color; layout (location = 2) in vec3 v_color;
layout (location = 3) in vec4 v_light; layout (location = 3) in vec4 v_light;
layout (location = 4) in vec4 v_normal;
out vec4 a_color; out float a_distance;
out vec2 a_texCoord;
out float a_fog; out float a_fog;
out vec2 a_texCoord;
out vec3 a_dir; 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_model;
uniform mat4 u_proj; uniform mat4 u_proj;
@ -16,36 +23,40 @@ uniform mat4 u_view;
uniform vec3 u_cameraPos; uniform vec3 u_cameraPos;
uniform float u_gamma; uniform float u_gamma;
uniform float u_opacity; uniform float u_opacity;
uniform float u_fogFactor; uniform float u_timer;
uniform float u_fogCurve; uniform samplerCube u_skybox;
uniform float u_weatherFogOpacity;
uniform float u_weatherFogDencity;
uniform float u_weatherFogCurve;
uniform samplerCube u_cubemap;
uniform vec3 u_torchlightColor; uniform vec3 u_torchlightColor;
uniform float u_torchlightDistance; uniform float u_torchlightDistance;
#include <lighting>
#include <fog>
void main() { void main() {
vec4 modelpos = u_model * vec4(v_position, 1.0); a_modelpos = u_model * vec4(v_position, 1.0);
vec3 pos3d = modelpos.xyz - u_cameraPos; vec3 pos3d = a_modelpos.xyz - u_cameraPos;
modelpos.xyz = apply_planet_curvature(modelpos.xyz, pos3d); 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; vec3 light = v_light.rgb;
float torchlight = max(0.0, 1.0-distance(u_cameraPos, modelpos.xyz) / float torchlight = calc_torch_light(a_realnormal, a_modelpos.xyz);
u_torchlightDistance);
light += torchlight * u_torchlightColor; 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_texCoord = v_texCoord;
a_dir = modelpos.xyz - u_cameraPos; a_dir = a_modelpos.xyz - u_cameraPos;
vec3 skyLightColor = pick_sky_color(u_cubemap); vec3 skyLightColor = pick_sky_color(u_skybox);
a_color.rgb = max(a_color.rgb, skyLightColor.rgb*v_light.a) * v_color; a_color.rgb = max(a_color.rgb, skyLightColor.rgb * v_light.a) * v_color;
a_color.a = u_opacity; a_color.a = u_opacity;
float dist = length(u_view * u_model * vec4(pos3d * FOG_POS_SCALE, 0.0)); mat4 viewmodel = u_view * u_model;
float depth = (dist / 256.0); a_distance = length(viewmodel * vec4(pos3d, 0.0));
a_fog = min(1.0, max(pow(depth * u_fogFactor, u_fogCurve), a_fog = calc_fog(length(viewmodel * vec4(pos3d * FOG_POS_SCALE, 0.0)) / 256.0);
min(pow(depth * u_weatherFogDencity, u_weatherFogCurve), u_weatherFogOpacity))); a_emission = v_normal.w;
gl_Position = u_proj * u_view * modelpos;
vec4 viewmodelpos = u_view * a_modelpos;
a_position = viewmodelpos.xyz;
gl_Position = u_proj * viewmodelpos;
} }

19
res/shaders/lib/fog.glsl Normal file
View File

@ -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_

View File

@ -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_

View File

@ -0,0 +1,47 @@
#ifndef SHADOWS_GLSL_
#define SHADOWS_GLSL_
#include <constants>
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_

View File

@ -1,20 +1,34 @@
in vec4 a_color; layout (location = 0) out vec4 f_color;
in vec2 a_texCoord; 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 float a_fog;
in vec2 a_texCoord;
in vec3 a_dir; 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 sampler2D u_texture0;
uniform samplerCube u_cubemap; uniform samplerCube u_skybox;
uniform vec3 u_sunDir;
// flags
uniform bool u_alphaClip; uniform bool u_alphaClip;
uniform bool u_debugLights; uniform bool u_debugLights;
uniform bool u_debugNormals;
#include <shadows>
void main() { void main() {
vec3 fogColor = texture(u_cubemap, a_dir).rgb; vec4 texColor = texture(u_texture0, a_texCoord);
vec4 tex_color = texture(u_texture0, a_texCoord); float alpha = texColor.a;
if (u_debugLights)
tex_color.rgb = vec3(1.0);
float alpha = a_color.a * tex_color.a;
if (u_alphaClip) { if (u_alphaClip) {
if (alpha < 0.2f) if (alpha < 0.2f)
discard; discard;
@ -23,6 +37,20 @@ void main() {
if (alpha < 0.002f) if (alpha < 0.002f)
discard; 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_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);
} }

View File

@ -3,48 +3,58 @@
layout (location = 0) in vec3 v_position; layout (location = 0) in vec3 v_position;
layout (location = 1) in vec2 v_texCoord; layout (location = 1) in vec2 v_texCoord;
layout (location = 2) in vec4 v_light; 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_distance;
out float a_fog; out float a_fog;
out vec2 a_texCoord;
out vec3 a_dir; 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_model;
uniform mat4 u_proj; uniform mat4 u_proj;
uniform mat4 u_view; uniform mat4 u_view;
uniform vec3 u_cameraPos; uniform vec3 u_cameraPos;
uniform float u_gamma; 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 float u_timer;
uniform samplerCube u_cubemap; uniform samplerCube u_skybox;
uniform vec3 u_torchlightColor; uniform vec3 u_torchlightColor;
uniform float u_torchlightDistance; uniform float u_torchlightDistance;
#include <lighting>
#include <fog>
void main() { void main() {
vec4 modelpos = u_model * vec4(v_position, 1.0f); a_modelpos = u_model * vec4(v_position, 1.0f);
vec3 pos3d = modelpos.xyz-u_cameraPos; vec3 pos3d = a_modelpos.xyz - u_cameraPos;
modelpos.xyz = apply_planet_curvature(modelpos.xyz, pos3d); 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; vec3 light = v_light.rgb;
float torchlight = max(0.0, 1.0-distance(u_cameraPos, modelpos.xyz) / float torchlight = calc_torch_light(a_realnormal, a_modelpos.xyz);
u_torchlightDistance); a_torchLight = vec4(pow(light + torchlight * u_torchlightColor, vec3(u_gamma)), 1.0f);
light += torchlight * u_torchlightColor;
a_color = vec4(pow(light, vec3(u_gamma)),1.0f);
a_texCoord = v_texCoord; a_texCoord = v_texCoord;
a_dir = modelpos.xyz - u_cameraPos; a_dir = a_modelpos.xyz - u_cameraPos;
vec3 skyLightColor = pick_sky_color(u_cubemap); vec3 skyLightColor = pick_sky_color(u_skybox);
a_color.rgb = max(a_color.rgb, skyLightColor.rgb*v_light.a); a_skyLight = skyLightColor.rgb*v_light.a;
a_distance = length(u_view * u_model * vec4(pos3d * FOG_POS_SCALE, 0.0)); mat4 viewmodel = u_view * u_model;
float depth = (a_distance / 256.0); a_distance = length(viewmodel * vec4(pos3d, 0.0));
a_fog = min(1.0, max(pow(depth * u_fogFactor, u_fogCurve), a_fog = calc_fog(length(viewmodel * vec4(pos3d * FOG_POS_SCALE, 0.0)) / 256.0);
min(pow(depth * u_weatherFogDencity, u_weatherFogCurve), u_weatherFogOpacity))); a_emission = v_normal.w;
gl_Position = u_proj * u_view * modelpos;
vec4 viewmodelpos = u_view * a_modelpos;
a_position = viewmodelpos.xyz;
gl_Position = u_proj * viewmodelpos;
} }

11
res/shaders/shadows.glslf Normal file
View File

@ -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
}

17
res/shaders/shadows.glslv Normal file
View File

@ -0,0 +1,17 @@
#include <commons>
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);
}

View File

@ -268,7 +268,7 @@ void main() {
camera_vector, // the camera vector (ray direction of this pixel) camera_vector, // the camera vector (ray direction of this pixel)
1e12f, // max dist, essentially the scene depth 1e12f, // max dist, essentially the scene depth
vec3(0.0f), // scene color, the color of the current pixel being rendered 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 vec3(40.0*fog), // light intensity, 40 looks nice
PLANET_POS, // position of the planet PLANET_POS, // position of the planet
PLANET_RADIUS, // radius of the planet in meters PLANET_RADIUS, // radius of the planet in meters

View File

@ -98,6 +98,8 @@ settings.V-Sync=Вертикальная Синхронизация
settings.Key=Кнопка settings.Key=Кнопка
settings.Controls Search Mode=Поиск по привязанной кнопки управления settings.Controls Search Mode=Поиск по привязанной кнопки управления
settings.Limit Background FPS=Ограничить фоновую частоту кадров settings.Limit Background FPS=Ограничить фоновую частоту кадров
settings.Advanced render=Продвинутый рендер
settings.Shadows quality=Качество теней
# Управление # Управление
chunks.reload=Перезагрузить Чанки chunks.reload=Перезагрузить Чанки

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -147,14 +147,12 @@ void AssetsLoader::processPreload(
add(tag, path, name); add(tag, path, name);
return; return;
} }
std::shared_ptr<AssetCfg> config = nullptr;
map.at("path").get(path); map.at("path").get(path);
switch (tag) { switch (tag) {
case AssetType::SOUND: { case AssetType::SOUND: {
bool keepPCM = false; bool keepPCM = false;
add(tag, config = std::make_shared<SoundCfg>(map.at("keep-pcm").get(keepPCM));
path,
name,
std::make_shared<SoundCfg>(map.at("keep-pcm").get(keepPCM)));
break; break;
} }
case AssetType::ATLAS: { case AssetType::ATLAS: {
@ -164,13 +162,19 @@ void AssetsLoader::processPreload(
if (typeName == "separate") { if (typeName == "separate") {
type = AtlasType::SEPARATE; type = AtlasType::SEPARATE;
} }
add(tag, path, name, std::make_shared<AtlasCfg>(type)); config = std::make_shared<AtlasCfg>(type);
break;
}
case AssetType::POST_EFFECT: {
bool advanced = false;
map.at("advanced").get(advanced);
config = std::make_shared<PostEffectCfg>(advanced);
break; break;
} }
default: default:
add(tag, path, name);
break; break;
} }
add(tag, path, name, std::move(config));
} }
void AssetsLoader::processPreloadList(AssetType tag, const dv::value& list) { void AssetsLoader::processPreloadList(AssetType tag, const dv::value& list) {

View File

@ -40,8 +40,7 @@ struct LayoutCfg : AssetCfg {
struct SoundCfg : AssetCfg { struct SoundCfg : AssetCfg {
bool keepPCM; bool keepPCM;
SoundCfg(bool keepPCM) : keepPCM(keepPCM) { SoundCfg(bool keepPCM) : keepPCM(keepPCM) {}
}
}; };
enum class AtlasType { enum class AtlasType {
@ -51,8 +50,13 @@ enum class AtlasType {
struct AtlasCfg : AssetCfg { struct AtlasCfg : AssetCfg {
AtlasType type; 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< using aloader_func = std::function<

View File

@ -76,6 +76,14 @@ static auto process_program(const ResPaths& paths, const std::string& filename)
return std::make_pair(vertex, fragment); 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( assetload::postfunc assetload::shader(
AssetsLoader*, AssetsLoader*,
const ResPaths& paths, const ResPaths& paths,
@ -83,21 +91,18 @@ assetload::postfunc assetload::shader(
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& const std::shared_ptr<AssetCfg>&
) { ) {
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 vertexFile = paths.find(filename + ".glslv");
io::path fragmentFile = paths.find(filename + ".glslf"); io::path fragmentFile = paths.find(filename + ".glslf");
std::string vertexSource = std::move(vertex.code);
std::string fragmentSource = std::move(fragment.code);
return [=](auto assets) { return [=](auto assets) {
assets->store( assets->store(
Shader::create( Shader::create(
vertexFile.string(), {vertexFile.string(), vertex},
fragmentFile.string(), {fragmentFile.string(), fragment}
vertexSource,
fragmentSource
), ),
name name
); );
@ -127,13 +132,16 @@ assetload::postfunc assetload::posteffect(
return [=](auto assets) { return [=](auto assets) {
auto program = Shader::create( auto program = Shader::create(
effectFile.string(), {effectFile.string(), vertexSource},
effectFile.string(), {effectFile.string(), fragmentSource}
vertexSource,
fragmentSource
); );
bool advanced = false;
if (settings) {
advanced = dynamic_cast<const PostEffectCfg*>(settings.get())->advanced;
}
assets->store( assets->store(
std::make_shared<PostEffect>(std::move(program), params), name std::make_shared<PostEffect>(advanced, std::move(program), params),
name
); );
}; };
} }

View File

@ -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( inline std::runtime_error parsing_error(
const io::path& file, uint linenum, const std::string& message 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) { static Value default_value_for(Type type) {
switch (type) { switch (type) {
case Type::INT:
return 0;
case Type::FLOAT: case Type::FLOAT:
return 0.0f; return 0.0f;
case Type::VEC2: case Type::VEC2:
@ -156,7 +166,6 @@ public:
} }
bool processVersionDirective() { bool processVersionDirective() {
parsing_warning(filename, line, "removed #version directive");
source_line(ss, line); source_line(ss, line);
skipLine(); skipLine();
return false; return false;
@ -183,6 +192,8 @@ public:
Value parseDefaultValue(Type type, const std::string& name) { Value parseDefaultValue(Type type, const std::string& name) {
switch (type) { switch (type) {
case Type::INT:
return static_cast<int>(parseNumber(1).asInteger());
case Type::FLOAT: case Type::FLOAT:
return static_cast<float>(parseNumber(1).asNumber()); return static_cast<float>(parseNumber(1).asNumber());
case Type::VEC2: case Type::VEC2:
@ -212,8 +223,22 @@ public:
if (params.find(paramName) != params.end()) { if (params.find(paramName) != params.end()) {
throw error("duplicating param " + util::quote(paramName)); throw error("duplicating param " + util::quote(paramName));
} }
skipWhitespace(false); 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); auto defValue = default_value_for(type);
// Parse default value // Parse default value
@ -225,7 +250,7 @@ public:
skipLine(); skipLine();
params[paramName] = PostEffect::Param(type, std::move(defValue)); params[paramName] = PostEffect::Param(type, std::move(defValue), array);
return false; return false;
} }

View File

@ -22,6 +22,7 @@ public:
void define(const std::string& name, std::string value); void define(const std::string& name, std::string value);
void undefine(const std::string& name); void undefine(const std::string& name);
void setDefined(const std::string& name, bool defined);
void addHeader(const std::string& name, ProcessingResult header); void addHeader(const std::string& name, ProcessingResult header);
const ProcessingResult& getHeader(const std::string& name) const; const ProcessingResult& getHeader(const std::string& name) const;

View File

@ -178,6 +178,8 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
uicamera = std::make_unique<Camera>(glm::vec3(), 1); uicamera = std::make_unique<Camera>(glm::vec3(), 1);
uicamera->perspective = false; uicamera->perspective = false;
uicamera->flipped = true; uicamera->flipped = true;
uicamera->near = -1.0f;
uicamera->far = 1.0f;
debugPanel = create_debug_panel( debugPanel = create_debug_panel(
engine, frontend.getLevel(), player, allowDebugCheats engine, frontend.getLevel(), player, allowDebugCheats

View File

@ -22,6 +22,8 @@ MenuScreen::MenuScreen(Engine& engine) : Screen(engine) {
uicamera = uicamera =
std::make_unique<Camera>(glm::vec3(), engine.getWindow().getSize().y); std::make_unique<Camera>(glm::vec3(), engine.getWindow().getSize().y);
uicamera->perspective = false; uicamera->perspective = false;
uicamera->near = -1.0f;
uicamera->far = 1.0f;
uicamera->flipped = true; uicamera->flipped = true;
} }

View File

@ -60,17 +60,17 @@ void Batch2D::vertex(
index++; index++;
} }
void Batch2D::texture(const Texture* new_texture){ void Batch2D::texture(const Texture* newTexture){
if (currentTexture == new_texture) { if (currentTexture == newTexture) {
return; return;
} }
flush(); flush();
currentTexture = new_texture; currentTexture = newTexture;
if (new_texture == nullptr) { if (newTexture == nullptr) {
blank->bind(); blank->bind();
region = blank->getUVRegion(); region = blank->getUVRegion();
} else { } else {
new_texture->bind(); newTexture->bind();
region = currentTexture->getUVRegion(); region = currentTexture->getUVRegion();
} }
} }

View File

@ -13,8 +13,6 @@ namespace {
Batch3D::Batch3D(size_t capacity) Batch3D::Batch3D(size_t capacity)
: capacity(capacity) { : capacity(capacity) {
buffer = std::make_unique<Batch3DVertex[]>(capacity); buffer = std::make_unique<Batch3DVertex[]>(capacity);
mesh = std::make_unique<Mesh<Batch3DVertex>>(buffer.get(), 0); mesh = std::make_unique<Mesh<Batch3DVertex>>(buffer.get(), 0);
index = 0; index = 0;

View File

@ -100,7 +100,7 @@ void DrawContext::setViewport(const glm::uvec2& viewport) {
glViewport(0, 0, viewport.x, viewport.y); glViewport(0, 0, viewport.x, viewport.y);
} }
void DrawContext::setFramebuffer(Framebuffer* fbo) { void DrawContext::setFramebuffer(Bindable* fbo) {
if (this->fbo == fbo) if (this->fbo == fbo)
return; return;
this->fbo = fbo; this->fbo = fbo;

View File

@ -16,7 +16,7 @@ class DrawContext {
glm::uvec2 viewport; glm::uvec2 viewport;
Batch2D* g2d; Batch2D* g2d;
Flushable* flushable = nullptr; Flushable* flushable = nullptr;
Framebuffer* fbo = nullptr; Bindable* fbo = nullptr;
bool depthMask = true; bool depthMask = true;
bool depthTest = false; bool depthTest = false;
bool cullFace = false; bool cullFace = false;
@ -37,7 +37,7 @@ public:
[[nodiscard]] DrawContext sub(Flushable* flushable=nullptr) const; [[nodiscard]] DrawContext sub(Flushable* flushable=nullptr) const;
void setViewport(const glm::uvec2& viewport); void setViewport(const glm::uvec2& viewport);
void setFramebuffer(Framebuffer* fbo); void setFramebuffer(Bindable* fbo);
void setDepthMask(bool flag); void setDepthMask(bool flag);
void setDepthTest(bool flag); void setDepthTest(bool flag);
void setCullFace(bool flag); void setCullFace(bool flag);

View File

@ -2,6 +2,9 @@
#include <GL/glew.h> #include <GL/glew.h>
#include "Texture.hpp" #include "Texture.hpp"
#include "debug/Logger.hpp"
static debug::Logger logger("gl-framebuffer");
Framebuffer::Framebuffer(uint fbo, uint depth, std::unique_ptr<Texture> texture) Framebuffer::Framebuffer(uint fbo, uint depth, std::unique_ptr<Texture> texture)
: fbo(fbo), depth(depth), texture(std::move(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) // Setup color attachment (texture)
texture = create_texture(width, height, format); texture = create_texture(width, height, format);
// Setup depth attachment // Setup depth attachment
glGenRenderbuffers(1, &depth); glGenRenderbuffers(1, &depth);
glBindRenderbuffer(GL_RENDERBUFFER, depth); glBindRenderbuffer(GL_RENDERBUFFER, depth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth); 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); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
@ -67,11 +75,12 @@ void Framebuffer::resize(uint width, uint height) {
this->width = width; this->width = width;
this->height = height; this->height = height;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindRenderbuffer(GL_RENDERBUFFER, depth); glBindRenderbuffer(GL_RENDERBUFFER, depth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0); glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
texture = create_texture(width, height, format); texture = create_texture(width, height, format);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
@ -87,3 +96,7 @@ uint Framebuffer::getWidth() const {
uint Framebuffer::getHeight() const { uint Framebuffer::getHeight() const {
return height; return height;
} }
uint Framebuffer::getFBO() const {
return fbo;
}

View File

@ -1,12 +1,13 @@
#pragma once #pragma once
#include "typedefs.hpp" #include "typedefs.hpp"
#include "commons.hpp"
#include <memory> #include <memory>
class Texture; class Texture;
class Framebuffer { class Framebuffer : public Bindable {
uint fbo; uint fbo;
uint depth; uint depth;
uint width; uint width;
@ -19,10 +20,10 @@ public:
~Framebuffer(); ~Framebuffer();
/// @brief Use framebuffer /// @brief Use framebuffer
void bind(); void bind() override;
/// @brief Stop using framebuffer /// @brief Stop using framebuffer
void unbind(); void unbind() override;
/// @brief Update framebuffer texture size /// @brief Update framebuffer texture size
/// @param width new width /// @param width new width
@ -36,4 +37,6 @@ public:
uint getWidth() const; uint getWidth() const;
/// @brief Get framebuffer height /// @brief Get framebuffer height
uint getHeight() const; uint getHeight() const;
uint getFBO() const;
}; };

View File

@ -0,0 +1,273 @@
#include "GBuffer.hpp"
#include <GL/glew.h>
#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<ImageData> GBuffer::toImage() const {
auto data = std::make_unique<ubyte[]>(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<ImageData>(
ImageFormat::rgb888, width, height, std::move(data)
);
}
uint GBuffer::getWidth() const {
return width;
}
uint GBuffer::getHeight() const {
return height;
}

View File

@ -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<ImageData> 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();
};

View File

@ -2,18 +2,81 @@
#include "Shader.hpp" #include "Shader.hpp"
#include "data/dv_util.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::FLOAT) {}
PostEffect::Param::Param(Type type, Value defValue) PostEffect::Param::Param(Type type, Value defValue, bool array)
: type(type), defValue(defValue), value(defValue) { : type(type), defValue(defValue), value(defValue), array(array) {
} }
PostEffect::PostEffect( PostEffect::PostEffect(
bool advanced,
std::shared_ptr<Shader> shader, std::shared_ptr<Shader> shader,
std::unordered_map<std::string, Param> params std::unordered_map<std::string, Param> 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<int>(param.value));
break;
case Type::FLOAT:
shader.uniform1f(name, std::get<float>(param.value));
break;
case Type::VEC2:
shader.uniform2f(name, std::get<glm::vec2>(param.value));
break;
case Type::VEC3:
shader.uniform3f(name, std::get<glm::vec3>(param.value));
break;
case Type::VEC4:
shader.uniform4f(name, std::get<glm::vec4>(param.value));
break;
default:
assert(false);
}
}
static void apply_uniform_array(
const PostEffect::Param& param,
Shader& shader,
const std::string& name,
const std::vector<ubyte>& values
) {
size_t size = values.size();
auto ibuffer = reinterpret_cast<const int*>(values.data());
auto fbuffer = reinterpret_cast<const float*>(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() { Shader& PostEffect::use() {
@ -23,27 +86,24 @@ Shader& PostEffect::use() {
if (!param.dirty) { if (!param.dirty) {
continue; continue;
} }
switch (param.type) { if (param.array) {
case Param::Type::FLOAT: const auto& found = arrayValues.find(name);
shader->uniform1f(name, std::get<float>(param.value)); if (found == arrayValues.end()) {
break; continue;
case Param::Type::VEC2: }
shader->uniform2f(name, std::get<glm::vec2>(param.value)); apply_uniform_array(param, *shader, name, found->second);
break; } else {
case Param::Type::VEC3: apply_uniform_value(param, *shader, name);
shader->uniform3f(name, std::get<glm::vec3>(param.value));
break;
case Param::Type::VEC4:
shader->uniform4f(name, std::get<glm::vec4>(param.value));
break;
default:
assert(false);
} }
param.dirty = false; param.dirty = false;
} }
return *shader; return *shader;
} }
Shader& PostEffect::getShader() {
return *shader;
}
float PostEffect::getIntensity() const { float PostEffect::getIntensity() const {
return intensity; return intensity;
} }
@ -66,6 +126,9 @@ void PostEffect::setParam(const std::string& name, const dv::value& value) {
} }
auto& param = found->second; auto& param = found->second;
switch (param.type) { switch (param.type) {
case Param::Type::INT:
param.value = static_cast<int>(value.asInteger());
break;
case Param::Type::FLOAT: case Param::Type::FLOAT:
param.value = static_cast<float>(value.asNumber()); param.value = static_cast<float>(value.asNumber());
break; break;
@ -81,3 +144,20 @@ void PostEffect::setParam(const std::string& name, const dv::value& value) {
} }
param.dirty = true; param.dirty = true;
} }
void PostEffect::setArray(const std::string& name, std::vector<ubyte>&& 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);
}

View File

@ -1,11 +1,13 @@
#pragma once #pragma once
#include <vector>
#include <memory> #include <memory>
#include <string> #include <string>
#include <variant> #include <variant>
#include <unordered_map> #include <unordered_map>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include "typedefs.hpp"
#include "data/dv_fwd.hpp" #include "data/dv_fwd.hpp"
#include "util/EnumMetadata.hpp" #include "util/EnumMetadata.hpp"
@ -14,27 +16,30 @@ class Shader;
class PostEffect { class PostEffect {
public: public:
struct Param { struct Param {
enum class Type { FLOAT, VEC2, VEC3, VEC4 }; enum class Type { INT, FLOAT, VEC2, VEC3, VEC4 };
VC_ENUM_METADATA(Type) VC_ENUM_METADATA(Type)
{"int", Type::INT},
{"float", Type::FLOAT}, {"float", Type::FLOAT},
{"vec2", Type::VEC2}, {"vec2", Type::VEC2},
{"vec3", Type::VEC3}, {"vec3", Type::VEC3},
{"vec4", Type::VEC4}, {"vec4", Type::VEC4},
VC_ENUM_END VC_ENUM_END
using Value = std::variant<float, glm::vec2, glm::vec3, glm::vec4>; using Value = std::variant<int, float, glm::vec2, glm::vec3, glm::vec4>;
Type type; Type type;
Value defValue; Value defValue;
Value value; Value value;
bool array = false;
bool dirty = true; bool dirty = true;
Param(); Param();
Param(Type type, Value defValue); Param(Type type, Value defValue, bool array);
}; };
PostEffect( PostEffect(
bool advanced,
std::shared_ptr<Shader> shader, std::shared_ptr<Shader> shader,
std::unordered_map<std::string, Param> params std::unordered_map<std::string, Param> params
); );
@ -43,16 +48,26 @@ public:
Shader& use(); Shader& use();
Shader& getShader();
float getIntensity() const; float getIntensity() const;
void setIntensity(float value); void setIntensity(float value);
void setParam(const std::string& name, const dv::value& value); void setParam(const std::string& name, const dv::value& value);
void setArray(const std::string& name, std::vector<ubyte>&& values);
bool isAdvanced() const {
return advanced;
}
bool isActive() { bool isActive() {
return intensity > 1e-4f; return intensity > 1e-4f;
} }
private: private:
bool advanced = false;
std::shared_ptr<Shader> shader; std::shared_ptr<Shader> shader;
std::unordered_map<std::string, Param> params; std::unordered_map<std::string, Param> params;
std::unordered_map<std::string, std::vector<ubyte>> arrayValues;
float intensity = 0.0f; float intensity = 0.0f;
}; };

View File

@ -1,58 +1,200 @@
#include "PostProcessing.hpp" #include "PostProcessing.hpp"
#include "Mesh.hpp" #include "Mesh.hpp"
#include "Shader.hpp" #include "Shader.hpp"
#include "GBuffer.hpp"
#include "Texture.hpp" #include "Texture.hpp"
#include "Framebuffer.hpp" #include "Framebuffer.hpp"
#include "DrawContext.hpp" #include "DrawContext.hpp"
#include "PostEffect.hpp" #include "PostEffect.hpp"
#include "assets/Assets.hpp" #include "assets/Assets.hpp"
#include "window/Camera.hpp"
#include <stdexcept> #include <stdexcept>
#include <random>
// TODO: REFACTOR WHOLE RENDER ENGINE
using namespace advanced_pipeline;
PostProcessing::PostProcessing(size_t effectSlotsCount) PostProcessing::PostProcessing(size_t effectSlotsCount)
: effectSlots(effectSlotsCount) { : effectSlots(effectSlotsCount) {
// Fullscreen quad mesh bulding // Fullscreen quad mesh bulding
PostProcessingVertex meshData[]{ 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}},
{{-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<Mesh<PostProcessingVertex>>(meshData, 6); quadMesh = std::make_unique<Mesh<PostProcessingVertex>>(meshData, 6);
std::vector<glm::vec3> ssaoNoise;
for (unsigned int i = 0; i < 16; i++)
{
glm::vec3 noise(
(rand() / static_cast<float>(RAND_MAX)) * 2.0 - 1.0,
(rand() / static_cast<float>(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; PostProcessing::~PostProcessing() = default;
void PostProcessing::use(DrawContext& context) { void PostProcessing::use(DrawContext& context, bool gbufferPipeline) {
const auto& vp = context.getViewport(); const auto& vp = context.getViewport();
if (fbo) {
fbo->resize(vp.x, vp.y); if (gbufferPipeline) {
fboSecond->resize(vp.x, vp.y); if (gbuffer == nullptr) {
gbuffer = std::make_unique<GBuffer>(vp.x, vp.y);
} else {
gbuffer->resize(vp.x, vp.y);
}
context.setFramebuffer(gbuffer.get());
} else { } else {
fbo = std::make_unique<Framebuffer>(vp.x, vp.y); gbuffer.reset();
fboSecond = std::make_unique<Framebuffer>(vp.x, vp.y); 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<Framebuffer>(width, height);
fboSecond = std::make_unique<Framebuffer>(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<PostEffect>("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<PostEffect>("deferred_lighting");
auto& shader = effect.use();
configureEffect(
context,
effect,
shader,
timer,
camera
);
quadMesh->draw();
} }
context.setFramebuffer(fbo.get());
} }
void PostProcessing::render( 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) { if (fbo == nullptr) {
throw std::runtime_error("'use(...)' was never called"); throw std::runtime_error("'use(...)' was never called");
} }
int totalPasses = 0; int totalPasses = 0;
for (const auto& effect : effectSlots) { 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) { if (totalPasses == 0) {
// replace 'default' blit shader with glBlitFramebuffer?
auto& effect = assets.require<PostEffect>("default"); auto& effect = assets.require<PostEffect>("default");
effect.use(); auto& shader = effect.use();
fbo->getTexture()->bind(); configureEffect(
context, effect, shader, timer, camera
);
quadMesh->draw(); quadMesh->draw();
return; return;
} }
@ -62,17 +204,26 @@ void PostProcessing::render(
if (effect == nullptr || !effect->isActive()) { if (effect == nullptr || !effect->isActive()) {
continue; continue;
} }
if (effect->isAdvanced() && gbuffer == nullptr) {
continue;
}
auto& shader = effect->use(); auto& shader = effect->use();
configureEffect(
context,
*effect,
shader,
timer,
camera
);
const auto& viewport = context.getViewport(); if (currentPass > 1) {
shader.uniform1i("u_screen", 0); fbo->getTexture()->bind();
shader.uniform2i("u_screenSize", viewport); }
shader.uniform1f("u_timer", timer);
fbo->getTexture()->bind();
if (currentPass < totalPasses) { if (currentPass < totalPasses) {
fboSecond->bind(); fboSecond->bind();
} }
quadMesh->draw(); quadMesh->draw();
if (currentPass < totalPasses) { if (currentPass < totalPasses) {
fboSecond->unbind(); fboSecond->unbind();

View File

@ -11,6 +11,9 @@ class Framebuffer;
class DrawContext; class DrawContext;
class ImageData; class ImageData;
class PostEffect; class PostEffect;
class Camera;
class GBuffer;
class Shader;
struct PostProcessingVertex { struct PostProcessingVertex {
glm::vec2 position; glm::vec2 position;
@ -24,25 +27,31 @@ struct PostProcessingVertex {
/// @attention Current implementation does not support multiple render passes /// @attention Current implementation does not support multiple render passes
/// for multiple effects. Will be implemented in v0.21 /// for multiple effects. Will be implemented in v0.21
class PostProcessing { class PostProcessing {
/// @brief Main framebuffer (lasy field)
std::unique_ptr<Framebuffer> fbo;
std::unique_ptr<Framebuffer> fboSecond;
/// @brief Fullscreen quad mesh as the post-processing canvas
std::unique_ptr<Mesh<PostProcessingVertex>> quadMesh;
std::vector<std::shared_ptr<PostEffect>> effectSlots;
public: public:
PostProcessing(size_t effectSlotsCount); PostProcessing(size_t effectSlotsCount);
~PostProcessing(); ~PostProcessing();
/// @brief Prepare and bind framebuffer /// @brief Prepare and bind framebuffer
/// @param context graphics context will be modified /// @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 /// @brief Render fullscreen quad using the passed shader
/// with framebuffer texture bound /// with framebuffer texture bound
/// @param context graphics context /// @param context graphics context
/// @throws std::runtime_error if use(...) wasn't called before /// @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<PostEffect> effect); void setEffect(size_t slot, std::shared_ptr<PostEffect> effect);
@ -52,4 +61,24 @@ public:
std::unique_ptr<ImageData> toImage(); std::unique_ptr<ImageData> toImage();
Framebuffer* getFramebuffer() const; 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<Framebuffer> fbo;
std::unique_ptr<Framebuffer> fboSecond;
/// @brief Fullscreen quad mesh as the post-processing canvas
std::unique_ptr<Mesh<PostProcessingVertex>> quadMesh;
std::vector<std::shared_ptr<PostEffect>> effectSlots;
std::unique_ptr<GBuffer> gbuffer;
uint noiseTexture;
}; };

View File

@ -12,20 +12,25 @@
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include "coders/GLSLExtension.hpp" #include "coders/GLSLExtension.hpp"
#include "debug/Logger.hpp"
static debug::Logger logger("gl-shader");
namespace fs = std::filesystem; namespace fs = std::filesystem;
GLSLExtension* Shader::preprocessor = new GLSLExtension(); GLSLExtension* Shader::preprocessor = new GLSLExtension();
Shader* Shader::used = nullptr; 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); glDeleteProgram(id);
} }
void Shader::use(){ void Shader::use() {
used = this; used = this;
glUseProgram(id); glUseProgram(id);
} }
@ -40,8 +45,16 @@ uint Shader::getUniformLocation(const std::string& name) {
return found->second; return found->second;
} }
void Shader::uniformMatrix(const std::string& name, const glm::mat4& matrix){ void Shader::uniformMatrix(const std::string& name, const glm::mat4& matrix) {
glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, glm::value_ptr(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){ 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); 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); glDeleteShader(*shader);
delete shader; delete shader;
}; };
@ -93,45 +125,73 @@ glshader compile_shader(GLenum type, const GLchar* source, const std::string& fi
glShaderSource(shader, 1, &source, nullptr); glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader); glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &success); glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success){ if (!success) {
GLchar infoLog[GL_LOG_LEN]; GLchar infoLog[GL_LOG_LEN];
glGetShaderInfoLog(shader, GL_LOG_LEN, nullptr, infoLog); glGetShaderInfoLog(shader, GL_LOG_LEN, nullptr, infoLog);
glDeleteShader(shader); glDeleteShader(shader);
throw std::runtime_error( 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 return glshader(new GLuint(shader), shader_deleter); //-V508
} }
std::unique_ptr<Shader> Shader::create( static GLuint compile_program(
const std::string& vertexFile, const Shader::Source& vertexSource, const Shader::Source& fragmentSource
const std::string& fragmentFile,
const std::string& vertexCode,
const std::string& fragmentCode
) { ) {
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* vCode = vertexCode.c_str();
const GLchar* fCode = fragmentCode.c_str(); const GLchar* fCode = fragmentCode.c_str();
glshader vertex = compile_shader(GL_VERTEX_SHADER, vCode, vertexFile); glshader vertex =
glshader fragment = compile_shader(GL_FRAGMENT_SHADER, fCode, fragmentFile); compile_shader(GL_VERTEX_SHADER, vCode, vertexSource.file);
glshader fragment =
compile_shader(GL_FRAGMENT_SHADER, fCode, fragmentSource.file);
// Shader Program // Shader Program
GLint success; GLint success;
GLuint id = glCreateProgram(); GLuint program = glCreateProgram();
glAttachShader(id, *vertex); glAttachShader(program, *vertex);
glAttachShader(id, *fragment); glAttachShader(program, *fragment);
glLinkProgram(id); glLinkProgram(program);
glGetProgramiv(id, GL_LINK_STATUS, &success); glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success){
if (!success) {
GLchar infoLog[GL_LOG_LEN]; GLchar infoLog[GL_LOG_LEN];
glGetProgramInfoLog(id, GL_LOG_LEN, nullptr, infoLog); glGetProgramInfoLog(program, GL_LOG_LEN, nullptr, infoLog);
throw std::runtime_error( throw std::runtime_error(
"shader program linking failed:\n"+std::string(infoLog) "shader program linking failed:\n" + std::string(infoLog)
); );
} }
return std::make_unique<Shader>(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> Shader::create(
Source&& vertexSource, Source&& fragmentSource
) {
return std::make_unique<Shader>(
compile_program(vertexSource, fragmentSource),
std::move(vertexSource),
std::move(fragmentSource)
);
} }
Shader& Shader::getUsed() { Shader& Shader::getUsed() {

View File

@ -10,19 +10,30 @@
class GLSLExtension; class GLSLExtension;
class Shader { class Shader {
public:
struct Source {
std::string file;
std::string code;
};
private:
static Shader* used; static Shader* used;
uint id; uint id;
std::unordered_map<std::string, uint> uniformLocations; std::unordered_map<std::string, uint> uniformLocations;
// source code used for re-compiling shaders after updating defines
Source vertexSource;
Source fragmentSource;
uint getUniformLocation(const std::string& name); uint getUniformLocation(const std::string& name);
public: public:
static GLSLExtension* preprocessor; static GLSLExtension* preprocessor;
Shader(uint id); Shader(uint id, Source&& vertexSource, Source&& fragmentSource);
~Shader(); ~Shader();
void use(); void use();
void uniformMatrix(const std::string&, const glm::mat4& matrix); 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 uniform1i(const std::string& name, int x);
void uniform1f(const std::string& name, float x); void uniform1f(const std::string& name, float x);
void uniform2f(const std::string& name, float x, float y); 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 uniform3f(const std::string& name, const glm::vec3& xyz);
void uniform4f(const std::string& name, const glm::vec4& xyzw); 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. /// @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 /// @return linked shader program containing vertex and fragment shaders
static std::unique_ptr<Shader> create( static std::unique_ptr<Shader> create(
const std::string& vertexFile, Source&& vertexSource, Source&& fragmentSource
const std::string& fragmentFile,
const std::string& vertexSource,
const std::string& fragmentSource
); );
static Shader& getUsed(); static Shader& getUsed();

View File

@ -0,0 +1,47 @@
#include "ShadowMap.hpp"
#include <GL/glew.h>
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;
}

View File

@ -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;
};

View File

@ -50,6 +50,17 @@ enum class CursorShape {
LAST=NOT_ALLOWED 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) VC_ENUM_METADATA(CursorShape)
{"arrow", CursorShape::ARROW}, {"arrow", CursorShape::ARROW},
{"text", CursorShape::TEXT}, {"text", CursorShape::TEXT},
@ -69,3 +80,11 @@ public:
virtual void flush() = 0; virtual void flush() = 0;
}; };
class Bindable {
public:
virtual ~Bindable() = default;
virtual void bind() = 0;
virtual void unbind() = 0;
};

View File

@ -39,17 +39,27 @@ BlocksRenderer::~BlocksRenderer() {
/// Basic vertex add method /// Basic vertex add method
void BlocksRenderer::vertex( 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].position = coord;
vertexBuffer[vertexCount].uv = {u,v}; vertexBuffer[vertexCount].uv = {u,v};
vertexBuffer[vertexCount].normal[0] = static_cast<uint8_t>(normal.r * 127 + 128);
vertexBuffer[vertexCount].normal[1] = static_cast<uint8_t>(normal.g * 127 + 128);
vertexBuffer[vertexCount].normal[2] = static_cast<uint8_t>(normal.b * 127 + 128);
vertexBuffer[vertexCount].normal[3] = static_cast<uint8_t>(emission * 255);
vertexBuffer[vertexCount].color[0] = static_cast<uint8_t>(light.r * 255); vertexBuffer[vertexCount].color[0] = static_cast<uint8_t>(light.r * 255);
vertexBuffer[vertexCount].color[1] = static_cast<uint8_t>(light.g * 255); vertexBuffer[vertexCount].color[1] = static_cast<uint8_t>(light.g * 255);
vertexBuffer[vertexCount].color[2] = static_cast<uint8_t>(light.b * 255); vertexBuffer[vertexCount].color[2] = static_cast<uint8_t>(light.b * 255);
vertexBuffer[vertexCount].color[3] = static_cast<uint8_t>(light.a * 255); vertexBuffer[vertexCount].color[3] = static_cast<uint8_t>(light.a * 255);
vertexCount++; vertexCount++;
} }
@ -82,10 +92,10 @@ void BlocksRenderer::face(
auto Y = axisY * h; auto Y = axisY * h;
auto Z = axisZ * d; auto Z = axisZ * d;
float s = 0.5f; float s = 0.5f;
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, lights[0] * 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); 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); 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); vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, lights[3] * tint, axisZ, 0);
index(0, 1, 3, 1, 2, 3); index(0, 1, 3, 1, 2, 3);
} }
@ -103,7 +113,7 @@ void BlocksRenderer::vertexAO(
axisX, axisX,
axisY axisY
); );
vertex(coord, u, v, light * tint); vertex(coord, u, v, light * tint, axisZ, 0.0f);
} }
void BlocksRenderer::faceAO( 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.u2, region.v2, tint, axisX, axisY, axisZ);
vertexAO(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, axisX, axisY, axisZ); vertexAO(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, axisX, axisY, axisZ);
} else { } else {
auto axisZ = glm::normalize(Z);
glm::vec4 tint(1.0f); glm::vec4 tint(1.0f);
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, 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); vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint, axisZ, 1);
vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint); vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, axisZ, 1);
vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint); vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, axisZ, 1);
} }
index(0, 1, 2, 0, 2, 3); index(0, 1, 2, 0, 2, 3);
} }
@ -163,10 +174,10 @@ void BlocksRenderer::face(
d = (1.0f - DIRECTIONAL_LIGHT_FACTOR) + d * DIRECTIONAL_LIGHT_FACTOR; d = (1.0f - DIRECTIONAL_LIGHT_FACTOR) + d * DIRECTIONAL_LIGHT_FACTOR;
tint *= d; tint *= d;
} }
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, 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); 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); 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); vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, Z, lights ? 0 : 1);
index(0, 1, 2, 0, 2, 3); index(0, 1, 2, 0, 2, 3);
} }
@ -198,14 +209,16 @@ void BlocksRenderer::blockXSprite(
const float w = size.x / 1.41f; const float w = size.x / 1.41f;
const glm::vec4 tint (0.8f); 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); 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); 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); 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); texface2, lights1, tint);
} }
@ -337,7 +350,9 @@ void BlocksRenderer::blockCustomModel(
coord + vcoord.x * X + vcoord.y * Y + vcoord.z * Z, coord + vcoord.x * X + vcoord.y * Y + vcoord.z * Z,
vertex.uv.x, vertex.uv.x,
vertex.uv.y, vertex.uv.y,
glm::vec4(d, d, d, d) * aoColor glm::vec4(d, d, d, d) * aoColor,
n,
0.0f
); );
indexBuffer[indexCount++] = vertexOffset++; indexBuffer[indexCount++] = vertexOffset++;
} }

View File

@ -44,7 +44,14 @@ class BlocksRenderer {
SortingMeshData sortingMesh; 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 index(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f);
void vertexAO( void vertexAO(

View File

@ -143,7 +143,7 @@ void ChunksRenderer::update() {
} }
const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk( const Mesh<ChunkVertex>* 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]; auto chunk = chunks.getChunks()[index];
if (chunk == nullptr) { if (chunk == nullptr) {
@ -182,13 +182,39 @@ const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk(
return mesh; return mesh;
} }
void ChunksRenderer::drawChunksShadowsPass(
const Camera& camera, Shader& shader
) {
const auto& atlas = assets.require<Atlas>("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( void ChunksRenderer::drawChunks(
const Camera& camera, Shader& shader const Camera& camera, Shader& shader
) { ) {
const auto& atlas = assets.require<Atlas>("blocks"); const auto& atlas = assets.require<Atlas>("blocks");
atlas.getTexture()->bind(); atlas.getTexture()->bind();
update();
// [warning] this whole method is not thread-safe for chunks // [warning] this whole method is not thread-safe for chunks
@ -219,7 +245,7 @@ void ChunksRenderer::drawChunks(
// TODO: minimize draw calls number // TODO: minimize draw calls number
for (int i = indices.size()-1; i >= 0; i--) { for (int i = indices.size()-1; i >= 0; i--) {
auto& chunk = chunks.getChunks()[indices[i].index]; 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) { if (mesh) {
glm::vec3 coord( glm::vec3 coord(

View File

@ -50,7 +50,7 @@ class ChunksRenderer {
std::vector<ChunksSortEntry> indices; std::vector<ChunksSortEntry> indices;
util::ThreadPool<std::shared_ptr<Chunk>, RendererResult> threadPool; util::ThreadPool<std::shared_ptr<Chunk>, RendererResult> threadPool;
const Mesh<ChunkVertex>* retrieveChunk( const Mesh<ChunkVertex>* retrieveChunk(
size_t index, const Camera& camera, Shader& shader, bool culling size_t index, const Camera& camera, bool culling
); );
public: public:
ChunksRenderer( ChunksRenderer(
@ -72,6 +72,9 @@ public:
const Mesh<ChunkVertex>* getOrRender( const Mesh<ChunkVertex>* getOrRender(
const std::shared_ptr<Chunk>& chunk, bool important const std::shared_ptr<Chunk>& chunk, bool important
); );
void drawChunksShadowsPass(const Camera& camera, Shader& shader);
void drawChunks(const Camera& camera, Shader& shader); void drawChunks(const Camera& camera, Shader& shader);
void drawSortedMeshes(const Camera& camera, Shader& shader); void drawSortedMeshes(const Camera& camera, Shader& shader);

View File

@ -94,39 +94,39 @@ void MainBatch::cube(
const glm::vec3 Z(0.0f, 0.0f, 1.0f); const glm::vec3 Z(0.0f, 0.0f, 1.0f);
quad( quad(
coord + Z * size.z * 0.5f, coord + Z * size.z * 0.5f,
X, Y, glm::vec2(size.x, size.y), X, Y, Z, glm::vec2(size.x, size.y),
(shading ? do_tint(0.8) * tint : tint), (shading ? do_tint(0.8) * tint : tint),
glm::vec3(1.0f), texfaces[5] glm::vec3(1.0f), texfaces[5]
); );
quad( quad(
coord - Z * size.z * 0.5f, coord - Z * size.z * 0.5f,
-X, Y, glm::vec2(size.x, size.y), -X, Y, -Z, glm::vec2(size.x, size.y),
(shading ? do_tint(0.9f) * tint : tint), (shading ? do_tint(0.9f) * tint : tint),
glm::vec3(1.0f), texfaces[4] glm::vec3(1.0f), texfaces[4]
); );
quad( quad(
coord + Y * size.y * 0.5f, coord + Y * size.y * 0.5f,
-X, Z, glm::vec2(size.x, size.z), -X, Z, Y, glm::vec2(size.x, size.z),
(shading ? do_tint(1.0f) * tint : tint), (shading ? do_tint(1.0f) * tint : tint),
glm::vec3(1.0f), texfaces[3] glm::vec3(1.0f), texfaces[3]
); );
quad( quad(
coord - Y * size.y * 0.5f, coord - Y * size.y * 0.5f,
X, Z, glm::vec2(size.x, size.z), X, Z, -Y, glm::vec2(size.x, size.z),
(shading ? do_tint(0.7f) * tint : tint), (shading ? do_tint(0.7f) * tint : tint),
glm::vec3(1.0f), texfaces[2] glm::vec3(1.0f), texfaces[2]
); );
quad( quad(
coord + X * size.x * 0.5f, coord + X * size.x * 0.5f,
-Z, Y, glm::vec2(size.z, size.y), -Z, Y, X, glm::vec2(size.z, size.y),
(shading ? do_tint(0.8f) * tint : tint), (shading ? do_tint(0.8f) * tint : tint),
glm::vec3(1.0f), texfaces[1] glm::vec3(1.0f), texfaces[1]
); );
quad( quad(
coord - X * size.x * 0.5f, coord - X * size.x * 0.5f,
Z, Y, glm::vec2(size.z, size.y), Z, Y, -X, glm::vec2(size.z, size.y),
(shading ? do_tint(0.9f) * tint : tint), (shading ? do_tint(0.9f) * tint : tint),
glm::vec3(1.0f), texfaces[1] glm::vec3(1.0f), texfaces[1]
); );
} }

View File

@ -18,13 +18,15 @@ struct MainBatchVertex {
glm::vec3 position; glm::vec3 position;
glm::vec2 uv; glm::vec2 uv;
glm::vec3 tint; glm::vec3 tint;
std::array<uint8_t,4> color; std::array<uint8_t, 4> color;
std::array<uint8_t, 4> normal;
static constexpr VertexAttribute ATTRIBUTES[] = { static constexpr VertexAttribute ATTRIBUTES[] = {
{VertexAttribute::Type::FLOAT, false, 3}, {VertexAttribute::Type::FLOAT, false, 3},
{VertexAttribute::Type::FLOAT, false, 2}, {VertexAttribute::Type::FLOAT, false, 2},
{VertexAttribute::Type::FLOAT, false, 3}, {VertexAttribute::Type::FLOAT, false, 3},
{VertexAttribute::Type::UNSIGNED_BYTE, true, 4}, {VertexAttribute::Type::UNSIGNED_BYTE, true, 4},
{VertexAttribute::Type::UNSIGNED_BYTE, true, 4},
{{}, 0}}; {{}, 0}};
}; };
@ -61,7 +63,9 @@ public:
const glm::vec3& pos, const glm::vec3& pos,
const glm::vec2& uv, const glm::vec2& uv,
const glm::vec4& light, const glm::vec4& light,
const glm::vec3& tint const glm::vec3& tint,
const glm::vec3& normal,
float emission
) { ) {
MainBatchVertex* buffer = this->buffer.get(); MainBatchVertex* buffer = this->buffer.get();
buffer[index].position = pos; buffer[index].position = pos;
@ -72,6 +76,11 @@ public:
buffer[index].color[1] = static_cast<uint8_t>(light.g * 255); buffer[index].color[1] = static_cast<uint8_t>(light.g * 255);
buffer[index].color[2] = static_cast<uint8_t>(light.b * 255); buffer[index].color[2] = static_cast<uint8_t>(light.b * 255);
buffer[index].color[3] = static_cast<uint8_t>(light.a * 255); buffer[index].color[3] = static_cast<uint8_t>(light.a * 255);
buffer[index].normal[0] = static_cast<uint8_t>(normal.x * 128 + 127);
buffer[index].normal[1] = static_cast<uint8_t>(normal.y * 128 + 127);
buffer[index].normal[2] = static_cast<uint8_t>(normal.z * 128 + 127);
buffer[index].normal[3] = static_cast<uint8_t>(emission * 255);
index++; index++;
} }
@ -79,48 +88,62 @@ public:
const glm::vec3& pos, const glm::vec3& pos,
const glm::vec3& right, const glm::vec3& right,
const glm::vec3& up, const glm::vec3& up,
const glm::vec3& normal,
const glm::vec2& size, const glm::vec2& size,
const glm::vec4& light, const glm::vec4& light,
const glm::vec3& tint, const glm::vec3& tint,
const UVRegion& subregion const UVRegion& subregion,
float emission = 0.0f
) { ) {
prepare(6); prepare(6);
vertex( vertex(
pos - right * size.x * 0.5f - up * size.y * 0.5f, pos - right * size.x * 0.5f - up * size.y * 0.5f,
{subregion.u1, subregion.v1}, {subregion.u1, subregion.v1},
light, light,
tint tint,
normal,
emission
); );
vertex( vertex(
pos + right * size.x * 0.5f - up * size.y * 0.5f, pos + right * size.x * 0.5f - up * size.y * 0.5f,
{subregion.u2, subregion.v1}, {subregion.u2, subregion.v1},
light, light,
tint tint,
normal,
emission
); );
vertex( vertex(
pos + right * size.x * 0.5f + up * size.y * 0.5f, pos + right * size.x * 0.5f + up * size.y * 0.5f,
{subregion.u2, subregion.v2}, {subregion.u2, subregion.v2},
light, light,
tint tint,
normal,
emission
); );
vertex( vertex(
pos - right * size.x * 0.5f - up * size.y * 0.5f, pos - right * size.x * 0.5f - up * size.y * 0.5f,
{subregion.u1, subregion.v1}, {subregion.u1, subregion.v1},
light, light,
tint tint,
normal,
emission
); );
vertex( vertex(
pos + right * size.x * 0.5f + up * size.y * 0.5f, pos + right * size.x * 0.5f + up * size.y * 0.5f,
{subregion.u2, subregion.v2}, {subregion.u2, subregion.v2},
light, light,
tint tint,
normal,
emission
); );
vertex( vertex(
pos - right * size.x * 0.5f + up * size.y * 0.5f, pos - right * size.x * 0.5f + up * size.y * 0.5f,
{subregion.u1, subregion.v2}, {subregion.u1, subregion.v2},
light, light,
tint tint,
normal,
emission
); );
} }

View File

@ -57,12 +57,14 @@ ModelBatch::ModelBatch(
ModelBatch::~ModelBatch() = default; ModelBatch::~ModelBatch() = default;
void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix, void ModelBatch::draw(
const glm::mat3& rotation, glm::vec3 tint, const model::Mesh& mesh,
const texture_names_map* varTextures, const glm::mat4& matrix,
bool backlight) { const glm::mat3& rotation,
glm::vec3 tint,
const texture_names_map* varTextures,
bool backlight
) {
setTexture(mesh.texture, varTextures); setTexture(mesh.texture, varTextures);
size_t vcount = mesh.vertices.size(); size_t vcount = mesh.vertices.size();
const auto& vertexData = mesh.vertices.data(); 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++) { for (size_t j = 0; j < 3; j++) {
const auto vert = vertexData[i * 3 + j]; const auto vert = vertexData[i * 3 + j];
float d = 1.0f; float d = 1.0f;
auto norm = rotation * vert.normal;
if (mesh.lighting) { if (mesh.lighting) {
auto norm = rotation * vert.normal;
d = glm::dot(norm, SUN_VECTOR); d = glm::dot(norm, SUN_VECTOR);
d = 0.8f + d * 0.2f; 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
);
} }
} }
} }

View File

@ -117,7 +117,6 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
light *= 0.9f + (particle.random % 100) * 0.001f; light *= 0.9f + (particle.random % 100) * 0.001f;
} }
glm::vec3 localRight = right; glm::vec3 localRight = right;
glm::vec3 localUp = preset.globalUpVector ? glm::vec3(0, 1, 0) : up; glm::vec3 localUp = preset.globalUpVector ? glm::vec3(0, 1, 0) : up;
float angle = particle.angle; float angle = particle.angle;
@ -134,10 +133,12 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
particle.position, particle.position,
localRight, localRight,
localUp, localUp,
-camera.front,
preset.size * scale, preset.size * scale,
light, light,
glm::vec3(1.0f), glm::vec3(1.0f),
particle.region particle.region,
preset.lighting ? 0.0f : 1.0f
); );
if (particle.lifetime <= 0.0f) { if (particle.lifetime <= 0.0f) {
iter = vec.erase(iter); iter = vec.erase(iter);

View File

@ -146,6 +146,7 @@ void PrecipitationRenderer::render(
pos, pos,
face.right, face.right,
{0, 1, 0}, {0, 1, 0},
glm::cross(glm::vec3(0, 1, 0), face.right),
FACE_SIZE, FACE_SIZE,
light_at(chunks, pos.x, y, pos.z), light_at(chunks, pos.x, y, pos.z),
glm::vec3(1.0f), glm::vec3(1.0f),

View File

@ -15,11 +15,10 @@
#include <iostream> #include <iostream>
#include <GL/glew.h> #include <GL/glew.h>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/constants.hpp> #include <glm/gtc/constants.hpp>
#ifndef M_PI using namespace advanced_pipeline;
#define M_PI 3.141592
#endif // M_PI
const int STARS_COUNT = 3000; const int STARS_COUNT = 3000;
const int STARS_SEED = 632; const int STARS_SEED = 632;
@ -46,18 +45,28 @@ Skybox::Skybox(uint size, Shader& shader)
mesh = std::make_unique<Mesh<SkyboxVertex>>(vertices, 6); mesh = std::make_unique<Mesh<SkyboxVertex>>(vertices, 6);
sprites.push_back(skysprite { sprites.push_back(SkySprite {
"misc/moon", "misc/moon",
glm::pi<float>()*0.5f, glm::pi<float>() * 0.5f,
4.0f, 4.0f,
false false,
glm::pi<float>() * 0.25f,
});
sprites.push_back(SkySprite {
"misc/moon_flare",
glm::pi<float>() * 0.5f,
0.5f,
false,
glm::pi<float>() * 0.25f,
}); });
sprites.push_back(skysprite { sprites.push_back(SkySprite {
"misc/sun", "misc/sun",
glm::pi<float>()*1.5f, glm::pi<float>() * 1.5f,
4.0f, 4.0f,
true true,
glm::pi<float>() * 0.25f,
}); });
} }
@ -69,9 +78,11 @@ void Skybox::drawBackground(
auto backShader = assets.get<Shader>("background"); auto backShader = assets.get<Shader>("background");
backShader->use(); backShader->use();
backShader->uniformMatrix("u_view", camera.getView(false)); 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<float>()
);
backShader->uniform1f("u_ar", float(width)/float(height)); backShader->uniform1f("u_ar", float(width)/float(height));
backShader->uniform1i("u_cubemap", 1); backShader->uniform1i("u_skybox", 1);
bind(); bind();
mesh->draw(); mesh->draw();
unbind(); unbind();
@ -80,20 +91,29 @@ void Skybox::drawBackground(
void Skybox::drawStars(float angle, float opacity) { void Skybox::drawStars(float angle, float opacity) {
batch3d->texture(nullptr); batch3d->texture(nullptr);
random.setSeed(STARS_SEED); random.setSeed(STARS_SEED);
glm::mat4 rotation = glm::rotate(
glm::mat4(1.0f),
-angle + glm::pi<float>() * 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++) { for (int i = 0; i < STARS_COUNT; i++) {
float rx = (random.randFloat()) - 0.5f; float rx = (random.randFloat()) - 0.5f;
float ry = (random.randFloat()) - 0.5f; float ry = (random.randFloat()) - 0.5f;
float z = (random.randFloat()) - 0.5f; float rz = (random.randFloat()) - 0.5f;
float x = rx * std::sin(angle) + ry * -std::cos(angle);
float y = rx * std::cos(angle) + ry * std::sin(angle); glm::vec3 pos = glm::vec4(rx, ry, rz, 1) * rotation;
float sopacity = random.randFloat(); float sopacity = random.randFloat();
if (y < 0.0f) if (pos.y < 0.0f)
continue; 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); glm::vec4 tint (1,1,1, sopacity * opacity);
batch3d->point(glm::vec3(x, y, z), tint); batch3d->point(pos * depth, tint);
} }
batch3d->flushPoints(); batch3d->flushPoints();
} }
@ -107,6 +127,8 @@ void Skybox::draw(
{ {
const glm::uvec2& viewport = pctx.getViewport(); const glm::uvec2& viewport = pctx.getViewport();
glActiveTexture(GL_TEXTURE0);
drawBackground(camera, assets, viewport.x, viewport.y); drawBackground(camera, assets, viewport.x, viewport.y);
DrawContext ctx = pctx.sub(); DrawContext ctx = pctx.sub();
@ -119,24 +141,32 @@ void Skybox::draw(
batch3d->begin(); batch3d->begin();
float angle = daytime * glm::pi<float>() * 2.0f; float angle = daytime * glm::pi<float>() * 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) { for (auto& sprite : sprites) {
batch3d->texture(assets.get<Texture>(sprite.texture)); batch3d->texture(assets.get<Texture>(sprite.texture));
float sangle = daytime * glm::pi<float>()*2.0 + sprite.phase; float sangle = daytime * glm::pi<float>() * 2.0 + sprite.phase;
float distance = sprite.distance; float distance = sprite.distance * depthScale;
glm::vec3 pos(-std::cos(sangle)*distance, std::sin(sangle)*distance, 0); glm::mat4 rotation = glm::rotate(
glm::vec3 up(-std::sin(-sangle), std::cos(-sangle), 0.0f); glm::mat4(1.0f),
-sangle + glm::pi<float>() * 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); glm::vec4 tint (1,1,1, opacity);
if (!sprite.emissive) { 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); up, 1, 1, UVRegion(), tint);
} }
batch3d->flush();
drawStars(angle, opacity); drawStars(angle, opacity);
} }
@ -153,12 +183,22 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality)
assert(cubemap != nullptr); assert(cubemap != nullptr);
ready = true; ready = true;
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE0 + TARGET_SKYBOX);
cubemap->bind(); cubemap->bind();
shader.use(); shader.use();
t *= glm::pi<float>()*2.0f; t *= glm::two_pi<float>();
lightDir = glm::normalize(glm::vec3(sin(t), -cos(t), 0.0f)); lightDir = glm::normalize(glm::vec3(sin(t), -cos(t), 0.0f));
float sunAngle = glm::radians((t / glm::two_pi<float>() - 0.25f) * 360.0f);
float x = -glm::cos(sunAngle + glm::pi<float>() * 0.5f) * glm::radians(sunAltitude);
float y = sunAngle - glm::pi<float>() * 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.uniform1i("u_quality", quality);
shader.uniform1f("u_mie", mie); shader.uniform1f("u_mie", mie);
shader.uniform1f("u_fog", mie - 1.0f); shader.uniform1f("u_fog", mie - 1.0f);
@ -223,13 +263,13 @@ void Skybox::refreshFace(uint face, Cubemap* cubemap) {
} }
void Skybox::bind() const { void Skybox::bind() const {
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE0 + TARGET_SKYBOX);
fbo->getTexture()->bind(); fbo->getTexture()->bind();
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
} }
void Skybox::unbind() const { void Skybox::unbind() const {
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE0 + TARGET_SKYBOX);
fbo->getTexture()->unbind(); fbo->getTexture()->unbind();
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
} }

View File

@ -25,11 +25,12 @@ struct SkyboxVertex {
{{}, 0}}; {{}, 0}};
}; };
struct skysprite { struct SkySprite {
std::string texture; std::string texture;
float phase; float phase;
float distance; float distance;
bool emissive; bool emissive;
float altitude;
}; };
class Skybox { class Skybox {
@ -42,11 +43,13 @@ class Skybox {
std::unique_ptr<Mesh<SkyboxVertex>> mesh; std::unique_ptr<Mesh<SkyboxVertex>> mesh;
std::unique_ptr<Batch3D> batch3d; std::unique_ptr<Batch3D> batch3d;
std::vector<skysprite> sprites; std::vector<SkySprite> sprites;
int frameid = 0; int frameid = 0;
float prevMie = -1.0f; float prevMie = -1.0f;
float prevT = -1.0f; float prevT = -1.0f;
float sunAltitude = 45.0f;
glm::mat4 rotation;
void drawStars(float angle, float opacity); void drawStars(float angle, float opacity);
void drawBackground( void drawBackground(

View File

@ -11,6 +11,7 @@
#include "assets/assets_util.hpp" #include "assets/assets_util.hpp"
#include "content/Content.hpp" #include "content/Content.hpp"
#include "engine/Engine.hpp" #include "engine/Engine.hpp"
#include "coders/GLSLExtension.hpp"
#include "frontend/LevelFrontend.hpp" #include "frontend/LevelFrontend.hpp"
#include "frontend/ContentGfxCache.hpp" #include "frontend/ContentGfxCache.hpp"
#include "items/Inventory.hpp" #include "items/Inventory.hpp"
@ -26,7 +27,6 @@
#include "voxels/Block.hpp" #include "voxels/Block.hpp"
#include "voxels/Chunk.hpp" #include "voxels/Chunk.hpp"
#include "voxels/Chunks.hpp" #include "voxels/Chunks.hpp"
#include "window/Camera.hpp"
#include "window/Window.hpp" #include "window/Window.hpp"
#include "world/Level.hpp" #include "world/Level.hpp"
#include "world/LevelEvents.hpp" #include "world/LevelEvents.hpp"
@ -38,9 +38,12 @@
#include "graphics/core/LineBatch.hpp" #include "graphics/core/LineBatch.hpp"
#include "graphics/core/Mesh.hpp" #include "graphics/core/Mesh.hpp"
#include "graphics/core/PostProcessing.hpp" #include "graphics/core/PostProcessing.hpp"
#include "graphics/core/Framebuffer.hpp"
#include "graphics/core/Shader.hpp" #include "graphics/core/Shader.hpp"
#include "graphics/core/Texture.hpp" #include "graphics/core/Texture.hpp"
#include "graphics/core/Font.hpp" #include "graphics/core/Font.hpp"
#include "graphics/core/ShadowMap.hpp"
#include "graphics/core/GBuffer.hpp"
#include "BlockWrapsRenderer.hpp" #include "BlockWrapsRenderer.hpp"
#include "ParticlesRenderer.hpp" #include "ParticlesRenderer.hpp"
#include "PrecipitationRenderer.hpp" #include "PrecipitationRenderer.hpp"
@ -52,8 +55,12 @@
#include "Emitter.hpp" #include "Emitter.hpp"
#include "TextNote.hpp" #include "TextNote.hpp"
using namespace advanced_pipeline;
inline constexpr size_t BATCH3D_CAPACITY = 4096; inline constexpr size_t BATCH3D_CAPACITY = 4096;
inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000; 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::showChunkBorders = false;
bool WorldRenderer::showEntitiesDebug = false; bool WorldRenderer::showEntitiesDebug = false;
@ -119,13 +126,37 @@ void WorldRenderer::setupWorldShader(
shader.uniform1f("u_fogFactor", fogFactor); shader.uniform1f("u_fogFactor", fogFactor);
shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get()); shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
shader.uniform1i("u_debugLights", lightsDebug); shader.uniform1i("u_debugLights", lightsDebug);
shader.uniform1i("u_debugNormals", false);
shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity()); shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity());
shader.uniform1f("u_weatherFogDencity", weather.fogDencity()); shader.uniform1f("u_weatherFogDencity", weather.fogDencity());
shader.uniform1f("u_weatherFogCurve", weather.fogCurve()); shader.uniform1f("u_weatherFogCurve", weather.fogCurve());
shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime); shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime);
shader.uniform2f("u_lightDir", skybox->getLightDir()); shader.uniform2f("u_lightDir", skybox->getLightDir());
shader.uniform3f("u_cameraPos", camera.position); 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(); auto indices = level.content.getIndices();
// Light emission when an emissive item is chosen // Light emission when an emissive item is chosen
@ -195,22 +226,6 @@ void WorldRenderer::renderLevel(
if (!pause) { if (!pause) {
scripting::on_frontend_render(); scripting::on_frontend_render();
} }
setupWorldShader(entityShader, camera, settings, fogFactor);
std::array<const WeatherPreset*, 2> 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(); skybox->unbind();
} }
@ -232,7 +247,7 @@ void WorldRenderer::renderBlockSelection() {
const glm::vec3 center = glm::vec3(pos) + hitbox.center(); const glm::vec3 center = glm::vec3(pos) + hitbox.center();
const glm::vec3 size = hitbox.size(); const glm::vec3 size = hitbox.size();
lineBatch->box( 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) { if (debug) {
lineBatch->line( lineBatch->line(
@ -324,6 +339,79 @@ void WorldRenderer::renderHands(
skybox->unbind(); skybox->unbind();
} }
void WorldRenderer::generateShadowsMap(
const Camera& camera,
const DrawContext& pctx,
ShadowMap& shadowMap,
Camera& shadowCamera,
float scale
) {
auto& shadowsShader = assets.require<Shader>("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<int>(t / sunCycleStep)) * sunCycleStep + 0.25f) * 360.0f
);
float sunAltitude = glm::pi<float>() * 0.25f;
shadowCamera.rotate(
-glm::cos(sunAngle + glm::pi<float>() * 0.5f) * sunAltitude,
sunAngle - glm::pi<float>() * 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( void WorldRenderer::draw(
const DrawContext& pctx, const DrawContext& pctx,
Camera& camera, Camera& camera,
@ -332,6 +420,7 @@ void WorldRenderer::draw(
float uiDelta, float uiDelta,
PostProcessing& postProcessing PostProcessing& postProcessing
) { ) {
// TODO: REFACTOR WHOLE RENDER ENGINE
float delta = uiDelta * !pause; float delta = uiDelta * !pause;
timer += delta; timer += delta;
weather.update(delta); weather.update(delta);
@ -341,29 +430,75 @@ void WorldRenderer::draw(
const auto& vp = pctx.getViewport(); const auto& vp = pctx.getViewport();
camera.setAspectRatio(vp.x / static_cast<float>(vp.y)); camera.setAspectRatio(vp.x / static_cast<float>(vp.y));
auto& mainShader = assets.require<Shader>("main");
auto& entityShader = assets.require<Shader>("entity");
auto& deferredShader = assets.require<PostEffect>("deferred_lighting").getShader();
const auto& settings = engine.getSettings(); 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<ShadowMap>(resolution);
wideShadowMap = std::make_unique<ShadowMap>(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<ShadowMap>(resolution);
wideShadowMap = std::make_unique<ShadowMap>(resolution);
}
const auto& worldInfo = world->getInfo(); const auto& worldInfo = world->getInfo();
float sqrtT = glm::sqrt(weather.t); float clouds = weather.clouds();
float clouds = weather.b.clouds * sqrtT +
weather.a.clouds * (1.0f - sqrtT);
clouds = glm::max(worldInfo.fog, clouds); clouds = glm::max(worldInfo.fog, clouds);
float mie = 1.0f + glm::max(worldInfo.fog, clouds * 0.5f) * 2.0f; float mie = 1.0f + glm::max(worldInfo.fog, clouds * 0.5f) * 2.0f;
skybox->refresh(pctx, worldInfo.daytime, mie, 4); skybox->refresh(pctx, worldInfo.daytime, mie, 4);
const auto& assets = *engine.getAssets(); chunks->update();
auto& linesShader = assets.require<Shader>("lines");
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<Shader>("lines");
/* World render scope with diegetic HUD included */ { /* World render scope with diegetic HUD included */ {
DrawContext wctx = pctx.sub(); DrawContext wctx = pctx.sub();
postProcessing.use(wctx); postProcessing.use(wctx, gbufferPipeline);
display::clearDepth(); display::clearDepth();
// Drawing background sky plane
skybox->draw(pctx, camera, assets, worldInfo.daytime, clouds);
/* Actually world render with depth buffer on */ { /* Actually world render with depth buffer on */ {
DrawContext ctx = wctx.sub(); DrawContext ctx = wctx.sub();
ctx.setDepthTest(true); ctx.setDepthTest(true);
@ -376,19 +511,59 @@ void WorldRenderer::draw(
ctx, camera, *lineBatch, linesShader, showChunkBorders ctx, camera, *lineBatch, linesShader, showChunkBorders
); );
} }
if (player.currentCamera == player.fpCamera) {
renderHands(camera, delta);
}
} }
} }
{ texts->render(pctx, camera, settings, hudVisible, true);
DrawContext ctx = wctx.sub();
texts->render(ctx, camera, settings, hudVisible, true);
}
renderBlockOverlay(wctx);
} }
skybox->bind();
float fogFactor =
15.0f / static_cast<float>(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<const WeatherPreset*, 2> 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) { void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {

View File

@ -9,6 +9,7 @@
#include "presets/WeatherPreset.hpp" #include "presets/WeatherPreset.hpp"
#include "world/Weather.hpp" #include "world/Weather.hpp"
#include "window/Camera.hpp"
class Level; class Level;
class Player; class Player;
@ -30,8 +31,16 @@ class PostProcessing;
class DrawContext; class DrawContext;
class ModelBatch; class ModelBatch;
class Assets; class Assets;
class ShadowMap;
class GBuffer;
struct EngineSettings; struct EngineSettings;
struct CompileTimeShaderSettings {
bool advancedRender = false;
bool shadows = false;
bool ssao = false;
};
class WorldRenderer { class WorldRenderer {
Engine& engine; Engine& engine;
const Level& level; const Level& level;
@ -44,11 +53,19 @@ class WorldRenderer {
std::unique_ptr<GuidesRenderer> guides; std::unique_ptr<GuidesRenderer> guides;
std::unique_ptr<ChunksRenderer> chunks; std::unique_ptr<ChunksRenderer> chunks;
std::unique_ptr<Skybox> skybox; std::unique_ptr<Skybox> skybox;
std::unique_ptr<ShadowMap> shadowMap;
std::unique_ptr<ShadowMap> wideShadowMap;
Weather weather {}; Weather weather {};
Camera shadowCamera;
Camera wideShadowCamera;
float timer = 0.0f; float timer = 0.0f;
bool debug = false; bool debug = false;
bool lightsDebug = false; bool lightsDebug = false;
bool gbufferPipeline = false;
bool shadows = false;
CompileTimeShaderSettings prevCTShaderSettings {};
/// @brief Render block selection lines /// @brief Render block selection lines
void renderBlockSelection(); void renderBlockSelection();
@ -70,6 +87,14 @@ class WorldRenderer {
const EngineSettings& settings, const EngineSettings& settings,
float fogFactor float fogFactor
); );
void generateShadowsMap(
const Camera& camera,
const DrawContext& pctx,
ShadowMap& shadowMap,
Camera& shadowCamera,
float scale
);
public: public:
std::unique_ptr<ParticlesRenderer> particles; std::unique_ptr<ParticlesRenderer> particles;
std::unique_ptr<TextsRenderer> texts; std::unique_ptr<TextsRenderer> texts;

View File

@ -14,11 +14,13 @@ struct ChunkVertex {
glm::vec3 position; glm::vec3 position;
glm::vec2 uv; glm::vec2 uv;
std::array<uint8_t, 4> color; std::array<uint8_t, 4> color;
std::array<uint8_t, 4> normal;
static constexpr VertexAttribute ATTRIBUTES[] = { static constexpr VertexAttribute ATTRIBUTES[] = {
{VertexAttribute::Type::FLOAT, false, 3}, {VertexAttribute::Type::FLOAT, false, 3},
{VertexAttribute::Type::FLOAT, false, 2}, {VertexAttribute::Type::FLOAT, false, 2},
{VertexAttribute::Type::UNSIGNED_BYTE, true, 4}, {VertexAttribute::Type::UNSIGNED_BYTE, true, 4},
{VertexAttribute::Type::UNSIGNED_BYTE, true, 4},
{{}, 0}}; {{}, 0}};
}; };

View File

@ -38,6 +38,8 @@ GUI::GUI(Engine& engine)
std::make_unique<Camera>(glm::vec3(), engine.getWindow().getSize().y); std::make_unique<Camera>(glm::vec3(), engine.getWindow().getSize().y);
uicamera->perspective = false; uicamera->perspective = false;
uicamera->flipped = true; uicamera->flipped = true;
uicamera->near = -1.0f;
uicamera->far = 1.0f;
menu = std::make_shared<Menu>(*this); menu = std::make_shared<Menu>(*this);
menu->setId("menu"); menu->setId("menu");

View File

@ -74,6 +74,9 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) {
builder.add("chunk-max-vertices", &settings.graphics.chunkMaxVertices); builder.add("chunk-max-vertices", &settings.graphics.chunkMaxVertices);
builder.add("chunk-max-vertices-dense", &settings.graphics.chunkMaxVerticesDense); builder.add("chunk-max-vertices-dense", &settings.graphics.chunkMaxVerticesDense);
builder.add("chunk-max-renderers", &settings.graphics.chunkMaxRenderers); 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.section("ui");
builder.add("language", &settings.ui.language); builder.add("language", &settings.ui.language);

View File

@ -17,7 +17,10 @@ static int l_set_effect(lua::State* L) {
size_t index = static_cast<size_t>(lua::tointeger(L, 1)); size_t index = static_cast<size_t>(lua::tointeger(L, 1));
auto name = lua::require_string(L, 2); auto name = lua::require_string(L, 2);
auto& assets = *engine->getAssets(); auto& assets = *engine->getAssets();
auto effect = std::make_shared<PostEffect>(assets.require<PostEffect>(name)); auto effect = assets.getShared<PostEffect>(name);
if (effect == nullptr) {
throw std::runtime_error(std::string("post-effect '") + name + "' not found");
}
post_processing->setEffect(index, std::move(effect)); post_processing->setEffect(index, std::move(effect));
return 0; return 0;
} }
@ -57,6 +60,22 @@ static int l_set_params(lua::State* L) {
return 0; return 0;
} }
static int l_set_array(lua::State* L) {
size_t index = static_cast<size_t>(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<ubyte> buffer(
reinterpret_cast<const ubyte*>(data.data()),
reinterpret_cast<const ubyte*>(data.data() + data.size())
);
effect->setArray(key, std::move(buffer));
return 0;
}
const luaL_Reg posteffectslib[] = { const luaL_Reg posteffectslib[] = {
{"index", lua::wrap<l_index>}, {"index", lua::wrap<l_index>},
{"set_effect", lua::wrap<l_set_effect>}, {"set_effect", lua::wrap<l_set_effect>},
@ -64,5 +83,6 @@ const luaL_Reg posteffectslib[] = {
{"set_intensity", lua::wrap<l_set_intensity>}, {"set_intensity", lua::wrap<l_set_intensity>},
{"is_active", lua::wrap<l_is_active>}, {"is_active", lua::wrap<l_is_active>},
{"set_params", lua::wrap<l_set_params>}, {"set_params", lua::wrap<l_set_params>},
{"set_array", lua::wrap<l_set_array>},
{NULL, NULL} {NULL, NULL}
}; };

View File

@ -1,5 +1,6 @@
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <random>
#include "libs/api_lua.hpp" #include "libs/api_lua.hpp"
#include "debug/Logger.hpp" #include "debug/Logger.hpp"
@ -149,6 +150,15 @@ int l_debug_print(lua::State* L) {
return 0; return 0;
} }
namespace {
std::normal_distribution<double> 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) { void initialize_libs_extends(lua::State* L) {
if (lua::getglobal(L, "debug")) { if (lua::getglobal(L, "debug")) {
lua::pushcfunction(L, lua::wrap<l_debug_error>); lua::pushcfunction(L, lua::wrap<l_debug_error>);
@ -163,6 +173,12 @@ void initialize_libs_extends(lua::State* L) {
lua::pushcfunction(L, lua::wrap<l_debug_print>); lua::pushcfunction(L, lua::wrap<l_debug_print>);
lua::setfield(L, "print"); lua::setfield(L, "print");
lua::pop(L);
}
if (lua::getglobal(L, "math")) {
lua::pushcfunction(L, lua::wrap<l_math_normal_random>);
lua::setfield(L, "normal_random");
lua::pop(L); lua::pop(L);
} }
} }

View File

@ -75,6 +75,12 @@ struct GraphicsSettings {
IntegerSetting chunkMaxVerticesDense {800'000, 0, 8'000'000}; IntegerSetting chunkMaxVerticesDense {800'000, 0, 8'000'000};
/// @brief Limit of chunk renderers count /// @brief Limit of chunk renderers count
IntegerSetting chunkMaxRenderers {6, -4, 32}; 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 { struct DebugSettings {

View File

@ -28,12 +28,15 @@ void Camera::rotate(float x, float y, float z) {
} }
glm::mat4 Camera::getProjection() const { glm::mat4 Camera::getProjection() const {
if (projset) {
return projection;
}
if (perspective) { if (perspective) {
return glm::perspective(fov * zoom, ar, near, far); return glm::perspective(fov * zoom, ar, near, far);
} else if (flipped) { } 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 { } 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) { if (perspective) {
return glm::lookAt(camera_pos, camera_pos + front, up); return glm::lookAt(camera_pos, camera_pos + front, up);
} else { } 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; return fov;
} }
void Camera::setProjection(const glm::mat4& matrix) {
projection = matrix;
projset = true;
}
float Camera::getAspectRatio() const { float Camera::getAspectRatio() const {
return ar; return ar;
} }

View File

@ -19,7 +19,10 @@ public:
bool perspective = true; bool perspective = true;
bool flipped = false; bool flipped = false;
float near = 0.05f; float near = 0.05f;
float far = 1500.0f; float far = 1e4f;
bool projset = false;
glm::mat4 projection;
Camera() { Camera() {
updateVectors(); updateVectors();
@ -36,6 +39,8 @@ public:
void setFov(float fov); void setFov(float fov);
float getFov() const; float getFov() const;
void setProjection(const glm::mat4& matrix);
float getAspectRatio() const; float getAspectRatio() const;
void setAspectRatio(float ar); void setAspectRatio(float ar);
}; };

View File

@ -519,6 +519,7 @@ public:
} }
std::unique_ptr<ImageData> takeScreenshot() override { std::unique_ptr<ImageData> takeScreenshot() override {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
auto data = std::make_unique<ubyte[]>(size.x * size.y * 3); auto data = std::make_unique<ubyte[]>(size.x * size.y * 3);
glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, size.x, size.y, GL_RGB, GL_UNSIGNED_BYTE, data.get()); glReadPixels(0, 0, size.x, size.y, GL_RGB, GL_UNSIGNED_BYTE, data.get());

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <glm/glm.hpp>
#include <string> #include <string>
#include "presets/WeatherPreset.hpp" #include "presets/WeatherPreset.hpp"
@ -45,6 +46,11 @@ struct Weather : Serializable {
return b.thunderRate * t + a.thunderRate * (1.0f - t); 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; dv::value serialize() const override;
void deserialize(const dv::value& src) override; void deserialize(const dv::value& src) override;
}; };