Skip to content

feat(water): BRDF sun specular#1635

Closed
davo0411 wants to merge 5 commits into
community-shaders:devfrom
davo0411:BRDF-specular-water
Closed

feat(water): BRDF sun specular#1635
davo0411 wants to merge 5 commits into
community-shaders:devfrom
davo0411:BRDF-specular-water

Conversation

@davo0411
Copy link
Copy Markdown
Collaborator

@davo0411 davo0411 commented Dec 11, 2025

more accurate sun specular model based on https://google.github.io/filament/Filament.md.html

would look better with real HDR, can't get it much "brighter" with current tonemapping.

  1. new
  2. old
    2085AD~1
    202CB2~1

20707D~1
20B15D~1

(Not attached to unified water in any way but used for screenshots for the better distant normals)

Summary by CodeRabbit

  • Refactor
    • Water rendering updated with an improved specular model for more realistic sun glints and surface reflections.
    • Better energy-conserving lighting yields consistent brightness of highlights.
    • Turbidity-aware specular response for more natural variation in water appearance.
    • Numerical safeguards reduce rendering artifacts and improve stability across viewing conditions.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 11, 2025

Walkthrough

Added a new WaterBRDF.hlsli shader module implementing a Cook‑Torrance microfacet BRDF for water specular, and updated Water.hlsl to include this module and call WaterBRDF::GetSunSpecular instead of its inline sun‑specular code.

Changes

Cohort / File(s) Change Summary
Water shader refactor & BRDF module
package/Shaders/Water.hlsl, package/Shaders/Water/WaterBRDF.hlsli
Water.hlsl now includes Water/WaterBRDF.hlsli and replaces its inlined sun‑specular calculation with WaterBRDF::GetSunSpecular(...). Added WaterBRDF.hlsli implementing Cook‑Torrance BRDF: WATER_F0, WATER_SPECULAR_ENERGY_BOOST, SpecularPowerToRoughness, D_GGX, V_SmithGGXCorrelated, F_Schlick, SpecularBRDF, and GetSunSpecular with horizon checks and energy scaling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas to focus review on:
    • Correctness of D_GGX, V_SmithGGXCorrelated, and F_Schlick implementations
    • Consistency of parameter mapping and types between Water.hlsl call and WaterBRDF functions
    • Validation of constants (WATER_F0, WATER_SPECULAR_ENERGY_BOOST) and energy‑conservation handling
    • Horizon/early‑exit logic and numerical stability safeguards

Suggested reviewers

  • doodlum
  • Pentalimbed

Poem

🐰
I hopped by waves of shader light,
Crafted gloss that caught the bright,
Fresnel whispers, roughness spun,
Water shimmers, sunbeams run,
A rabbit's code — reflections done.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a BRDF-based sun specular calculation for water. It is concise, specific, and clearly reflects the primary objective of the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

Using provided base ref: 5dce430
Using base ref: 5dce430
Base commit date: 2025-12-10T21:06:31Z (Wednesday, December 10, 2025 09:06 PM)
No actionable suggestions for changed features.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
package/Shaders/Water/WaterBRDF.hlsli (2)

37-51: Consider a higher minimum roughness to prevent numerical precision issues.

The minimum perceptual roughness of 0.001 results in α = 0.000001. This is significantly lower than Filament's recommended 0.045 minimum. While the code guards against division by zero, extremely low roughness values can cause:

  • Numerical precision artifacts on some GPUs
  • Very sharp specular spikes that may alias

Since the comment acknowledges Filament's recommendation, consider using a value closer to 0.01-0.02 for perceptual roughness (α = 0.0001-0.0004) which still produces sharp water reflections while avoiding edge cases.

 		// Clamp minimum roughness to prevent numerical issues
 		// Filament recommends 0.045 for fp32, but we use slightly lower for water
 		// Water can be extremely smooth, but not perfectly mirror-like
-		perceptualRoughness = max(perceptualRoughness, 0.001f);
+		perceptualRoughness = max(perceptualRoughness, 0.02f);

175-194: Minor redundancy in NdotL calculation.

L, V, and NdotL are computed twice—once in SpecularBRDF and again here. For a hot path like sun specular, consider inlining the BRDF computation directly in GetSunSpecular or returning additional outputs from SpecularBRDF.

That said, the current implementation prioritizes readability and modularity, which is reasonable for an initial implementation.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5dce430 and 10d21d7.

