载入中...
搜索中...
未找到
FogRayMarch.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
11namespace eve::graphics::fog {
12namespace {
13
14[[nodiscard]] std::optional<FogRayInterval> intersectSlab(float origin, float dir, float minV,
15 float maxV) {
16 if (std::fabs(dir) < 1e-8f) {
17 if (origin < minV || origin > maxV) return std::nullopt;
18 return FogRayInterval{0.f, 1e6f};
19 }
20 float t0 = (minV - origin) / dir;
21 float t1 = (maxV - origin) / dir;
22 if (t0 > t1) std::swap(t0, t1);
23 return FogRayInterval{t0, t1};
24}
25
26[[nodiscard]] std::optional<FogRayInterval> intersectRayAabb(const glm::vec3& o, const glm::vec3& d,
27 const glm::vec3& mn, const glm::vec3& mx) {
28 auto sx = intersectSlab(o.x, d.x, mn.x, mx.x);
29 auto sy = intersectSlab(o.y, d.y, mn.y, mx.y);
30 auto sz = intersectSlab(o.z, d.z, mn.z, mx.z);
31 if (!sx || !sy || !sz) return std::nullopt;
32 const float tEnter = std::max({sx->tEnter, sy->tEnter, sz->tEnter, 0.f});
33 const float tExit = std::min({sx->tExit, sy->tExit, sz->tExit});
34 if (tExit <= tEnter) return std::nullopt;
35 return FogRayInterval{tEnter, tExit};
36}
37
38} // namespace
39
40std::optional<FogRayInterval> FogRayMarch::intersectBound(const glm::vec3& origin,
41 const glm::vec3& direction,
42 const FogVolumeBound& bound) {
43 const float len = glm::length(direction);
44 if (!(len > 1e-8f)) return std::nullopt;
45 const glm::vec3 d = direction / len;
46
47 switch (bound.kind) {
49 // Ray vs sphere centered at origin xz / y=0 shell approximation.
50 const glm::vec3 center(origin.x, 0.f, origin.z);
51 const glm::vec3 oc = origin - center;
52 const float b = glm::dot(oc, d);
53 const float c = glm::dot(oc, oc) - bound.skyRadius * bound.skyRadius;
54 const float disc = b * b - c;
55 if (disc < 0.f) return std::nullopt;
56 const float s = std::sqrt(disc);
57 const float t0 = -b - s;
58 const float t1 = -b + s;
59 const float tEnter = std::max(t0, 0.f);
60 if (t1 <= tEnter) return std::nullopt;
61 return FogRayInterval{tEnter, t1};
62 }
64 auto slab = intersectSlab(origin.y, d.y, bound.heightMin, bound.heightMax);
65 if (!slab) return std::nullopt;
66 const float tEnter = std::max(slab->tEnter, 0.f);
67 if (slab->tExit <= tEnter) return std::nullopt;
68 // Cap far distance so infinite rays remain bounded.
69 return FogRayInterval{tEnter, std::min(slab->tExit, tEnter + 200.f)};
70 }
72 // Transform ray into OBB local space, then AABB test.
73 const glm::vec3 o = origin - bound.obbCenter;
74 const glm::vec3 localO(glm::dot(o, bound.obbAxisX), glm::dot(o, bound.obbAxisY),
75 glm::dot(o, bound.obbAxisZ));
76 const glm::vec3 localD(glm::dot(d, bound.obbAxisX), glm::dot(d, bound.obbAxisY),
77 glm::dot(d, bound.obbAxisZ));
78 return intersectRayAabb(localO, localD, -bound.obbHalfExtents, bound.obbHalfExtents);
79 }
80 }
81 return std::nullopt;
82}
83
85 const FogDensityField& field, const FogProfile& profile, const glm::vec3& origin,
86 const glm::vec3& direction, const FogVolumeBound& bound, const glm::vec3& lightDir,
87 const glm::vec3& lightColor, float lightIntensity, const glm::vec3& skyAmbient,
88 const glm::vec3& groundAmbient, int sampleCount, float sceneDepth, const BeerLightCache* beerCache,
89 bool useAnalyticSegments) const {
90 if (sampleCount < 1 || sampleCount > 256) {
92 DiagnosticCode::InvalidArgument, "sampleCount must be in [1,256]", "sampleCount", {},
93 "graphics.fog"));
94 }
95 const float dirLen = glm::length(direction);
96 if (!(dirLen > 1e-8f)) {
98 DiagnosticCode::InvalidArgument, "direction must be non-zero", "direction", {},
99 "graphics.fog"));
100 }
101 const glm::vec3 dir = direction / dirLen;
102 auto interval = intersectBound(origin, dir, bound);
103 if (!interval) {
104 FogRayResult empty;
105 return Result<FogRayResult>::success(empty);
106 }
107 if (sceneDepth > 0.f) interval->tExit = std::min(interval->tExit, sceneDepth);
108 if (!interval->valid()) {
109 FogRayResult empty;
110 return Result<FogRayResult>::success(empty);
111 }
112
113 const glm::vec3 L = glm::length(lightDir) > 1e-6f ? glm::normalize(lightDir) : glm::vec3(0.f, 1.f, 0.f);
114 const float cosTheta = glm::dot(-dir, L);
115 const float phase = profile.phase(cosTheta);
116
117 FogRayResult result;
118 float T = 1.f;
119 const float segLen = interval->length() / static_cast<float>(sampleCount);
120 float prevDensity = -1.f;
121
122 for (int i = 0; i < sampleCount && T > 1e-4f; ++i) {
123 const float t = interval->tEnter + (static_cast<float>(i) + 0.5f) * segLen;
124 const glm::vec3 p = origin + dir * t;
125 const float density = field.sampleDensity(p);
126 const float sigmaT = profile.extinctionAt(density);
127 const glm::vec3 sigmaS = profile.scatteringAt(density);
128
129 float stepOptical = sigmaT * segLen;
130 float stepT;
131 if (useAnalyticSegments && prevDensity >= 0.f &&
132 std::fabs(density - prevDensity) < 0.02f * std::max(1.f, prevDensity)) {
133 // Closed-form Beer segment for locally stable media.
134 stepT = std::exp(-stepOptical);
135 } else {
136 stepT = std::exp(-stepOptical);
137 }
138 prevDensity = density;
139
140 float visibility = 1.f;
141 if (beerCache && beerCache->valid()) {
142 visibility = beerCache->sampleTransmittance(field, p);
143 }
144 const glm::vec3 ambient = profile.ambientRadiance(skyAmbient, groundAmbient, density);
145 const glm::vec3 assist = field.sampleLightAssist(p);
146 const glm::vec3 Li = lightColor * lightIntensity * visibility * phase + ambient + assist;
147 // Front-to-back: in-scatter attenuated by current transmittance.
148 const glm::vec3 scatter = sigmaS * Li * ((1.f - stepT) / std::max(sigmaT, 1e-6f));
149 result.inScatter += T * scatter;
150 T *= stepT;
151 result.opticalDepth += stepOptical;
152 ++result.samplesUsed;
153 }
154
155 result.transmittance = T;
156 return Result<FogRayResult>::success(result);
157}
158
159} // namespace eve::graphics::fog
const std::string & s
float phase
Definition CaveMesh.cpp:58
glm::vec4 p[6]
Stable, structured diagnostics shared by engine modules.
std::int32_t c
MeleePoint3 b
Definition MeleeHit.cpp:41
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
Cached light-path transmittance used by Fast / Enhanced quality tiers.
float sampleTransmittance(const FogDensityField &field, const glm::vec3 &world) const noexcept
Sample cached transmittance at a world position (1 = fully lit).
World-space density bands, curl assist velocity, and lighting helpers.
Unit-density optical profile for participating media.
Definition FogProfile.h:16
float extinctionAt(float density) const noexcept
Extinction for a sampled density value.
glm::vec3 ambientRadiance(const glm::vec3 &skyUp, const glm::vec3 &groundDown, float density) const noexcept
Ambient term using up/down visibility and AO.
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.
static std::optional< FogRayInterval > intersectBound(const glm::vec3 &origin, const glm::vec3 &direction, const FogVolumeBound &bound)
Intersect a ray with a volume bound; returns empty when misses.
Result< FogRayResult > integrate(const FogDensityField &field, const FogProfile &profile, const glm::vec3 &origin, const glm::vec3 &direction, const FogVolumeBound &bound, const glm::vec3 &lightDir, const glm::vec3 &lightColor, float lightIntensity, const glm::vec3 &skyAmbient, const glm::vec3 &groundAmbient, int sampleCount, float sceneDepth, const BeerLightCache *beerCache, bool useAnalyticSegments) const
Integrate one view ray through the density field.
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
Analytic volume used to bound a camera ray before sampling.
Definition FogRayMarch.h:17
glm::vec4 lightColor
glm::vec4 ambient
glm::vec4 lightDir