载入中...
搜索中...
未找到
AnalyticalVolLight.cpp
浏览该文件的文档.
2
3#include "common/Diagnostic.h"
4
5#include <algorithm>
6#include <cmath>
7
8#include <glm/common.hpp>
9#include <glm/geometric.hpp>
10
12
13std::optional<FogRayInterval> AnalyticalVolLight::intersectBeam(const glm::vec3& origin,
14 const glm::vec3& direction,
15 const AnalyticBeam& beam) {
16 const float dirLen = glm::length(direction);
17 const float beamLen = glm::length(beam.direction);
18 if (!(dirLen > 1e-8f) || !(beamLen > 1e-8f)) return std::nullopt;
19 if (!(beam.farDistance > beam.nearDistance) || beam.nearRadius < 0.f || beam.farRadius < 0.f)
20 return std::nullopt;
21
22 const glm::vec3 d = direction / dirLen;
23 const glm::vec3 axis = beam.direction / beamLen;
24
25 // Project the ray onto the beam axis and keep the capped interval, then
26 // reject samples that leave the linearly interpolated radius.
27 float tEnter = 1e9f;
28 float tExit = -1e9f;
29 constexpr int kProbe = 32;
30 for (int i = 0; i <= kProbe; ++i) {
31 const float u = static_cast<float>(i) / static_cast<float>(kProbe);
32 const float along = beam.nearDistance + u * (beam.farDistance - beam.nearDistance);
33 const glm::vec3 center = beam.apex + axis * along;
34 const float radius =
35 beam.nearRadius + u * (beam.farRadius - beam.nearRadius);
36 // Solve |o + t d - center|^2 = radius^2 for the nearest positive hit.
37 const glm::vec3 oc = origin - center;
38 const float b = glm::dot(oc, d);
39 const float c = glm::dot(oc, oc) - radius * radius;
40 const float disc = b * b - c;
41 if (disc < 0.f) continue;
42 const float s = std::sqrt(disc);
43 for (float t : {-b - s, -b + s}) {
44 if (t < 0.f) continue;
45 const glm::vec3 p = origin + d * t;
46 const float axial = glm::dot(p - beam.apex, axis);
47 if (axial < beam.nearDistance || axial > beam.farDistance) continue;
48 tEnter = std::min(tEnter, t);
49 tExit = std::max(tExit, t);
50 }
51 // Also accept the sample if the ray point at axial distance is inside.
52 const float tAxis = glm::dot(center - origin, d);
53 if (tAxis > 0.f) {
54 const glm::vec3 p = origin + d * tAxis;
55 if (glm::length(p - center) <= radius) {
56 tEnter = std::min(tEnter, tAxis);
57 tExit = std::max(tExit, tAxis);
58 }
59 }
60 }
61 if (tExit <= tEnter || tEnter > 1e8f) return std::nullopt;
62 return FogRayInterval{tEnter, tExit};
63}
64
66 const glm::vec3& origin, const glm::vec3& direction,
67 float sceneDepth, int segments,
68 float mediumDensity) const {
69 if (segments != 2 && segments != 4 && segments != 8) {
71 DiagnosticCode::InvalidArgument, "segments must be 2, 4, or 8", "segments", {},
72 "graphics.fog"));
73 }
74 if (!std::isfinite(mediumDensity) || mediumDensity < 0.f) {
76 DiagnosticCode::InvalidArgument, "mediumDensity must be finite and >= 0",
77 "mediumDensity", {}, "graphics.fog"));
78 }
79
80 auto interval = intersectBeam(origin, direction, beam);
81 FogRayResult result;
82 if (!interval) return Result<FogRayResult>::success(result);
83 if (sceneDepth > 0.f) interval->tExit = std::min(interval->tExit, sceneDepth);
84 if (!interval->valid()) return Result<FogRayResult>::success(result);
85
86 const glm::vec3 dir = glm::normalize(direction);
87 const glm::vec3 axis = glm::normalize(beam.direction);
88 const float sigmaT = profile.extinctionAt(mediumDensity);
89 const glm::vec3 sigmaS = profile.scatteringAt(mediumDensity);
90 const float phase = profile.phase(glm::dot(-dir, -axis));
91
92 float T = 1.f;
93 const float seg = interval->length() / static_cast<float>(segments);
94 for (int i = 0; i < segments && T > 1e-4f; ++i) {
95 const float t = interval->tEnter + (static_cast<float>(i) + 0.5f) * seg;
96 const glm::vec3 p = origin + dir * t;
97 const float axial = glm::dot(p - beam.apex, axis);
98 const float u = std::clamp((axial - beam.nearDistance) /
99 std::max(beam.farDistance - beam.nearDistance, 1e-4f),
100 0.f, 1.f);
101 const float radius = beam.nearRadius + u * (beam.farRadius - beam.nearRadius);
102 const glm::vec3 center = beam.apex + axis * axial;
103 const float radial = glm::length(p - center) / std::max(radius, 1e-4f);
104 const float mask = std::clamp(1.f - radial * radial, 0.f, 1.f);
105 const float stepT = std::exp(-sigmaT * seg);
106 const glm::vec3 Li = beam.color * beam.intensity * mask * phase;
107 result.inScatter += T * sigmaS * Li * ((1.f - stepT) / std::max(sigmaT, 1e-6f));
108 T *= stepT;
109 result.opticalDepth += sigmaT * seg;
110 ++result.samplesUsed;
111 }
112 result.transmittance = T;
113 return Result<FogRayResult>::success(result);
114}
115
116} // namespace eve::graphics::fog
const std::string & s
int mask
float phase
Definition CaveMesh.cpp:58
glm::vec4 p[6]
Stable, structured diagnostics shared by engine modules.
float u
Definition Grass.cpp:233
std::int32_t c
MeleePoint3 b
Definition MeleeHit.cpp:41
float radius
float d
float t
V3 origin
Definition RoadBake.cpp:138
RoadLaneDirection direction
V3 dir
Definition TreeMesh.cpp:150
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static std::optional< FogRayInterval > intersectBeam(const glm::vec3 &origin, const glm::vec3 &direction, const AnalyticBeam &beam)
Intersect a view ray with a capped beam; empty on miss.
Result< FogRayResult > integrate(const AnalyticBeam &beam, const FogProfile &profile, const glm::vec3 &origin, const glm::vec3 &direction, float sceneDepth, int segments, float mediumDensity) const
Integrate the beam contribution along a view ray.
Unit-density optical profile for participating media.
Definition FogProfile.h:16
float extinctionAt(float density) const noexcept
Extinction for a sampled density value.
float phase(float cosTheta) const noexcept
Dual-lobe Henyey–Greenstein phase evaluated for cosθ.
glm::vec3 scatteringAt(float density) const noexcept
Scattering coefficient σ_s = albedo * σ_t.
Analytic beam volume: capped cone frustum or pyramidal spot.
Interval along a ray where media is present (t in world units).
Definition FogTypes.h:112
Optical result of integrating one view ray through fog.
Definition FogTypes.h:122