📒 Files selected for processing (2)
  • package/Shaders/Water.hlsl (2 hunks)
  • package/Shaders/Water/WaterBRDF.hlsli (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{cpp,cxx,cc,c,h,hpp,hxx,hlsl,hlsli,fx,fxh,py}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not include TODO/FIXME placeholders; provide complete, working solutions

Files:

  • package/Shaders/Water/WaterBRDF.hlsli
  • package/Shaders/Water.hlsl
**/*

⚙️ CodeRabbit configuration file

**/*: When reviewing PRs, please provide suggestions for:

  1. Conventional Commit Titles (if not following https://www.conventionalcommits.org/ or
    if the existing title does not describe the code changes):
    Format: type(scope): description
    Length: 50 characters limit for title, 72 for body
    Style: lowercase description, no ending period
    Examples:

    • feat(vr): add cross-eye sampling
    • fix(water): resolve flowmap bug
    • docs: update shader documentation
  2. Issue References (if PR fixes bugs or implements features):
    Suggest adding appropriate GitHub keywords:

    • "Fixes #123" or "Closes #123" for bug fixes
    • "Implements #123" or "Addresses #123" for features
    • "Related to #123" for partial implementations

Otherwise, use your standard review approach focusing on code quality.

Files:

  • package/Shaders/Water/WaterBRDF.hlsli
  • package/Shaders/Water.hlsl
🧠 Learnings (2)
📓 Common learnings
Learnt from: alandtse
Repo: doodlum/skyrim-community-shaders PR: 0
File: :0-0
Timestamp: 2025-06-24T07:17:36.604Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.
Learnt from: jiayev
Repo: doodlum/skyrim-community-shaders PR: 0
File: :0-0
Timestamp: 2025-08-03T18:37:19.690Z
Learning: ISReflectionsRayTracing.hlsl and ISWorldMap.hlsl in the skyrim-community-shaders repository are image-space post-processing shaders that perform color sampling and blending operations that need proper linear color space handling for the linear lighting system. ISReflectionsRayTracing handles screen-space reflections and should use conditional Color::IrradianceToLinear/Gamma conversions similar to ISCompositeLensFlareVolumetricLighting.hlsl. ISWorldMap performs 7x7 color accumulation that should be done in linear space similar to the pattern used in ISSAOComposite.hlsl.
📚 Learning: 2025-08-03T18:37:19.690Z
Learnt from: jiayev
Repo: doodlum/skyrim-community-shaders PR: 0
File: :0-0
Timestamp: 2025-08-03T18:37:19.690Z
Learning: ISReflectionsRayTracing.hlsl and ISWorldMap.hlsl in the skyrim-community-shaders repository are image-space post-processing shaders that perform color sampling and blending operations that need proper linear color space handling for the linear lighting system. ISReflectionsRayTracing handles screen-space reflections and should use conditional Color::IrradianceToLinear/Gamma conversions similar to ISCompositeLensFlareVolumetricLighting.hlsl. ISWorldMap performs 7x7 color accumulation that should be done in linear space similar to the pattern used in ISSAOComposite.hlsl.

Applied to files:

  • package/Shaders/Water/WaterBRDF.hlsli
  • package/Shaders/Water.hlsl
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Validate shader compilation (VR, .github/configs/shader-validation-vr.yaml)
  • GitHub Check: Validate shader compilation (Flatrim, .github/configs/shader-validation.yaml)
  • GitHub Check: Build plugin and addons
🔇 Additional comments (9)
package/Shaders/Water.hlsl (2)

573-574: LGTM!

The include is correctly placed within the appropriate conditional compilation block, ensuring the BRDF module is only compiled when needed for the water specular path.


953-953: LGTM!

The refactored call cleanly delegates sun specular computation to the new BRDF module. Parameters map correctly: VarAmounts.x for specular power and DeepColor.w for turbidity/scatter factor.

package/Shaders/Water/WaterBRDF.hlsli (7)

1-5: LGTM!

Proper include guards and namespace encapsulation.


24-30: LGTM!

The F0 value of 0.02 is physically correct for water (IOR 1.33). The energy boost is well-documented as an artistic compensation for real-world wave statistics and HDR limitations.


59-73: LGTM!

Correct GGX NDF implementation following Filament's optimized formulation. The algebraic refactoring maintains numerical stability by avoiding explicit division by terms that could approach zero.


82-97: LGTM!

Correct height-correlated Smith-GGX visibility with proper division-by-zero protection. The combined visibility term efficiently incorporates both geometric shadowing and the standard 4·NdotV·NdotL denominator.


105-125: LGTM!

Correct Schlick Fresnel approximation. The algebraic rearrangement f + f0 * (1 - f) is equivalent to the standard form F0 + (1 - F0) * (1 - u)^5 and follows Filament's optimization.


135-163: LGTM!

Correct Cook-Torrance BRDF composition with proper view direction convention (negation of incoming direction), appropriate clamping for numerical stability, and early exit optimization for below-horizon lighting.


195-197: LGTM!

Clean namespace and include guard closure with helpful trailing comment.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
package/Shaders/Water/WaterBRDF.hlsli (3)

1-31: Header guard, namespace, and physical constants look solid (PR metadata also fine)

Header guard, namespace scoping, and the chosen F0 value for water (0.02 from IOR≈1.33) all look correct, and the extra energy boost constant is clearly documented for artistic tuning. The PR title feat(water): BRDF sun specular already follows conventional-commit style; if there is a tracking issue, consider adding something like Implements #123 or Related to #123 in the PR description to tie this feature back to an issue.


32-51: Guard specPower against invalid negative values

The Blinn/Phong→roughness mapping assumes specPower >= 0. If bad input ever makes it here (e.g. specPower < -2), sqrt(2/(specPower+2)) can go NaN before saturate. A tiny clamp will make this more robust without changing valid cases.

 float SpecularPowerToRoughness(float specPower)
 {
+    // Guard against invalid inputs to avoid NaNs from sqrt
+    specPower = max(specPower, 0.0f);
+
     // Convert specular power to perceptual roughness
     // ...
     float perceptualRoughness = saturate(sqrt(2.0f / (specPower + 2.0f)));
@@
     // Square for roughness (α in Filament terminology)
     return perceptualRoughness * perceptualRoughness;
 }

167-186: Tidy GetSunSpecular and confirm linear‑space inputs

There are a couple of easy cleanups and one pipeline check here:

  • float3 V = -viewDirection; is unused and can be dropped.
  • You already compute NdotL inside SpecularBRDF; if this ends up as a hot path, consider refactoring SpecularBRDF to optionally return NdotL so you don’t recompute the dot, though it’s a micro‑optimization.
  • Per existing work on linear lighting in this repo, please ensure sunColor (and the output of GetSunSpecular) are in linear space before tonemapping/compositing, so this physically‑based BRDF behaves as intended. Based on learnings, this matches how other image‑space shaders handle color in this project.

A minimal code tidy would be:

 float3 GetSunSpecular(float3 normal, float3 viewDirection, float4 sunDir, float3 sunColor, float specPower, float turbidity)
 {
     // Calculate BRDF
     float3 specularBRDF = SpecularBRDF(normal, viewDirection, sunDir.xyz, specPower);
 
     // Calculate NdotL for energy conservation
     float3 L = sunDir.xyz;
-    float3 V = -viewDirection;
     float NdotL = saturate(dot(normal, L));
@@
     return sunSpecular;
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 10d21d7 and aeb07a6.

📒 Files selected for processing (1)
  • package/Shaders/Water/WaterBRDF.hlsli (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{cpp,cxx,cc,c,h,hpp,hxx,hlsl,hlsli,fx,fxh,py}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not include TODO/FIXME placeholders; provide complete, working solutions

Files:

  • package/Shaders/Water/WaterBRDF.hlsli
**/*

⚙️ CodeRabbit configuration file

**/*: When reviewing PRs, please provide suggestions for:

  1. Conventional Commit Titles (if not following https://www.conventionalcommits.org/ or
    if the existing title does not describe the code changes):
    Format: type(scope): description
    Length: 50 characters limit for title, 72 for body
    Style: lowercase description, no ending period
    Examples:

    • feat(vr): add cross-eye sampling
    • fix(water): resolve flowmap bug
    • docs: update shader documentation
  2. Issue References (if PR fixes bugs or implements features):
    Suggest adding appropriate GitHub keywords:

    • "Fixes #123" or "Closes #123" for bug fixes
    • "Implements #123" or "Addresses #123" for features
    • "Related to #123" for partial implementations

Otherwise, use your standard review approach focusing on code quality.

Files:

  • package/Shaders/Water/WaterBRDF.hlsli
🧠 Learnings (2)
📓 Common learnings
Learnt from: alandtse
Repo: doodlum/skyrim-community-shaders PR: 0
File: :0-0
Timestamp: 2025-06-24T07:17:36.604Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.
📚 Learning: 2025-08-03T18:37:19.690Z
Learnt from: jiayev
Repo: doodlum/skyrim-community-shaders PR: 0
File: :0-0
Timestamp: 2025-08-03T18:37:19.690Z
Learning: ISReflectionsRayTracing.hlsl and ISWorldMap.hlsl in the skyrim-community-shaders repository are image-space post-processing shaders that perform color sampling and blending operations that need proper linear color space handling for the linear lighting system. ISReflectionsRayTracing handles screen-space reflections and should use conditional Color::IrradianceToLinear/Gamma conversions similar to ISCompositeLensFlareVolumetricLighting.hlsl. ISWorldMap performs 7x7 color accumulation that should be done in linear space similar to the pattern used in ISSAOComposite.hlsl.

Applied to files:

  • package/Shaders/Water/WaterBRDF.hlsli
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Validate shader compilation (Flatrim, .github/configs/shader-validation.yaml)
  • GitHub Check: Build plugin and addons
  • GitHub Check: Validate shader compilation (VR, .github/configs/shader-validation-vr.yaml)
🔇 Additional comments (3)
package/Shaders/Water/WaterBRDF.hlsli (3)

59-89: GGX NDF and Smith visibility implementations match standard formulations

D_GGX and V_SmithGGXCorrelated are consistent with the common GGX microfacet NDF and height‑correlated Smith visibility (with roughness interpreted as α), and the max(..., 1e-6f) guard in V avoids numerical blow‑ups at grazing angles. No changes needed here.


97-117: Fresnel implementation is correct and efficient for dielectric water

F_Schlick correctly implements the Schlick approximation with F0 and F90=1 and uses the optimized form f + f0 * (1 - f), which is algebraically equivalent to F0 + (1 - F0) * (1 - u)^5. This is appropriate for a dielectric water surface.


127-155: Verify direction conventions for viewDirection and lightDirection

SpecularBRDF assumes viewDirection is opposite of the usual V (you build V = -viewDirection) and that lightDirection already points from surface to light. This is fine as long as the call sites match the documented convention; otherwise N·V / N·L will be flipped and the lobe will move incorrectly. It might be worth double‑checking the Water.hlsl call to ensure viewDirection and sunDir.xyz are passed with these exact meanings (and normalized) to avoid subtle BRDF shape issues.

@github-actions
Copy link
Copy Markdown

✅ A pre-release build is available for this PR:
Download

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think too much duplicate to BRDF.hlsli

Copy link
Copy Markdown
Collaborator Author

@davo0411 davo0411 Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

functions were changed somewhat based on optimisations from that Filament page, but I can adapt to BRDF.hlsli stuff specifically

@alandtse
Copy link
Copy Markdown
Collaborator

Did you write the code based on reading the principles or was there code from the material? If code, what's the license?

@davo0411 davo0411 marked this pull request as draft December 11, 2025 10:03
@davo0411
Copy link
Copy Markdown
Collaborator Author

davo0411 commented Dec 11, 2025

Did you write the code based on reading the principles or was there code from the material? If code, what's the license?

its just code from the material, they only have math in that link.
draft for now, gonna change to be for pbr water feature, not direct vanilla replacement

@alandtse
Copy link
Copy Markdown
Collaborator

I don't understand your response. Did they provide code you copied? If so what is the license.

@davo0411
Copy link
Copy Markdown
Collaborator Author

davo0411 commented Dec 11, 2025

Sorry yeah my response was dumb idk lol

No, if you look at the link it's a maths resource basically. They have an engine as well elsewhere but I didn't look at any code in it. I coded it from the maths in the resource, plus a little bit of extra less physically accurate multipliers for the sake of appearance

And the second part was saying that I will move this to a separate water feature and still support vanilla instead, as currently this was a full replacement.

@alandtse
Copy link
Copy Markdown
Collaborator

Please close or mark as a draft if you don't need this PR. We have limited reviewer time so PRs that are not ready for review should be clearly marked.

@davo0411 davo0411 closed this Dec 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants