From a8d2ddbacdbbb0e9c3e352e0bbdd695feb1f4971 Mon Sep 17 00:00:00 2001 From: zhenyan121 <104683324+zhenyan121@users.noreply.github.com> Date: Sat, 20 Jun 2026 09:04:09 +0800 Subject: [PATCH] feat: water effects (#19) * feat(shaders): add lighting to block accumulation OIT shaders * feat(renderer): add water rendering with OIT and new shaders * feat(water): implement screen-space reflection and refraction for water with depth fade and perturbation * refactor(shader): replace glUniform calls with generic set_loc and remove cached locations * feat(water): add noise-based caustics and configurable fog density * fix(water shader): update depth fade and remove underwater check - Increase DEPTH_FADE_DISTANCE from 8 to 10 - Remove conditional so depth fade always applies - Darken deepColor from (0, 0.08, 0.15) to (0, 0.015, 0.045) * feat(underwater): add volume scattering and shadow mapping for light shafts --- .../shaders/block_accumulation_f_shader.glsl | 77 ++++- .../shaders/block_accumulation_v_shader.glsl | 13 +- assets/shaders/sky_f_shader.glsl | 9 +- assets/shaders/under_water_f_shader.glsl | 116 ++++++- assets/shaders/under_water_v_shader.glsl | 13 +- assets/shaders/water_f_shader.glsl | 316 ++++++++++++++++++ assets/shaders/water_v_shader.glsl | 32 ++ include/Cubed/gameplay/chunk.hpp | 6 +- include/Cubed/gameplay/world.hpp | 2 + include/Cubed/primitive_data.hpp | 2 +- include/Cubed/renderer.hpp | 32 +- include/Cubed/shader.hpp | 23 ++ src/dev_panel.cpp | 10 + src/gameplay/chunk.cpp | 14 +- src/gameplay/world.cpp | 1 + src/renderer.cpp | 273 +++++++++------ 16 files changed, 813 insertions(+), 126 deletions(-) create mode 100644 assets/shaders/water_f_shader.glsl create mode 100644 assets/shaders/water_v_shader.glsl diff --git a/assets/shaders/block_accumulation_f_shader.glsl b/assets/shaders/block_accumulation_f_shader.glsl index 6720521..46c04ee 100644 --- a/assets/shaders/block_accumulation_f_shader.glsl +++ b/assets/shaders/block_accumulation_f_shader.glsl @@ -4,10 +4,21 @@ layout (location = 0) out vec4 accum; layout (location = 1) out float reveal; in vec2 tc; +in vec3 normal; +in vec3 vert_pos; +in float roughness; flat in int tex_layer; in float v_depth; layout (binding = 0) uniform sampler2DArray samp; +uniform float ambientStrength; +uniform vec3 sunlightColor; +uniform vec3 ambientColor; +uniform vec3 sunlightDir; +uniform vec3 cameraPos; +uniform bool shader_on; +uniform float specularStrength; + float weight(float z, float a) { float intermediate = 0.03 / (1e-5 + pow(z / 200.0, 4.0)); @@ -15,7 +26,71 @@ float weight(float z, float a) { } void main() { - vec4 color = texture(samp, vec3(tc, tex_layer)); + + vec4 objectColor = texture(samp, vec3(tc, tex_layer)); + + if (!shader_on) { + vec4 color = objectColor; + float alpha = color.a; + if (alpha < 1e-4) discard; + + + float w = weight(v_depth, alpha); + + accum = vec4(color.rgb * alpha * w, alpha * w); + + reveal = alpha; + return; + } + + vec3 lightDir = normalize(-sunlightDir); + + vec3 norm = normalize(normal); + + vec3 V = + normalize(cameraPos - vert_pos); + + vec3 H = + normalize(lightDir + V); + + vec3 ambient = ambientStrength * ambientColor; + + float diff = max(dot(norm, lightDir), 0.0); + + vec3 diffuse = diff * sunlightColor; + + float r = + clamp(roughness, 0.0, 1.0); + + float shininess = + mix( + 512.0, + 4.0, + r + ); + float ks = + mix( + 0.8, + 0.02, + r + ); + + float spec = 0.0; + + if(diff > 0.0) + { + spec = + ks * + pow( + max(dot(norm,H),0.0), + shininess + ); + } + + vec3 specular = spec * sunlightColor * specularStrength; + + vec4 color = vec4((ambient + diffuse) * objectColor.rgb + specular, objectColor.a); + float alpha = color.a; if (alpha < 1e-4) discard; diff --git a/assets/shaders/block_accumulation_v_shader.glsl b/assets/shaders/block_accumulation_v_shader.glsl index b7a22e5..f6f4c4c 100644 --- a/assets/shaders/block_accumulation_v_shader.glsl +++ b/assets/shaders/block_accumulation_v_shader.glsl @@ -3,19 +3,28 @@ layout (location = 0) in vec3 pos; layout (location = 1) in vec2 texCoord; layout (location = 2) in float layer; +layout (location = 3) in vec3 aNormal; +layout (location = 4) in float Roughness; + out vec2 tc; +out vec3 normal; +out vec3 vert_pos; flat out int tex_layer; +out float roughness; out float v_depth; uniform mat4 mv_matrix; uniform mat4 proj_matrix; - +uniform mat4 norm_matrix; void main(void) { vec4 view_pos = mv_matrix * vec4(pos, 1.0); - gl_Position = proj_matrix * view_pos; + vert_pos = pos; + roughness = Roughness; tc = texCoord; tex_layer = int(layer); v_depth = -view_pos.z; + normal = mat3(norm_matrix) * aNormal; + gl_Position = proj_matrix * view_pos; } diff --git a/assets/shaders/sky_f_shader.glsl b/assets/shaders/sky_f_shader.glsl index 0c9e0e6..e5672b0 100644 --- a/assets/shaders/sky_f_shader.glsl +++ b/assets/shaders/sky_f_shader.glsl @@ -46,7 +46,7 @@ float fbm(vec2 p) { return v; } -void main(void) { +vec3 computeSkyColor(vec3 dir) { vec3 sund = normalize(sunDir); float t = @@ -88,6 +88,13 @@ void main(void) { sky += glow * sunColor; + return sky; +} + +void main(void) { + + vec3 sky = computeSkyColor(dir); + frag_color = vec4(sky, 1.0); //frag_color = vec4(vec3(sunAmount), 1.0); //frag_color = vec4(t,0,0,1); diff --git a/assets/shaders/under_water_f_shader.glsl b/assets/shaders/under_water_f_shader.glsl index 1d78c11..390064c 100644 --- a/assets/shaders/under_water_f_shader.glsl +++ b/assets/shaders/under_water_f_shader.glsl @@ -1,35 +1,137 @@ #version 460 in vec2 TexCoord; +in vec3 rayDir; out vec4 FragColor; -uniform sampler2D u_sceneTexture; +layout (binding = 0) uniform sampler2D u_sceneTexture; +layout (binding = 1) uniform sampler2D u_depthTexture; +layout (binding = 2) uniform sampler2D u_shadowMap; +uniform mat4 u_lightSpaceMatrix; uniform float u_time; uniform bool u_underwater; uniform vec3 u_waterColor; uniform float u_fogDensity; +uniform mat4 InverseViewProjection; +uniform vec3 cameraPos; +uniform vec3 sunDir; +uniform vec3 sunColor; +uniform float waterDensity; + +float hash(vec2 p) { + return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); +} + +float noise(vec2 p) { + vec2 i = floor(p); + vec2 f = fract(p); + f = f * f * (3.0 - 2.0 * f); + + return mix( + mix(hash(i), hash(i + vec2(1.0, 0.0)), f.x), + mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), f.x), + f.y + ); +} + +float fbm(vec2 p) { + float value = 0.0; + float amp = 0.5; + float freq = 1.0; + for (int i = 0; i < 4; ++i) { + value += amp * noise(p * freq); + freq *= 2.0; + amp *= 0.5; + } + return value; +} + +float getCausticValue(float x, float y, float z) { + float w = 8.0; + float strength = 4.0; + vec2 coord = vec2(x, y) * w + z * 0.5; + + float n = fbm(coord); + + float caustic = pow(n, 3.0) * strength; + return caustic; +} + +float getShadow(vec3 worldPos) { + vec4 posLightSpace = u_lightSpaceMatrix * vec4(worldPos, 1.0); + vec3 projCoords = posLightSpace.xyz / posLightSpace.w; + projCoords = projCoords * 0.5 + 0.5; + + if (projCoords.z > 1.0) return 1.0; + + float closestDepth = texture(u_shadowMap, projCoords.xy).r; + float currentDepth = projCoords.z; + + return currentDepth - 0.005 > closestDepth ? 0.0 : 1.0; +} void main() { vec4 original = texture(u_sceneTexture, TexCoord); - + if (!u_underwater) { FragColor = original; return; } + float rawDepth = texture(u_depthTexture, TexCoord).r; + vec3 ndc = vec3(TexCoord * 2.0 - 1.0, rawDepth * 2.0 - 1.0); + vec4 worldPosH = InverseViewProjection * vec4(ndc, 1.0); + vec3 worldPos = worldPosH.xyz / worldPosH.w; + float sceneDistance = distance(cameraPos, worldPos); + vec2 distoredUV = TexCoord; float strength = 0.003; distoredUV.x += sin(TexCoord.y * 15.0 + u_time * 5.0) * strength; distoredUV.y += cos(TexCoord.x * 15.0 + u_time * 4.3) * strength; distoredUV = clamp(distoredUV, 0.001, 0.999); - vec4 distorted = texture(u_sceneTexture, distoredUV); + vec4 distorted = texture(u_sceneTexture, distoredUV); + + float rawCaustic = getCausticValue(TexCoord.x, TexCoord.y, u_time * 0.3); + float caustic = 0.5 + rawCaustic * 0.5; + vec3 causticLight = vec3(caustic * 0.9, caustic, caustic * 0.7); - float caustic = 0.9 + 0.1 * sin(TexCoord.x * 20.0 + u_time) * cos(TexCoord.y * 20.0 + u_time * 1.2); - vec3 causticLight = vec3(caustic, caustic * 0.95, caustic * 0.9); - //vec3 causticLight = vec3(1.0); float fogFactor = clamp(1.0 - (TexCoord.y * u_fogDensity * 10.0), 0.0, 1.0); vec3 mixed = mix(u_waterColor, distorted.rgb * causticLight, fogFactor); + // Volume Scattering + vec3 rayOrigin = cameraPos; + vec3 rayDirection = normalize(rayDir); - FragColor = vec4(mixed, 1.0); + float maxDist = 10.0; // Maximum visible distance + float stepSize = 0.4; + float phasePower = 8.0; // Beam Sharpness + + vec3 scattering = vec3(0.0); + float transmittance = 1.0; + + float cosTheta = dot(rayDirection, sunDir); + float phase = pow(max(cosTheta, 0.0), phasePower); + + for (float t = 0.0; t < maxDist; t += stepSize) { + + if (t > sceneDistance) break; + vec3 pos = rayOrigin + rayDirection * t; + + float shadow = getShadow(pos); + + float density = waterDensity; + + + vec3 absorption = vec3(0.3, 0.08, 0.02); // Absorption coefficient per meter + vec3 sunLightAtPos = sunColor * exp(-absorption * t); + + scattering += density * phase * sunLightAtPos * transmittance * stepSize * shadow; + + float extinction = waterDensity * 1.5; // Extinction coefficient, tunable + transmittance *= exp(-extinction * stepSize); + if (transmittance < 0.01) break; + } + + FragColor.rgb = mixed + scattering; + FragColor.a = 1.0; } \ No newline at end of file diff --git a/assets/shaders/under_water_v_shader.glsl b/assets/shaders/under_water_v_shader.glsl index e0863a8..6b8d102 100644 --- a/assets/shaders/under_water_v_shader.glsl +++ b/assets/shaders/under_water_v_shader.glsl @@ -3,9 +3,18 @@ layout (location = 0) in vec2 pos; layout (location = 1) in vec2 texCoord; -out vec2 TexCoord; +uniform mat4 InverseViewProjection; +uniform vec3 cameraPos; +out vec2 TexCoord; +out vec3 rayDir; void main() { - gl_Position = vec4(pos.x, pos.y, 0.0, 1.0); TexCoord = texCoord; + vec4 clipPos = vec4(pos, 1.0, 1.0); + vec4 worldPosH = InverseViewProjection * clipPos; + vec3 worldPos = worldPosH.xyz / worldPosH.w; + vec3 RayDir = worldPos - cameraPos; + rayDir = normalize(RayDir); + gl_Position = clipPos; + } \ No newline at end of file diff --git a/assets/shaders/water_f_shader.glsl b/assets/shaders/water_f_shader.glsl new file mode 100644 index 0000000..31ff808 --- /dev/null +++ b/assets/shaders/water_f_shader.glsl @@ -0,0 +1,316 @@ +#version 460 + +layout (location = 0) out vec4 accum; +layout (location = 1) out float reveal; + +in vec2 tc; +in vec3 normal; +in vec3 vert_pos; +in vec3 world_pos; +in float roughness; +flat in int tex_layer; +in float v_depth; +layout (binding = 0) uniform sampler2DArray samp; +uniform sampler2D sceneColorTex; // binding 1 +uniform sampler2D sceneDepthTex; // binding 2 + +uniform mat4 proj_matrix; +uniform mat4 inv_proj_matrix; +uniform mat4 inv_view_matrix; + +uniform float ambientStrength; +uniform vec3 sunlightColor; +uniform vec3 ambientColor; +uniform vec3 sunlightDir; +uniform vec3 cameraPos; +uniform bool shader_on; +uniform float specularStrength; + +uniform vec3 skyTop; +uniform vec3 skyBottom; +uniform vec3 sunColor; +uniform float horizonSharpness; +uniform float cloudWhiteMix; +uniform float cloudThresholdLow; +uniform float cloudThresholdHigh; +uniform float time; + +uniform vec3 sunDir; // world! + +uniform bool underwater; + +uniform float refractStrength; + +uniform bool enablePerturb; +uniform bool enableDepthFade; +float weight(float z, float a) { + float intermediate = 0.03 / (1e-5 + pow(z / 200.0, 4.0)); + + return a * clamp(intermediate, 1e-2, 3e2); +} + +float hash(vec2 p) { + return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123); +} + +float noise(vec2 p) { + vec2 i = floor(p); + vec2 f = fract(p); + f = f * f * (3.0 - 2.0 * f); + float a = hash(i); + float b = hash(i + vec2(1.0, 0.0)); + float c = hash(i + vec2(0.0, 1.0)); + float d = hash(i + vec2(1.0, 1.0)); + return mix(mix(a, b, f.x), mix(c, d, f.x), f.y); +} + +float fbm(vec2 p) { + float v = 0.0; + float amp = 0.5; + for (int i = 0; i < 5; i++) { + v += amp * noise(p); + p *= 2.0; + amp *= 0.5; + } + return v; +} + +vec3 computeSkyColor(vec3 dir) { + vec3 sund = normalize(sunDir); + + float t = + clamp( + dir.y * 0.5 + 0.5, + 0.0, + 1.0 + ); + + + vec3 sky = + mix( + skyBottom, + skyTop, + pow(t, horizonSharpness) + ); + + // cloud + if (dir.y > 0.0) { + vec2 cloud_uv = dir.xz / (dir.y + 0.15) * 0.5 + vec2(time * 0.005, time * 0.002); + float cloud_density = fbm(cloud_uv * 2.0); + float safeLow = cloudThresholdLow; + float safeHigh = max(cloudThresholdHigh, cloudThresholdLow + 0.001); + cloud_density = smoothstep(safeLow,safeHigh, cloud_density); + + + float fade = smoothstep(0.0, 0.3, dir.y) * (1.0 - smoothstep(0.85, 1.0, dir.y)); + cloud_density *= fade; + + vec3 cloud_color = mix(skyBottom, vec3(1.0), cloudWhiteMix); + sky = mix(sky, cloud_color, cloud_density * 0.6); + } + + float sunAmount = max(dot(dir, sund), 0.0); + + //float glow = pow(sunAmount, 8.0) * 0.15; + + float glow = pow(sunAmount, 8.0) * 0.15 + pow(sunAmount, 32.0) * 0.3; + + sky += glow * sunColor; + + return sky; +} + +// Reconstruct eye-space coordinates from screen UV and depth buffer value +vec3 reconstructViewPos(vec2 uv, float depth) { + vec4 clip = vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0); + vec4 view = inv_proj_matrix * clip; + return view.xyz / view.w; +} +// Screen-space ray marching, origin/dir are in eye space +bool traceSSR(vec3 origin, vec3 dir, out vec2 hitUV) { + const int COARSE_STEPS = 32; + const float COARSE_STEP = 0.6; + const int REFINE_STEPS = 8; + const float THICKNESS = 0.3; + + vec3 pos = origin; + vec3 prevPos = origin; + bool foundCoarse = false; + + + for (int i = 0; i < COARSE_STEPS; ++i) { + prevPos = pos; + pos += dir * COARSE_STEP; + + vec4 clip = proj_matrix * vec4(pos, 1.0); + if (clip.w <= 0.0) return false; + vec3 ndc = clip.xyz / clip.w; + if (abs(ndc.x) > 1.0 || abs(ndc.y) > 1.0) return false; + + vec2 uv = ndc.xy * 0.5 + 0.5; + float sceneDepth = texture(sceneDepthTex, uv).r; + vec3 scenePos = reconstructViewPos(uv, sceneDepth); + + if (pos.z - scenePos.z < 0.0) { + foundCoarse = true; + break; + } + } + + if (!foundCoarse) return false; + + + vec3 lo = prevPos; + vec3 hi = pos; + + for (int i = 0; i < REFINE_STEPS; ++i) { + vec3 mid = (lo + hi) * 0.5; + + vec4 clip = proj_matrix * vec4(mid, 1.0); + vec3 ndc = clip.xyz / clip.w; + vec2 uv = ndc.xy * 0.5 + 0.5; + float sceneDepth = texture(sceneDepthTex, uv).r; + vec3 scenePos = reconstructViewPos(uv, sceneDepth); + + if (mid.z - scenePos.z < 0.0) { + hi = mid; + } else { + lo = mid; + } + } + + vec4 finalClip = proj_matrix * vec4(hi, 1.0); + vec3 finalNdc = finalClip.xyz / finalClip.w; + vec2 finalUV = finalNdc.xy * 0.5 + 0.5; + + float finalDepth = texture(sceneDepthTex, finalUV).r; + vec3 finalScenePos = reconstructViewPos(finalUV, finalDepth); + + if (abs(hi.z - finalScenePos.z) > THICKNESS) return false; + + hitUV = finalUV; + return true; +} +void main() { + + vec4 objectColor = texture(samp, vec3(tc, tex_layer)); + + if (!shader_on) { + vec4 color = objectColor; + float alpha = color.a; + if (alpha < 1e-4) discard; + + + float w = weight(v_depth, alpha); + + accum = vec4(color.rgb * alpha * w, alpha * w); + + reveal = alpha; + return; + } + // Normal perturbation + vec3 N; + if (enablePerturb) { + const float wave_speed_scale = 40.0; + vec2 waveUV1 = world_pos.xz * 8.0 + vec2(time * 0.05 * wave_speed_scale, time * 0.03 * wave_speed_scale); + vec2 waveUV2 = world_pos.xz * 13.0 + vec2(-time * 0.04 * wave_speed_scale, time * 0.06 * wave_speed_scale); + + float wave1 = noise(waveUV1); + float wave2 = noise(waveUV2); + + vec2 normalPerturb = vec2(wave1 - 0.5, wave2 - 0.5) * 0.1; + N = normalize(normal + vec3(normalPerturb.x, 0.0, normalPerturb.y)); + } else { + N = normalize(normal); + } + + vec3 V = normalize(-vert_pos); + + vec3 reflectColor; + vec3 refractColor; + float fresnel; + + vec2 screenUV = gl_FragCoord.xy / vec2(textureSize(sceneDepthTex, 0)); + + // water depth + float sceneDepthRaw = texture(sceneDepthTex, screenUV).r; + vec3 sceneViewPos = reconstructViewPos(screenUV, sceneDepthRaw); + float waterDepth = vert_pos.z - sceneViewPos.z; + waterDepth = max(waterDepth, 0.0); + + const float DEPTH_FADE_DISTANCE = 10; + float depthFactor = clamp(waterDepth / DEPTH_FADE_DISTANCE, 0.0, 1.0); + + if (underwater) { + reflectColor = vec3(0.0); + refractColor = objectColor.rgb; + fresnel = 0.0; + } else { + vec3 R = reflect(-V, N); + vec3 origin = vert_pos + N * 0.05; + vec2 hitUV; + + if (traceSSR(origin, R, hitUV)) { + reflectColor = texture(sceneColorTex, hitUV).rgb; + } else { + vec3 worldR = mat3(inv_view_matrix) * R; + reflectColor = computeSkyColor(worldR); + } + + float effectiveRefractStrength = refractStrength * (1.0 - depthFactor * 0.5); + vec2 refractOffset = N.xz * effectiveRefractStrength; + vec2 refractUV = clamp(screenUV + refractOffset, vec2(0.001), vec2(0.999)); + + refractColor = texture(sceneColorTex, refractUV).rgb; + + fresnel = pow(1.0 - max(dot(N, V), 0.0), 5.0); + fresnel = mix(0.02, 1.0, fresnel); + } + + vec3 lightDir = normalize(-sunlightDir); + vec3 H = normalize(lightDir + V); + vec3 ambient = ambientStrength * ambientColor; + float diff = max(dot(N, lightDir), 0.0); + vec3 diffuse = diff * sunlightColor; + + float r = clamp(roughness, 0.0, 1.0); + float shininess = mix(512.0, 4.0, r); + float ks = mix(0.8, 0.02, r); + + float spec = 0.0; + if (diff > 0.0) { + spec = ks * pow(max(dot(N, H), 0.0), shininess); + } + vec3 specular = spec * sunlightColor * specularStrength; + + vec3 tintedRefract; + + if (enableDepthFade) { + vec3 shallowColor = vec3(0.4, 0.75, 0.7); + vec3 deepColor = vec3(0.0, 0.015, 0.045); + + vec3 waterTint = mix(shallowColor, deepColor, depthFactor); + + tintedRefract = mix(refractColor, refractColor * waterTint, depthFactor); + tintedRefract = mix(tintedRefract, objectColor.rgb, 0.4); // Forcefully blend 40% texture color, independent of depth + } else { + tintedRefract = mix(refractColor, objectColor.rgb, 0.4); + } + + //vec3 baseWaterColor = (ambient + diffuse) * objectColor.rgb + specular; + vec3 baseWaterColor = (ambient + diffuse) * tintedRefract + specular; + + vec3 finalColor = mix(baseWaterColor, reflectColor, fresnel); + + float alpha = objectColor.a; + if (alpha < 1e-4) discard; + + + float w = weight(v_depth, alpha); + + accum = vec4(finalColor * alpha * w, alpha * w); + + reveal = alpha; + + return; +} \ No newline at end of file diff --git a/assets/shaders/water_v_shader.glsl b/assets/shaders/water_v_shader.glsl new file mode 100644 index 0000000..1a1d671 --- /dev/null +++ b/assets/shaders/water_v_shader.glsl @@ -0,0 +1,32 @@ +#version 460 + +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 texCoord; +layout (location = 2) in float layer; +layout (location = 3) in vec3 aNormal; +layout (location = 4) in float Roughness; + +out vec2 tc; +out vec3 normal; +out vec3 vert_pos; +out vec3 world_pos; +flat out int tex_layer; +out float roughness; +out float v_depth; + +uniform mat4 mv_matrix; +uniform mat4 proj_matrix; +uniform mat4 norm_matrix; + + +void main(void) { + vec4 view_pos = mv_matrix * vec4(pos, 1.0); + world_pos = pos; + vert_pos = view_pos.xyz; + normal = mat3(norm_matrix) * aNormal; + roughness = Roughness; + tc = texCoord; + tex_layer = int(layer); + v_depth = -view_pos.z; + gl_Position = proj_matrix * view_pos; +} diff --git a/include/Cubed/gameplay/chunk.hpp b/include/Cubed/gameplay/chunk.hpp index 9efcbf3..95df9a1 100644 --- a/include/Cubed/gameplay/chunk.hpp +++ b/include/Cubed/gameplay/chunk.hpp @@ -19,7 +19,7 @@ private: static constexpr int SIZE_X = CHUNK_SIZE; static constexpr int SIZE_Y = WORLD_SIZE_Y; static constexpr int SIZE_Z = CHUNK_SIZE; - static constexpr int VERTEX_DATA_SUM = 4; + static constexpr int VERTEX_DATA_SUM = 5; std::atomic m_dirty{false}; std::atomic m_need_upload{true}; std::atomic m_is_on_gen_vertex_data{false}; @@ -39,6 +39,7 @@ private: 1 - cross_plane 2 - normal_discard 3 - transparent and blend + 4 - water */ std::vector m_vertex_data; float frequency = 0.01f; @@ -113,6 +114,9 @@ public: GLuint get_normal_blend_vao() const; size_t get_normal_blend_vertices_sum() const; + GLuint get_water_vao() const; + size_t get_water_vertices_sum() const; + bool is_dirty() const; void mark_dirty(); diff --git a/include/Cubed/gameplay/world.hpp b/include/Cubed/gameplay/world.hpp index 21fe48c..aafe7c4 100644 --- a/include/Cubed/gameplay/world.hpp +++ b/include/Cubed/gameplay/world.hpp @@ -24,6 +24,8 @@ struct ChunkRenderSnapshot { size_t normal_discard_vertices_count; GLuint normal_blend_vao; size_t normal_blend_vertices_count; + GLuint water_vao; + size_t water_vertices_count; glm::vec3 center; glm::vec3 half_extents; }; diff --git a/include/Cubed/primitive_data.hpp b/include/Cubed/primitive_data.hpp index 725d931..39e5a61 100644 --- a/include/Cubed/primitive_data.hpp +++ b/include/Cubed/primitive_data.hpp @@ -212,7 +212,7 @@ constexpr float CROSS_NORMALS[2][6][3] = { {0.0f, 1.0f, 0.0f}}}; #pragma endregion - +// [-1, 1] constexpr float QUAD_VERTICES[] = { // postion // texcoorlds -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, diff --git a/include/Cubed/renderer.hpp b/include/Cubed/renderer.hpp index 0a187cb..275e8f0 100644 --- a/include/Cubed/renderer.hpp +++ b/include/Cubed/renderer.hpp @@ -32,6 +32,8 @@ public: bool& discard_transparent(); bool& shader_on(); + bool& water_perturb(); + bool& water_depth_fade(); int& shadow_mode(); int& light_cull_face(); int& light_size_uv(); @@ -42,10 +44,13 @@ public: float& cloud_speed(); float& cloud_threshold_low(); float& cloud_threshold_high(); + float& refract_strength(); + float& underwater_fog_density(); + float& water_density(); private: struct ParallelLight { - glm::vec3 sundir; + glm::vec3 sundir; // direction from sun to vertex glm::vec3 lightdir; float sun_height = 0.0f; float day_light = 0.0f; @@ -53,6 +58,15 @@ private: glm::vec3 sun_color; glm::vec3 directional_light_color; glm::vec3 finnal_ambient_color; + glm::mat4 light_space_matrix; + }; + + struct SkyUniform { + glm::vec3 sky_top; + glm::vec3 sky_bottom; + glm::vec3 sun_dir_view; + float horizon_sharpness; + float cloud_white_mix; }; static constexpr glm::vec3 SUN_COLOR{1.00f, 0.95f, 0.80f}; @@ -79,6 +93,8 @@ private: bool m_discard_tranparent = true; bool m_shader_on = true; + bool m_water_perturb = true; + bool m_water_depth_fade = true; int m_shadow_mode = 0; int m_light_cull_face = 0; float m_aspect = 0.0f; @@ -94,9 +110,6 @@ private: glm::mat4 m_p_mat, m_v_mat, m_m_mat, m_mv_mat, m_mvp_mat, m_norm_mat; - GLuint m_mv_loc = 0; - GLuint m_proj_loc = 0; - GLuint m_sky_vbo = 0; GLuint m_text_vbo = 0; GLuint m_outline_indices_vbo = 0; @@ -105,12 +118,12 @@ private: GLuint m_fbo = 0; GLuint m_screen_texture = 0; - GLuint m_depth_render_buffer = 0; + GLuint m_screen_depth_texture = 0; GLuint m_oit_fbo = 0; GLuint m_accum_texture = 0; GLuint m_reveal_texture = 0; - GLuint m_oit_depth_render_buffer = 0; + GLuint m_oit_depth_texture = 0; GLuint m_depth_map_fbo = 0; GLuint m_depth_map_texture = 0; @@ -140,7 +153,14 @@ private: float m_cloud_threshold_low = 0.5f; float m_cloud_threshold_high = 0.75f; + float m_refract_strength = 0.03f; + + float m_underwater_fog_density = 0.08f; + + float m_water_density = 0.12f; + ParallelLight m_parallel_light; + SkyUniform m_sky_uniform; /* 0 - quad vao 1 - sky vao diff --git a/include/Cubed/shader.hpp b/include/Cubed/shader.hpp index e8a5f88..c46fd11 100644 --- a/include/Cubed/shader.hpp +++ b/include/Cubed/shader.hpp @@ -1,4 +1,7 @@ #pragma once +#include "glm/ext/vector_float3.hpp" +#include "glm/gtc/type_ptr.hpp" + #include #include #include @@ -21,6 +24,26 @@ public: GLuint loc(const std::string& loc) const; const std::string& name() const; void use() const; + template struct always_false : std::false_type {}; // NOLINT + template + void set_loc(const std::string& location, T&& value) const { + using std::is_same_v; + using dT = std::decay_t; + if constexpr (is_same_v || is_same_v) { + glUniform1i(loc(location), value); + } else if constexpr (is_same_v) { + glUniform1f(loc(location), value); + } else if constexpr (is_same_v) { + glUniform1f(loc(location), static_cast(value)); + } else if constexpr (is_same_v) { + glUniform3fv(loc(location), 1, glm::value_ptr(value)); + } else if constexpr (is_same_v) { + glUniformMatrix4fv(loc(location), 1, GL_FALSE, + glm::value_ptr(value)); + } else { + static_assert(always_false
::value, "Unknown Type"); + } + }; private: GLuint m_program = 0; diff --git a/src/dev_panel.cpp b/src/dev_panel.cpp index ca45c53..be84f9b 100644 --- a/src/dev_panel.cpp +++ b/src/dev_panel.cpp @@ -657,6 +657,16 @@ void DevPanel::show_shader_tab_item() { ImGui::SliderFloat("Cloud Threshold High", &m_app.renderer().cloud_threshold_high(), 0.0f, 1.0f); + ImGui::SliderFloat("Water Refract Strength", + &m_app.renderer().refract_strength(), 0.0f, 1.0f); + ImGui::Checkbox("Water Perturb", &m_app.renderer().water_perturb()); + ImGui::SameLine(); + ImGui::Checkbox("WaterDepthFade", &m_app.renderer().water_depth_fade()); + ImGui::SliderFloat("Underwater Fog Density", + &m_app.renderer().underwater_fog_density(), 0.0f, + 1.0f); + ImGui::SliderFloat("Water Density", &m_app.renderer().water_density(), + 0.0f, 1.0f); ImGui::EndTabItem(); } } diff --git a/src/gameplay/chunk.cpp b/src/gameplay/chunk.cpp index d9d3289..2d31253 100644 --- a/src/gameplay/chunk.cpp +++ b/src/gameplay/chunk.cpp @@ -146,6 +146,11 @@ size_t Chunk::get_normal_blend_vertices_sum() const { return m_vertex_data[3].m_sum.load(); } +GLuint Chunk::get_water_vao() const { return m_vertex_data[4].m_vao; } +size_t Chunk::get_water_vertices_sum() const { + return m_vertex_data[4].m_sum.load(); +} + void Chunk::gen_phase_one() { m_generator = std::make_unique(*this); if (!m_generator) { @@ -392,7 +397,14 @@ void Chunk::gen_vertices(const OptionalBlockVectorArray& neighbor_block) { if (BlockManager::is_discard(cur_id)) { m_vertex_data[2].m_vertices.emplace_back(vex); } else if (BlockManager::is_blend(cur_id)) { - m_vertex_data[3].m_vertices.emplace_back(vex); + if (cur_id == 7) { + m_vertex_data[4].m_vertices.emplace_back( + vex); + } else { + m_vertex_data[3].m_vertices.emplace_back( + vex); + } + } else { Logger::warn("Id {} is transparent but not " "discard or blend", diff --git a/src/gameplay/world.cpp b/src/gameplay/world.cpp index 0c319cb..b8d145a 100644 --- a/src/gameplay/world.cpp +++ b/src/gameplay/world.cpp @@ -971,6 +971,7 @@ void World::update(float delta_time) { chunk.get_normal_discard_vertices_sum(), chunk.get_normal_blend_vao(), chunk.get_normal_blend_vertices_sum(), + chunk.get_water_vao(), chunk.get_water_vertices_sum(), glm::vec3(static_cast(pos.x * CHUNK_SIZE) + static_cast(CHUNK_SIZE / 2), static_cast(WORLD_SIZE_Y / 2), diff --git a/src/renderer.cpp b/src/renderer.cpp index a3d44ec..dea33b7 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -37,12 +37,12 @@ Renderer::~Renderer() { glDeleteVertexArrays(NUM_VAO, m_vao.data()); glDeleteFramebuffers(1, &m_fbo); glDeleteTextures(1, &m_screen_texture); - glDeleteRenderbuffers(1, &m_depth_render_buffer); + glDeleteTextures(1, &m_screen_depth_texture); glDeleteFramebuffers(1, &m_oit_fbo); glDeleteTextures(1, &m_accum_texture); glDeleteTextures(1, &m_reveal_texture); - glDeleteRenderbuffers(1, &m_oit_depth_render_buffer); + glDeleteTextures(1, &m_oit_depth_texture); glDeleteFramebuffers(1, &m_depth_map_fbo); glDeleteTextures(1, &m_depth_map_texture); @@ -84,6 +84,9 @@ void Renderer::init() { "shaders/depth_fragment_shader.glsl"}; Shader billboard{"billboard", "shaders/billboard_v_shader.glsl", "shaders/billboard_f_shader.glsl"}; + Shader water_shader{"water", "shaders/water_v_shader.glsl", + "shaders/water_f_shader.glsl"}; + m_shaders.insert({world_shader.hash(), std::move(world_shader)}); m_shaders.insert({outline_shader.hash(), std::move(outline_shader)}); m_shaders.insert({sky_shdaer.hash(), std::move(sky_shdaer)}); @@ -96,6 +99,7 @@ void Renderer::init() { {composite_block_shader.hash(), std::move(composite_block_shader)}); m_shaders.insert({depth_shader.hash(), std::move(depth_shader)}); m_shaders.insert({billboard.hash(), std::move(billboard)}); + m_shaders.insert({water_shader.hash(), std::move(water_shader)}); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); @@ -260,17 +264,14 @@ void Renderer::render_outline() { if (block_pos != std::nullopt) { - m_mv_loc = shader.loc("mv_matrix"); - m_proj_loc = shader.loc("proj_matrix"); - m_m_mat = glm::translate(glm::mat4(1.0f), glm::vec3(block_pos.value().pos)); m_v_mat = m_camera.get_camera_lookat(); m_mv_mat = m_v_mat * m_m_mat; - glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat)); - glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat)); + shader.set_loc("mv_matrix", m_mv_mat); + shader.set_loc("proj_matrix", m_p_mat); glBindVertexArray(m_vao[2]); @@ -306,19 +307,20 @@ void Renderer::render_sky() { glm::vec3 day_bottom = mix(sunset_horizon, horizon, m_parallel_light.day_light); - glm::vec3 sky_top = mix(night_zenith, day_top, m_parallel_light.day_factor); - glm::vec3 sky_bottom = + m_sky_uniform.sky_top = + mix(night_zenith, day_top, m_parallel_light.day_factor); + m_sky_uniform.sky_bottom = mix(night_horizon, day_bottom, m_parallel_light.day_factor); float day_sharpness = glm::mix(SUNSET_SHARPNESS, NOON_SHARPNESS, m_parallel_light.day_light); - float horizon_sharpness = + m_sky_uniform.horizon_sharpness = glm::mix(NIGHT_SHARPNESS, day_sharpness, m_parallel_light.day_factor); float day_cloud_mix = glm::mix(SUNSET_CLOUD_MIX, NOON_CLOUD_MIX, m_parallel_light.day_light); - float cloud_white_mix = + m_sky_uniform.cloud_white_mix = glm::mix(NIGHT_CLOUD_MIX, day_cloud_mix, m_parallel_light.day_factor); m_cloud_time += m_delta_time * m_cloud_speed; @@ -326,28 +328,24 @@ void Renderer::render_sky() { const auto& sky_shader = get_shader("sky"); sky_shader.use(); - m_mv_loc = sky_shader.loc("mv_matrix"); - m_proj_loc = sky_shader.loc("proj_matrix"); m_m_mat = glm::translate(glm::mat4(1.0f), m_camera.get_camera_pos() - glm::vec3(0.5f, 0.5f, 0.5f)); m_v_mat = m_camera.get_camera_lookat(); m_mv_mat = m_v_mat * m_m_mat; - glm::vec3 sun_dir_view = (-m_parallel_light.sundir); - - glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat)); - glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat)); - glUniform3fv(sky_shader.loc("skyTop"), 1, glm::value_ptr(sky_top)); - glUniform3fv(sky_shader.loc("skyBottom"), 1, glm::value_ptr(sky_bottom)); - glUniform3fv(sky_shader.loc("sunDir"), 1, glm::value_ptr(sun_dir_view)); - glUniform3fv(sky_shader.loc("sunColor"), 1, - glm::value_ptr(m_parallel_light.directional_light_color)); - glUniform1f(sky_shader.loc("horizonSharpness"), horizon_sharpness); - glUniform1f(sky_shader.loc("time"), m_cloud_time); - glUniform1f(sky_shader.loc("cloudWhiteMix"), cloud_white_mix); - glUniform1f(sky_shader.loc("cloudThresholdLow"), m_cloud_threshold_low); - glUniform1f(sky_shader.loc("cloudThresholdHigh"), m_cloud_threshold_high); + m_sky_uniform.sun_dir_view = (-m_parallel_light.sundir); + sky_shader.set_loc("mv_matrix", m_mv_mat); + sky_shader.set_loc("proj_matrix", m_p_mat); + sky_shader.set_loc("skyTop", m_sky_uniform.sky_top); + sky_shader.set_loc("skyBottom", m_sky_uniform.sky_bottom); + sky_shader.set_loc("sunDir", m_sky_uniform.sun_dir_view); + sky_shader.set_loc("sunColor", m_parallel_light.directional_light_color); + sky_shader.set_loc("horizonSharpness", m_sky_uniform.horizon_sharpness); + sky_shader.set_loc("time", m_cloud_time); + sky_shader.set_loc("cloudWhiteMix", m_sky_uniform.cloud_white_mix); + sky_shader.set_loc("cloudThresholdLow", m_cloud_threshold_low); + sky_shader.set_loc("cloudThresholdHigh", m_cloud_threshold_high); glBindVertexArray(m_vao[1]); glDisable(GL_DEPTH_TEST); @@ -369,12 +367,9 @@ void Renderer::render_sky() { glm::scale(glm::mat4(1.0f), glm::vec3(SUN_SIZE)) * glm::translate(glm::mat4(1.0f), glm::vec3(-0.5f, -0.5f, 0.0f)); - m_mv_loc = billboard.loc("mv_matrix"); - m_proj_loc = billboard.loc("proj_matrix"); - - glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat)); - glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat)); - glUniform3fv(billboard.loc("color"), 1, glm::value_ptr(SUN_COLOR)); + billboard.set_loc("mv_matrix", m_mv_mat); + billboard.set_loc("proj_matrix", m_p_mat); + billboard.set_loc("color", SUN_COLOR); glDrawArrays(GL_TRIANGLES, 0, 6); @@ -384,9 +379,9 @@ void Renderer::render_sky() { m_mv_mat = glm::translate(glm::mat4(1.0f), moon_view_pos) * glm::scale(glm::mat4(1.0f), glm::vec3(MOON_SIZE)) * glm::translate(glm::mat4(1.0f), glm::vec3(-0.5f, -0.5f, 0.0f)); - glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat)); - glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat)); - glUniform3fv(billboard.loc("color"), 1, glm::value_ptr(MOON_COLOR)); + billboard.set_loc("mv_matrix", m_mv_mat); + billboard.set_loc("proj_matrix", m_p_mat); + billboard.set_loc("color", MOON_COLOR); glDrawArrays(GL_TRIANGLES, 0, 6); @@ -406,9 +401,7 @@ void Renderer::render_text() { glDisable(GL_DEPTH_TEST); - m_proj_loc = shader.loc("projection"); - - glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_ui_proj)); + shader.set_loc("projection", m_ui_proj); auto& texts = DebugCollector::get().all_texts(); for (auto& t : texts) { @@ -425,11 +418,9 @@ void Renderer::render_ui() { glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - m_mv_loc = shader.loc("m_matrix"); - m_proj_loc = shader.loc("proj_matrix"); - glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_ui_m_matrix)); - glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_ui_proj)); + shader.set_loc("mv_matrix", m_ui_m_matrix); + shader.set_loc("proj_matrix", m_ui_proj); glBindVertexArray(m_vao[3]); @@ -448,15 +439,23 @@ void Renderer::render_underwater() { glBindVertexArray(m_vao[0]); - glUniform1i(shader.loc("u_sceneTexture"), 0); - glUniform1f(shader.loc("u_time"), glfwGetTime()); - glUniform1i(shader.loc("u_underwater"), m_camera.is_under_water()); - glUniform3f(shader.loc("u_waterColor"), 0.1f, 0.25f, 0.35f); - glUniform1f(shader.loc("u_fogDensity"), 0.08f); - + shader.set_loc("u_sceneTexture", 0); + shader.set_loc("u_time", static_cast(glfwGetTime())); + shader.set_loc("u_underwater", m_camera.is_under_water()); + shader.set_loc("u_waterColor", glm::vec3(0.1f, 0.25f, 0.35f)); + shader.set_loc("u_fogDensity", m_underwater_fog_density); + shader.set_loc("cameraPos", m_camera.get_camera_pos()); + shader.set_loc("sunDir", -m_parallel_light.sundir); + shader.set_loc("waterDensity", m_water_density); + shader.set_loc("InverseViewProjection", glm::inverse(m_p_mat * m_v_mat)); + shader.set_loc("sunColor", m_parallel_light.sun_color); + shader.set_loc("u_lightSpaceMatrix", m_parallel_light.light_space_matrix); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_screen_texture); - + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, m_screen_depth_texture); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, m_depth_map_texture); glDrawArrays(GL_TRIANGLES, 0, 6); glBindVertexArray(0); } @@ -492,7 +491,7 @@ void Renderer::updata_framebuffer(int width, int height) { glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); glDeleteTextures(1, &m_screen_texture); - glDeleteRenderbuffers(1, &m_depth_render_buffer); + glDeleteTextures(1, &m_screen_depth_texture); glGenTextures(1, &m_screen_texture); glBindTexture(GL_TEXTURE_2D, m_screen_texture); @@ -504,11 +503,14 @@ void Renderer::updata_framebuffer(int width, int height) { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_screen_texture, 0); - glGenRenderbuffers(1, &m_depth_render_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, m_depth_render_buffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, m_depth_render_buffer); + glGenTextures(1, &m_screen_depth_texture); + glBindTexture(GL_TEXTURE_2D, m_screen_depth_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, width, height, 0, + GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, + m_screen_depth_texture, 0); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { Logger::error("FBO incomplete after resize!"); @@ -520,7 +522,7 @@ void Renderer::updata_framebuffer(int width, int height) { glBindFramebuffer(GL_FRAMEBUFFER, m_oit_fbo); glDeleteTextures(1, &m_accum_texture); glDeleteTextures(1, &m_reveal_texture); - glDeleteRenderbuffers(1, &m_oit_depth_render_buffer); + glDeleteTextures(1, &m_oit_depth_texture); glGenTextures(1, &m_accum_texture); glBindTexture(GL_TEXTURE_2D, m_accum_texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, @@ -537,11 +539,14 @@ void Renderer::updata_framebuffer(int width, int height) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, m_reveal_texture, 0); - glGenRenderbuffers(1, &m_oit_depth_render_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, m_oit_depth_render_buffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, m_oit_depth_render_buffer); + glGenTextures(1, &m_oit_depth_texture); + glBindTexture(GL_TEXTURE_2D, m_oit_depth_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, width, height, 0, + GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, + m_oit_depth_texture, 0); GLenum draw_buffer[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; glDrawBuffers(2, draw_buffer); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { @@ -590,7 +595,7 @@ void Renderer::updata_framebuffer(int width, int height) { #pragma region render_world void Renderer::render_world() { // shader map - glm::mat4 light_space_matrix; + glm::mat4& light_space_matrix = m_parallel_light.light_space_matrix; auto& m_render_snapshots = m_world.render_snapshots(); auto& camera_pos = m_camera.get_camera_pos(); float texels_per_unit = 0.0f; @@ -635,10 +640,9 @@ void Renderer::render_world() { near_plane, far_plane); light_space_matrix = light_projection * light_view; - glUniformMatrix4fv(depth_shader.loc("lightSpaceMatrix"), 1, GL_FALSE, - glm::value_ptr(light_space_matrix)); - glUniform1i(depth_shader.loc("is_discard_tranparent"), - m_discard_tranparent); + depth_shader.set_loc("lightSpaceMatrix", light_space_matrix); + depth_shader.set_loc("is_discard_tranparent", m_discard_tranparent); + glViewport(0, 0, DEPTH_MAP_SIZE, DEPTH_MAP_SIZE); if (m_light_cull_face == 0) { glCullFace(GL_FRONT); @@ -698,9 +702,6 @@ void Renderer::render_world() { const auto& normal_block_shader = get_shader("normal_block"); normal_block_shader.use(); - m_mv_loc = normal_block_shader.loc("mv_matrix"); - m_proj_loc = normal_block_shader.loc("proj_matrix"); - glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_depth_map_texture); glActiveTexture(GL_TEXTURE1); @@ -711,31 +712,27 @@ void Renderer::render_world() { m_norm_mat = glm::transpose(glm::inverse(m_mv_mat)); glm::vec3 light_dir_view = glm::normalize(glm::mat3(m_v_mat) * lightdir); - glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat)); - glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat)); - glUniformMatrix4fv(normal_block_shader.loc("norm_matrix"), 1, GL_FALSE, - glm::value_ptr(m_norm_mat)); - glUniformMatrix4fv(normal_block_shader.loc("lightSpaceMatrix"), 1, GL_FALSE, - glm::value_ptr(light_space_matrix)); - glUniform1f(normal_block_shader.loc("ambientStrength"), m_ambient_strength); - glUniform3fv(normal_block_shader.loc("sunlightColor"), 1, - glm::value_ptr(m_parallel_light.directional_light_color)); - glUniform3fv(normal_block_shader.loc("ambientColor"), 1, - glm::value_ptr(m_parallel_light.finnal_ambient_color)); - glUniform3fv(normal_block_shader.loc("sunlightDir"), 1, - glm::value_ptr(light_dir_view)); - glUniform1i(normal_block_shader.loc("shadowMode"), m_shadow_mode); - glUniform1i(normal_block_shader.loc("shader_on"), m_shader_on); - glUniform1f(normal_block_shader.loc("texelsPerUnit"), texels_per_unit); - glUniform1f(normal_block_shader.loc("lightSizeUV"), - static_cast(m_light_size_uv)); - glUniform1f(normal_block_shader.loc("minRadius"), m_min_radius); - glUniform1f(normal_block_shader.loc("maxRadius"), m_max_radius); - glUniform1i(normal_block_shader.loc("samples"), m_samples); - glUniform1f(normal_block_shader.loc("specularStrength"), - m_specular_strength); - glUniform3fv(normal_block_shader.loc("cameraPos"), 1, - glm::value_ptr(m_camera.get_camera_pos())); + normal_block_shader.set_loc("mv_matrix", m_mv_mat); + normal_block_shader.set_loc("proj_matrix", m_p_mat); + normal_block_shader.set_loc("norm_matrix", m_norm_mat); + normal_block_shader.set_loc("lightSpaceMatrix", light_space_matrix); + normal_block_shader.set_loc("ambientStrength", m_ambient_strength); + normal_block_shader.set_loc("sunlightColor", + m_parallel_light.directional_light_color); + normal_block_shader.set_loc("ambientColor", + m_parallel_light.finnal_ambient_color); + normal_block_shader.set_loc("sunlightDir", light_dir_view); + normal_block_shader.set_loc("shadowMode", m_shadow_mode); + normal_block_shader.set_loc("shader_on", m_shader_on); + normal_block_shader.set_loc("texelsPerUnit", texels_per_unit); + normal_block_shader.set_loc("lightSizeUV", + static_cast(m_light_size_uv)); + normal_block_shader.set_loc("minRadius", m_min_radius); + normal_block_shader.set_loc("maxRadius", m_max_radius); + normal_block_shader.set_loc("samples", m_samples); + normal_block_shader.set_loc("specularStrength", m_specular_strength); + normal_block_shader.set_loc("cameraPos", m_camera.get_camera_pos()); + m_mvp_mat = m_p_mat * m_mv_mat; auto& m_planes = m_world.planes(); @@ -797,15 +794,6 @@ void Renderer::render_world() { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_oit_fbo); // pass one accumulate - auto& accum_shader = get_shader("accum"); - accum_shader.use(); - - GLint mv_loc = accum_shader.loc("mv_matrix"); - GLint proj_loc = accum_shader.loc("proj_matrix"); - - glUniformMatrix4fv(mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat)); - glUniformMatrix4fv(proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat)); - glBindFramebuffer(GL_FRAMEBUFFER, m_oit_fbo); glClearBufferfv(GL_COLOR, 0, glm::value_ptr(glm::vec4(0.0f))); @@ -819,6 +807,28 @@ void Renderer::render_world() { glBlendFunci(0, GL_ONE, GL_ONE); glBlendFunci(1, GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + + auto set_accum_loc = [&](const Shader& accum_shader) { + accum_shader.set_loc("mv_matrix", m_mv_mat); + accum_shader.set_loc("proj_matrix", m_p_mat); + accum_shader.set_loc("norm_matrix", m_norm_mat); + accum_shader.set_loc("lightSpaceMatrix", light_space_matrix); + accum_shader.set_loc("ambientStrength", m_ambient_strength); + accum_shader.set_loc("sunlightColor", + m_parallel_light.directional_light_color); + accum_shader.set_loc("ambientColor", + m_parallel_light.finnal_ambient_color); + accum_shader.set_loc("sunlightDir", light_dir_view); + accum_shader.set_loc("shader_on", m_shader_on); + accum_shader.set_loc("specularStrength", m_specular_strength); + accum_shader.set_loc("cameraPos", m_camera.get_camera_pos()); + }; + + auto& accum_shader = get_shader("accum"); + accum_shader.use(); + + set_accum_loc(accum_shader); + glActiveTexture(GL_TEXTURE0); for (const auto& snapshot : m_render_snapshots) { if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents, @@ -834,6 +844,56 @@ void Renderer::render_world() { glDrawArrays(GL_TRIANGLES, 0, snapshot.normal_blend_vertices_count); } } + + // use SSR + + auto& water_shader = get_shader("water"); + water_shader.use(); + + set_accum_loc(water_shader); + + water_shader.set_loc("sceneColorTex", 1); + water_shader.set_loc("sceneDepthTex", 2); + water_shader.set_loc("inv_proj_matrix", glm::inverse(m_p_mat)); + water_shader.set_loc("inv_view_matrix", glm::inverse(m_v_mat)); + + // sky loc + + water_shader.set_loc("skyTop", m_sky_uniform.sky_top); + water_shader.set_loc("skyBottom", m_sky_uniform.sky_bottom); + water_shader.set_loc("sunDir", m_sky_uniform.sun_dir_view); + water_shader.set_loc("sunColor", m_parallel_light.directional_light_color); + water_shader.set_loc("horizonSharpness", m_sky_uniform.horizon_sharpness); + water_shader.set_loc("time", glfwGetTime()); + water_shader.set_loc("cloudWhiteMix", m_sky_uniform.cloud_white_mix); + water_shader.set_loc("cloudThresholdLow", m_cloud_threshold_low); + water_shader.set_loc("cloudThresholdHigh", m_cloud_threshold_high); + water_shader.set_loc("underwater", m_camera.is_under_water()); + water_shader.set_loc("refractStrength", m_refract_strength); + water_shader.set_loc("enablePerturb", m_water_perturb); + water_shader.set_loc("enableDepthFade", m_water_depth_fade); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, m_screen_texture); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, m_screen_depth_texture); + + glActiveTexture(GL_TEXTURE0); + for (const auto& snapshot : m_render_snapshots) { + if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents, + m_planes)) { + continue; + } + + if (snapshot.water_vertices_count != 0) { + glBindTexture(GL_TEXTURE_2D_ARRAY, + m_texture_manager.get_texture_array()); + glBindVertexArray(snapshot.water_vao); + + glDrawArrays(GL_TRIANGLES, 0, snapshot.water_vertices_count); + } + } + auto& composite_shader = get_shader("composite"); glDisable(GL_BLEND); composite_shader.use(); @@ -915,6 +975,8 @@ Renderer::get_smoothed_shadow_lightdir(const glm::vec3& raw_shadow_lightdir, float& Renderer::ambient_strength() { return m_ambient_strength; } bool& Renderer::discard_transparent() { return m_discard_tranparent; } bool& Renderer::shader_on() { return m_shader_on; } +bool& Renderer::water_perturb() { return m_water_perturb; } +bool& Renderer::water_depth_fade() { return m_water_depth_fade; } int& Renderer::shadow_mode() { return m_shadow_mode; } int& Renderer::light_cull_face() { return m_light_cull_face; } int& Renderer::light_size_uv() { return m_light_size_uv; } @@ -925,4 +987,7 @@ float& Renderer::specular_strength() { return m_specular_strength; } float& Renderer::cloud_speed() { return m_cloud_speed; } float& Renderer::cloud_threshold_low() { return m_cloud_threshold_low; } float& Renderer::cloud_threshold_high() { return m_cloud_threshold_high; } +float& Renderer::refract_strength() { return m_refract_strength; } +float& Renderer::underwater_fog_density() { return m_underwater_fog_density; } +float& Renderer::water_density() { return m_water_density; } } // namespace Cubed \ No newline at end of file