Improve lighting
- No specular on surfaces facing away from the light source - Transform the light direction to view space to ensure that all lighting calculations happen in a consistent coordinate space. - Other lighting tweaks
This commit is contained in:
parent
a51aeb23bd
commit
71e991db77
3 changed files with 28 additions and 15 deletions
|
|
@ -6,7 +6,7 @@ in vec3 v_position;
|
||||||
|
|
||||||
out vec4 frag_color;
|
out vec4 frag_color;
|
||||||
|
|
||||||
uniform vec3 u_light;
|
uniform vec3 u_light; // direction TO the light (normalized)
|
||||||
uniform sampler2D tex;
|
uniform sampler2D tex;
|
||||||
uniform vec3 color; // base colour factor (acts as solid colour when no texture)
|
uniform vec3 color; // base colour factor (acts as solid colour when no texture)
|
||||||
|
|
||||||
|
|
@ -14,20 +14,29 @@ void main() {
|
||||||
// Combine base texture (or constant white) with colour factor supplied by CPU.
|
// Combine base texture (or constant white) with colour factor supplied by CPU.
|
||||||
vec3 base_col = texture(tex, v_tex).rgb * color;
|
vec3 base_col = texture(tex, v_tex).rgb * color;
|
||||||
|
|
||||||
vec3 ambient_color = base_col * 0.2;
|
vec3 N = normalize(v_normal);
|
||||||
vec3 diffuse_color = base_col * 0.6;
|
vec3 L = normalize(u_light);
|
||||||
vec3 specular_color = vec3(1.0);
|
|
||||||
|
|
||||||
// u_light is the direction **from the light towards the fragment**.
|
// Classic Blinn-Phong lighting
|
||||||
float diffuse = max(dot(normalize(v_normal), normalize(u_light)), 0.0);
|
// Ambient: always present
|
||||||
|
vec3 ambient = base_col * 0.15;
|
||||||
|
|
||||||
vec3 camera_dir = normalize(-v_position);
|
// Diffuse: N dot L, clamped
|
||||||
vec3 half_dir = normalize(normalize(u_light) + camera_dir);
|
float NdotL = max(dot(N, L), 0.0);
|
||||||
float specular = pow(max(dot(half_dir, normalize(v_normal)), 0.0), 16.0);
|
vec3 diffuse = base_col * NdotL * 0.7;
|
||||||
|
|
||||||
vec3 result = ambient_color + diffuse * diffuse_color + specular * specular_color;
|
// Specular: only on surfaces facing the light (NdotL > 0)
|
||||||
|
float specular = 0.0;
|
||||||
|
if (NdotL > 0.0) {
|
||||||
|
vec3 V = normalize(-v_position); // view direction (camera at origin in view space)
|
||||||
|
vec3 H = normalize(L + V); // half-vector
|
||||||
|
float NdotH = max(dot(N, H), 0.0);
|
||||||
|
specular = pow(NdotH, 32.0) * 0.5; // tighter highlight, moderated intensity
|
||||||
|
}
|
||||||
|
|
||||||
// Convert from linear to sRGB for display (approximate γ-correction)
|
vec3 result = ambient + diffuse + vec3(specular);
|
||||||
|
|
||||||
|
// Convert from linear to sRGB for display (approximate gamma correction)
|
||||||
result = pow(result, vec3(1.0 / 2.2));
|
result = pow(result, vec3(1.0 / 2.2));
|
||||||
|
|
||||||
frag_color = vec4(result, 1.0);
|
frag_color = vec4(result, 1.0);
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,11 @@ impl RenderingSystem for BasicMeshRenderingSystem {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use HDR-derived environment light direction if provided, otherwise default to downward
|
// Use HDR-derived environment light direction if provided, otherwise default to downward
|
||||||
let light_dir: Vec3 = if ctx.env_light_dir.length_squared() > 0.0 { ctx.env_light_dir.normalize() } else { Vec3::new(0.0, -1.0, 0.0) };
|
let light_dir_world: Vec3 = if ctx.env_light_dir.length_squared() > 0.0 { ctx.env_light_dir.normalize() } else { Vec3::new(0.0, -1.0, 0.0) };
|
||||||
|
|
||||||
|
// Transform light direction to view space (normals/positions are in view space)
|
||||||
|
let view_mat3 = glam::Mat3::from_mat4(cam.view());
|
||||||
|
let light_dir_view = (view_mat3 * light_dir_world).normalize();
|
||||||
|
|
||||||
let asset_manager = ctx.asset_manager.borrow();
|
let asset_manager = ctx.asset_manager.borrow();
|
||||||
|
|
||||||
|
|
@ -90,7 +94,7 @@ impl RenderingSystem for BasicMeshRenderingSystem {
|
||||||
model: tr.matrix().to_cols_array_2d(),
|
model: tr.matrix().to_cols_array_2d(),
|
||||||
view: cam.view().to_cols_array_2d(),
|
view: cam.view().to_cols_array_2d(),
|
||||||
projection: cam.projection().to_cols_array_2d(),
|
projection: cam.projection().to_cols_array_2d(),
|
||||||
u_light: [light_dir.x, light_dir.y, light_dir.z],
|
u_light: [light_dir_view.x, light_dir_view.y, light_dir_view.z],
|
||||||
tex: sampler,
|
tex: sampler,
|
||||||
color: [c[0], c[1], c[2]],
|
color: [c[0], c[1], c[2]],
|
||||||
uv_offset: [mat.uv_offset.x, mat.uv_offset.y],
|
uv_offset: [mat.uv_offset.x, mat.uv_offset.y],
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ impl RenderingSystem for SkyboxRenderingSystem {
|
||||||
|
|
||||||
// Load EXR from assets/exr
|
// Load EXR from assets/exr
|
||||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||||
let path = std::path::Path::new(manifest_dir).join("../assets/exr/qwantani_sunset_puresky_2k.exr");
|
let path = std::path::Path::new(manifest_dir).join("../assets/exr/citrus_orchard_road_puresky_4k.exr");
|
||||||
let (equirect_srgb, light_dir) = Self::load_hdr_equirect_and_analyze(display, &path);
|
let (equirect_srgb, light_dir) = Self::load_hdr_equirect_and_analyze(display, &path);
|
||||||
Self { program, quad_vb, quad_ib, equirect_srgb, light_dir }
|
Self { program, quad_vb, quad_ib, equirect_srgb, light_dir }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue