mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
ifcviewer: hemisphere ambient + fill light + cavity hint
Replaces the flat 0.25 ambient + single-Lambert key with three cheap shape-readability tricks, all in the fragment shader: - Hemisphere ambient (sky/ground tint mixed by n.z) so floors, ceilings, and walls get visibly different ambient colour even when shadowed. +Z is world-up. - Secondary fill light at 35% intensity from roughly the opposite horizontal direction so backs of objects are not pitch black. - Cavity hint: clamp(length(fwidth(n)) * 1.5, 0, 0.35) darkens fragments where adjacent normals diverge sharply. Catches wall-floor seams, column-slab joints, and stair edges as faint dark lines without any post-process. Total cost: ~8 extra ALU ops per fragment, no extra passes, no extra buffers. No change to cull/HiZ/MDI. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -155,7 +155,10 @@ in vec3 v_world_pos;
|
||||
flat in uint v_object_id;
|
||||
flat in uint v_selected;
|
||||
|
||||
uniform vec3 u_light_dir;
|
||||
uniform vec3 u_light_dir; // primary key direction (world-space)
|
||||
uniform vec3 u_fill_dir; // secondary fill direction
|
||||
uniform vec3 u_sky_color; // hemisphere top tint
|
||||
uniform vec3 u_ground_color; // hemisphere bottom tint
|
||||
|
||||
// Section planes — clip the half-space dot(n,p)+d > 0. AND-combined.
|
||||
const int MAX_CLIP_PLANES = 8;
|
||||
@@ -177,10 +180,28 @@ void main() {
|
||||
// true and has no effect.
|
||||
vec3 n = normalize(v_normal);
|
||||
if (!gl_FrontFacing) n = -n;
|
||||
float ndotl = max(dot(n, u_light_dir), 0.0);
|
||||
float ambient = 0.25;
|
||||
float diffuse = 0.75 * ndotl;
|
||||
vec3 color = v_color.rgb * (ambient + diffuse);
|
||||
|
||||
// Hemisphere ambient: faces pointing up read sky, down read ground.
|
||||
// World-up is +Z so n.z drives the mix. Floors/ceilings/walls get
|
||||
// visibly distinct ambient even when shadowed.
|
||||
float hemi_t = 0.5 + 0.5 * n.z;
|
||||
vec3 ambient = mix(u_ground_color, u_sky_color, hemi_t);
|
||||
|
||||
// Key + fill direct light. Fill is a softer secondary so backs of
|
||||
// objects don't go pitch black — keeps shape readable from any angle.
|
||||
float key = max(dot(n, u_light_dir), 0.0);
|
||||
float fill = max(dot(n, u_fill_dir), 0.0) * 0.35;
|
||||
|
||||
vec3 color = v_color.rgb * (ambient + (key + fill) * 0.7);
|
||||
|
||||
// Cavity shading: where adjacent fragments have a sharp normal change
|
||||
// (concave creases, edges where two faces meet), darken slightly.
|
||||
// length(fwidth(n)) spikes at those boundaries; the clamp caps how
|
||||
// dark a single edge can get so the effect is a hint, not a heavy
|
||||
// outline.
|
||||
float cavity = clamp(length(fwidth(n)) * 1.5, 0.0, 0.35);
|
||||
color *= (1.0 - cavity);
|
||||
|
||||
if (v_selected == 1u) color = mix(color, vec3(0.2, 0.6, 1.0), 0.5);
|
||||
frag_color = vec4(color, v_color.a);
|
||||
}
|
||||
@@ -2402,9 +2423,21 @@ void ViewportWindow::render() {
|
||||
gl_->glUseProgram(main_program_);
|
||||
GLint u_vp = gl_->glGetUniformLocation(main_program_, "u_view_projection");
|
||||
GLint u_light = gl_->glGetUniformLocation(main_program_, "u_light_dir");
|
||||
GLint u_fill = gl_->glGetUniformLocation(main_program_, "u_fill_dir");
|
||||
GLint u_sky = gl_->glGetUniformLocation(main_program_, "u_sky_color");
|
||||
GLint u_ground = gl_->glGetUniformLocation(main_program_, "u_ground_color");
|
||||
GLint u_sel = gl_->glGetUniformLocation(main_program_, "u_selected_id");
|
||||
gl_->glUniformMatrix4fv(u_vp, 1, GL_FALSE, vp.constData());
|
||||
gl_->glUniform3f(u_light, 0.3f, 0.5f, 0.8f);
|
||||
// Key light: high noon-ish from off-camera; fill ~120° away so back-of-
|
||||
// object surfaces still get some direct contribution. Sky/ground tints
|
||||
// are a neutral cool-on-warm pairing — readable on white walls and grey
|
||||
// slabs without colouring shaded faces noticeably.
|
||||
// Both directions are roughly unit-length (~0.99) — matches how
|
||||
// u_light_dir was already supplied and keeps the brightness math sane.
|
||||
gl_->glUniform3f(u_light, 0.3f, 0.5f, 0.8f);
|
||||
gl_->glUniform3f(u_fill, -0.3f, -0.5f, 0.8f);
|
||||
gl_->glUniform3f(u_sky, 0.55f, 0.60f, 0.70f);
|
||||
gl_->glUniform3f(u_ground, 0.35f, 0.32f, 0.28f);
|
||||
gl_->glUniform1ui(u_sel, selected_object_id_);
|
||||
uploadClipPlaneUniforms(main_program_);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user