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"
],
"emission": [13, 13, 12],
"emission": [13, 10, 2],
"model": "aabb",
"hitbox": [0.4375, 0.0, 0.4375, 0.125, 0.5, 0.125],
"light-passing": true,

View File

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

View File

@ -42,4 +42,7 @@ function on_open()
create_setting("graphics.gamma", "Gamma", 0.05, "", "graphics.gamma.tooltip")
create_checkbox("graphics.backlight", "Backlight", "graphics.backlight.tooltip")
create_checkbox("graphics.dense-render", "Dense blocks render", "graphics.dense-render.tooltip")
create_checkbox("graphics.advanced-render", "Advanced render", "graphics.advanced-render.tooltip")
create_checkbox("graphics.ssao", "SSAO", "graphics.ssao.tooltip")
create_setting("graphics.shadows-quality", "Shadows quality", 1)
end

View File

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

View File

@ -4,5 +4,8 @@
"third-person-front",
"third-person-back",
"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()
input.add_callback("player.pick", function ()
if hud.is_paused() or hud.is_inventory_open() then
@ -55,4 +79,6 @@ function on_hud_open()
player.set_vel(pid, 0, 1, 0)
end
end)
configure_SSAO()
end

View File

@ -1,9 +1,14 @@
in vec3 v_coord;
out vec4 f_color;
layout (location = 0) out vec4 f_color;
layout (location = 1) out vec4 f_position;
layout (location = 2) out vec4 f_normal;
uniform samplerCube u_cubemap;
uniform mat4 u_view;
uniform samplerCube u_skybox;
void main(){
vec3 dir = normalize(v_coord);
f_color = texture(u_cubemap, dir);
vec3 dir = normalize(v_coord) * 1e6;
f_position = u_view * vec4(dir, 1.0);
f_normal = vec4(0.0, 0.0, 1.0, 1.0);
f_color = texture(u_skybox, dir);
}

View File

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

View File

@ -2,8 +2,22 @@ in vec2 v_uv;
out vec4 f_color;
uniform sampler2D u_screen;
uniform sampler2D u_position;
uniform sampler2D u_normal;
uniform sampler2D u_emission;
uniform sampler2D u_noise;
uniform sampler2D u_ssao;
uniform samplerCube u_skybox;
uniform ivec2 u_screenSize;
uniform float u_intensity;
uniform float u_timer;
uniform bool u_enableShadows;
uniform mat4 u_projection;
uniform mat4 u_view;
uniform mat4 u_inverseView;
uniform vec3 u_sunDir;
uniform vec3 u_cameraPos;
#include <__effect__>

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 vec3 a_dir;
in float a_fog;
out vec4 f_color;
in vec3 a_normal;
in vec3 a_position;
in vec3 a_realnormal;
in vec4 a_color;
in vec4 a_modelpos;
in float a_emission;
uniform sampler2D u_texture0;
uniform samplerCube u_cubemap;
uniform samplerCube u_skybox;
uniform vec3 u_fogColor;
uniform float u_fogFactor;
uniform float u_fogCurve;
uniform bool u_alphaClip;
uniform vec3 u_sunDir;
#include <shadows>
void main() {
vec3 fogColor = texture(u_cubemap, a_dir).rgb;
vec4 tex_color = texture(u_texture0, a_texCoord);
float alpha = a_color.a * tex_color.a;
vec4 texColor = texture(u_texture0, a_texCoord);
float alpha = a_color.a * texColor.a;
// anyway it's any alpha-test alternative required
if (alpha < (u_alphaClip ? 0.5f : 0.15f))
if (alpha < (u_alphaClip ? 0.5f : 0.15f)) {
discard;
f_color = mix(a_color * tex_color, vec4(fogColor,1.0), a_fog);
}
f_color = a_color * texColor;
#ifndef ADVANCED_RENDER
vec3 fogColor = texture(u_skybox, a_dir).rgb;
f_color = mix(f_color, vec4(fogColor, 1.0), a_fog);
#endif
f_color.a = alpha;
f_position = vec4(a_position, 1.0);
f_normal = vec4(a_normal, 1.0);
f_emission = vec4(vec3(a_emission), 1.0);
}

View File

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

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;
in vec2 a_texCoord;
layout (location = 0) out vec4 f_color;
layout (location = 1) out vec4 f_position;
layout (location = 2) out vec4 f_normal;
layout (location = 3) out vec4 f_emission;
in float a_distance;
in float a_fog;
in vec2 a_texCoord;
in vec3 a_dir;
out vec4 f_color;
in vec3 a_normal;
in vec3 a_position;
in vec3 a_realnormal;
in vec3 a_skyLight;
in vec4 a_modelpos;
in vec4 a_torchLight;
in float a_emission;
uniform sampler2D u_texture0;
uniform samplerCube u_cubemap;
uniform samplerCube u_skybox;
uniform vec3 u_sunDir;
// flags
uniform bool u_alphaClip;
uniform bool u_debugLights;
uniform bool u_debugNormals;
#include <shadows>
void main() {
vec3 fogColor = texture(u_cubemap, a_dir).rgb;
vec4 tex_color = texture(u_texture0, a_texCoord);
if (u_debugLights)
tex_color.rgb = vec3(1.0);
float alpha = a_color.a * tex_color.a;
vec4 texColor = texture(u_texture0, a_texCoord);
float alpha = texColor.a;
if (u_alphaClip) {
if (alpha < 0.2f)
discard;
@ -23,6 +37,20 @@ void main() {
if (alpha < 0.002f)
discard;
}
f_color = mix(a_color * tex_color, vec4(fogColor,1.0), a_fog);
if (u_debugLights)
texColor.rgb = u_debugNormals ? (a_normal * 0.5 + 0.5) : vec3(1.0);
else if (u_debugNormals) {
texColor.rgb *= a_normal * 0.5 + 0.5;
}
f_color = texColor;
f_color.rgb *= min(vec3(1.0), a_torchLight.rgb + a_skyLight);
#ifndef ADVANCED_RENDER
vec3 fogColor = texture(u_skybox, a_dir).rgb;
f_color = mix(f_color, vec4(fogColor, 1.0), a_fog);
#endif
f_color.a = alpha;
f_position = vec4(a_position, 1.0);
f_normal = vec4(a_normal, 1.0);
f_emission = vec4(vec3(a_emission), 1.0);
}

View File

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

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)
1e12f, // max dist, essentially the scene depth
vec3(0.0f), // scene color, the color of the current pixel being rendered
u_lightDir, // light direction
vec3(u_lightDir.x, pow(u_lightDir.y, 3.0), u_lightDir.z), // light direction
vec3(40.0*fog), // light intensity, 40 looks nice
PLANET_POS, // position of the planet
PLANET_RADIUS, // radius of the planet in meters

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

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

View File

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

View File

@ -76,6 +76,14 @@ static auto process_program(const ResPaths& paths, const std::string& filename)
return std::make_pair(vertex, fragment);
}
static auto read_program(const ResPaths& paths, const std::string& filename) {
io::path vertexFile = paths.find(filename + ".glslv");
io::path fragmentFile = paths.find(filename + ".glslf");
return std::make_pair(
io::read_string(vertexFile), io::read_string(fragmentFile)
);
}
assetload::postfunc assetload::shader(
AssetsLoader*,
const ResPaths& paths,
@ -83,21 +91,18 @@ assetload::postfunc assetload::shader(
const std::string& name,
const std::shared_ptr<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 fragmentFile = paths.find(filename + ".glslf");
std::string vertexSource = std::move(vertex.code);
std::string fragmentSource = std::move(fragment.code);
return [=](auto assets) {
assets->store(
Shader::create(
vertexFile.string(),
fragmentFile.string(),
vertexSource,
fragmentSource
{vertexFile.string(), vertex},
{fragmentFile.string(), fragment}
),
name
);
@ -127,13 +132,16 @@ assetload::postfunc assetload::posteffect(
return [=](auto assets) {
auto program = Shader::create(
effectFile.string(),
effectFile.string(),
vertexSource,
fragmentSource
{effectFile.string(), vertexSource},
{effectFile.string(), fragmentSource}
);
bool advanced = false;
if (settings) {
advanced = dynamic_cast<const PostEffectCfg*>(settings.get())->advanced;
}
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(
const io::path& file, uint linenum, const std::string& message
) {
@ -98,6 +106,8 @@ inline void source_line(std::stringstream& ss, uint linenum) {
static Value default_value_for(Type type) {
switch (type) {
case Type::INT:
return 0;
case Type::FLOAT:
return 0.0f;
case Type::VEC2:
@ -156,7 +166,6 @@ public:
}
bool processVersionDirective() {
parsing_warning(filename, line, "removed #version directive");
source_line(ss, line);
skipLine();
return false;
@ -183,6 +192,8 @@ public:
Value parseDefaultValue(Type type, const std::string& name) {
switch (type) {
case Type::INT:
return static_cast<int>(parseNumber(1).asInteger());
case Type::FLOAT:
return static_cast<float>(parseNumber(1).asNumber());
case Type::VEC2:
@ -212,8 +223,22 @@ public:
if (params.find(paramName) != params.end()) {
throw error("duplicating param " + util::quote(paramName));
}
skipWhitespace(false);
ss << "uniform " << typeName << " " << paramName << ";\n";
int start = pos;
ss << "uniform " << typeName << " " << paramName;
bool array = false;
if (peekNoJump() == '[') {
skip(1);
array = true;
readUntil(']');
skip(1);
ss << source.substr(start, pos - start + 1);
}
ss << ";\n";
auto defValue = default_value_for(type);
// Parse default value
@ -225,7 +250,7 @@ public:
skipLine();
params[paramName] = PostEffect::Param(type, std::move(defValue));
params[paramName] = PostEffect::Param(type, std::move(defValue), array);
return false;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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 "data/dv_util.hpp"
#include "debug/Logger.hpp"
static debug::Logger logger("post-effect");
PostEffect::Param::Param() : type(Type::FLOAT) {}
PostEffect::Param::Param(Type type, Value defValue)
: type(type), defValue(defValue), value(defValue) {
PostEffect::Param::Param(Type type, Value defValue, bool array)
: type(type), defValue(defValue), value(defValue), array(array) {
}
PostEffect::PostEffect(
bool advanced,
std::shared_ptr<Shader> shader,
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() {
@ -23,27 +86,24 @@ Shader& PostEffect::use() {
if (!param.dirty) {
continue;
}
switch (param.type) {
case Param::Type::FLOAT:
shader->uniform1f(name, std::get<float>(param.value));
break;
case Param::Type::VEC2:
shader->uniform2f(name, std::get<glm::vec2>(param.value));
break;
case Param::Type::VEC3:
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);
if (param.array) {
const auto& found = arrayValues.find(name);
if (found == arrayValues.end()) {
continue;
}
apply_uniform_array(param, *shader, name, found->second);
} else {
apply_uniform_value(param, *shader, name);
}
param.dirty = false;
}
return *shader;
}
Shader& PostEffect::getShader() {
return *shader;
}
float PostEffect::getIntensity() const {
return intensity;
}
@ -66,6 +126,9 @@ void PostEffect::setParam(const std::string& name, const dv::value& value) {
}
auto& param = found->second;
switch (param.type) {
case Param::Type::INT:
param.value = static_cast<int>(value.asInteger());
break;
case Param::Type::FLOAT:
param.value = static_cast<float>(value.asNumber());
break;
@ -81,3 +144,20 @@ void PostEffect::setParam(const std::string& name, const dv::value& value) {
}
param.dirty = true;
}
void PostEffect::setArray(const std::string& name, std::vector<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
#include <vector>
#include <memory>
#include <string>
#include <variant>
#include <unordered_map>
#include <glm/glm.hpp>
#include "typedefs.hpp"
#include "data/dv_fwd.hpp"
#include "util/EnumMetadata.hpp"
@ -14,27 +16,30 @@ class Shader;
class PostEffect {
public:
struct Param {
enum class Type { FLOAT, VEC2, VEC3, VEC4 };
enum class Type { INT, FLOAT, VEC2, VEC3, VEC4 };
VC_ENUM_METADATA(Type)
{"int", Type::INT},
{"float", Type::FLOAT},
{"vec2", Type::VEC2},
{"vec3", Type::VEC3},
{"vec4", Type::VEC4},
VC_ENUM_END
using Value = std::variant<float, glm::vec2, glm::vec3, glm::vec4>;
using Value = std::variant<int, float, glm::vec2, glm::vec3, glm::vec4>;
Type type;
Value defValue;
Value value;
bool array = false;
bool dirty = true;
Param();
Param(Type type, Value defValue);
Param(Type type, Value defValue, bool array);
};
PostEffect(
bool advanced,
std::shared_ptr<Shader> shader,
std::unordered_map<std::string, Param> params
);
@ -43,16 +48,26 @@ public:
Shader& use();
Shader& getShader();
float getIntensity() const;
void setIntensity(float value);
void setParam(const std::string& name, const dv::value& value);
void setArray(const std::string& name, std::vector<ubyte>&& values);
bool isAdvanced() const {
return advanced;
}
bool isActive() {
return intensity > 1e-4f;
}
private:
bool advanced = false;
std::shared_ptr<Shader> shader;
std::unordered_map<std::string, Param> params;
std::unordered_map<std::string, std::vector<ubyte>> arrayValues;
float intensity = 0.0f;
};

View File

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

View File

@ -11,6 +11,9 @@ class Framebuffer;
class DrawContext;
class ImageData;
class PostEffect;
class Camera;
class GBuffer;
class Shader;
struct PostProcessingVertex {
glm::vec2 position;
@ -24,25 +27,31 @@ struct PostProcessingVertex {
/// @attention Current implementation does not support multiple render passes
/// for multiple effects. Will be implemented in v0.21
class PostProcessing {
/// @brief Main framebuffer (lasy field)
std::unique_ptr<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:
PostProcessing(size_t effectSlotsCount);
~PostProcessing();
/// @brief Prepare and bind framebuffer
/// @param context graphics context will be modified
void use(DrawContext& context);
void use(DrawContext& context, bool gbufferPipeline);
void renderDeferredShading(
const DrawContext& context,
const Assets& assets,
float timer,
const Camera& camera
);
/// @brief Render fullscreen quad using the passed shader
/// with framebuffer texture bound
/// @param context graphics context
/// @throws std::runtime_error if use(...) wasn't called before
void render(const DrawContext& context, const Assets& assets, float timer);
void render(
const DrawContext& context,
const Assets& assets,
float timer,
const Camera& camera
);
void setEffect(size_t slot, std::shared_ptr<PostEffect> effect);
@ -52,4 +61,24 @@ public:
std::unique_ptr<ImageData> toImage();
Framebuffer* getFramebuffer() const;
void bindDepthBuffer();
private:
void configureEffect(
const DrawContext& context,
PostEffect& effect,
Shader& shader,
float timer,
const Camera& camera
);
void refreshFbos(uint width, uint height);
/// @brief Main framebuffer (lasy field)
std::unique_ptr<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 "coders/GLSLExtension.hpp"
#include "debug/Logger.hpp"
static debug::Logger logger("gl-shader");
namespace fs = std::filesystem;
GLSLExtension* Shader::preprocessor = new GLSLExtension();
Shader* Shader::used = nullptr;
Shader::Shader(uint id) : id(id){
}
Shader::Shader(uint id, Source&& vertexSource, Source&& fragmentSource)
: id(id),
vertexSource(std::move(vertexSource)),
fragmentSource(std::move(fragmentSource)) {}
Shader::~Shader(){
Shader::~Shader() {
glDeleteProgram(id);
}
void Shader::use(){
void Shader::use() {
used = this;
glUseProgram(id);
}
@ -40,8 +45,16 @@ uint Shader::getUniformLocation(const std::string& name) {
return found->second;
}
void Shader::uniformMatrix(const std::string& name, const glm::mat4& matrix){
glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix));
void Shader::uniformMatrix(const std::string& name, const glm::mat4& matrix) {
glUniformMatrix4fv(
getUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix)
);
}
void Shader::uniformMatrix(const std::string& name, const glm::mat3& matrix) {
glUniformMatrix3fv(
getUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix)
);
}
void Shader::uniform1i(const std::string& name, int x){
@ -76,8 +89,27 @@ void Shader::uniform4f(const std::string& name, const glm::vec4& xyzw) {
glUniform4f(getUniformLocation(name), xyzw.x, xyzw.y, xyzw.z, xyzw.w);
}
void Shader::uniform1v(const std::string& name, int length, const int* v) {
glUniform1iv(getUniformLocation(name), length, v);
}
inline auto shader_deleter = [](GLuint* shader) {
void Shader::uniform1v(const std::string& name, int length, const float* v) {
glUniform1fv(getUniformLocation(name), length, v);
}
void Shader::uniform2v(const std::string& name, int length, const float* v) {
glUniform2fv(getUniformLocation(name), length, v);
}
void Shader::uniform3v(const std::string& name, int length, const float* v) {
glUniform3fv(getUniformLocation(name), length, v);
}
void Shader::uniform4v(const std::string& name, int length, const float* v) {
glUniform4fv(getUniformLocation(name), length, v);
}
static inline auto shader_deleter = [](GLuint* shader) {
glDeleteShader(*shader);
delete shader;
};
@ -93,45 +125,73 @@ glshader compile_shader(GLenum type, const GLchar* source, const std::string& fi
glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success){
if (!success) {
GLchar infoLog[GL_LOG_LEN];
glGetShaderInfoLog(shader, GL_LOG_LEN, nullptr, infoLog);
glDeleteShader(shader);
throw std::runtime_error(
"vertex shader compilation failed ("+file+"):\n"+std::string(infoLog)
"vertex shader compilation failed (" + file + "):\n" +
std::string(infoLog)
);
}
}
return glshader(new GLuint(shader), shader_deleter); //-V508
}
std::unique_ptr<Shader> Shader::create(
const std::string& vertexFile,
const std::string& fragmentFile,
const std::string& vertexCode,
const std::string& fragmentCode
static GLuint compile_program(
const Shader::Source& vertexSource, const Shader::Source& fragmentSource
) {
auto& preprocessor = *Shader::preprocessor;
auto vertexCode = std::move(
preprocessor.process(vertexSource.file, vertexSource.code).code
);
auto fragmentCode = std::move(
preprocessor.process(fragmentSource.file, fragmentSource.code).code
);
const GLchar* vCode = vertexCode.c_str();
const GLchar* fCode = fragmentCode.c_str();
glshader vertex = compile_shader(GL_VERTEX_SHADER, vCode, vertexFile);
glshader fragment = compile_shader(GL_FRAGMENT_SHADER, fCode, fragmentFile);
glshader vertex =
compile_shader(GL_VERTEX_SHADER, vCode, vertexSource.file);
glshader fragment =
compile_shader(GL_FRAGMENT_SHADER, fCode, fragmentSource.file);
// Shader Program
GLint success;
GLuint id = glCreateProgram();
glAttachShader(id, *vertex);
glAttachShader(id, *fragment);
glLinkProgram(id);
GLuint program = glCreateProgram();
glAttachShader(program, *vertex);
glAttachShader(program, *fragment);
glLinkProgram(program);
glGetProgramiv(id, GL_LINK_STATUS, &success);
if (!success){
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
GLchar infoLog[GL_LOG_LEN];
glGetProgramInfoLog(id, GL_LOG_LEN, nullptr, infoLog);
glGetProgramInfoLog(program, GL_LOG_LEN, nullptr, infoLog);
throw std::runtime_error(
"shader program linking failed:\n"+std::string(infoLog)
"shader program linking failed:\n" + std::string(infoLog)
);
}
return std::make_unique<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() {

View File

@ -10,19 +10,30 @@
class GLSLExtension;
class Shader {
public:
struct Source {
std::string file;
std::string code;
};
private:
static Shader* used;
uint id;
std::unordered_map<std::string, uint> uniformLocations;
// source code used for re-compiling shaders after updating defines
Source vertexSource;
Source fragmentSource;
uint getUniformLocation(const std::string& name);
public:
static GLSLExtension* preprocessor;
Shader(uint id);
Shader(uint id, Source&& vertexSource, Source&& fragmentSource);
~Shader();
void use();
void uniformMatrix(const std::string&, const glm::mat4& matrix);
void uniformMatrix(const std::string&, const glm::mat3& matrix);
void uniform1i(const std::string& name, int x);
void uniform1f(const std::string& name, float x);
void uniform2f(const std::string& name, float x, float y);
@ -32,17 +43,19 @@ public:
void uniform3f(const std::string& name, const glm::vec3& xyz);
void uniform4f(const std::string& name, const glm::vec4& xyzw);
void uniform1v(const std::string& name, int length, const int* v);
void uniform1v(const std::string& name, int length, const float* v);
void uniform2v(const std::string& name, int length, const float* v);
void uniform3v(const std::string& name, int length, const float* v);
void uniform4v(const std::string& name, int length, const float* v);
/// @brief Re-preprocess source code and re-compile shader program
void recompile();
/// @brief Create shader program using vertex and fragment shaders source.
/// @param vertexFile vertex shader file name
/// @param fragmentFile fragment shader file name
/// @param vertexSource vertex shader source code
/// @param fragmentSource fragment shader source code
/// @return linked shader program containing vertex and fragment shaders
static std::unique_ptr<Shader> create(
const std::string& vertexFile,
const std::string& fragmentFile,
const std::string& vertexSource,
const std::string& fragmentSource
Source&& vertexSource, Source&& fragmentSource
);
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
};
namespace advanced_pipeline {
inline constexpr int TARGET_COLOR = 0;
inline constexpr int TARGET_SKYBOX = 1;
inline constexpr int TARGET_POSITIONS = 2;
inline constexpr int TARGET_NORMALS = 3;
inline constexpr int TARGET_EMISSION = 4;
inline constexpr int TARGET_SSAO = 5;
inline constexpr int TARGET_SHADOWS0 = 6;
inline constexpr int TARGET_SHADOWS1 = 7;
}
VC_ENUM_METADATA(CursorShape)
{"arrow", CursorShape::ARROW},
{"text", CursorShape::TEXT},
@ -69,3 +80,11 @@ public:
virtual void flush() = 0;
};
class Bindable {
public:
virtual ~Bindable() = default;
virtual void bind() = 0;
virtual void unbind() = 0;
};

View File

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

View File

@ -44,7 +44,14 @@ class BlocksRenderer {
SortingMeshData sortingMesh;
void vertex(const glm::vec3& coord, float u, float v, const glm::vec4& light);
void vertex(
const glm::vec3& coord,
float u,
float v,
const glm::vec4& light,
const glm::vec3& normal,
float emission
);
void index(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f);
void vertexAO(

View File

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

View File

@ -50,7 +50,7 @@ class ChunksRenderer {
std::vector<ChunksSortEntry> indices;
util::ThreadPool<std::shared_ptr<Chunk>, RendererResult> threadPool;
const Mesh<ChunkVertex>* retrieveChunk(
size_t index, const Camera& camera, Shader& shader, bool culling
size_t index, const Camera& camera, bool culling
);
public:
ChunksRenderer(
@ -72,6 +72,9 @@ public:
const Mesh<ChunkVertex>* getOrRender(
const std::shared_ptr<Chunk>& chunk, bool important
);
void drawChunksShadowsPass(const Camera& camera, Shader& shader);
void drawChunks(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);
quad(
coord + Z * size.z * 0.5f,
X, Y, glm::vec2(size.x, size.y),
(shading ? do_tint(0.8) * tint : tint),
glm::vec3(1.0f), texfaces[5]
coord + Z * size.z * 0.5f,
X, Y, Z, glm::vec2(size.x, size.y),
(shading ? do_tint(0.8) * tint : tint),
glm::vec3(1.0f), texfaces[5]
);
quad(
coord - Z * size.z * 0.5f,
-X, Y, glm::vec2(size.x, size.y),
(shading ? do_tint(0.9f) * tint : tint),
glm::vec3(1.0f), texfaces[4]
coord - Z * size.z * 0.5f,
-X, Y, -Z, glm::vec2(size.x, size.y),
(shading ? do_tint(0.9f) * tint : tint),
glm::vec3(1.0f), texfaces[4]
);
quad(
coord + Y * size.y * 0.5f,
-X, Z, glm::vec2(size.x, size.z),
(shading ? do_tint(1.0f) * tint : tint),
glm::vec3(1.0f), texfaces[3]
coord + Y * size.y * 0.5f,
-X, Z, Y, glm::vec2(size.x, size.z),
(shading ? do_tint(1.0f) * tint : tint),
glm::vec3(1.0f), texfaces[3]
);
quad(
coord - Y * size.y * 0.5f,
X, Z, glm::vec2(size.x, size.z),
(shading ? do_tint(0.7f) * tint : tint),
glm::vec3(1.0f), texfaces[2]
coord - Y * size.y * 0.5f,
X, Z, -Y, glm::vec2(size.x, size.z),
(shading ? do_tint(0.7f) * tint : tint),
glm::vec3(1.0f), texfaces[2]
);
quad(
coord + X * size.x * 0.5f,
-Z, Y, glm::vec2(size.z, size.y),
(shading ? do_tint(0.8f) * tint : tint),
glm::vec3(1.0f), texfaces[1]
coord + X * size.x * 0.5f,
-Z, Y, X, glm::vec2(size.z, size.y),
(shading ? do_tint(0.8f) * tint : tint),
glm::vec3(1.0f), texfaces[1]
);
quad(
coord - X * size.x * 0.5f,
Z, Y, glm::vec2(size.z, size.y),
(shading ? do_tint(0.9f) * tint : tint),
glm::vec3(1.0f), texfaces[1]
coord - X * size.x * 0.5f,
Z, Y, -X, glm::vec2(size.z, size.y),
(shading ? do_tint(0.9f) * tint : tint),
glm::vec3(1.0f), texfaces[1]
);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -38,6 +38,8 @@ GUI::GUI(Engine& engine)
std::make_unique<Camera>(glm::vec3(), engine.getWindow().getSize().y);
uicamera->perspective = false;
uicamera->flipped = true;
uicamera->near = -1.0f;
uicamera->far = 1.0f;
menu = std::make_shared<Menu>(*this);
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-dense", &settings.graphics.chunkMaxVerticesDense);
builder.add("chunk-max-renderers", &settings.graphics.chunkMaxRenderers);
builder.add("advanced-render", &settings.graphics.advancedRender);
builder.add("ssao", &settings.graphics.ssao);
builder.add("shadows-quality", &settings.graphics.shadowsQuality);
builder.section("ui");
builder.add("language", &settings.ui.language);

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));
auto name = lua::require_string(L, 2);
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));
return 0;
}
@ -57,6 +60,22 @@ static int l_set_params(lua::State* L) {
return 0;
}
static int l_set_array(lua::State* L) {
size_t index = static_cast<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[] = {
{"index", lua::wrap<l_index>},
{"set_effect", lua::wrap<l_set_effect>},
@ -64,5 +83,6 @@ const luaL_Reg posteffectslib[] = {
{"set_intensity", lua::wrap<l_set_intensity>},
{"is_active", lua::wrap<l_is_active>},
{"set_params", lua::wrap<l_set_params>},
{"set_array", lua::wrap<l_set_array>},
{NULL, NULL}
};

View File

@ -1,5 +1,6 @@
#include <iostream>
#include <iomanip>
#include <random>
#include "libs/api_lua.hpp"
#include "debug/Logger.hpp"
@ -149,6 +150,15 @@ int l_debug_print(lua::State* L) {
return 0;
}
namespace {
std::normal_distribution<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) {
if (lua::getglobal(L, "debug")) {
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::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);
}
}

View File

@ -75,6 +75,12 @@ struct GraphicsSettings {
IntegerSetting chunkMaxVerticesDense {800'000, 0, 8'000'000};
/// @brief Limit of chunk renderers count
IntegerSetting chunkMaxRenderers {6, -4, 32};
/// @brief Advanced render pipeline
FlagSetting advancedRender {true};
/// @brief Screen space ambient occlusion
FlagSetting ssao {true};
/// @brief Shadows quality
IntegerSetting shadowsQuality {0, 0, 3};
};
struct DebugSettings {

View File

@ -28,12 +28,15 @@ void Camera::rotate(float x, float y, float z) {
}
glm::mat4 Camera::getProjection() const {
if (projset) {
return projection;
}
if (perspective) {
return glm::perspective(fov * zoom, ar, near, far);
} else if (flipped) {
return glm::ortho(0.0f, fov * ar, fov, 0.0f);
return glm::ortho(0.0f, fov * ar, fov, 0.0f, near, far);
} else {
return glm::ortho(0.0f, fov * ar, 0.0f, fov);
return glm::ortho(0.0f, fov * ar, 0.0f, fov, near, far);
}
}
@ -45,7 +48,8 @@ glm::mat4 Camera::getView(bool pos) const {
if (perspective) {
return glm::lookAt(camera_pos, camera_pos + front, up);
} else {
return glm::translate(glm::mat4(1.0f), camera_pos);
return glm::lookAt(camera_pos, camera_pos + front, up);
//return glm::translate(glm::mat4(1.0f), camera_pos);
}
}
@ -61,6 +65,11 @@ float Camera::getFov() const {
return fov;
}
void Camera::setProjection(const glm::mat4& matrix) {
projection = matrix;
projset = true;
}
float Camera::getAspectRatio() const {
return ar;
}

View File

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

View File

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

View File

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