载入中...
搜索中...
未找到
WaterUnderwaterEffects.cpp
浏览该文件的文档.
2#include "graphics/Graphics.h"
5
6#include <algorithm>
7#include <cmath>
8
9namespace eve::graphics {
10namespace {
11bool finite3(const glm::vec3& v) { return std::isfinite(v.x) && std::isfinite(v.y) && std::isfinite(v.z); }
12float lerp(float a, float b, float t) { return a + (b - a) * t; }
13} // namespace
14
16 const glm::vec3 color{r, g, b};
17 if (!std::isfinite(time) || time < 0.0F || time > 1.0F || !finite3(color))
19 DiagnosticCode::InvalidArgument, "water.underwater: gradient keys require normalized finite values"));
20 if (std::any_of(stops_.begin(), stops_.end(), [time](const auto& stop) { return stop.time == time; }))
22 DiagnosticCode::InvalidArgument, "water.underwater: duplicate gradient key time"));
23 auto candidate = stops_;
24 candidate.push_back({time, color});
25 std::stable_sort(candidate.begin(), candidate.end(), [](const auto& a, const auto& b) { return a.time < b.time; });
26 stops_ = std::move(candidate);
27 return Result<void>::success();
28}
29
31 if (stops_.empty() || !std::isfinite(time))
33 DiagnosticCode::InvalidArgument, "water.underwater: nonempty gradient and finite sample required"));
34 if (time <= stops_.front().time) return Result<glm::vec3>::success(stops_.front().color);
35 if (time >= stops_.back().time) return Result<glm::vec3>::success(stops_.back().color);
36 const auto upper = std::upper_bound(stops_.begin(), stops_.end(), time,
37 [](float t, const auto& stop) { return t < stop.time; });
38 const auto& b = *upper;
39 const auto& a = *(upper - 1);
40 return Result<glm::vec3>::success(glm::mix(a.color, b.color, (time - a.time) / (b.time - a.time)));
41}
42
44 if (!std::isfinite(time) || time < 0.0F || time > 1.0F || !std::isfinite(value))
46 DiagnosticCode::InvalidArgument, "water.underwater: scalar keys require normalized finite time"));
47 if (std::any_of(keys_.begin(), keys_.end(), [time](const auto& key) { return key.x == time; }))
49 DiagnosticCode::InvalidArgument, "water.underwater: duplicate scalar key time"));
50 auto candidate = keys_;
51 candidate.push_back({time, value});
52 std::stable_sort(candidate.begin(), candidate.end(), [](const auto& a, const auto& b) { return a.x < b.x; });
53 keys_ = std::move(candidate);
54 return Result<void>::success();
55}
56
58 if (keys_.empty() || !std::isfinite(time))
60 DiagnosticCode::InvalidArgument, "water.underwater: nonempty scalar curve and finite sample required"));
61 if (time <= keys_.front().x) return Result<float>::success(keys_.front().y);
62 if (time >= keys_.back().x) return Result<float>::success(keys_.back().y);
63 const auto upper = std::upper_bound(keys_.begin(), keys_.end(), time,
64 [](float t, const auto& key) { return t < key.x; });
65 const auto& b = *upper;
66 const auto& a = *(upper - 1);
67 return Result<float>::success(lerp(a.y, b.y, (time - a.x) / (b.x - a.x)));
68}
69
72 const WaterUnderwaterInput& input, const UnderwaterColorGradient& depthGradient,
73 const UnderwaterColorGradient& timeGradient, const UnderwaterScalarCurve& postExposureCurve,
74 const UnderwaterColorGradient& postColorGradient) {
75 if (!std::isfinite(input.cameraY) || !std::isfinite(input.seaLevel) || !std::isfinite(input.timeOfDay) ||
76 input.timeOfDay < 0.0F || input.timeOfDay > 1.0F || !finite3(input.mainLightColor) ||
77 input.causticTicks < 0 || !std::isfinite(settings.fogDepth) || settings.fogDepth <= 0.0F ||
78 !std::isfinite(settings.fogDistance) || !std::isfinite(settings.hdrpFogDistance) ||
79 !std::isfinite(settings.nearFogDistance) || !std::isfinite(settings.fogDensity) || settings.fogDensity < 0.0F ||
80 !std::isfinite(settings.playbackVolume) || settings.playbackVolume < 0.0F || settings.playbackVolume > 1.0F ||
81 !std::isfinite(settings.causticSize) || settings.causticSize < 1.0F || settings.causticSize > 100.0F ||
82 settings.framesPerSecond <= 0 || settings.causticTextureCount < 0 || !finite3(settings.fogColorMultiplier) ||
83 !finite3(settings.photoModeFogColor) ||
84 !finite3(settings.overrideFogMultiplier) || !std::isfinite(settings.overrideFogCurve) ||
85 !std::isfinite(settings.anisotropyShallow) || !std::isfinite(settings.anisotropyDeep) ||
86 !std::isfinite(settings.constantPostExposure) || !finite3(settings.constantPostColor) ||
87 !std::isfinite(settings.transitionHalfHeight) || settings.transitionHalfHeight < 0.0F ||
88 !std::isfinite(settings.transitionBlendDistance) || settings.transitionBlendDistance <= 0.0F ||
89 !std::isfinite(settings.transitionVignette) || settings.transitionVignette < 0.0F ||
90 settings.transitionVignette > 1.0F || !std::isfinite(settings.transitionVignetteSmoothness) ||
91 settings.transitionVignetteSmoothness < 0.01F || settings.transitionVignetteSmoothness > 1.0F ||
92 !std::isfinite(settings.transitionLensDistortion) ||
93 settings.transitionLensDistortion < 0.0F || settings.transitionLensDistortion > 1.0F ||
94 !std::isfinite(settings.transitionLensScale) || settings.transitionLensScale < 0.01F ||
95 settings.transitionLensScale > 5.0F ||
96 !finite3(settings.transitionLift) || !finite3(settings.transitionInverseGamma) ||
97 glm::any(glm::lessThan(settings.transitionInverseGamma, glm::vec3(0.001F))) ||
98 !finite3(settings.transitionGain) || !finite3(settings.transitionColorFilter) ||
99 glm::any(glm::lessThan(settings.transitionColorFilter, glm::vec3(0.0F)))) {
101 DiagnosticCode::InvalidArgument, "water.underwater: finite normalized settings and frame input required"));
102 }
103
104 WaterUnderwaterState candidateState = state;
105 WaterUnderwaterOutput candidate;
106 const bool underwater = settings.enabled && input.cameraY <= input.seaLevel;
107 candidate.entered = state.initialized && !state.isUnderwater && underwater;
108 candidate.exited = state.initialized && state.isUnderwater && !underwater;
109 candidate.isUnderwater = underwater;
110 candidate.playSubmergeDown = candidate.entered;
111 candidate.playSubmergeUp = candidate.exited || (!settings.enabled && state.isUnderwater);
112 candidate.loopAudio = underwater;
113 candidate.particles = underwater;
114 candidate.horizon = underwater && !settings.hdrp;
115 candidate.surfaceVfx = !underwater;
116 candidate.postFx = underwater && settings.supportPostFx;
117 candidate.transitionFx = underwater && settings.enableTransitionFx;
118 candidate.caustics = underwater && settings.useCaustics && settings.causticTextureCount > 0;
119 candidate.causticSize = settings.causticSize;
120 candidate.underwaterMaterialColor = input.hasMainLight
121 ? input.mainLightColor
122 : glm::vec3(207.0F / 255.0F);
123 if (candidate.transitionFx) {
124 const float belowSurface = input.seaLevel - input.cameraY;
125 candidate.transitionWeight = belowSurface <= settings.transitionHalfHeight
126 ? 1.0F
127 : std::clamp(1.0F - (belowSurface - settings.transitionHalfHeight) /
128 settings.transitionBlendDistance,
129 0.0F, 1.0F);
130 candidate.transitionVignette = settings.transitionVignette * candidate.transitionWeight;
132 0.2F + (settings.transitionVignetteSmoothness - 0.2F) * candidate.transitionWeight;
133 candidate.transitionLensDistortion =
134 settings.transitionLensDistortion * candidate.transitionWeight;
135 candidate.transitionLensScale =
136 1.0F + (settings.transitionLensScale - 1.0F) * candidate.transitionWeight;
137 candidate.transitionLift = settings.transitionLift * candidate.transitionWeight;
138 candidate.transitionInverseGamma = glm::mix(glm::vec3(1.0F), settings.transitionInverseGamma,
139 candidate.transitionWeight);
140 candidate.transitionGain = glm::mix(glm::vec3(1.0F), settings.transitionGain,
141 candidate.transitionWeight);
142 candidate.transitionColorFilter = glm::mix(glm::vec3(1.0F), settings.transitionColorFilter,
143 candidate.transitionWeight);
144 }
145
146 if (candidate.caustics && input.causticTicks > 0) {
147 const std::int64_t advanced = static_cast<std::int64_t>(candidateState.causticFrame) + input.causticTicks;
148 candidateState.causticFrame = static_cast<int>(advanced % settings.causticTextureCount);
149 } else if (!underwater) {
150 candidateState.causticFrame = 0;
151 }
152 candidate.causticFrame = candidateState.causticFrame;
153
154 if (underwater && settings.supportFog) {
155 candidate.depth01 = std::clamp((input.seaLevel - input.cameraY) / settings.fogDepth, 0.0F, 1.0F);
156 glm::vec3 color;
157 if (settings.photoModeFogColorEnabled) {
158 color = settings.photoModeFogColor;
159 } else {
160 auto sampled = settings.overrideFogColor ? timeGradient.evaluate(input.timeOfDay)
161 : depthGradient.evaluate(candidate.depth01);
162 if (!sampled) return Result<void>::failure(sampled.status());
163 color = sampled.value();
164 if (settings.overrideFogColor) {
165 color *= settings.overrideFogMultiplier * settings.overrideFogCurve;
166 } else {
167 color *= input.mainLightColor;
168 if (settings.fogColorMultiplier.r >= 0.0F) color.r = std::clamp(color.r + settings.fogColorMultiplier.r, 0.0F, 1.0F);
169 if (settings.fogColorMultiplier.g >= 0.0F) color.g = std::clamp(color.g + settings.fogColorMultiplier.g, 0.0F, 1.0F);
170 if (settings.fogColorMultiplier.b >= 0.0F) color.b = std::clamp(color.b + settings.fogColorMultiplier.b, 0.0F, 1.0F);
171 }
172 } candidate.fogColor = color;
173 candidate.fogDensity = settings.fogDensity;
174 candidate.fogStart = settings.nearFogDistance;
175 candidate.fogEnd = settings.hdrp ? settings.hdrpFogDistance : settings.fogDistance;
176 const float d = candidate.depth01;
177 candidate.hdrpBaseHeight = lerp(-150.0F, 6000.0F, d);
178 candidate.fogHeight = settings.hdrp ? candidate.hdrpBaseHeight : input.seaLevel;
179 candidate.hdrpMeanFreePath = lerp(3000.0F, 1.0F, d);
180 candidate.fogColor = settings.hdrp ? glm::mix(color, color / 6.0F, d) : color;
181 candidate.hdrpProbeDimmer = lerp(1.0F, 0.325F, d);
182 candidate.hdrpAnisotropy = lerp(settings.anisotropyShallow, settings.anisotropyDeep, d);
183 candidate.hdrpDepthExtent = lerp(150.0F, 300.0F, d);
184 }
185 if (underwater && settings.supportPostFx) {
186 if (settings.timeDrivenPostFx) {
187 auto exposure = postExposureCurve.evaluate(input.timeOfDay);
188 if (!exposure) return Result<void>::failure(exposure.status());
189 auto color = postColorGradient.evaluate(input.timeOfDay);
190 if (!color) return Result<void>::failure(color.status());
191 candidate.postExposure = exposure.value();
192 candidate.postColor = color.value();
193 } else {
194 candidate.postExposure = settings.constantPostExposure;
195 candidate.postColor = settings.constantPostColor;
196 }
197 }
198
199 candidateState.initialized = true;
200 candidateState.isUnderwater = underwater;
201 state = candidateState;
202 output = candidate;
203 return Result<void>::success();
204}
205
208 int expectedSensorTag, int expectedVisitorTag) {
209 if (expectedSensorTag < 0 || expectedVisitorTag < 0 || event.sensorTag < 0 ||
210 event.visitorTag < 0 || event.entered == event.exited) {
213 "water.underwaterTrigger: non-negative tags and exactly one event direction required"));
214 }
215 if (event.sensorTag != expectedSensorTag || event.visitorTag != expectedVisitorTag)
216 return Result<void>::success();
217 state.effectsEnabled = event.exited;
218 return Result<void>::success();
219}
220
222 const WaterSurfaceFogSnapshot& surface) {
223 if (!volumetric || !finite3(output.fogColor) || !std::isfinite(output.fogDensity) ||
224 output.fogDensity < 0.0F || !std::isfinite(output.fogStart) || !std::isfinite(output.fogEnd) ||
225 !finite3(surface.color) || !std::isfinite(surface.density) || surface.density < 0.0F ||
226 !std::isfinite(surface.start) || !std::isfinite(surface.end) || surface.end <= surface.start ||
227 !std::isfinite(surface.height) || !std::isfinite(surface.heightFalloff) || surface.heightFalloff < 0.0F) {
229 DiagnosticCode::InvalidArgument, "water.underwaterFog: valid provider and finite fog snapshot required"));
230 }
231 const glm::vec3 color = output.isUnderwater ? output.fogColor : surface.color;
232 const float density = output.isUnderwater ? output.fogDensity : surface.density;
233 const float start = output.isUnderwater ? output.fogStart : surface.start;
234 const float end = output.isUnderwater ? output.fogEnd : surface.end;
235 if (end <= std::max(0.0F, start))
237 DiagnosticCode::InvalidArgument, "water.underwaterFog: fog end must exceed the clamped start"));
238 volumetric->setMode("fog");
239 volumetric->setFogColor(color.r, color.g, color.b);
240 volumetric->setDensity(density);
241 volumetric->setFogStart(start);
242 volumetric->setFogEnd(end);
243 volumetric->setFogHeight(output.isUnderwater ? output.fogHeight : surface.height);
244 volumetric->setFogHeightFalloff(output.isUnderwater ? 0.0F : surface.heightFalloff);
245 return Result<void>::success();
246}
247
250 const WaterSurfacePostFxSnapshot& surface) {
251 if (!graphics || !camera || !std::isfinite(output.postExposure) ||
252 !finite3(output.postColor) || !std::isfinite(surface.exposureEv) ||
253 !finite3(surface.color) || glm::any(glm::lessThan(surface.color, glm::vec3(0.0F))) ||
254 !std::isfinite(output.transitionVignette) || output.transitionVignette < 0.0F ||
255 output.transitionVignette > 1.0F || !std::isfinite(output.transitionVignetteSmoothness) ||
256 output.transitionVignetteSmoothness < 0.01F || output.transitionVignetteSmoothness > 1.0F ||
257 !std::isfinite(output.transitionLensDistortion) ||
258 output.transitionLensDistortion < 0.0F || output.transitionLensDistortion > 1.0F ||
259 !std::isfinite(output.transitionLensScale) || output.transitionLensScale < 0.01F ||
260 output.transitionLensScale > 5.0F ||
261 !std::isfinite(surface.vignette) || surface.vignette < 0.0F || surface.vignette > 1.0F ||
262 !std::isfinite(surface.vignetteSmoothness) || surface.vignetteSmoothness < 0.01F ||
263 surface.vignetteSmoothness > 1.0F ||
264 !std::isfinite(surface.lensDistortion) || surface.lensDistortion < 0.0F ||
265 surface.lensDistortion > 1.0F || !std::isfinite(surface.lensScale) ||
266 surface.lensScale < 0.01F || surface.lensScale > 5.0F || !finite3(output.transitionLift) ||
267 !finite3(output.transitionInverseGamma) ||
268 glm::any(glm::lessThan(output.transitionInverseGamma, glm::vec3(0.001F))) ||
269 !finite3(output.transitionGain) || !finite3(output.transitionColorFilter) ||
270 glm::any(glm::lessThan(output.transitionColorFilter, glm::vec3(0.0F))) || !finite3(surface.lift) ||
271 !finite3(surface.inverseGamma) ||
272 glm::any(glm::lessThan(surface.inverseGamma, glm::vec3(0.001F))) || !finite3(surface.gain))
275 "water.underwaterPostFx: valid providers and non-negative finite surface values required"));
276 const bool postActive = output.isUnderwater && output.postFx;
277 const bool transitionActive = output.isUnderwater && output.transitionFx;
278 camera->setExposure(postActive ? output.postExposure : surface.exposureEv);
279 const glm::vec3 baseColor = postActive ? output.postColor : surface.color;
280 graphics->setSceneColorFilter(baseColor *
281 (transitionActive ? output.transitionColorFilter : glm::vec3(1.0F)));
282 graphics->setSceneTransitionFx(
283 transitionActive ? output.transitionVignette : surface.vignette,
284 transitionActive ? output.transitionVignetteSmoothness : surface.vignetteSmoothness,
285 transitionActive ? output.transitionLensDistortion : surface.lensDistortion,
286 transitionActive ? output.transitionLensScale : surface.lensScale);
287 graphics->setSceneLiftGammaGain(
288 transitionActive ? output.transitionLift : surface.lift,
289 transitionActive ? output.transitionInverseGamma : surface.inverseGamma,
290 transitionActive ? output.transitionGain : surface.gain);
291 return Result<void>::success();
292}
293
296 if (!horizon)
298 DiagnosticCode::InvalidArgument, "water.underwaterHorizon: non-null renderable required"));
299 horizon->setVisible(output.horizon);
300 return Result<void>::success();
301}
302
305 const WaterSurfaceMaterialSnapshot& surface) {
306 if (!renderable || !finite3(output.underwaterMaterialColor) ||
307 glm::any(glm::lessThan(output.underwaterMaterialColor, glm::vec3(0.0F))) ||
308 !finite3(surface.color) || glm::any(glm::lessThan(surface.color, glm::vec3(0.0F))) ||
309 !std::isfinite(surface.alpha) || surface.alpha < 0.0F || surface.alpha > 1.0F) {
312 "water.underwaterMaterial: valid renderable and non-negative finite tint required"));
313 }
314 const glm::vec3 color = output.isUnderwater ? output.underwaterMaterialColor : surface.color;
315 renderable->setTint(color.r, color.g, color.b, surface.alpha);
316 return Result<void>::success();
317}
318
319} // namespace eve::graphics
double value
Duration start
std::string output
EvpackChunkInput input
Definition Evpack.cpp:170
tensor::Graph g
Definition GpuGraph.cpp:7
float camera[2]
std::uint32_t key
double r
float v
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float d
float t
TerrainThermalSettings settings
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
EVENGINE_API_BACKENDS public API.
virtual void setSceneTransitionFx(float vignette, float vignetteSmoothness, float lensDistortion, float lensScale)=0
Set final-composite vignette and radial lens distortion strengths in [0,1].
virtual void setSceneColorFilter(const glm::vec3 &color)=0
Set a non-negative linear RGB multiplier applied during final HDR exposure.
virtual void setSceneLiftGammaGain(const glm::vec3 &lift, const glm::vec3 &inverseGamma, const glm::vec3 &gain)=0
Set pre-tonemap HDR lift, inverse-gamma and gain coefficients.
EVENGINE_API_BACKENDS public API.
void setTint(float r, float g, float b, float a=1.f)
void setVisible(bool visible)
Caller-owned ordered color gradient used for depth or time-of-day fog.
Result< void > addStop(float time, float r, float g, float b)
Add a finite key in [0,1]; duplicate times are rejected atomically.
Result< glm::vec3 > evaluate(float time) const
Evaluate with endpoint clamping and linear interpolation.
Caller-owned normalized scalar curve used by Pcg underwater post exposure.
Result< void > addKey(float time, float value)
Add a finite key in [0,1]; duplicate times are rejected atomically.
Result< float > evaluate(float time) const
Evaluate with endpoint clamping and linear interpolation.
Volumetric light + fog.
Definition Volumetric.h:38
void setMode(const std::string &mode)
"screenspace" | "raymarch" | "fog" | "froxel" | "cloud".
void setFogEnd(float endDistance)
Sets the fog end.
void setFogHeightFalloff(float falloff)
Sets the fog height falloff.
void setFogHeight(float worldY)
Height fog: denser near world Y = fogHeight; falloff is 1/meters scale.
void setDensity(float density)
Sets the density.
void setFogStart(float startDistance)
View-distance ramp where fog appears (world units along the ray).
void setFogColor(float r, float g, float b)
Sets the fog color.
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
Definition Animation.h:25
Result< void > applyWaterUnderwaterHorizon(Renderable3D *horizon, const WaterUnderwaterOutput &output)
Synchronize Pcg's underwater horizon mesh visibility.
Result< void > applyWaterUnderwaterMaterial(Renderable3D *renderable, const WaterUnderwaterOutput &output, const WaterSurfaceMaterialSnapshot &surface)
Apply Pcg's per-frame underwater material color to a dedicated water renderable.
Result< void > applyWaterUnderwaterFog(Volumetric *volumetric, const WaterUnderwaterOutput &output, const WaterSurfaceFogSnapshot &surface)
Apply a water-effects snapshot to an existing EVEngine volumetric fog provider.
Result< void > advanceWaterUnderwaterDisableTrigger(WaterUnderwaterDisableTriggerState &state, const WaterUnderwaterTriggerEvent &event, int expectedSensorTag, int expectedVisitorTag)
Apply one Box3D trigger event using Pcg DisableUnderwaterFXTrigger semantics.
Result< void > applyWaterUnderwaterPostFx(Graphics *graphics, Camera3D *camera, const WaterUnderwaterOutput &output, const WaterSurfacePostFxSnapshot &surface)
Apply underwater exposure and final-composite color to borrowed graphics providers.
Result< void > advanceWaterUnderwaterEffects(WaterUnderwaterState &state, WaterUnderwaterOutput &output, const WaterUnderwaterSettings &settings, const WaterUnderwaterInput &input, const UnderwaterColorGradient &depthGradient, const UnderwaterColorGradient &timeGradient, const UnderwaterScalarCurve &postExposureCurve, const UnderwaterColorGradient &postColorGradient)
Advance Pcg underwater transitions and derive provider-neutral effects.
constexpr HexVec3 lerp(HexVec3 a, HexVec3 b, float t) noexcept
Linear interpolation between two positions.
Definition HexMetrics.h:51
Caller-owned surface fog values restored after leaving or disabling underwater effects.
Caller-owned water-renderable tint restored outside the underwater state.
Caller-owned surface post-processing values restored above water.
Caller-owned enable state driven by Pcg disable-underwater trigger events.
Immutable per-frame water, camera and lighting snapshot.
Effects snapshot for graphics, audio, particles and VFX providers to consume.
Authored Pcg underwater-effects policy; owns no renderer, audio source or scene object.
Persistent caller-owned transition and caustic animation state.
Immutable normalized 3D trigger event snapshot supplied by the physics owner.
glm::vec4 color