Realistic View: Phong Shading + Ambient Oclusion + Cast Shadows (#13704)

* Phong Shading

* Add shader selection to graphics preferences

* SSAO

* 3D canvas menu

Co-Authored-By: yw4z <yw4z@outlook.com>

* better SSAO

* Adjust

* phong in preview mode

* cast shadows

sombra 3

sombra 2

* fix 1

* SSAO independent

* Fix 2

* clean 1

* shadows availables with gouraud

* Update Preferences.cpp

* tweeks

* No Normal textures

* Depth texture allocation

* avoid rebinding/redefining state each render.

* free SSAO

* set shadow fill color

* remove duplicated code

* cached model to avoid per-frame uploads

* yw4z suggestion

Co-Authored-By: yw4z <yw4z@outlook.com>

* Update Preferences.cpp

Co-Authored-By: yw4z <yw4z@outlook.com>

* Update GLCanvas3D.cpp

* only gouraud for preview mode

* Apply suggestions from code review

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Add new OpenGL shading model setting

* Update src/slic3r/GUI/GLCanvas3D.cpp

* Apply suggestion from @RF47

* Title Case

* normal textures

* gamma 2.2

Co-Authored-By: Ian Bassi <12130714+ianalexis@users.noreply.github.com>

* Revert "gamma 2.2"

This reverts commit 909a84af604a080b3f4b8dd67d13956473a77afe.

* Reapply "gamma 2.2"

This reverts commit 0f0c3d9ec0d2c9647ce06afac4fe9266b5ffda97.

* AO blend

Co-Authored-By: Ian Bassi <12130714+ianalexis@users.noreply.github.com>

* Revert "AO blend"

This reverts commit c5c9a3aa6b295704e71299451b937648e5c5f109.

* 4.0 pixel radius

* windows light effect

direccion

brillo

* smoothing

* ajuste de brillo

* ajustes de brillo

* No SSAO  for bed

* disable realistic view on paint gismos

* Update ssao.fs

* circular panel

---------

Co-authored-by: yw4z <yw4z@outlook.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
Co-authored-by: Ian Bassi <12130714+ianalexis@users.noreply.github.com>
Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
Rodrigo Faselli
2026-06-04 07:22:06 -03:00
committed by GitHub
parent a86f4ad32a
commit 77002ed0f3
16 changed files with 1303 additions and 9 deletions

View File

@@ -0,0 +1,247 @@
#version 110
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
const vec3 LightRed = vec3(0.78, 0.0, 0.0);
const vec3 LightBlue = vec3(0.73, 1.0, 1.0);
const float EPSILON = 0.0001;
#define INTENSITY_CORRECTION 0.6
#define PHONG_BRIGHTNESS 1.0
// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SPECULAR (0.8 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SHININESS 128.0
// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
#define LIGHT_FRONT_SPECULAR (0.28 * INTENSITY_CORRECTION)
#define LIGHT_FRONT_SHININESS 64.0
#define INTENSITY_AMBIENT 0.22
#define WINDOW_REFLECTION_INTENSITY 0.55
struct PrintVolumeDetection
{
// 0 = rectangle, 1 = circle, 2 = custom, 3 = invalid
int type;
// type = 0 (rectangle):
// x = min.x, y = min.y, z = max.x, w = max.y
// type = 1 (circle):
// x = center.x, y = center.y, z = radius
vec4 xy_data;
// x = min z, y = max z
vec2 z_data;
};
struct SlopeDetection
{
bool actived;
float normal_z;
mat3 volume_world_normal_matrix;
};
uniform vec4 uniform_color;
uniform bool use_color_clip_plane;
uniform vec4 uniform_color_clip_plane_1;
uniform vec4 uniform_color_clip_plane_2;
uniform SlopeDetection slope;
//BBS: add outline_color
uniform bool is_outline;
uniform sampler2D depth_tex;
uniform vec2 screen_size;
#ifdef ENABLE_ENVIRONMENT_MAP
uniform sampler2D environment_tex;
uniform bool use_environment_tex;
#endif // ENABLE_ENVIRONMENT_MAP
uniform PrintVolumeDetection print_volume;
uniform float z_far;
uniform float z_near;
uniform bool enable_ssao;
varying vec3 clipping_planes_dots;
varying float color_clip_plane_dot;
varying vec4 world_pos;
varying float world_normal_z;
varying vec3 eye_normal;
varying vec3 eye_position;
vec3 getBackfaceColor(vec3 fill) {
float brightness = 0.2126 * fill.r + 0.7152 * fill.g + 0.0722 * fill.b;
return (brightness > 0.75) ? vec3(0.11, 0.165, 0.208) : vec3(0.988, 0.988, 0.988);
}
// Silhouette edge detection & rendering algorithm by leoneruggiero
// https://www.shadertoy.com/view/DslXz2
#define INFLATE 1
float GetTolerance(float d, float k)
{
float A = -(z_far+z_near)/(z_far-z_near);
float B = -2.0*z_far*z_near /(z_far-z_near);
d = d*2.0-1.0;
return -k*(d+A)*(d+A)/B;
}
float DetectSilho(vec2 fragCoord, vec2 dir)
{
float x0 = abs(texture2D(depth_tex, (fragCoord + dir*-2.0) / screen_size).r);
float x1 = abs(texture2D(depth_tex, (fragCoord + dir*-1.0) / screen_size).r);
float x2 = abs(texture2D(depth_tex, (fragCoord + dir* 0.0) / screen_size).r);
float x3 = abs(texture2D(depth_tex, (fragCoord + dir* 1.0) / screen_size).r);
float d0 = (x1-x0);
float d1 = (x2-x3);
float r0 = x1 + d0 - x2;
float r1 = x2 + d1 - x1;
float tol = GetTolerance(x2, 0.04);
return smoothstep(0.0, tol*tol, max( - r0*r1, 0.0));
}
float DetectSilho(vec2 fragCoord)
{
return max(
DetectSilho(fragCoord, vec2(1,0)),
DetectSilho(fragCoord, vec2(0,1))
);
}
float compute_ssao_factor(vec3 normal, vec3 view_dir, vec3 eye_pos)
{
vec3 normal_dx = dFdx(normal);
vec3 normal_dy = dFdy(normal);
float normal_variation = clamp(length(normal_dx) + length(normal_dy), 0.0, 1.0);
float depth_gradient = clamp(length(vec2(dFdx(eye_pos.z), dFdy(eye_pos.z))) * 0.8, 0.0, 1.0);
float cavity = clamp(normal_variation * 0.70 + depth_gradient * 0.60, 0.0, 1.0);
float cavity_mask = smoothstep(0.25, 0.75, cavity);
float ao_strength = pow(cavity, 1.15) * cavity_mask;
return clamp(1.0 - ao_strength * 0.90, 0.25, 1.0);
}
float soft_circle(vec2 p, vec2 center, float radius, float blur)
{
float dist = distance(p, center);
return 1.0 - smoothstep(radius - blur, radius, dist);
}
vec3 compute_window_reflection(vec3 normal, vec3 view_dir)
{
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
vec3 light_dir = normalize(LIGHT_TOP_DIR);
vec3 reflect_light = normalize(reflect(-light_dir, normal));
// UV coordinates for the reflection
vec2 uv = (reflect_light.xy / (1.0 + max(reflect_light.z, 0.3))) * 2.2;
vec2 grad = fwidth(uv) * 0.8;
float blur = 0.12 + grad.x * 1.5;
// === CIRCULAR WINDOW (porthole style) ===
// Single round window, no bars
vec2 window_center = vec2(0.0, 0.0);
float window_radius = 0.5; // Radius of the circular window
float window_light = soft_circle(uv, window_center, window_radius, blur);
// No bars - just pure circular glass
float bars = 1.0;
// Fresnel effect for edge glow
float fresnel = pow(1.0 - max(dot(normal, view_dir), 0.0), 1.0);
float facing = smoothstep(-0.4, 0.6, reflect_light.z);
float intensity = window_light * bars * (0.25 + 0.25 * fresnel) * facing;
intensity = clamp(intensity, 0.0, 0.45);
return vec3(intensity);
}
void main()
{
if (any(lessThan(clipping_planes_dots, ZERO)))
discard;
vec4 color;
if (use_color_clip_plane) {
color.rgb = (color_clip_plane_dot < 0.0) ? uniform_color_clip_plane_1.rgb : uniform_color_clip_plane_2.rgb;
color.a = uniform_color.a;
}
else
color = uniform_color;
if (slope.actived) {
if(world_pos.z<0.1 && world_pos.z>-0.1)
{
color.rgb = LightBlue;
color.a = 0.8;
}
else if( world_normal_z < slope.normal_z - EPSILON)
{
color.rgb = color.rgb * 0.5 + LightRed * 0.5;
color.a = 0.8;
}
}
vec3 pv_check_min = ZERO;
vec3 pv_check_max = ZERO;
if (print_volume.type == 0) {
pv_check_min = world_pos.xyz - vec3(print_volume.xy_data.x, print_volume.xy_data.y, print_volume.z_data.x);
pv_check_max = world_pos.xyz - vec3(print_volume.xy_data.z, print_volume.xy_data.w, print_volume.z_data.y);
}
else if (print_volume.type == 1) {
float delta_radius = print_volume.xy_data.z - distance(world_pos.xy, print_volume.xy_data.xy);
pv_check_min = vec3(delta_radius, 0.0, world_pos.z - print_volume.z_data.x);
pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y);
}
color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb;
vec3 normal = normalize(eye_normal);
vec3 view_dir = normalize(-eye_position);
float NdotL_top = max(dot(normal, LIGHT_TOP_DIR), 0.0);
float diffuse = INTENSITY_AMBIENT + NdotL_top * LIGHT_TOP_DIFFUSE;
vec3 half_top = normalize(LIGHT_TOP_DIR + view_dir);
float specular = LIGHT_TOP_SPECULAR * pow(max(dot(normal, half_top), 0.0), LIGHT_TOP_SHININESS);
float NdotL_front = max(dot(normal, LIGHT_FRONT_DIR), 0.0);
diffuse += NdotL_front * LIGHT_FRONT_DIFFUSE;
vec3 half_front = normalize(LIGHT_FRONT_DIR + view_dir);
specular += LIGHT_FRONT_SPECULAR * pow(max(dot(normal, half_front), 0.0), LIGHT_FRONT_SHININESS);
vec3 window_reflection = compute_window_reflection(normal, view_dir);
// SSAO is applied in post-process pass. Keep base lighting unchanged here.
if (is_outline) {
vec3 shaded_rgb = (vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS;
vec4 shaded_color = vec4(clamp(shaded_rgb, vec3(0.0), vec3(1.0)), color.a);
vec2 fragCoord = gl_FragCoord.xy;
float s = DetectSilho(fragCoord);
for(int i=1;i<=INFLATE; i++)
{
s = max(s, DetectSilho(fragCoord.xy + vec2(i, 0)));
s = max(s, DetectSilho(fragCoord.xy + vec2(0, i)));
}
gl_FragColor = vec4(mix(shaded_color.rgb, getBackfaceColor(shaded_color.rgb), s), shaded_color.a);
}
#ifdef ENABLE_ENVIRONMENT_MAP
else if (use_environment_tex)
gl_FragColor = vec4(clamp((0.45 * texture2D(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + window_reflection + 0.8 * color.rgb * diffuse) * PHONG_BRIGHTNESS, vec3(0.0), vec3(1.0)), color.a);
#endif
else
gl_FragColor = vec4(clamp((vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS, vec3(0.0), vec3(1.0)), color.a);
}

View File

@@ -0,0 +1,54 @@
#version 110
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
struct SlopeDetection
{
bool actived;
float normal_z;
mat3 volume_world_normal_matrix;
};
uniform mat4 view_model_matrix;
uniform mat4 projection_matrix;
uniform mat3 view_normal_matrix;
uniform mat4 volume_world_matrix;
uniform SlopeDetection slope;
// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane.
uniform vec2 z_range;
// Clipping plane - general orientation. Used by the SLA gizmo.
uniform vec4 clipping_plane;
// Color clip plane - general orientation. Used by the cut gizmo.
uniform vec4 color_clip_plane;
attribute vec3 v_position;
attribute vec3 v_normal;
varying vec3 clipping_planes_dots;
varying float color_clip_plane_dot;
varying vec4 world_pos;
varying float world_normal_z;
varying vec3 eye_normal;
varying vec3 eye_position;
void main()
{
// First transform the normal into camera space and normalize the result.
eye_normal = normalize(view_normal_matrix * v_normal);
vec4 position = view_model_matrix * vec4(v_position, 1.0);
eye_position = position.xyz;
// Point in homogenous coordinates.
world_pos = volume_world_matrix * vec4(v_position, 1.0);
// z component of normal vector in world coordinate used for slope shading
world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * v_normal)).z : 0.0;
gl_Position = projection_matrix * position;
// Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
color_clip_plane_dot = dot(world_pos, color_clip_plane);
}

View File

@@ -0,0 +1,85 @@
#version 110
/**
* SSAO Shader - GLSL 110 version with highlight protection
* Preserves brightness on upward-facing surfaces (top areas)
*/
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;
float linearize_depth(float depth)
{
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()
{
vec3 base = texture2D(color_texture, tex_coord).rgb;
float depth_center = linearize_depth(texture2D(depth_texture, tex_coord).r);
// 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
// Adaptive sampling radius
float radius = mix(2.0, 4.0, depth_center / z_far);
vec2 offsets[8];
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) {
vec2 uv = tex_coord + offsets[i] * inv_tex_size * radius;
uv = clamp(uv, vec2(0.001), vec2(0.999));
float sample_depth = linearize_depth(texture2D(depth_texture, uv).r);
float depth_diff = max(0.0, depth_center - sample_depth);
float threshold = 0.015 * (0.5 + depth_center / z_far);
float contribution = smoothstep(0.001, threshold, depth_diff);
float diagonal_weight = 1.0 - abs(offsets[i].x * offsets[i].y) * 0.5;
occlusion += contribution * diagonal_weight;
valid_samples++;
}
if (valid_samples > 0)
occlusion /= float(valid_samples);
// flatter/top-like surfaces get less darkening
float ao_intensity = 0.55;
float ambient_occlusion = 1.0 - occlusion * ao_intensity;
// 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, 2.2) * brightness_boost;
ambient_occlusion = clamp(ambient_occlusion, 0.45, 1.05);
gl_FragColor = vec4(base * ambient_occlusion, 1.0);
}

View File

@@ -0,0 +1,15 @@
#version 110
uniform mat4 view_model_matrix;
uniform mat4 projection_matrix;
attribute vec3 v_position;
attribute vec2 v_tex_coord;
varying vec2 tex_coord;
void main()
{
tex_coord = v_tex_coord;
gl_Position = projection_matrix * view_model_matrix * vec4(v_position, 1.0);
}

View File

@@ -0,0 +1,250 @@
#version 140
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
const vec3 LightRed = vec3(0.78, 0.0, 0.0);
const vec3 LightBlue = vec3(0.73, 1.0, 1.0);
const float EPSILON = 0.0001;
#define INTENSITY_CORRECTION 0.6
#define PHONG_BRIGHTNESS 1.0
// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SPECULAR (0.8 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SHININESS 128.0
// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
#define LIGHT_FRONT_SPECULAR (0.28 * INTENSITY_CORRECTION)
#define LIGHT_FRONT_SHININESS 64.0
#define INTENSITY_AMBIENT 0.22
#define WINDOW_REFLECTION_INTENSITY 0.55
struct PrintVolumeDetection
{
// 0 = rectangle, 1 = circle, 2 = custom, 3 = invalid
int type;
// type = 0 (rectangle):
// x = min.x, y = min.y, z = max.x, w = max.y
// type = 1 (circle):
// x = center.x, y = center.y, z = radius
vec4 xy_data;
// x = min z, y = max z
vec2 z_data;
};
struct SlopeDetection
{
bool actived;
float normal_z;
mat3 volume_world_normal_matrix;
};
uniform vec4 uniform_color;
uniform bool use_color_clip_plane;
uniform vec4 uniform_color_clip_plane_1;
uniform vec4 uniform_color_clip_plane_2;
uniform SlopeDetection slope;
//BBS: add outline_color
uniform bool is_outline;
uniform sampler2D depth_tex;
uniform vec2 screen_size;
#ifdef ENABLE_ENVIRONMENT_MAP
uniform sampler2D environment_tex;
uniform bool use_environment_tex;
#endif // ENABLE_ENVIRONMENT_MAP
uniform PrintVolumeDetection print_volume;
uniform float z_far;
uniform float z_near;
uniform bool enable_ssao;
in vec3 clipping_planes_dots;
in float color_clip_plane_dot;
in vec4 world_pos;
in float world_normal_z;
in vec3 eye_normal;
in vec3 eye_position;
out vec4 out_color;
vec3 getBackfaceColor(vec3 fill) {
float brightness = 0.2126 * fill.r + 0.7152 * fill.g + 0.0722 * fill.b;
return (brightness > 0.75) ? vec3(0.11, 0.165, 0.208) : vec3(0.988, 0.988, 0.988);
}
// Silhouette edge detection & rendering algorithm by leoneruggiero
// https://www.shadertoy.com/view/DslXz2
#define INFLATE 1
float GetTolerance(float d, float k)
{
float A=- (z_far+z_near)/(z_far-z_near);
float B=-2.0*z_far*z_near /(z_far-z_near);
d = d*2.0-1.0;
return -k*(d+A)*(d+A)/B;
}
float DetectSilho(vec2 fragCoord, vec2 dir)
{
float x0 = abs(texture(depth_tex, (fragCoord + dir*-2.0) / screen_size).r);
float x1 = abs(texture(depth_tex, (fragCoord + dir*-1.0) / screen_size).r);
float x2 = abs(texture(depth_tex, (fragCoord + dir* 0.0) / screen_size).r);
float x3 = abs(texture(depth_tex, (fragCoord + dir* 1.0) / screen_size).r);
float d0 = (x1-x0);
float d1 = (x2-x3);
float r0 = x1 + d0 - x2;
float r1 = x2 + d1 - x1;
float tol = GetTolerance(x2, 0.04);
return smoothstep(0.0, tol*tol, max( - r0*r1, 0.0));
}
float DetectSilho(vec2 fragCoord)
{
return max(
DetectSilho(fragCoord, vec2(1,0)),
DetectSilho(fragCoord, vec2(0,1))
);
}
float compute_ssao_factor(vec3 normal, vec3 view_dir, vec3 eye_pos)
{
vec3 normal_dx = dFdx(normal);
vec3 normal_dy = dFdy(normal);
float normal_variation = clamp(length(normal_dx) + length(normal_dy), 0.0, 1.0);
float depth_gradient = clamp(length(vec2(dFdx(eye_pos.z), dFdy(eye_pos.z))) * 0.8, 0.0, 1.0);
float cavity = clamp(normal_variation * 0.70 + depth_gradient * 0.60, 0.0, 1.0);
float cavity_mask = smoothstep(0.25, 0.75, cavity);
float ao_strength = pow(cavity, 1.15) * cavity_mask;
return clamp(1.0 - ao_strength * 0.90, 0.25, 1.0);
}
float soft_circle(vec2 p, vec2 center, float radius, float blur)
{
float dist = distance(p, center);
return 1.0 - smoothstep(radius - blur, radius, dist);
}
vec3 compute_window_reflection(vec3 normal, vec3 view_dir)
{
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
vec3 light_dir = normalize(LIGHT_TOP_DIR);
vec3 reflect_light = normalize(reflect(-light_dir, normal));
// UV coordinates for the reflection
vec2 uv = (reflect_light.xy / (1.0 + max(reflect_light.z, 0.3))) * 2.2;
vec2 grad = fwidth(uv) * 0.8;
float blur = 0.12 + grad.x * 1.5;
// === CIRCULAR WINDOW (porthole style) ===
// Single round window, no bars
vec2 window_center = vec2(0.0, 0.0);
float window_radius = 0.5; // Radius of the circular window
float window_light = soft_circle(uv, window_center, window_radius, blur);
// No bars - just pure circular glass
float bars = 1.0;
// Fresnel effect for edge glow
float fresnel = pow(1.0 - max(dot(normal, view_dir), 0.0), 1.0);
float facing = smoothstep(-0.4, 0.6, reflect_light.z);
float intensity = window_light * bars * (0.25 + 0.25 * fresnel) * facing;
intensity = clamp(intensity, 0.0, 0.45);
return vec3(intensity);
}
void main()
{
if (any(lessThan(clipping_planes_dots, ZERO)))
discard;
vec4 color;
if (use_color_clip_plane) {
color.rgb = (color_clip_plane_dot < 0.0) ? uniform_color_clip_plane_1.rgb : uniform_color_clip_plane_2.rgb;
color.a = uniform_color.a;
}
else
color = uniform_color;
if (slope.actived) {
if(world_pos.z<0.1&&world_pos.z>-0.1)
{
color.rgb = LightBlue;
color.a = 0.8;
}
else if( world_normal_z < slope.normal_z - EPSILON)
{
color.rgb = color.rgb * 0.5 + LightRed * 0.5;
color.a = 0.8;
}
}
vec3 pv_check_min = ZERO;
vec3 pv_check_max = ZERO;
if (print_volume.type == 0) {
pv_check_min = world_pos.xyz - vec3(print_volume.xy_data.x, print_volume.xy_data.y, print_volume.z_data.x);
pv_check_max = world_pos.xyz - vec3(print_volume.xy_data.z, print_volume.xy_data.w, print_volume.z_data.y);
}
else if (print_volume.type == 1) {
float delta_radius = print_volume.xy_data.z - distance(world_pos.xy, print_volume.xy_data.xy);
pv_check_min = vec3(delta_radius, 0.0, world_pos.z - print_volume.z_data.x);
pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y);
}
color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb;
vec3 normal = normalize(eye_normal);
vec3 view_dir = normalize(-eye_position);
float NdotL_top = max(dot(normal, LIGHT_TOP_DIR), 0.0);
float diffuse = INTENSITY_AMBIENT + NdotL_top * LIGHT_TOP_DIFFUSE;
vec3 half_top = normalize(LIGHT_TOP_DIR + view_dir);
float specular = LIGHT_TOP_SPECULAR * pow(max(dot(normal, half_top), 0.0), LIGHT_TOP_SHININESS);
float NdotL_front = max(dot(normal, LIGHT_FRONT_DIR), 0.0);
diffuse += NdotL_front * LIGHT_FRONT_DIFFUSE;
vec3 half_front = normalize(LIGHT_FRONT_DIR + view_dir);
specular += LIGHT_FRONT_SPECULAR * pow(max(dot(normal, half_front), 0.0), LIGHT_FRONT_SHININESS);
vec3 window_reflection = compute_window_reflection(normal, view_dir);
// SSAO is applied in post-process pass. Keep base lighting unchanged here.
if (is_outline) {
vec3 shaded_rgb = (vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS;
vec4 shaded_color = vec4(clamp(shaded_rgb, vec3(0.0), vec3(1.0)), color.a);
vec2 fragCoord = gl_FragCoord.xy;
float s = DetectSilho(fragCoord);
for(int i=1;i<=INFLATE; i++)
{
s = max(s, DetectSilho(fragCoord.xy + vec2(i, 0)));
s = max(s, DetectSilho(fragCoord.xy + vec2(0, i)));
}
out_color = vec4(mix(shaded_color.rgb, getBackfaceColor(shaded_color.rgb), s), shaded_color.a);
}
#ifdef ENABLE_ENVIRONMENT_MAP
else if (use_environment_tex)
out_color = vec4(clamp((0.45 * texture(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + window_reflection + 0.8 * color.rgb * diffuse) * PHONG_BRIGHTNESS, vec3(0.0), vec3(1.0)), color.a);
#endif
else
out_color = vec4(clamp((vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS, vec3(0.0), vec3(1.0)), color.a);
}

View File

@@ -0,0 +1,54 @@
#version 140
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
struct SlopeDetection
{
bool actived;
float normal_z;
mat3 volume_world_normal_matrix;
};
uniform mat4 view_model_matrix;
uniform mat4 projection_matrix;
uniform mat3 view_normal_matrix;
uniform mat4 volume_world_matrix;
uniform SlopeDetection slope;
// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane.
uniform vec2 z_range;
// Clipping plane - general orientation. Used by the SLA gizmo.
uniform vec4 clipping_plane;
// Color clip plane - general orientation. Used by the cut gizmo.
uniform vec4 color_clip_plane;
in vec3 v_position;
in vec3 v_normal;
out vec3 clipping_planes_dots;
out float color_clip_plane_dot;
out vec4 world_pos;
out float world_normal_z;
out vec3 eye_normal;
out vec3 eye_position;
void main()
{
// First transform the normal into camera space and normalize the result.
eye_normal = normalize(view_normal_matrix * v_normal);
vec4 position = view_model_matrix * vec4(v_position, 1.0);
eye_position = position.xyz;
// Point in homogenous coordinates.
world_pos = volume_world_matrix * vec4(v_position, 1.0);
// z component of normal vector in world coordinate used for slope shading
world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * v_normal)).z : 0.0;
gl_Position = projection_matrix * position;
// Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
color_clip_plane_dot = dot(world_pos, color_clip_plane);
}

View File

@@ -0,0 +1,103 @@
#version 140
/**
* SSAO Shader - GLSL 140 version with sharp depth threshold
* Only darkens valleys/concave areas, ignores smooth variations
*/
uniform sampler2D color_texture;
uniform sampler2D depth_texture;
uniform sampler2D normal_texture;
uniform float z_near;
uniform float z_far;
in vec2 tex_coord;
out vec4 frag_color;
float linearize_depth(float depth)
{
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()
{
ivec2 pixel = ivec2(gl_FragCoord.xy);
float center_depth = linearize_depth(texelFetch(depth_texture, pixel, 0).r);
// 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 (Z-up coordinate system)
float up_factor = clamp(normal_center.z * 1.5, 0.0, 1.0);
// Adaptive radius in pixel space
int radius = int(mix(2.0, 4.0, center_depth / z_far));
// Optimized sampling pattern
const ivec2 offsets[12] = ivec2[](
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++) {
ivec2 sample_pixel = pixel + offsets[i] * radius;
if (sample_pixel.x < 0 || sample_pixel.y < 0)
continue;
float sample_depth = linearize_depth(texelFetch(depth_texture, sample_pixel, 0).r);
// Sample normal at neighbor
vec3 normal_sample = texelFetch(normal_texture, sample_pixel, 0).rgb * 2.0 - 1.0;
// Depth difference (positive if neighbor is closer to camera)
float depth_diff = center_depth - sample_depth;
// Sharp depth threshold ===
// Minimum depth difference to consider occlusion (ignores small variations)
float threshold_min = 0.008; // Higher = only deep valleys get darkened
float threshold_max = 0.04; // Transition range for full occlusion
float contribution = 0.0;
if (depth_diff > threshold_min) {
// Abrupt mapping with power curve
contribution = (depth_diff - threshold_min) / (threshold_max - threshold_min);
contribution = clamp(contribution, 0.0, 1.0);
contribution = pow(contribution, 2.0); // Steeper curve for sharper transition
}
// Reduce occlusion on planar surfaces (similar normals)
float normal_similarity = dot(normal_center, normal_sample);
float planar_factor = smoothstep(0.75, 0.95, normal_similarity);
contribution *= (1.0 - planar_factor * 0.6);
occlusion += contribution;
valid_samples++;
}
if (valid_samples > 0) {
// Calculate ambient occlusion factor with higher base intensity
float ao_factor = 1.0 - (occlusion / float(valid_samples)) * 0.6;
// Keep bright areas clean (higher minimum for upward-facing surfaces)
float ao_min = mix(0.55, 0.85, up_factor);
ao_factor = clamp(ao_factor, ao_min, 1.0);
// Slight brightness boost for upward-facing surfaces
float brightness_boost = 1.0 + up_factor * 0.15;
ao_factor = ao_factor * brightness_boost;
occlusion = ao_factor;
} else {
occlusion = 1.0;
}
vec3 color = texture(color_texture, tex_coord).rgb;
frag_color = vec4(color * occlusion, 1.0);
}

View File

@@ -0,0 +1,15 @@
#version 140
uniform mat4 view_model_matrix;
uniform mat4 projection_matrix;
in vec3 v_position;
in vec2 v_tex_coord;
out vec2 tex_coord;
void main()
{
tex_coord = v_tex_coord;
gl_Position = projection_matrix * view_model_matrix * vec4(v_position, 1.0);
}

View File

@@ -264,6 +264,21 @@ void AppConfig::set_defaults()
if (get(SETTING_OPENGL_SHOW_FPS_OVERLAY).empty())
set_bool(SETTING_OPENGL_SHOW_FPS_OVERLAY, false);
if (get(SETTING_OPENGL_REALISTIC_MODE).empty())
set_bool(SETTING_OPENGL_REALISTIC_MODE, false);
if (get(SETTING_OPENGL_REALISTIC_PHONG).empty())
set_bool(SETTING_OPENGL_REALISTIC_PHONG, true);
if (get(SETTING_OPENGL_SHADING_MODEL).empty())
set(SETTING_OPENGL_SHADING_MODEL, "gouraud");
if (get(SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS).empty())
set_bool(SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS, false);
if (get(SETTING_OPENGL_PHONG_SSAO).empty())
set_bool(SETTING_OPENGL_PHONG_SSAO, false);
if (get("export_sources_full_pathnames").empty())
set_bool("export_sources_full_pathnames", false);

View File

@@ -34,6 +34,11 @@ using namespace nlohmann;
#define SETTING_OPENGL_FXAA_ENABLED "opengl_fxaa_enabled"
#define SETTING_OPENGL_FPS_CAP "opengl_fps_cap"
#define SETTING_OPENGL_SHOW_FPS_OVERLAY "opengl_show_fps_overlay"
#define SETTING_OPENGL_REALISTIC_MODE "opengl_realistic_mode"
#define SETTING_OPENGL_REALISTIC_PHONG "opengl_realistic_phong"
#define SETTING_OPENGL_SHADING_MODEL "opengl_shading_model"
#define SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS "opengl_phong_basic_plate_shadows"
#define SETTING_OPENGL_PHONG_SSAO "opengl_phong_ssao"
#if defined(_WIN32) || defined(_WIN64)
#define BAMBU_NETWORK_AGENT_VERSION_LEGACY "01.10.01.09"

View File

@@ -1218,10 +1218,22 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
GLCanvas3D::~GLCanvas3D()
{
if (m_fxaa_texture_id != 0 && _set_current()) {
glsafe(::glDeleteTextures(1, &m_fxaa_texture_id));
m_fxaa_texture_id = 0;
if (_set_current()) {
if (m_fxaa_texture_id != 0) {
glsafe(::glDeleteTextures(1, &m_fxaa_texture_id));
m_fxaa_texture_id = 0;
}
if (m_ssao_color_texture_id != 0) {
glsafe(::glDeleteTextures(1, &m_ssao_color_texture_id));
m_ssao_color_texture_id = 0;
}
if (m_ssao_depth_texture_id != 0) {
glsafe(::glDeleteTextures(1, &m_ssao_depth_texture_id));
m_ssao_depth_texture_id = 0;
}
m_plate_shadow_mask.reset();
}
m_plate_shadow_mask_key.clear();
reset_volumes();
@@ -2039,14 +2051,16 @@ void GLCanvas3D::render(bool only_init)
/* view3D render*/
int hover_id = (m_hover_plate_idxs.size() > 0)?m_hover_plate_idxs.front():-1;
if (m_canvas_type == ECanvasType::CanvasView3D) {
//BBS: add outline logic
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
_render_sla_slices();
_render_selection();
if (!no_partplate)
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), m_show_world_axes);
if (!no_partplate) //BBS: add outline logic
_render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, only_body, hover_id, true, show_grid);
//BBS: add outline logic
_render_cast_shadows_on_plate(camera.get_view_matrix(), camera.get_projection_matrix());
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
_render_sla_slices();
_render_selection();
_render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running());
}
/* preview render */
@@ -2103,6 +2117,9 @@ void GLCanvas3D::render(bool only_init)
if (m_picking_enabled && m_rectangle_selection.is_dragging())
m_rectangle_selection.render(*this);
if (_is_ssao_enabled())
_render_ssao_pass(static_cast<unsigned int>(cnv_size.get_width()), static_cast<unsigned int>(cnv_size.get_height()));
if (_is_fxaa_enabled())
_render_fxaa_pass(static_cast<unsigned int>(cnv_size.get_width()), static_cast<unsigned int>(cnv_size.get_height()));
@@ -7502,6 +7519,14 @@ bool GLCanvas3D::_is_fxaa_enabled() const
return wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_FXAA_ENABLED);
}
bool GLCanvas3D::_is_ssao_enabled() const
{
if (wxGetApp().app_config == nullptr)
return false;
return wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_MODE) &&
wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SSAO);
}
int GLCanvas3D::_get_effective_fps_cap() const
{
if (wxGetApp().app_config == nullptr)
@@ -7596,6 +7621,159 @@ void GLCanvas3D::_render_fxaa_pass(unsigned int width, unsigned int height)
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
}
void GLCanvas3D::_render_ssao_pass(unsigned int width, unsigned int height)
{
if (width == 0 || height == 0)
return;
GLShaderProgram* shader = wxGetApp().get_shader("ssao");
if (shader == nullptr)
return;
if (m_ssao_color_texture_id == 0) {
glsafe(::glGenTextures(1, &m_ssao_color_texture_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_color_texture_id));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
}
if (m_ssao_depth_texture_id == 0) {
glsafe(::glGenTextures(1, &m_ssao_depth_texture_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_depth_texture_id));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
}
if (m_ssao_texture_size[0] != width || m_ssao_texture_size[1] != height) {
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_color_texture_id));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_depth_texture_id));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr));
m_ssao_texture_size = { { width, height } };
}
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_color_texture_id));
glsafe(::glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_depth_texture_id));
glsafe(::glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height));
const Camera& camera = wxGetApp().plater()->get_camera();
GLint prev_stencil_mask = 0xFF;
glsafe(::glGetIntegerv(GL_STENCIL_WRITEMASK, &prev_stencil_mask));
GLboolean prev_stencil_test = GL_FALSE;
glsafe(::glGetBooleanv(GL_STENCIL_TEST, &prev_stencil_test));
GLboolean prev_depth_mask = GL_TRUE;
glsafe(::glGetBooleanv(GL_DEPTH_WRITEMASK, &prev_depth_mask));
GLint prev_depth_func = GL_LESS;
glsafe(::glGetIntegerv(GL_DEPTH_FUNC, &prev_depth_func));
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glDisable(GL_BLEND));
// Build stencil mask for bed/plate and apply SSAO only outside this mask.
glsafe(::glEnable(GL_STENCIL_TEST));
glsafe(::glStencilMask(0xFF));
glsafe(::glClearStencil(0));
glsafe(::glClear(GL_STENCIL_BUFFER_BIT));
glsafe(::glStencilFunc(GL_ALWAYS, 1, 0xFF));
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE));
// Mark only visible plate pixels (do not exclude objects in front of plate).
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDepthMask(GL_FALSE));
glsafe(::glDepthFunc(GL_LEQUAL));
GLboolean prev_color_mask[4] = { GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE };
glsafe(::glGetBooleanv(GL_COLOR_WRITEMASK, prev_color_mask));
glsafe(::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
if (const BuildVolume& build_volume = m_bed.build_volume(); build_volume.valid()) {
GLShaderProgram* flat = wxGetApp().get_shader("flat");
if (flat != nullptr) {
flat->start_using();
flat->set_uniform("projection_matrix", camera.get_projection_matrix());
GLModel plate_mask;
GLModel::Geometry mask;
mask.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 };
if (build_volume.type() == BuildVolume_Type::Rectangle) {
const BoundingBox3Base<Vec3d> bb = build_volume.bounding_volume();
mask.reserve_vertices(4);
mask.reserve_indices(6);
mask.add_vertex(Vec3f((float)bb.min.x(), (float)bb.min.y(), 0.0f));
mask.add_vertex(Vec3f((float)bb.max.x(), (float)bb.min.y(), 0.0f));
mask.add_vertex(Vec3f((float)bb.max.x(), (float)bb.max.y(), 0.0f));
mask.add_vertex(Vec3f((float)bb.min.x(), (float)bb.max.y(), 0.0f));
mask.add_triangle(0, 1, 2);
mask.add_triangle(0, 2, 3);
} else if (build_volume.type() == BuildVolume_Type::Circle) {
const Vec2f c = Vec2f(unscaled<float>(build_volume.circle().center.x()), unscaled<float>(build_volume.circle().center.y()));
const float r = unscaled<float>(build_volume.circle().radius);
const int segments = 64;
mask.reserve_vertices(segments + 1);
mask.reserve_indices(segments * 3);
mask.add_vertex(Vec3f(c.x(), c.y(), 0.0f));
for (int i = 0; i < segments; ++i) {
const float a = (2.0f * float(PI) * float(i)) / float(segments);
mask.add_vertex(Vec3f(c.x() + r * std::cos(a), c.y() + r * std::sin(a), 0.0f));
}
for (int i = 0; i < segments; ++i) {
const unsigned int i1 = 1 + i;
const unsigned int i2 = 1 + ((i + 1) % segments);
mask.add_triangle(0, i1, i2);
}
}
if (mask.vertices_count() > 0 && mask.indices_count() > 0) {
plate_mask.init_from(std::move(mask));
flat->set_uniform("view_model_matrix", camera.get_view_matrix());
plate_mask.render(flat);
}
flat->stop_using();
}
}
glsafe(::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glStencilMask(0x00));
glsafe(::glStencilFunc(GL_NOTEQUAL, 1, 0xFF));
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
shader->start_using();
shader->set_uniform("view_model_matrix", Transform3d::Identity());
shader->set_uniform("projection_matrix", Transform3d::Identity());
shader->set_uniform("color_texture", 0);
shader->set_uniform("depth_texture", 1);
shader->set_uniform("inv_tex_size", Vec2f(1.0f / static_cast<float>(width), 1.0f / static_cast<float>(height)));
shader->set_uniform("z_near", camera.get_near_z());
shader->set_uniform("z_far", camera.get_far_z());
glsafe(::glActiveTexture(GL_TEXTURE0));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_color_texture_id));
glsafe(::glActiveTexture(GL_TEXTURE1));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_ssao_depth_texture_id));
m_background.render();
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
glsafe(::glActiveTexture(GL_TEXTURE0));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
shader->stop_using();
if (!prev_stencil_test)
glsafe(::glDisable(GL_STENCIL_TEST));
glsafe(::glStencilMask(prev_stencil_mask));
glsafe(::glColorMask(prev_color_mask[0], prev_color_mask[1], prev_color_mask[2], prev_color_mask[3]));
glsafe(::glDepthMask(prev_depth_mask));
glsafe(::glDepthFunc(prev_depth_func));
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
}
void GLCanvas3D::_render_background()
{
bool use_error_color = false;
@@ -7686,6 +7864,206 @@ void GLCanvas3D::_render_platelist(const Transform3d& view_matrix, const Transfo
wxGetApp().plater()->get_partplate_list().render(view_matrix, projection_matrix, bottom, only_current, only_body, hover_id, render_cali, show_grid);
}
void GLCanvas3D::_render_cast_shadows_on_plate(const Transform3d& view_matrix, const Transform3d& projection_matrix)
{
// Check if shadow rendering is enabled in configuration
if (wxGetApp().app_config == nullptr)
return;
if (!wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_MODE))
return;
if (!wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS))
return;
if (m_volumes.empty())
return;
GLShaderProgram* shader = wxGetApp().get_shader("flat");
if (shader == nullptr)
return;
// Fixed light direction (pointing downward at an angle)
// Drive shadow direction from current view angle: define light in eye-space,
// then transform it to world-space with inverse view rotation.
const Vec3d light_dir_eye = Vec3d(-0.4574957, 0.4574957, 0.7624929).normalized();
const Matrix3d view_rot = view_matrix.matrix().block<3, 3>(0, 0);
const Vec3d light_dir_to_light = (view_rot.transpose() * light_dir_eye).normalized();
const Vec3d ray_dir = -light_dir_to_light; // Direction of shadow projection
if (std::abs(ray_dir.z()) < 1e-6)
return;
// Shadow projection matrix - flattens geometry onto Z=0 plane along light direction
Matrix4d shadow_proj = Matrix4d::Identity();
shadow_proj(0, 2) = -ray_dir.x() / ray_dir.z();
shadow_proj(1, 2) = -ray_dir.y() / ray_dir.z();
shadow_proj(2, 0) = 0.0;
shadow_proj(2, 1) = 0.0;
shadow_proj(2, 2) = 0.0;
shadow_proj(2, 3) = 0.01; // Bias to prevent shadow acne
// Save OpenGL state
GLint prev_depth_func = GL_LESS;
glsafe(::glGetIntegerv(GL_DEPTH_FUNC, &prev_depth_func));
GLboolean prev_depth_mask = GL_TRUE;
glsafe(::glGetBooleanv(GL_DEPTH_WRITEMASK, &prev_depth_mask));
GLint prev_stencil_mask = 0xFF;
glsafe(::glGetIntegerv(GL_STENCIL_WRITEMASK, &prev_stencil_mask));
GLboolean prev_stencil_test = GL_FALSE;
glsafe(::glGetBooleanv(GL_STENCIL_TEST, &prev_stencil_test));
// ============================================================
// PASS 0: Create stencil mask for the build plate (value = 1)
// ============================================================
glsafe(::glEnable(GL_STENCIL_TEST));
glsafe(::glStencilMask(0xFF));
glsafe(::glClearStencil(0));
glsafe(::glClear(GL_STENCIL_BUFFER_BIT));
glsafe(::glStencilFunc(GL_ALWAYS, 1, 0xFF));
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE));
glsafe(::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
glsafe(::glDisable(GL_DEPTH_TEST));
shader->start_using();
shader->set_uniform("projection_matrix", projection_matrix);
// Draw the build plate (cached model to avoid per-frame uploads)
if (const BuildVolume& build_volume = m_bed.build_volume(); build_volume.valid()) {
const std::string mask_key = build_volume.type() == BuildVolume_Type::Rectangle
? (boost::format("rect|%1$.5f|%2$.5f|%3$.5f|%4$.5f")
% build_volume.bounding_volume().min.x()
% build_volume.bounding_volume().min.y()
% build_volume.bounding_volume().max.x()
% build_volume.bounding_volume().max.y()).str()
: (build_volume.type() == BuildVolume_Type::Circle
? (boost::format("circle|%1$.5f|%2$.5f|%3$.5f")
% unscaled<double>(build_volume.circle().center.x())
% unscaled<double>(build_volume.circle().center.y())
% unscaled<double>(build_volume.circle().radius)).str()
: std::string("invalid"));
if (mask_key != m_plate_shadow_mask_key) {
m_plate_shadow_mask.reset();
m_plate_shadow_mask_key = mask_key;
GLModel::Geometry mask;
mask.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 };
if (build_volume.type() == BuildVolume_Type::Rectangle) {
const BoundingBox3Base<Vec3d> bb = build_volume.bounding_volume();
mask.reserve_vertices(4);
mask.reserve_indices(6);
mask.add_vertex(Vec3f((float)bb.min.x(), (float)bb.min.y(), 0.0f));
mask.add_vertex(Vec3f((float)bb.max.x(), (float)bb.min.y(), 0.0f));
mask.add_vertex(Vec3f((float)bb.max.x(), (float)bb.max.y(), 0.0f));
mask.add_vertex(Vec3f((float)bb.min.x(), (float)bb.max.y(), 0.0f));
mask.add_triangle(0, 1, 2);
mask.add_triangle(0, 2, 3);
}
else if (build_volume.type() == BuildVolume_Type::Circle) {
const Vec2f c = Vec2f(unscaled<float>(build_volume.circle().center.x()), unscaled<float>(build_volume.circle().center.y()));
const float r = unscaled<float>(build_volume.circle().radius);
const int segments = 64;
mask.reserve_vertices(segments + 1);
mask.reserve_indices(segments * 3);
mask.add_vertex(Vec3f(c.x(), c.y(), 0.0f));
for (int i = 0; i < segments; ++i) {
const float a = (2.0f * float(PI) * float(i)) / float(segments);
mask.add_vertex(Vec3f(c.x() + r * std::cos(a), c.y() + r * std::sin(a), 0.0f));
}
for (int i = 0; i < segments; ++i) {
const unsigned int i1 = 1 + i;
const unsigned int i2 = 1 + ((i + 1) % segments);
mask.add_triangle(0, i1, i2);
}
}
if (mask.vertices_count() > 0 && mask.indices_count() > 0)
m_plate_shadow_mask.init_from(std::move(mask));
}
if (m_plate_shadow_mask.is_initialized()) {
shader->set_uniform("view_model_matrix", view_matrix);
m_plate_shadow_mask.render(shader);
}
}
// ============================================================
// PASS 1: Project object shadows onto plate (increment stencil to 2)
// ============================================================
// Only render where plate exists (stencil == 1), then increment to 2
glsafe(::glStencilFunc(GL_EQUAL, 1, 0xFF));
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_INCR));
glsafe(::glDepthMask(GL_FALSE));
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDepthFunc(GL_ALWAYS)); // Shadows don't need depth testing
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
glsafe(::glPolygonOffset(-2.0f, -2.0f));
glsafe(::glDisable(GL_CULL_FACE));
// Render projected shadow geometry
for (GLVolume* volume : m_volumes.volumes) {
if (volume == nullptr || !volume->is_active || !volume->printable || volume->is_modifier || volume->is_wipe_tower)
continue;
// CRITICAL FIX: Apply shadow projection in object's local space, then to world, then to view
// This ensures shadows are cast from the object's actual position
Matrix4d world_matrix = volume->world_matrix().matrix();
// Project the shadow - this flattens the geometry onto Z=0 in WORLD space
Matrix4d shadow_world_matrix = shadow_proj * world_matrix;
// Transform to view space for rendering
Matrix4d view_shadow_matrix = view_matrix.matrix() * shadow_world_matrix;
shader->set_uniform("view_model_matrix", view_shadow_matrix);
shader->set_uniform("projection_matrix", projection_matrix);
volume->model.render(shader);
}
// ============================================================
// PASS 2: Draw shadow color where stencil == 2
// ============================================================
glsafe(::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
glsafe(::glStencilFunc(GL_EQUAL, 2, 0xFF));
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
glsafe(::glStencilMask(0x00));
glsafe(::glDepthFunc(GL_ALWAYS));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
// Draw shadow fill
shader->set_uniform("view_model_matrix", Transform3d::Identity());
shader->set_uniform("projection_matrix", Transform3d::Identity());
const ColorRGBA shadow_fill_color(0.0f, 0.0f, 0.0f, 0.4f); // Darker shadow for visibility
const ColorRGBA prev_bg_color = m_background.get_geometry().color;
m_background.set_color(shadow_fill_color);
shader->set_uniform("uniform_color", shadow_fill_color);
m_background.render(shader);
m_background.set_color(prev_bg_color);
shader->set_uniform("uniform_color", prev_bg_color);
shader->stop_using();
// ============================================================
// RESTORE STATE
// ============================================================
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDepthMask(prev_depth_mask));
glsafe(::glDepthFunc(prev_depth_func));
glsafe(::glEnable(GL_CULL_FACE));
glsafe(::glDisable(GL_POLYGON_OFFSET_FILL));
glsafe(::glDisable(GL_BLEND));
if (!prev_stencil_test)
glsafe(::glDisable(GL_STENCIL_TEST));
glsafe(::glStencilMask(prev_stencil_mask));
}
void GLCanvas3D::_render_plane() const
{
;//TODO render assemble plane
@@ -7756,12 +8134,20 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
else
m_volumes.set_show_sinking_contours(!m_gizmos.is_hiding_instances());
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
const bool realistic_mode = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_MODE);
const bool realistic_phong = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_PHONG);
const std::string shader_name = (realistic_mode && realistic_phong) ? "phong" : "gouraud";
GLShaderProgram* shader = wxGetApp().get_shader(shader_name);
if (shader == nullptr && shader_name != "gouraud")
shader = wxGetApp().get_shader("gouraud");
ECanvasType canvas_type = this->m_canvas_type;
bool partly_inside_enable = canvas_type == ECanvasType::CanvasAssembleView ? false : true;
if (shader != nullptr) {
shader->start_using();
const bool phong_ssao = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SSAO);
shader->set_uniform("enable_ssao", phong_ssao);
const Size& cvn_size = get_canvas_size();
{
const Camera& camera = wxGetApp().plater()->get_camera();
@@ -8836,6 +9222,15 @@ void GLCanvas3D::_render_canvas_toolbar()
[this]{wxGetApp().toggle_show_outline();}
);
create_menu_item( _utf8(L("Realistic View")),
true,
cfg->get_bool(SETTING_OPENGL_REALISTIC_MODE),
[this, &cfg]{
cfg->set_bool(SETTING_OPENGL_REALISTIC_MODE, !cfg->get_bool(SETTING_OPENGL_REALISTIC_MODE));
cfg->save();
}
);
ImGui::Separator();
create_menu_item( _utf8(L("Perspective")),

View File

@@ -727,6 +727,11 @@ public:
GLModel m_background;
unsigned int m_fxaa_texture_id{ 0 };
std::array<unsigned int, 2> m_fxaa_texture_size{ 0, 0 };
unsigned int m_ssao_color_texture_id{ 0 };
unsigned int m_ssao_depth_texture_id{ 0 };
std::array<unsigned int, 2> m_ssao_texture_size{ { 0, 0 } };
GLModel m_plate_shadow_mask;
std::string m_plate_shadow_mask_key;
public:
explicit GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed);
~GLCanvas3D();
@@ -1238,12 +1243,15 @@ private:
void _picking_pass();
void _rectangular_selection_picking_pass();
bool _is_fxaa_enabled() const;
bool _is_ssao_enabled() const;
int _get_effective_fps_cap() const;
bool _is_fps_overlay_enabled() const;
void _render_fps_overlay(int fps) const;
void _render_fxaa_pass(unsigned int width, unsigned int height);
void _render_ssao_pass(unsigned int width, unsigned int height);
void _render_background();
void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes);
void _render_cast_shadows_on_plate(const Transform3d& view_matrix, const Transform3d& projection_matrix);
//BBS: add part plate related logic
void _render_platelist(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false, bool show_grid = true);
//BBS: add outline drawing logic

View File

@@ -50,6 +50,8 @@ std::pair<bool, std::string> GLShadersManager::init()
valid &= append_shader("flat_texture", { prefix + "flat_texture.vs", prefix + "flat_texture.fs" });
// used to apply post-processing antialiasing in screen space
valid &= append_shader("fxaa", { prefix + "fxaa.vs", prefix + "fxaa.fs" });
// used to apply screen-space ambient occlusion in post process
valid &= append_shader("ssao", { prefix + "ssao.vs", prefix + "ssao.fs" });
// used to render 3D scene background
valid &= append_shader("background", { prefix + "background.vs", prefix + "background.fs" });
#if SLIC3R_OPENGL_ES
@@ -76,6 +78,12 @@ std::pair<bool, std::string> GLShadersManager::init()
valid &= append_shader("gouraud", { prefix + "gouraud.vs", prefix + "gouraud.fs" }
#if ENABLE_ENVIRONMENT_MAP
, { "ENABLE_ENVIRONMENT_MAP"sv }
#endif // ENABLE_ENVIRONMENT_MAP
);
// used to render objects in 3d editor with phong shading
valid &= append_shader("phong", { prefix + "phong.vs", prefix + "phong.fs" }
#if ENABLE_ENVIRONMENT_MAP
, { "ENABLE_ENVIRONMENT_MAP"sv }
#endif // ENABLE_ENVIRONMENT_MAP
);
// used to render variable layers heights in 3d editor

View File

@@ -1386,8 +1386,13 @@ bool GLGizmosManager::activate_gizmo(EType type)
UndoRedo::SnapshotType::LeavingGizmoWithAction);
}
if (type == Undefined) {
if (type == Undefined) {
// it is deactivation of gizmo
if (m_restore_realistic_view_after_paint && wxGetApp().app_config != nullptr) {
wxGetApp().app_config->set_bool(SETTING_OPENGL_REALISTIC_MODE, true);
wxGetApp().app_config->save();
m_restore_realistic_view_after_paint = false;
}
m_current = Undefined;
return true;
}
@@ -1396,6 +1401,16 @@ bool GLGizmosManager::activate_gizmo(EType type)
GLGizmoBase& new_gizmo = *m_gizmos[type];
if (!new_gizmo.is_activable()) return false;
if (type == Seam || type == FdmSupports || type == FuzzySkin) {
if (wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_MODE)) {
m_restore_realistic_view_after_paint = true;
wxGetApp().app_config->set_bool(SETTING_OPENGL_REALISTIC_MODE, false);
wxGetApp().app_config->save();
}
} else {
m_restore_realistic_view_after_paint = false;
}
if (!m_serializing && new_gizmo.wants_enter_leave_snapshots())
Plater::TakeSnapshot snapshot(wxGetApp().plater(),
new_gizmo.get_gizmo_entering_text(),

View File

@@ -150,6 +150,7 @@ private:
static std::map<int, void*> icon_list;
bool m_is_dark = false;
bool m_restore_realistic_view_after_paint = false;
/// <summary>
/// Process mouse event on gizmo toolbar

View File

@@ -1554,6 +1554,30 @@ void PreferencesDialog::create_items()
g_sizer = f_sizers.back();
g_sizer->AddGrowableCol(0, 1);
//// GRAPHICS > Realistic view
g_sizer->Add(create_item_title(_L("Realistic View")), 1, wxEXPAND);
auto item_realistic_phong = create_item_checkbox(
_L("Phong shading"),
_L("Uses Phong shading inside realistic view.")
, SETTING_OPENGL_REALISTIC_PHONG
);
g_sizer->Add(item_realistic_phong);
auto item_realistic_ssao = create_item_checkbox(
_L("SSAO ambient occlusion"),
_L("Applies SSAO in realistic view."),
SETTING_OPENGL_PHONG_SSAO
);
g_sizer->Add(item_realistic_ssao);
auto item_realistic_shadows = create_item_checkbox(
_L("Shadows"),
_L("Renders cast shadows on the plate in realistic view."),
SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS
);
g_sizer->Add(item_realistic_shadows);
//// GRAPHICS > Anti-aliasing
g_sizer->Add(create_item_title(_L("Anti-aliasing")), 1, wxEXPAND);