Skip to content

Latest commit

 

History

History
111 lines (91 loc) · 6.84 KB

File metadata and controls

111 lines (91 loc) · 6.84 KB

OpenPBR Viewer v1.2 Update — Session Notes

Project Overview

Updating the OpenPBR-viewer (WebGL pathtracer + rasterizer) to fully support OpenPBR v1.2 spec. Full plan at: .claude/plans/refactored-juggling-snowglobe.md Reference document: ~/dev/light_transport/s2025_openpbr/arxiv/course_notes.tex Reference code: ~/dev/light_transport/s2025_openpbr/arxiv/code/

Completed Work

Phase 1: Fix corrupted openpbr_bsdf_sample

  • File: glsl/pathtracing/openpbr_surface.glsl
  • Rewrote the function which had duplicated/interleaved code from thin-walled weight logic
  • Proper CDF lobe selection, per-lobe sampling, MIS PDF computation, throughput calculation

Phase 2: Merged v1.2 PRs

  • 2.1 PR #253 — Coat darkening fix
    • Files: openpbr_surface.glsl, openpbr.frag.glsl
    • Corrected to match reference coat_darkening.cpp: base_darkening = mix(1, Delta, coat_darkening) with Delta = max(1-K,0) / (1 - E_base*K*coat_color)
    • Rasterizer was also missing coat_color in Delta denominator — fixed
  • 2.2 PR #256 — Clamp negative metallic Fresnel (FresnelF82Tint / FresnelConductorF82)
    • Files: main.glsl, openpbr.frag.glsl
  • 2.3 PR #277 — Input parameter clamping per spec section 4
    • Files: openpbr_surface.glsl, openpbr.frag.glsl
  • 2.4 PR #286transmission_scatter as volume albedo (simpler parametrization)
    • Files: openpbr_surface.glsl
  • 2.5 PR #247specular_weight as decoupled IOR via FresnelDielectricReflectanceModulated
    • Already implemented; verified correctness and added PR hyperlink comments

Phase 5: Thin-walled mode

  • File: glsl/pathtracing/openpbr_surface.glsl
  • Fixed layering to maintain fuzz + coat layers in thin-walled mode
  • Added ID_DIFF_BTDF lobe for thin-walled diffuse transmission
  • Fixed evaluation and sampling functions for thin-walled branches

Phase 4.1: Thin-film pathtracer (hero wavelength Airy)

  • 4.1a: Complete rewrite of glsl/pathtracing/thin-film.glsl
    • Replaced per-channel Belcour & Barla with hero wavelength Airy summation
    • Reference: Kutz & Portsmouth, "Thin-Film Iridescence via Complex Arithmetic"
    • Python reference: C:/Users/jamportz/dev/light_transport/thin-film-paper/code/airy_complex.py
    • Key functions: conductor_ior_at_wavelength(), FresnelThinFilm() (scalar at wavelength_nm)
    • Wrapper API unchanged: FresnelThinFilmOverDielectric/Conductor still return vec3
  • 4.1b: Added thin-film integration to glsl/pathtracing/specular_brdf.glsl
    • #ifdef THIN_FILM_ENABLED blocks in both evaluate and sample functions
    • Blends modulated Fresnel with thin-film Fresnel via thin_film_weight
  • 4.1c: Updated glsl/pathtracing/pathtracer.glsl spectral rendering
    • Wavelength sampling guard: #if defined(TRANSMISSION_ENABLED) || defined(THIN_FILM_ENABLED)
    • Spectral weight at path start: throughput *= xyzToRgb(xyzFit_1931(wavelength_nm)) * SPECTRAL_NORM
    • Guard against double spectral weight when both thin-film and dispersion active

Ground plane

  • Added textured ground plane to pathtracer matching the rasterizer's existing ground mesh
  • Analytical ray-plane intersection in pathtracer.glsl trace()
  • Ground BRDF (Lambertian with texture lookup) in pathtracer.glsl
  • UV mapping: vec2(pW.x, -pW.z) / 200.0 * 2.0 + 0.5 to match rasterizer's PlaneGeometry repeat/offset
  • Ground texture upgraded to 7500x7500 from USD-WG StandardShaderBall asset (ACEScg EXR → sRGB PNG)

Shader compilation improvements

  • Async compilation fix: trigger_recompile() now compiles FullScreenQuad material via renderer.compileAsync(pathtracedQuad._mesh, tmp_cam) — prevents browser freeze
  • Warm-up render: Added post-compilation warm-up render to flush GPU pipeline stalls
  • Loop bounds as uniforms: Converted BOUNCES and MAX_VOLUME_STEPS from #define to uniform int — changing these in GUI no longer triggers recompilation
  • BVH stack reduced: stack[60]stack[32] in pathtracer.glsl (32 levels supports ~4B leaves)

Other fixes

  • Coat BRDF DV denominator: Added abs() on denominator in coat_brdf_evaluate (line 49)
  • PR hyperlinks: Added full GitHub URLs for PRs #247, #253, #256, #277, #286 across all modified files

Git repo cleanup

  • Removed accidentally committed OpenPBR/ submodule from history via git filter-repo
  • Removed large env maps (etzwihl_16k.hdr, metro_vijzelgracht_16k.hdr/.png) from history

Remaining Work

Phase 4.2: Thin-film rasterizer (per-channel Airy)

  • File: glsl/rasterization/openpbr.frag.glsl
  • Port thin-film using same Airy formula at 3 fixed wavelengths (R=700nm, G=546.1nm, B=435.8nm)
  • Apply to both specular and metal BRDF evaluations
  • Needs complex arithmetic helpers, FresnelDielectricReflectionPolarizations, FresnelDielectricTransmissionPolarizations
  • Also needs FresnelConductorReflectionPolarizations for metal substrate

Phase 3.1: PR #254 — Specular haze (dual-lobe NDF)

  • New params: specular_haze, specular_haze_roughening
  • Blend two GGX lobes in specular/metal BRDFs
  • Guard with #ifdef HAZE_ENABLED
  • Files: specular_brdf.glsl, specular_btdf.glsl, metal_brdf.glsl, main.glsl, openpbr.frag.glsl

Phase 3.2: PR #255 — Specular retroreflectivity

  • New param: specular_retroreflectivity
  • Modify specular/metal BRDFs with retroreflective component
  • Guard with #ifdef RETRO_ENABLED

Phase 6: main.js GUI updates

  • Add new uniform params (haze, retroreflectivity)
  • Add #define toggles (HAZE_ENABLED, RETRO_ENABLED)
  • Add GUI controls in appropriate folders

Key Architecture Notes

  • Pathtracer lobe IDs: ID_FUZZ_BRDF=0, ID_COAT_BRDF=1, ID_META_BRDF=2, ID_SPEC_BRDF=3, ID_SPEC_BTDF=4, ID_DIFF_BRDF=5, ID_DIFF_BTDF=6, ID_SSSC_BTDF=7 (NUM_LOBES=8)
  • Rasterizer lobe IDs: ID_FUZZ_BRDF=0, ID_COAT_BRDF=1, ID_META_BRDF=2, ID_SPEC_BRDF=3, ID_SPEC_BTDF=4, ID_DIFF_BRDF=5, ID_SSSC_BTDF=6 (NUM_LOBES=7, no DIFF_BTDF)
  • Spectral rendering: SPECTRAL_NORM constant normalizes CIE→RGB so E[spectral_weight] ≈ (1,1,1) for uniform λ∈[360,700]nm
  • Thin-film API: FresnelThinFilmOverDielectric(mui, eta_fe) / FresnelThinFilmOverConductor(mui, eta_fe) — both return vec3
  • #ifdef guards: THIN_FILM_ENABLED, TRANSMISSION_ENABLED, SUBSURFACE_ENABLED controlled by main.js
  • Rasterizer uses struct-based approach: OpenPBRMaterial pbr passed to all functions; pathtracer uses global uniforms directly
  • Coat darkening reference: ~/dev/light_transport/s2025_openpbr/arxiv/code/coat_darkening.cpp
  • coat_color = T²_coat: The square of coat transmittance at normal incidence. absorb_tint = pow(coat_color, 0.5*(1/μᵢ + 1/μₒ))

Known Issues

  • Pathtracer has specular_weight in both lobe weight computation AND FresnelDielectricReflectanceModulated, while rasterizer only has it in Fresnel. Potential double-counting in pathtracer (out of scope for this update).