载入中...
搜索中...
未找到
SkyAtmospherePass.cpp
浏览该文件的文档.
2#include <algorithm>
3#include <bit>
4#include <cmath>
5#include <cstdio>
6#include <glm/gtc/matrix_inverse.hpp>
7#include <glm/gtc/type_ptr.hpp>
8#include "graphics/Canvas.h"
9#include "graphics/Graphics.h"
10#include "graphics/Mesh.h"
12#include "graphics/Shader.h"
21
22namespace eve::graphics {
23namespace {
24Diagnostic invalid(const char* message) {
26}
27Result<std::array<float, Shader::kMaxFloats>> viewFrame(const glm::mat4& viewProjection, const glm::vec3& eyeMetres,
28 const SkyCelestialLight& light) {
29 const float determinant = glm::determinant(viewProjection);
30 if (!std::isfinite(determinant) || std::abs(determinant) < 1e-20f)
31 return Result<std::array<float, Shader::kMaxFloats>>::failure(
32 invalid("Sky camera transform must be finite and invertible"));
33 const auto inverse = glm::inverse(viewProjection);
34 std::array<float, Shader::kMaxFloats> data{};
35 std::copy_n(glm::value_ptr(inverse), 16, data.begin());
36 for (int axis = 0; axis < 3; ++axis) {
37 data[16 + axis] = eyeMetres[axis];
38 data[20 + axis] = light.direction[axis];
39 data[24 + axis] = light.irradiance[axis];
40 }
41 const auto farCenter = inverse * glm::vec4(0, 0, 1, 1);
42 const auto forward = glm::vec3(farCenter) - eyeMetres * farCenter.w;
43 if (!std::isfinite(glm::length(forward)) || glm::length(forward) < 1e-20f)
44 return Result<std::array<float, Shader::kMaxFloats>>::failure(invalid("Sky camera forward must be nonzero"));
45 data[28] = forward.x;
46 data[29] = forward.y;
47 data[30] = forward.z;
48 if (!std::all_of(data.begin(), data.end(), [](float v) { return std::isfinite(v); }))
49 return Result<std::array<float, Shader::kMaxFloats>>::failure(invalid("Sky camera parameters must be finite"));
50 return Result<std::array<float, Shader::kMaxFloats>>::success(data);
51}
52} // namespace
53
55 Graphics* graphics = nullptr;
56 std::weak_ptr<const void> lifetime;
57 Shader* shader = nullptr;
58 Mesh* mesh = nullptr;
59 Shader* wispsShader = nullptr;
60 Mesh* wispsMesh = nullptr;
61 std::array<float, 3> moonForward{};
62 float wispsMorphPhase = 0;
63 float dayPhase = 0;
64 bool daylightEnabled = false;
66 uint64_t forward = 0, capture = 0, preparation = 0;
67 Shader* viewShader = nullptr;
68 Canvas* viewCanvas = nullptr; // Provider-owned, valid through Graphics shutdown.
69 std::array<float, Shader::kMaxFloats> preparedFrame{};
70 bool viewReady = false;
71};
72
73SkyAtmospherePass::SkyAtmospherePass() : impl_(std::make_unique<Impl>()) {}
75 detach();
76 if (!impl_->lifetime.expired()) {
77 if (impl_->viewShader) {
78 if (impl_->graphics->releaseShader(impl_->viewShader))
79 delete impl_->viewShader;
80 else
81 std::fputs("SkyAtmospherePass: view shader release rejected\n", stderr);
82 }
83 if (impl_->wispsMesh) {
84 if (impl_->graphics->releaseMesh(impl_->wispsMesh))
85 delete impl_->wispsMesh;
86 else
87 std::fputs("SkyAtmospherePass: wisps mesh release rejected\n", stderr);
88 }
89 if (impl_->wispsShader) {
90 if (impl_->graphics->releaseShader(impl_->wispsShader))
91 delete impl_->wispsShader;
92 else
93 std::fputs("SkyAtmospherePass: wisps shader release rejected\n", stderr);
94 }
95 if (impl_->mesh) {
96 if (impl_->graphics->releaseMesh(impl_->mesh))
97 delete impl_->mesh;
98 else
99 std::fputs("SkyAtmospherePass: mesh release rejected\n", stderr);
100 }
101 if (impl_->shader) {
102 if (impl_->graphics->releaseShader(impl_->shader))
103 delete impl_->shader;
104 else
105 std::fputs("SkyAtmospherePass: shader release rejected\n", stderr);
106 }
107 }
108}
109
112 const SkyWispsLayer* wisps) {
114 auto valid = p.validate();
115 if (!valid) return Prepared::failure(valid.status());
116 if (wisps) {
117 const auto& w = *wisps;
118 const std::array scalars{w.domeScale, w.morphRate, w.morphPeriod, w.morphAmount,
119 w.opacity, w.litIntensity, w.cloudTime};
120 const auto finite = [](const auto& values) {
121 return std::all_of(values.begin(), values.end(), [](float v) { return std::isfinite(v); });
122 };
123 const auto bounded = [](const auto& values) {
124 return std::all_of(values.begin(), values.end(),
125 [](float v) { return std::isfinite(v) && std::abs(v) <= 1e6f; });
126 };
127 if (!w.densityTexture || w.corners.empty() || w.corners.size() % 27 || w.corners.size() > 9000000 ||
128 !finite(w.corners) || !bounded(scalars) || !bounded(w.color) || !bounded(w.gradient) ||
129 !bounded(w.movement) || !bounded(w.moonForward) || !bounded(w.materialContrast) ||
130 !bounded(w.sunDiskColor) || !bounded(w.sunDiskShape) ||
131 *std::min_element(w.sunDiskColor.begin(), w.sunDiskColor.end()) < 0 || w.sunDiskShape[0] <= 0 ||
132 w.sunDiskShape[0] > 3.141593f || w.sunDiskShape[1] <= 0 || w.sunDiskShape[2] < 0 ||
133 w.materialContrast[1] <= 0 || w.materialContrast[2] < 0 || w.domeScale <= 0 || w.domeScale > 100000 ||
134 w.morphPeriod < .0001f || w.morphAmount < 0 || w.opacity < 0 || w.opacity > 1 || w.litIntensity < 0)
135 return Prepared::failure(invalid("Invalid authored wisps geometry, texture or material"));
136 if (w.daylightEnabled) {
137 auto checked = w.daylight.validate();
138 if (!checked) return Prepared::failure(checked.status());
139 if (!w.starsTexture || !w.starsNoiseTexture || w.starsTexture->dimension != ShaderImageDimension::Image2D ||
140 w.starsNoiseTexture->dimension != ShaderImageDimension::Image2D)
141 return Prepared::failure(invalid("Daylight layer requires two-dimensional star and noise textures"));
142 }
143 if (w.daylight.moonDiskColor[3] > 0 && (!w.daylightEnabled || !w.moonColorTexture || !w.moonNormalTexture ||
144 w.moonColorTexture->dimension != ShaderImageDimension::Image2D ||
145 w.moonNormalTexture->dimension != ShaderImageDimension::Image2D))
146 return Prepared::failure(invalid("Lunar disk requires daylight and both two-dimensional textures"));
147 if (w.sunDiskCurveEnabled) {
148 for (size_t i = 0; i < w.sunDiskCurve.size(); ++i) {
149 const auto& key = w.sunDiskCurve[i];
150 if (!bounded(key) || key[0] < 0 || key[0] > 1 || key[1] < 0 ||
151 (i % 4 && key[0] <= w.sunDiskCurve[i - 1][0]) || (i % 4 == 3 && key[1] <= 0))
152 return Prepared::failure(invalid("Invalid solar elevation curve"));
153 }
154 }
155 for (size_t i = 0; i < w.corners.size(); i += 9) {
156 for (size_t axis = 0; axis < 3; ++axis)
157 if (std::abs(w.corners[i + axis]) > 1e8f)
158 return Prepared::failure(invalid("Wisps geometry exceeds supported extent"));
159 if (w.corners[i] == 0 && w.corners[i + 1] == 0 && w.corners[i + 2] == 0)
160 return Prepared::failure(invalid("Wisps dome vertex must be away from its origin"));
161 for (size_t channel = 5; channel < 9; ++channel)
162 if (w.corners[i + channel] < 0 || w.corners[i + channel] > 1)
163 return Prepared::failure(invalid("Wisps vertex colors must be normalized"));
164 }
165 }
166#ifdef EVENGINE_WEBGPU
167 (void)graphics;
168 return Prepared::failure(
169 Diagnostic::error(DiagnosticCode::Unsupported, "Atmosphere resource program requires Vulkan"));
170#else
171 auto pass = std::unique_ptr<SkyAtmospherePass>(new SkyAtmospherePass());
172 auto& state = *pass->impl_;
173 state.graphics = &graphics;
174 state.lifetime = graphics.resourceLifetime();
175 if (state.lifetime.expired()) return Prepared::failure(invalid("Graphics provider has retired"));
176 try {
177 auto baked = detail::bakeSkyAtmosphereLuts(p);
178 if (!baked) return Prepared::failure(baked.status());
179 auto tables = std::move(baked).takeValue();
180 const bool optical = wisps && wisps->opticalCycleEnabled;
181 if (optical && !wisps->daylightEnabled)
182 return Prepared::failure(invalid("Optical cycle requires daylight material"));
183 if (optical) {
184 tables.transmittance.clear();
185 tables.multiScattering.clear();
186 for (unsigned layer = 0; layer < detail::OpticalLayers; ++layer) {
188 float(layer) / float(detail::OpticalLayers - 1);
190 if (!sample) return Prepared::failure(sample.status());
191 const auto& value = sample.value();
192 tables.transmittance.insert(tables.transmittance.end(), value.transmittance.begin(),
193 value.transmittance.end());
194 tables.multiScattering.insert(tables.multiScattering.end(), value.multiScattering.begin(),
195 value.multiScattering.end());
196 }
197 }
198 state.shader = graphics.newMeshShaderFromSpv({}, {});
199 if (!state.shader) return Prepared::failure(invalid("Graphics did not create an atmosphere shader"));
200 const auto& fog = p.heightFog;
201 std::vector<float> constants{p.rayleigh[0],
202 p.rayleigh[1],
203 p.rayleigh[2],
204 p.rayleighHeightKm,
205 p.mieScattering[0],
206 p.mieScattering[1],
207 p.mieScattering[2],
208 p.mieHeightKm,
209 p.mieAbsorption[0],
210 p.mieAbsorption[1],
211 p.mieAbsorption[2],
212 p.mieAnisotropy,
213 p.ozoneAbsorption[0],
214 p.ozoneAbsorption[1],
215 p.ozoneAbsorption[2],
216 p.ozoneCenterKm,
217 p.groundRadiusKm,
218 p.groundRadiusKm + p.atmosphereHeightKm,
219 p.ozoneHalfWidthKm,
220 fog.directionalElevationRange[0],
221 fog.densityPerMetre,
222 fog.heightFalloffPerMetre,
223 fog.baseHeightMetres,
224 fog.startDistanceMetres,
225 fog.inscattering[0],
226 fog.inscattering[1],
227 fog.inscattering[2],
228 fog.maximumOpacity,
229 fog.directionalInscattering[0],
230 fog.directionalInscattering[1],
231 fog.directionalInscattering[2],
232 fog.directionalExponent,
233 fog.directionalStartDistanceMetres,
234 fog.atmosphereContribution,
235 fog.skyDistanceMetres,
236 fog.directionalElevationRange[1]};
237 constants.insert(constants.end(), {optical ? float(detail::OpticalLayers) : 1.f, detail::OpticalMinimum,
238 detail::OpticalMaximum, 0});
239 const SkyDaylightLayer neutral;
240 const auto& opticalValues = optical ? wisps->daylight : neutral;
241 for (const auto* v : {&opticalValues.rayDay, &opticalValues.rayDusk, &opticalValues.rayNight,
242 &opticalValues.absorptionDay, &opticalValues.absorptionNight})
243 constants.insert(constants.end(), v->begin(), v->end());
244 std::array<ShaderImageInput, 2> images;
245 for (size_t i = 0; i < images.size(); ++i) {
246 images[i].binding = static_cast<uint32_t>(i);
247 images[i].format = ShaderImageFormat::RGBA32Float;
248 images[i].dimension = ShaderImageDimension::Image3D;
249 images[i].depth = optical ? detail::OpticalLayers : 1;
250 images[i].sampler = TextureSampler::linear();
251 images[i].sampler.repeatU = images[i].sampler.repeatV = false;
252 }
253 images[0].width = tables.TransmittanceWidth;
254 images[0].height = tables.TransmittanceHeight;
255 auto transBytes = std::make_shared<const std::vector<float>>(std::move(tables.transmittance));
256 auto multiBytes = std::make_shared<const std::vector<float>>(std::move(tables.multiScattering));
257 images[0].bytes = std::as_bytes(std::span(*transBytes));
258 images[0].contentOwner = transBytes;
259 images[1].width = tables.MultiWidth;
260 images[1].height = tables.MultiHeight;
261 images[1].bytes = std::as_bytes(std::span(*multiBytes));
262 images[1].contentOwner = multiBytes;
264 {images, std::as_bytes(std::span(constants)), {}});
265 if (!uploaded) return Prepared::failure(uploaded.status());
266 auto surface = graphics.configureMeshShaderSurface(*state.shader, BlendMode::Opaque, false, true);
267 if (!surface) return Prepared::failure(surface.status());
270 auto configured = graphics.configureMeshShaderRaster(*state.shader, raster);
271 if (!configured) return Prepared::failure(configured.status());
272 const float positions[]{-1, -1, 1, 3, -1, 1, -1, 3, 1};
273 const uint32_t indices[]{0, 1, 2};
274 state.mesh = graphics.newMeshFromArrays(positions, nullptr, nullptr, 3, indices, 3);
275 if (!state.mesh) return Prepared::failure(invalid("Graphics did not create atmosphere geometry"));
276 state.viewShader = graphics.newMeshShaderFromSpv({}, {});
277 if (!state.viewShader) return Prepared::failure(invalid("Could not create sky-view producer"));
278 std::vector<float> viewConstants(constants);
279 viewConstants.resize(280, 0);
280 auto viewUpload = graphics.replaceMeshShaderResources(*state.viewShader, sky_view_lut_vert, sky_view_lut_frag,
281 {images, std::as_bytes(std::span(viewConstants)), {}});
282 if (!viewUpload) return Prepared::failure(viewUpload.status());
283 auto viewSurface = graphics.configureMeshShaderSurface(*state.viewShader, BlendMode::Opaque, false, true);
284 if (!viewSurface) return Prepared::failure(viewSurface.status());
285 state.viewCanvas = graphics.newHDRCanvas(192, 104);
286 if (!state.viewCanvas) return Prepared::failure(invalid("Could not allocate sky-view target"));
287 if (wisps) {
288 const auto& w = *wisps;
289 std::vector<float> cloudConstants(constants.begin(), constants.end());
290 cloudConstants.insert(cloudConstants.end(), {w.color[0],
291 w.color[1],
292 w.color[2],
293 w.morphAmount,
294 w.gradient[0],
295 w.gradient[1],
296 w.gradient[2],
297 w.gradient[3],
298 w.movement[0],
299 w.movement[1],
300 w.morphRate,
301 w.morphPeriod,
302 w.litIntensity,
303 w.moonGradient ? 1.f : 0.f,
304 w.staticClouds ? 1.f : 0.f,
305 w.opacity,
306 w.materialContrast[0],
307 w.materialContrast[1],
308 w.materialContrast[2],
309 w.authoredSkyLighting ? 1.f : 0.f,
310 w.sunDiskColor[0],
311 w.sunDiskColor[1],
312 w.sunDiskColor[2],
313 w.sunDiskShape[0],
314 w.sunDiskShape[1],
315 w.sunDiskShape[2],
316 w.sunDiskCurveEnabled ? 1.f : 0.f,
317 w.daylightEnabled ? 1.f : 0.f});
318 for (const auto& key : w.sunDiskCurve) cloudConstants.insert(cloudConstants.end(), key.begin(), key.end());
319 for (const auto* v : {&w.daylight.sun, &w.daylight.moon, &w.daylight.moonOrbit, &w.daylight.dayTint,
320 &w.daylight.duskTint, &w.daylight.nightTint, &w.daylight.glow, &w.daylight.controls,
321 &w.daylight.stars, &w.daylight.starUv, &w.daylight.twinkle})
322 cloudConstants.insert(cloudConstants.end(), v->begin(), v->end());
323 for (const auto& key : w.daylight.scatteringCurve)
324 cloudConstants.insert(cloudConstants.end(), key.begin(), key.end());
325 for (const auto* v : {&w.daylight.moonDiskColor, &w.daylight.moonDiskShape, &w.daylight.moonDiskLighting,
326 &w.daylight.moonDiskGlow})
327 cloudConstants.insert(cloudConstants.end(), v->begin(), v->end());
328 if (optical) {
329 auto updatedView =
330 graphics.replaceMeshShaderResources(*state.viewShader, sky_view_lut_vert, sky_view_lut_frag,
331 {images, std::as_bytes(std::span(cloudConstants)), {}});
332 if (!updatedView) return Prepared::failure(updatedView.status());
333 }
334 // Legacy programs never sample these bindings, but Vulkan requires valid descriptors.
335 const std::array<std::byte, 4> inactivePixel{};
336 ShaderImageInput inactive;
337 inactive.width = inactive.height = 1;
338 inactive.format = ShaderImageFormat::RGBA8;
339 inactive.bytes = inactivePixel;
340 std::array<ShaderImageInput, 7> cloudImages{
341 images[0],
342 images[1],
343 *w.densityTexture,
344 w.daylightEnabled ? *w.starsTexture : inactive,
345 w.daylightEnabled ? *w.starsNoiseTexture : inactive,
346 w.daylight.moonDiskColor[3] > 0 ? *w.moonColorTexture : inactive,
347 w.daylight.moonDiskColor[3] > 0 ? *w.moonNormalTexture : inactive};
348 cloudImages[3].binding = 3;
349 cloudImages[4].binding = 4;
350 cloudImages[5].binding = 5;
351 cloudImages[6].binding = 6;
352 state.daylightEnabled = w.daylightEnabled;
353 cloudImages[2].binding = 2;
354 if (cloudImages[2].dimension != ShaderImageDimension::Image2D)
355 return Prepared::failure(invalid("Wisps density texture must be two dimensional"));
356 state.wispsShader = graphics.newMeshShaderFromSpv({}, {});
357 if (!state.wispsShader) return Prepared::failure(invalid("Could not create wisps shader"));
358 auto upload =
359 graphics.replaceMeshShaderResources(*state.wispsShader, sky_wisps_vert, sky_wisps_frag,
360 {cloudImages, std::as_bytes(std::span(cloudConstants)), {}});
361 if (!upload) return Prepared::failure(upload.status());
362 auto cloudSurface = graphics.configureMeshShaderSurface(*state.wispsShader, BlendMode::Opaque, false, true);
363 if (!cloudSurface) return Prepared::failure(cloudSurface.status());
364 auto cloudRaster = graphics.configureMeshShaderRaster(*state.wispsShader, raster);
365 if (!cloudRaster) return Prepared::failure(cloudRaster.status());
366 const size_t count = w.corners.size() / 9;
367 std::vector<float> xyz(count * 3), attributes(count * 3), uv(count * 2);
368 std::vector<uint32_t> corners(count);
369 const float scale = w.domeScale * .01f;
370 for (size_t i = 0; i < count; ++i) {
371 xyz[i * 3] = w.corners[i * 9] * scale;
372 xyz[i * 3 + 1] = w.corners[i * 9 + 2] * scale;
373 xyz[i * 3 + 2] = w.corners[i * 9 + 1] * scale;
374 attributes[i * 3] = w.corners[i * 9 + 5];
375 uv[i * 2] = w.corners[i * 9 + 3];
376 uv[i * 2 + 1] = w.corners[i * 9 + 4];
377 corners[i] = static_cast<uint32_t>(i);
378 }
379 state.wispsMesh =
380 graphics.newMeshFromArrays(xyz.data(), attributes.data(), uv.data(), static_cast<int>(count),
381 corners.data(), static_cast<int>(count));
382 if (!state.wispsMesh) return Prepared::failure(invalid("Could not create wisps dome"));
383 state.moonForward = w.moonForward;
384 const double phase = std::fmod(double(w.cloudTime) * w.morphRate / w.morphPeriod, 1.0);
385 state.wispsMorphPhase = static_cast<float>(phase < 0 ? phase + 1.0 : phase);
386 if (state.wispsMorphPhase >= 1) state.wispsMorphPhase = 0;
387 }
388 return Prepared::success(std::move(pass));
389 } catch (const std::exception& error) {
390 return Prepared::failure(Diagnostic::error(DiagnosticCode::Failed, error.what(), "sky.atmosphere"));
391 }
392#endif
393}
394
396 return setFrame({light, impl_->wispsMorphPhase, impl_->dayPhase});
397}
399 if (impl_->lifetime.expired()) return Result<void>::failure(invalid("Sky graphics provider retired"));
400 if (!std::isfinite(frame.wispsMorphPhase) || frame.wispsMorphPhase < 0 || frame.wispsMorphPhase >= 1)
401 return Result<void>::failure(invalid("Wisps phase must be finite and normalized"));
402 if (!std::isfinite(frame.dayPhase) || frame.dayPhase < 0 || frame.dayPhase >= 1)
403 return Result<void>::failure(invalid("Day phase must be finite and normalized"));
404 const auto& light = frame.sun;
405 for (int axis = 0; axis < 3; ++axis)
406 if (!std::isfinite(light.direction[axis]) || !std::isfinite(light.irradiance[axis]) ||
407 light.irradiance[axis] < 0)
408 return Result<void>::failure(invalid("Sky light direction and energy must be finite; energy nonnegative"));
409 const float length = glm::length(light.direction);
410 if (!std::isfinite(length) || length < 1e-6f)
411 return Result<void>::failure(invalid("Sky light direction must be nonzero"));
412 impl_->light = light;
413 impl_->light.direction /= length;
414 impl_->wispsMorphPhase = frame.wispsMorphPhase;
415 impl_->dayPhase = frame.dayPhase;
416 return Result<void>::success();
417}
418
419Result<void> SkyAtmospherePass::prepareView(const glm::mat4& viewProjection, const glm::vec3& eyeMetres) {
420 if (impl_->lifetime.expired()) return Result<void>::failure(invalid("Sky graphics provider retired"));
421 auto frame = viewFrame(viewProjection, eyeMetres, impl_->light);
422 if (!frame) return Result<void>::failure(frame.status());
423 frame.value()[19] = impl_->dayPhase;
424 if (impl_->viewReady && impl_->preparedFrame == frame.value()) return Result<void>::success();
425 impl_->viewReady = false;
426 bool opened = false;
427 try {
428 impl_->viewShader->setPushConstantBlock(frame.value());
429 impl_->graphics->begin3DFrameToCanvas(impl_->viewCanvas);
430 opened = true;
431 impl_->graphics->drawMeshShader(impl_->mesh, glm::mat4(1), nullptr, Color(1, 1, 1, 1), impl_->viewShader);
432 impl_->graphics->end3DFrameToCanvas();
433 opened = false;
434 impl_->preparedFrame = frame.value();
435 impl_->viewReady = true;
436 return Result<void>::success();
437 } catch (const std::exception& error) {
438 if (opened) impl_->graphics->end3DFrameToCanvas();
440 }
441}
442
443Result<void> SkyAtmospherePass::draw(const glm::mat4& viewProjection, const glm::vec3& eyeMetres) {
444 return drawView(viewProjection, eyeMetres, false);
445}
446Result<void> SkyAtmospherePass::drawView(const glm::mat4& viewProjection, const glm::vec3& eyeMetres, bool reflection) {
447 if (impl_->lifetime.expired())
448 return Result<void>::failure(Diagnostic::error(DiagnosticCode::StaleHandle, "Sky graphics provider retired"));
449 auto frame = viewFrame(viewProjection, eyeMetres, impl_->light);
450 if (!frame) return Result<void>::failure(frame.status());
451 auto data = frame.value();
452 data[19] = impl_->dayPhase;
453 if (!impl_->viewReady || impl_->preparedFrame != data)
454 return Result<void>::failure(invalid("Sky view must be prepared before its destination pass opens"));
455 impl_->shader->setPushConstantBlock(data);
456 try {
457 impl_->graphics->drawMeshShader(impl_->mesh, glm::mat4(1), impl_->viewCanvas->getTexture(), Color(1, 1, 1, 1),
458 impl_->shader);
459 if (impl_->wispsMesh) {
460 std::copy_n(glm::value_ptr(viewProjection), 16, data.begin());
461 data[19] = impl_->daylightEnabled ? impl_->dayPhase : impl_->moonForward[0];
462 data[23] = impl_->moonForward[1];
463 data[27] = impl_->moonForward[2];
464 // Phase is nonnegative: reserve its sign bit for capture without losing mantissa precision.
465 data[31] =
466 std::bit_cast<float>(std::bit_cast<uint32_t>(impl_->wispsMorphPhase) | (reflection ? 0x80000000u : 0u));
467 impl_->wispsShader->setPushConstantBlock(data);
468 impl_->graphics->drawMeshShader(impl_->wispsMesh, glm::mat4(1), impl_->viewCanvas->getTexture(),
469 Color(1, 1, 1, 1), impl_->wispsShader);
470 }
471 } catch (const std::exception& error) {
472 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Failed, error.what(), "sky.atmosphere"));
473 }
474 return Result<void>::success();
475}
476
478 if (impl_->lifetime.expired()) return Result<void>::failure(invalid("Sky graphics provider retired"));
479 if (impl_->forward) return Result<void>::success();
480 auto render = [this](Graphics& graphics, const Camera3D::Data& camera, const glm::mat4& vp, float) {
481 if (impl_->lifetime.expired() || &graphics != impl_->graphics) return;
482 auto result = draw(vp, {camera.eyeX, camera.eyeY, camera.eyeZ});
483 if (!result) std::fprintf(stderr, "SkyAtmospherePass: %s\n", result.status().describe().c_str());
484 };
485 try {
486 auto preparation = addViewPreparation([this](Graphics& graphics, const glm::mat4& vp, const glm::vec3& eye) {
487 if (&graphics != impl_->graphics) return Result<void>::success();
488 return prepareView(vp, eye);
489 });
490 if (!preparation) return Result<void>::failure(preparation.status());
491 impl_->preparation = preparation.value();
492 impl_->forward = RenderSystem3D::addForwardExtraDrawer(render);
494 ~uint32_t(0),
495 [this](Graphics& graphics, const Camera3D::Data& camera, const glm::mat4& vp, float, uint32_t) {
496 if (impl_->lifetime.expired() || &graphics != impl_->graphics) return;
497 auto result = drawView(vp, {camera.eyeX, camera.eyeY, camera.eyeZ}, true);
498 if (!result) std::fprintf(stderr, "SkyAtmospherePass: %s\n", result.status().describe().c_str());
499 });
500 if (!impl_->forward || !impl_->capture) {
501 detach();
502 return Result<void>::failure(invalid("Sky contributor registration failed"));
503 }
504 return Result<void>::success();
505 } catch (const std::exception& error) {
506 detach();
507 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Failed, error.what(), "sky.atmosphere"));
508 }
509}
511 removeViewPreparation(impl_->preparation);
512 impl_->preparation = 0;
515 impl_->forward = impl_->capture = 0;
516}
517} // namespace eve::graphics
double value
float w
Definition AnimClip.cpp:738
float uv
float phase
Definition CaveMesh.cpp:58
float length
Definition CaveMesh.cpp:94
glm::vec4 p[6]
std::map< std::string, Var > values
std::string message
float camera[2]
std::uint32_t key
float u
Definition Grass.cpp:233
std::vector< std::uint32_t > indices
std::vector< float > positions
float v
std::uint32_t height
std::array< float, 3 > scale
bool valid
MeshVfxBatchedDraw draw
bool finite
std::string error
Definition Package.cpp:60
TileLayer * layer
float begin
glm::vec3 eye
Light3D::Data * light
const std::vector< uint32_t > sky_atmosphere_frag
const std::vector< uint32_t > sky_atmosphere_vert
const std::vector< uint32_t > sky_view_lut_frag
const std::vector< uint32_t > sky_view_lut_vert
const std::vector< uint32_t > sky_wisps_frag
const std::vector< uint32_t > sky_wisps_vert
Definition SkyWispsSpv.h:5
std::uint32_t count
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
Canvas public API.
Definition Canvas.h:17
virtual Shader * newMeshShaderFromSpv(const std::vector< uint32_t > &vertSpv, const std::vector< uint32_t > &fragSpv)=0
Create a Mesh3D custom shader (MeshVertex + Frame UBO + albedo). Empty vert → default mesh3d....
std::weak_ptr< const void > resourceLifetime() const
Observe this provider's resource lifetime without extending it.
virtual Result< void > replaceMeshShaderResources(Shader &shader, const std::vector< uint32_t > &vertSpv, const std::vector< uint32_t > &fragSpv, const ShaderResourceInputs &resources)
Replace a mesh program and its immutable set-1 resources atomically.
virtual eve::Result< void > configureMeshShaderRaster(Shader &shader, const MeshShaderRasterState &state)
Transactionally configure an owned ordinary mesh shader's raster state.
Definition Graphics.h:1847
virtual eve::Result< void > configureMeshShaderSurface(Shader &, BlendMode, bool, bool)
Prepare a custom mesh shader's blend, depth and culling state.
Definition Graphics.h:1830
virtual Mesh * newMeshFromArrays(const float *posXYZ, const float *nrmXYZ, const float *uvST, int vertexCount, const uint32_t *indices, int indexCount)=0
Upload a triangle mesh from packed CPU arrays. Owned by Graphics. posXYZ required (vertexCount*3)....
GPU mesh handle (+ optional CPU morph targets).
Definition Mesh.h:25
static void removeForwardExtraDrawer(uint64_t token)
Unregister a main forward-pass contributor.
static void removeCaptureExtraDrawer(uint64_t token)
Unregister an offscreen capture contributor.
static uint64_t addCaptureExtraDrawer(uint32_t reflectionCaptureMask, CaptureExtraDrawer drawer)
Register custom forward geometry for offscreen captures.
static uint64_t addForwardExtraDrawer(ForwardExtraDrawer drawer)
Register a main forward-pass contributor.
Custom GPU program.
Definition Shader.h:39
Prepared HDR atmospheric sky draw with independent optical parameters and lookup tables.
Result< void > prepareView(const glm::mat4 &viewProjection, const glm::vec3 &eyeMetres)
Prepare the cached sky-view texture before opening a destination pass.
Result< void > setLight(const SkyCelestialLight &light)
Atomically set explicit light direction and RGB irradiance; rejects nonfinite or negative energy.
Result< void > setFrame(const SkyAtmosphereFrame &frame)
Validate and atomically publish solar lighting, normalized cloud phase and solar-day fraction....
static Result< std::unique_ptr< SkyAtmospherePass > > prepare(Graphics &graphics, const SkyAtmosphereParameters &parameters, const SkyWispsLayer *wisps=nullptr)
Prepare an independent atmospheric program and immutable coefficient buffer.
Result< void > draw(const glm::mat4 &viewProjection, const glm::vec3 &eyeMetres)
Draw a prepared view into an already-open HDR 3D pass; sky writes only far-depth background pixels.
void detach() noexcept
Cancel both registrations; does not mutate legacy DayNight state.
Result< void > attach()
Register the prepared pass for main view and reflection captures, idempotently.
std::vector< double > forward(const Policy &p, const Observation &o)
Forward.
Definition Learning.h:65
eve::Diagnostic Diagnostic
EVENGINE_API_BACKENDS Result< SkyAtmosphereLuts > bakeSkyAtmosphereLuts(const SkyAtmosphereParameters &parameters)
Bake coefficients using UE's default 10/15-sample, two-direction approximation.
constexpr float OpticalMinimum
constexpr unsigned OpticalLayers
SkyAtmosphereParameters opticalSample(SkyAtmosphereParameters p, const SkyDaylightLayer &d, float sunY)
Pure clear/manual optical projection. Inputs are validated authored values and sun Y in [-1,...
constexpr float OpticalMaximum
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
Definition Animation.h:25
eve::Color Color
RGBA color used by every graphics draw call. Lives inside eve::graphics so including a graphics heade...
Definition Color.h:13
void removeViewPreparation(uint64_t token) noexcept
Remove a registration; missing/zero tokens are harmless. @thread Graphics thread outside dispatch....
Result< uint64_t > addViewPreparation(ViewPreparation callback)
Register a preparer for main and offscreen views; empty callbacks fail. @ownership Registry owns the ...
Result< int > invalid(std::string message)
Invalid.
int axis(int64_t a, size_t rank)
Axis.
Value snapshot of custom mesh rasterization state, independent of blending/culling.
One atomic render snapshot, independent of view count or wall-clock time.
float dayPhase
Injected solar-day fraction in [0,1); zero is midnight, independent of render count.
Spherical atmospheric coefficients in inverse kilometres and kilometre heights.
std::array< float, Shader::kMaxFloats > preparedFrame
Explicit celestial lighting snapshot; unit direction points towards the light.
Immutable clear-sky daylight material controls, copied during sky preparation.
Borrowed authored thin-cloud layer, consumed synchronously by sky preparation.
bool opticalCycleEnabled
Opt-in source optical cycle, prepared as an immutable height-sampled LUT volume.
bool daylightEnabled
Opt-in daylight chain. Disabled for legacy manifests; no independent time source.
static TextureSampler linear()
Linear.