diff --git a/resources/shaders/110/ssao.fs b/resources/shaders/110/ssao.fs index 5dfd9549de..0c9f36a5c7 100644 --- a/resources/shaders/110/ssao.fs +++ b/resources/shaders/110/ssao.fs @@ -1,94 +1,89 @@ #version 110 /** - * SSAO (Screen Space Ambient Occlusion) Shader - GLSL 110 version - * Uses depth-based sampling with adaptive radius and weighting - * Compatible with OpenGL 2.1 and older hardware + * SSAO Shader - GLSL 110 version with highlight protection + * Preserves brightness on upward-facing surfaces (top areas) */ -uniform sampler2D color_texture; // Original scene color -uniform sampler2D depth_texture; // Depth buffer texture -uniform vec2 inv_tex_size; // 1.0/width, 1.0/height - for UV offset calculation -uniform float z_near; // Near clipping plane distance -uniform float z_far; // Far clipping plane distance +uniform sampler2D color_texture; +uniform sampler2D depth_texture; +uniform sampler2D normal_texture; +uniform vec2 inv_tex_size; +uniform float z_near; +uniform float z_far; -varying vec2 tex_coord; // Texture coordinates from vertex shader +varying vec2 tex_coord; -/** - * Convert linear depth buffer value to world/view space depth - * Uses standard OpenGL perspective projection reverse mapping - */ float linearize_depth(float depth) { - float z = depth * 2.0 - 1.0; // Convert to NDC [-1, 1] range + float z = depth * 2.0 - 1.0; return (2.0 * z_near * z_far) / (z_far + z_near - z * (z_far - z_near)); } void main() { - // Sample base color at current fragment vec3 base = texture2D(color_texture, tex_coord).rgb; - - // Linearize center pixel depth for accurate world-space comparisons float depth_center = linearize_depth(texture2D(depth_texture, tex_coord).r); - // Adaptive sampling radius: larger radius for distant objects (perspective effect) - // Closer objects need smaller radius to capture fine details + // Sample normal at current fragment (range: -1 to 1) + vec3 normal_center = texture2D(normal_texture, tex_coord).rgb * 2.0 - 1.0; + + // Calculate how much the surface faces upward + // up_factor = 1.0 for surfaces pointing straight up (0,0,1) + // up_factor = 0.0 for surfaces pointing down or sideways + float up_factor = max(0.0, normal_center.z); // Assuming Z is up axis + // Alternative: if Y is up, use normal_center.y + + // Enhance brightness on top surfaces (reduce AO effect) + // Top surfaces get only 30% of AO, bottom surfaces get 100% + float highlight_protection = mix(1.0, 0.3, up_factor); + + // Adaptive sampling radius float radius = mix(1.5, 4.0, depth_center / z_far); - // Circular sampling pattern (unit circle) with diagonal weighting - // Offsets normalized to unit circle for uniform directional sampling vec2 offsets[8]; - offsets[0] = vec2( 1.0, 0.0); // Right - offsets[1] = vec2( 0.707, 0.707); // Top-right diagonal - offsets[2] = vec2( 0.0, 1.0); // Top - offsets[3] = vec2(-0.707, 0.707); // Top-left diagonal - offsets[4] = vec2(-1.0, 0.0); // Left - offsets[5] = vec2(-0.707,-0.707); // Bottom-left diagonal - offsets[6] = vec2( 0.0, -1.0); // Bottom - offsets[7] = vec2( 0.707,-0.707); // Bottom-right diagonal + offsets[0] = vec2( 1.0, 0.0); + offsets[1] = vec2( 0.707, 0.707); + offsets[2] = vec2( 0.0, 1.0); + offsets[3] = vec2(-0.707, 0.707); + offsets[4] = vec2(-1.0, 0.0); + offsets[5] = vec2(-0.707,-0.707); + offsets[6] = vec2( 0.0, -1.0); + offsets[7] = vec2( 0.707,-0.707); float occlusion = 0.0; int valid_samples = 0; for (int i = 0; i < 8; ++i) { - // Apply radius and convert to UV space vec2 uv = tex_coord + offsets[i] * inv_tex_size * radius; - - // Clamp to texture edges to prevent border artifacts uv = clamp(uv, vec2(0.001), vec2(0.999)); - // Sample and linearize neighbor depth float sample_depth = linearize_depth(texture2D(depth_texture, uv).r); - - // Calculate depth difference (positive if neighbor is closer to camera) float depth_diff = max(0.0, depth_center - sample_depth); - // Adaptive threshold: larger tolerance for distant objects - // smoothstep creates soft occlusion falloff float threshold = 0.015 * (0.5 + depth_center / z_far); float contribution = smoothstep(0.001, threshold, depth_diff); - // Weight diagonal samples less (they're further in screen space) - // Reduces over-occlusion at 45-degree angles float diagonal_weight = 1.0 - abs(offsets[i].x * offsets[i].y) * 0.5; - occlusion += contribution * diagonal_weight; valid_samples++; } - // Average occlusion from all valid samples if (valid_samples > 0) occlusion /= float(valid_samples); - // Apply AO intensity curve - // 0.55 intensity factor - subtle effect that preserves original lighting - float ambient_occlusion = 1.0 - occlusion * 0.55; - ambient_occlusion = clamp(ambient_occlusion, 0.55, 1.0); + // Apply highlight protection - top surfaces get much less darkening + float ao_intensity = 0.55 * highlight_protection; + float ambient_occlusion = 1.0 - occlusion * ao_intensity; - // Gamma-style curve for more natural appearance - ambient_occlusion = pow(ambient_occlusion, 1.1); + // Different min values for top vs bottom surfaces + float ao_min = mix(0.45, 0.70, up_factor); // Bottom: 0.45, Top: 0.70 + ambient_occlusion = clamp(ambient_occlusion, ao_min, 1.0); + + // Boost brightness on top surfaces (optional) + float brightness_boost = 1.0 + up_factor * 0.15; // 15% extra brightness on top + ambient_occlusion = pow(ambient_occlusion, 1.1) * brightness_boost; + ambient_occlusion = clamp(ambient_occlusion, 0.45, 1.05); - // Final composite: modulate base color with AO factor gl_FragColor = vec4(base * ambient_occlusion, 1.0); } \ No newline at end of file diff --git a/resources/shaders/140/ssao.fs b/resources/shaders/140/ssao.fs index cf68ab083f..8bf02186ad 100644 --- a/resources/shaders/140/ssao.fs +++ b/resources/shaders/140/ssao.fs @@ -1,93 +1,99 @@ #version 140 /** - * SSAO (Screen Space Ambient Occlusion) Shader - GLSL 140 version - * Uses texelFetch for precise pixel access and better performance - * Requires OpenGL 3.1+ / GLSL 1.40 + * SSAO Shader - GLSL 140 version with highlight protection + * Preserves brightness on upward-facing surfaces for better visual quality */ -uniform sampler2D color_texture; // Original scene color -uniform sampler2D depth_texture; // Depth buffer texture -uniform float z_near; // Near clipping plane distance -uniform float z_far; // Far clipping plane distance +uniform sampler2D color_texture; +uniform sampler2D depth_texture; +uniform sampler2D normal_texture; +uniform float z_near; +uniform float z_far; -in vec2 tex_coord; // Texture coordinates from vertex shader -out vec4 frag_color; // Final fragment color output +in vec2 tex_coord; +out vec4 frag_color; -/** - * Convert linear depth buffer value to world/view space depth - * Same math as 110 version but with modern syntax - */ float linearize_depth(float depth) { - float z = depth * 2.0 - 1.0; // Convert to NDC [-1, 1] range + float z = depth * 2.0 - 1.0; return (2.0 * z_near * z_far) / (z_far + z_near - z * (z_far - z_near)); } void main() { - // Get exact pixel coordinates using gl_FragCoord (pixel-perfect access) ivec2 pixel = ivec2(gl_FragCoord.xy); - - // Use texelFetch for direct pixel access without texture filtering - // Much faster and more precise than texture2D for depth buffers float center_depth = linearize_depth(texelFetch(depth_texture, pixel, 0).r); - // Adaptive radius in pixel space (not UV space) - // int cast ensures exact pixel offsets without floating point errors + // Sample normal buffer (stored as RGB in 0-1 range, convert to -1 to 1) + vec3 normal_center = texelFetch(normal_texture, pixel, 0).rgb * 2.0 - 1.0; + normal_center = normalize(normal_center); + + // Calculate upward-facing factor + // Assumes Z-up coordinate system (typical for 3D printing) + float up_factor = clamp(normal_center.z * 1.5, 0.0, 1.0); // Boosted for better response + + // Alternative if using Y-up: float up_factor = clamp(normal_center.y * 1.5, 0.0, 1.0); + + // Adaptive radius in pixel space int radius = int(mix(2.0, 5.0, center_depth / z_far)); - // Optimized sampling pattern with more samples (12 vs 8) - // Includes both cardinal directions and diagonals at different distances + // Optimized sampling pattern const ivec2 offsets[12] = ivec2[]( - ivec2(1, 0), ivec2(-1, 0), ivec2(0, 1), ivec2(0, -1), // Cardinal directions (distance 1) - ivec2(1, 1), ivec2(-1, 1), ivec2(1, -1), ivec2(-1, -1), // Diagonals (distance 1.414) - ivec2(2, 0), ivec2(-2, 0), ivec2(0, 2), ivec2(0, -2) // Far cardinal (distance 2) + ivec2(1, 0), ivec2(-1, 0), ivec2(0, 1), ivec2(0, -1), + ivec2(1, 1), ivec2(-1, 1), ivec2(1, -1), ivec2(-1, -1), + ivec2(2, 0), ivec2(-2, 0), ivec2(0, 2), ivec2(0, -2) ); float occlusion = 0.0; int valid_samples = 0; for (int i = 0; i < 12; i++) { - // Calculate neighbor pixel position with adaptive radius ivec2 sample_pixel = pixel + offsets[i] * radius; - // Boundary check to prevent reading outside framebuffer - // Avoids artifacts at screen edges if (sample_pixel.x < 0 || sample_pixel.y < 0) continue; - // Direct pixel fetch - no filtering, exact depth value float sample_depth = linearize_depth(texelFetch(depth_texture, sample_pixel, 0).r); - // Depth difference (positive = neighbor is in front) + // Sample normal at neighbor + vec3 normal_sample = texelFetch(normal_texture, sample_pixel, 0).rgb * 2.0 - 1.0; + + // Direction from center to sample in screen space + vec2 dir_2d = normalize(vec2(sample_pixel - pixel)); + + // Reduce occlusion when normals are similar (planar surfaces) + float normal_similarity = dot(normal_center, normal_sample); + float planar_factor = smoothstep(0.7, 0.95, normal_similarity); + float depth_diff = max(0.0, center_depth - sample_depth); - - // Adaptive threshold based on distance - // Distant objects need larger threshold due to depth compression float threshold = 0.02 * (0.5 + center_depth / z_far); + float contribution = smoothstep(0.001, threshold, depth_diff); - // Smoothstep for soft occlusion falloff - occlusion += smoothstep(0.001, threshold, depth_diff); + // Reduce contribution on planar surfaces and top areas + float top_factor = 1.0 - up_factor * 0.6; // 60% less occlusion on tops + contribution *= (1.0 - planar_factor * 0.5) * top_factor; + + occlusion += contribution; valid_samples++; } - // Calculate final AO factor if (valid_samples > 0) { - // Average occlusion and apply intensity (0.5 = subtle effect) - float ao_factor = 1.0 - (occlusion / float(valid_samples)) * 0.5; - ao_factor = clamp(ao_factor, 0.58, 1.0); + float ao_factor = 1.0 - (occlusion / float(valid_samples)) * 0.45; - // Apply power curve for better visual response - ao_factor = pow(ao_factor, 1.15); - occlusion = ao_factor; + // Brighter minimum for top surfaces + float ao_min = mix(0.50, 0.75, up_factor); + ao_factor = clamp(ao_factor, ao_min, 1.0); + + // Additional brightness boost for upward-facing surfaces + float brightness_boost = 1.0 + up_factor * 0.2; + ao_factor = pow(ao_factor, 1.1) * brightness_boost; + + occlusion = clamp(ao_factor, 0.45, 1.05); } else { - occlusion = 1.0; // No samples available, no occlusion + occlusion = 1.0; } - // Sample color using standard filtering (better for colors) vec3 color = texture(color_texture, tex_coord).rgb; - - // Apply ambient occlusion to final color frag_color = vec4(color * occlusion, 1.0); } \ No newline at end of file