载入中...
搜索中...
未找到
VegetationFieldGpu.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <bit>
5#include <cmath>
6#include <limits>
7#include <new>
8#include <vector>
10#include "graphics/PbrSurface.h"
12
13namespace eve::graphics {
14namespace {
15constexpr uint32_t layerCount = 9;
16constexpr uint64_t uploadByteBudget = 256ull * 1024 * 1024;
17
18Diagnostic invalid(std::string message) {
20 "graphics.vegetation.extras-atlas");
21}
22
23uint16_t floatToHalf(float value) {
24 const uint32_t bits = std::bit_cast<uint32_t>(value);
25 const uint32_t sign = (bits >> 16) & 0x8000u;
26 const uint32_t mantissa = bits & 0x7fffffu;
27 const int32_t exponent = int32_t((bits >> 23) & 0xffu) - 127;
28 if (exponent < -24) return uint16_t(sign);
29 if (exponent < -14) {
30 const uint32_t shift = uint32_t(-exponent - 1);
31 uint32_t halfMantissa = (mantissa | 0x800000u) >> shift;
32 const uint32_t remainder = (mantissa | 0x800000u) & ((1u << shift) - 1u);
33 const uint32_t halfway = 1u << (shift - 1u);
34 if (remainder > halfway || (remainder == halfway && (halfMantissa & 1u))) ++halfMantissa;
35 return uint16_t(sign | halfMantissa);
36 }
37 uint32_t halfExponent = uint32_t(exponent + 15);
38 uint32_t halfMantissa = mantissa >> 13;
39 const uint32_t remainder = mantissa & 0x1fffu;
40 if (remainder > 0x1000u || (remainder == 0x1000u && (halfMantissa & 1u))) {
41 if (++halfMantissa == 0x400u) {
42 halfMantissa = 0;
43 ++halfExponent;
44 }
45 }
46 return uint16_t(sign | (halfExponent << 10) | halfMantissa);
47}
48
49bool finite(glm::vec3 value) { return std::isfinite(value.x) && std::isfinite(value.y) && std::isfinite(value.z); }
50} // namespace
51
53 VegetationChannel channel, glm::vec3 center, glm::vec3 extent, uint32_t width,
54 uint32_t height, bool snapToTexel) {
55 const uint64_t texels = uint64_t(width) * height * layerCount;
56 if (!finite(center) || !finite(extent) || extent.x < 1e-4f || extent.y < 1e-4f || extent.z < 1e-4f || !width ||
57 !height || width > 2048 || height > 2048 || texels > uploadByteBudget / (4 * sizeof(uint16_t)))
58 return Result<VegetationGpuAtlas>::failure(invalid("invalid atlas geometry or upload budget exceeded"));
59
60 const double dx = 2.0 * extent.x / width;
61 const double dz = 2.0 * extent.z / height;
62 if (snapToTexel) {
63 center.x = float(std::round(double(center.x) / dx) * dx);
64 center.z = float(std::round(double(center.z) / dz) * dz);
65 }
66 if (!finite(center) || !finite(center + extent) || !finite(center - extent))
67 return Result<VegetationGpuAtlas>::failure(invalid("atlas bounds overflow"));
68
69 const uint64_t sourceRevision = field.revision();
70 try {
71 std::vector<uint16_t> pixels(size_t(texels) * 4);
72 for (uint32_t layer = 0; layer < layerCount; ++layer) {
73 for (uint32_t y = 0; y < height; ++y) {
74 for (uint32_t x = 0; x < width; ++x) {
75 const glm::vec3 position(float(double(center.x) - extent.x + (x + 0.5) * dx), center.y,
76 float(double(center.z) - extent.z + (y + 0.5) * dz));
77 std::array<uint8_t, 4> selected{};
78 selected[size_t(channel)] = uint8_t(layer);
79 auto sample = field.sample(position, selected);
80 if (!sample.ok()) return Result<VegetationGpuAtlas>::failure(sample.status());
81 const auto& sampled = sample.value();
82 const glm::vec4 value = channel == VegetationChannel::Color ? sampled.color
83 : channel == VegetationChannel::Extras ? sampled.extras
84 : channel == VegetationChannel::Motion ? sampled.motion
85 : sampled.vertex;
86 if (std::abs(value.x) > 65504.f || std::abs(value.y) > 65504.f || std::abs(value.z) > 65504.f ||
87 std::abs(value.w) > 65504.f)
89 invalid("vegetation value exceeds finite RGBA16F range"));
90 const size_t offset = ((size_t(layer) * height + y) * width + x) * 4;
91 pixels[offset + 0] = floatToHalf(value.x);
92 pixels[offset + 1] = floatToHalf(value.y);
93 pixels[offset + 2] = floatToHalf(value.z);
94 pixels[offset + 3] = floatToHalf(value.w);
95 }
96 }
97 }
98 if (field.revision() != sourceRevision)
99 return Result<VegetationGpuAtlas>::failure(invalid("field changed while baking vegetation atlas"));
100 auto uploaded = factory.newTextureArrayRgba16f(width, height, layerCount, pixels);
101 if (!uploaded.ok()) return Result<VegetationGpuAtlas>::failure(uploaded.status());
103 {uploaded.value(), sourceRevision, width, height, layerCount, center, extent});
104 } catch (const std::bad_alloc&) {
106 DiagnosticCode::Failed, "vegetation atlas allocation failed", {}, {}, "graphics.vegetation.atlas"));
107 }
108}
109
111 glm::vec3 center, glm::vec3 extent, uint32_t width,
112 uint32_t height, bool snapToTexel) {
113 return uploadAtlas(factory, field, VegetationChannel::Extras, center, extent, width, height, snapToTexel);
114}
115
117 glm::vec3 center, glm::vec3 extent, uint32_t width,
118 uint32_t height, bool snapToTexel) {
119 return uploadAtlas(factory, field, VegetationChannel::Color, center, extent, width, height, snapToTexel);
120}
121
123 glm::vec3 center, glm::vec3 extent, uint32_t width,
124 uint32_t height, bool snapToTexel) {
125 return uploadAtlas(factory, field, VegetationChannel::Motion, center, extent, width, height, snapToTexel);
126}
127
129 glm::vec3 center, glm::vec3 extent, uint32_t width,
130 uint32_t height, bool snapToTexel) {
131 return uploadAtlas(factory, field, VegetationChannel::Vertex, center, extent, width, height, snapToTexel);
132}
133
135 bool failed = false;
136 for (auto* atlas : {&set.extras, &set.colors, &set.motion, &set.vertex}) {
137 if (!atlas->texture) continue;
138 if (factory.releaseTexture(atlas->texture))
139 atlas->texture = nullptr;
140 else
141 failed = true;
142 }
143 if (failed)
145 "vegetation GPU field texture release failed", {}, {},
146 "graphics.vegetation.atlas"));
147 return Result<void>::success();
148}
149
151 glm::vec3 center, glm::vec3 extent, uint32_t width,
152 uint32_t height, bool snapToTexel) {
153 VegetationGpuFieldSet candidate;
154 auto fail = [&](const Status& status) {
155 auto released = releaseVegetationGpuFieldSet(factory, candidate);
156 if (!released)
158 Diagnostic::error(DiagnosticCode::Failed, "vegetation GPU field upload rollback failed", {}, {},
159 "graphics.vegetation.atlas"));
161 };
162 auto extras = uploadVegetationExtrasAtlas(factory, field, center, extent, width, height, snapToTexel);
163 if (!extras) return Result<VegetationGpuFieldSet>::failure(extras.status());
164 candidate.extras = std::move(extras).takeValue();
165 auto colors = uploadVegetationColorsAtlas(factory, field, candidate.extras.center, extent, width, height, false);
166 if (!colors) return fail(colors.status());
167 candidate.colors = std::move(colors).takeValue();
168 auto motion = uploadVegetationMotionAtlas(factory, field, candidate.extras.center, extent, width, height, false);
169 if (!motion) return fail(motion.status());
170 candidate.motion = std::move(motion).takeValue();
171 auto vertex = uploadVegetationVertexAtlas(factory, field, candidate.extras.center, extent, width, height, false);
172 if (!vertex) return fail(vertex.status());
173 candidate.vertex = std::move(vertex).takeValue();
174 if (candidate.extras.fieldRevision != field.revision()) {
175 auto released = releaseVegetationGpuFieldSet(factory, candidate);
176 if (!released)
178 Diagnostic::error(DiagnosticCode::Failed, "vegetation GPU field upload rollback failed", {}, {},
179 "graphics.vegetation.atlas"));
180 return Result<VegetationGpuFieldSet>::failure(invalid("field changed during set upload"));
181 }
182 return Result<VegetationGpuFieldSet>::success(std::move(candidate));
183}
184
185namespace {
186Result<VegetationGpuAtlas> uploadBakedChannel(IResourceFactory& factory,
187 std::span<const VegetationChannelAtlas, layerCount> layers,
188 std::uint64_t sourceRevision) {
189 const auto& geometry = layers.front();
190 const std::uint64_t texels = std::uint64_t(geometry.width) * geometry.height * layerCount;
191 try {
192 std::vector<std::uint16_t> pixels(std::size_t(texels) * 4);
193 for (std::size_t layer = 0; layer < layerCount; ++layer) {
194 const auto& values = layers[layer].pixels;
195 for (std::size_t index = 0; index < values.size(); ++index) {
196 const auto value = values[index];
197 if (!std::isfinite(value.x) || !std::isfinite(value.y) || !std::isfinite(value.z) ||
198 !std::isfinite(value.w) || std::abs(value.x) > 65504.f || std::abs(value.y) > 65504.f ||
199 std::abs(value.z) > 65504.f || std::abs(value.w) > 65504.f)
200 return Result<VegetationGpuAtlas>::failure(invalid("pre-baked vegetation value exceeds RGBA16F range"));
201 const std::size_t offset = (layer * values.size() + index) * 4;
202 pixels[offset] = floatToHalf(value.x);
203 pixels[offset + 1] = floatToHalf(value.y);
204 pixels[offset + 2] = floatToHalf(value.z);
205 pixels[offset + 3] = floatToHalf(value.w);
206 }
207 }
208 auto uploaded = factory.newTextureArrayRgba16f(geometry.width, geometry.height, layerCount, pixels);
209 if (!uploaded) return Result<VegetationGpuAtlas>::failure(uploaded.status());
210 return Result<VegetationGpuAtlas>::success({uploaded.value(), sourceRevision, geometry.width, geometry.height,
211 layerCount, geometry.center, geometry.extent});
212 } catch (const std::bad_alloc&) {
214 Diagnostic::error(DiagnosticCode::Failed, "pre-baked vegetation atlas allocation failed", {}, {},
215 "graphics.vegetation.atlas"));
216 }
217}
218
219Result<void> validateBakedChannel(std::span<const VegetationChannelAtlas, layerCount> layers) {
220 const auto& first = layers.front();
221 const std::uint64_t pixels = std::uint64_t(first.width) * first.height;
222 if (first.width == 0 || first.height == 0 || first.width > 2048 || first.height > 2048 ||
223 !finite(first.center) || !finite(first.extent) || first.extent.x < 1e-4f || first.extent.y < 1e-4f ||
224 first.extent.z < 1e-4f)
225 return Result<void>::failure(invalid("pre-baked vegetation channel geometry is invalid"));
226 for (const auto& layer : layers) {
227 if (layer.width != first.width || layer.height != first.height || layer.center != first.center ||
228 layer.extent != first.extent || layer.pixels.size() != pixels)
229 return Result<void>::failure(invalid("pre-baked vegetation channel layers are incoherent"));
230 }
231 return Result<void>::success();
232}
233} // namespace
234
236 IResourceFactory& factory, std::span<const VegetationAtlas, 9> layers, std::uint64_t sourceRevision) {
237 const auto& first = layers.front();
238 const std::uint64_t pixels = std::uint64_t(first.width) * first.height;
239 if (sourceRevision == 0 || first.width == 0 || first.height == 0 || first.width > 2048 || first.height > 2048 ||
240 !finite(first.center) || !finite(first.extent) || first.extent.x < 1e-4f || first.extent.y < 1e-4f ||
241 first.extent.z < 1e-4f || pixels * layerCount > uploadByteBudget / (4 * sizeof(std::uint16_t)))
242 return Result<VegetationGpuFieldSet>::failure(invalid("pre-baked vegetation atlas geometry is invalid"));
243 for (const auto& layer : layers) {
244 if (layer.width != first.width || layer.height != first.height || layer.center != first.center ||
245 layer.extent != first.extent || std::any_of(layer.channels.begin(), layer.channels.end(),
246 [&](const auto& values) { return values.size() != pixels; }))
247 return Result<VegetationGpuFieldSet>::failure(invalid("pre-baked vegetation atlas layers are incoherent"));
248 }
249 try {
250 std::array<VegetationChannelAtlas, layerCount> colors, extras, motion, vertex;
251 for (std::size_t layer = 0; layer < layerCount; ++layer) {
252 auto copy = [&](VegetationChannelAtlas& destination, std::size_t channel) {
253 destination.width = layers[layer].width;
254 destination.height = layers[layer].height;
255 destination.center = layers[layer].center;
256 destination.extent = layers[layer].extent;
257 destination.pixels = layers[layer].channels[channel];
258 };
259 copy(colors[layer], 0);
260 copy(extras[layer], 1);
261 copy(motion[layer], 2);
262 copy(vertex[layer], 3);
263 }
264 return uploadVegetationGpuFieldSet(factory, colors, extras, motion, vertex, sourceRevision);
265 } catch (const std::bad_alloc&) {
267 Diagnostic::error(DiagnosticCode::Failed, "pre-baked vegetation channel projection allocation failed",
268 {}, {}, "graphics.vegetation.atlas"));
269 }
270}
271
273 IResourceFactory& factory, std::span<const VegetationChannelAtlas, 9> colors,
274 std::span<const VegetationChannelAtlas, 9> extras, std::span<const VegetationChannelAtlas, 9> motion,
275 std::span<const VegetationChannelAtlas, 9> vertex, std::uint64_t sourceRevision) {
276 if (sourceRevision == 0)
277 return Result<VegetationGpuFieldSet>::failure(invalid("pre-baked vegetation revision must be nonzero"));
278 for (const auto channel : {colors, extras, motion, vertex}) {
279 auto valid = validateBakedChannel(channel);
281 }
282 std::uint64_t combinedTexels = 0;
283 for (const auto channel : {colors, extras, motion, vertex}) {
284 const auto& first = channel.front();
285 const std::uint64_t channelTexels = std::uint64_t(first.width) * first.height * layerCount;
286 if (channelTexels > std::numeric_limits<std::uint64_t>::max() - combinedTexels)
287 return Result<VegetationGpuFieldSet>::failure(invalid("pre-baked vegetation upload size overflow"));
288 combinedTexels += channelTexels;
289 }
290 if (combinedTexels > uploadByteBudget / (4 * sizeof(std::uint16_t)))
291 return Result<VegetationGpuFieldSet>::failure(invalid("pre-baked vegetation upload budget exceeded"));
292
293 VegetationGpuFieldSet candidate;
294 auto rollback = [&](const Status& status) {
295 auto released = releaseVegetationGpuFieldSet(factory, candidate);
296 if (!released)
298 Diagnostic::error(DiagnosticCode::Failed, "pre-baked vegetation GPU upload rollback failed", {}, {},
299 "graphics.vegetation.atlas"));
301 };
302 auto extrasUpload = uploadBakedChannel(factory, extras, sourceRevision);
303 if (!extrasUpload) return Result<VegetationGpuFieldSet>::failure(extrasUpload.status());
304 candidate.extras = std::move(extrasUpload).takeValue();
305 auto colorsUpload = uploadBakedChannel(factory, colors, sourceRevision);
306 if (!colorsUpload) return rollback(colorsUpload.status());
307 candidate.colors = std::move(colorsUpload).takeValue();
308 auto motionUpload = uploadBakedChannel(factory, motion, sourceRevision);
309 if (!motionUpload) return rollback(motionUpload.status());
310 candidate.motion = std::move(motionUpload).takeValue();
311 auto vertexUpload = uploadBakedChannel(factory, vertex, sourceRevision);
312 if (!vertexUpload) return rollback(vertexUpload.status());
313 candidate.vertex = std::move(vertexUpload).takeValue();
314 return Result<VegetationGpuFieldSet>::success(std::move(candidate));
315}
316
317namespace {
318Result<void> bindVegetationGpuFieldsAtRevision(PbrSurface& surface, std::uint64_t expectedRevision,
320 const VegetationMotionGpuAtlas& motion, const VegetationVertexGpuAtlas& vertex,
321 const VegetationGpuRuntime& runtime) {
322 const auto validAtlas = [&](const VegetationGpuAtlas& atlas) {
323 return atlas.texture && atlas.fieldRevision == extras.fieldRevision && atlas.width && atlas.height &&
324 atlas.width <= 2048 && atlas.height <= 2048 && atlas.layers == layerCount && finite(atlas.center) &&
325 finite(atlas.extent) && atlas.extent.x >= 1e-4f && atlas.extent.y >= 1e-4f && atlas.extent.z >= 1e-4f;
326 };
327 if (!validAtlas(extras) || !validAtlas(colors) || !validAtlas(motion) || !validAtlas(vertex) ||
328 extras.fieldRevision != expectedRevision || extras.layers != layerCount ||
329 (runtime.enableMotion && !runtime.motionNoise) ||
330 !finite(glm::vec3(runtime.motionDirection[0], 0, runtime.motionDirection[1])) ||
331 !finite(glm::vec3(runtime.worldOrigin[0], runtime.worldOrigin[1], runtime.worldOrigin[2])) ||
332 !std::isfinite(runtime.time) || std::abs(runtime.time) > 1e12 || !std::isfinite(runtime.globalBending) ||
333 !std::isfinite(runtime.globalBranch) || !std::isfinite(runtime.globalFlutter) ||
334 !std::isfinite(runtime.noiseTiling) || !std::isfinite(runtime.motionFadeDistance) ||
335 !std::isfinite(runtime.cameraFadeMin) || !std::isfinite(runtime.cameraFadeMax) ||
336 !std::isfinite(runtime.fadeNoiseTiling) || runtime.globalBending < 0 || runtime.globalBending > 1000000 ||
337 runtime.globalBranch < 0 || runtime.globalBranch > 1000000 || runtime.globalFlutter < 0 ||
338 runtime.globalFlutter > 1000000 || runtime.noiseTiling < 0 || runtime.noiseTiling > 1000000 ||
339 runtime.motionFadeDistance < 0 || runtime.motionFadeDistance > 1000000 || runtime.cameraFadeMin < 0 ||
340 runtime.cameraFadeMin > 1000000 || runtime.cameraFadeMax < 0 || runtime.cameraFadeMax > 1000000 ||
341 runtime.fadeNoiseTiling < 0 || runtime.fadeNoiseTiling > 1000000)
342 return Result<void>::failure(invalid("incoherent, stale or invalid vegetation GPU field binding"));
343
344 const auto coords = [](const VegetationGpuAtlas& atlas) {
345 return std::array<float, 4>{1.f / (2.f * atlas.extent.x), 1.f / (2.f * atlas.extent.z),
346 .5f - atlas.center.x / (2.f * atlas.extent.x),
347 .5f - atlas.center.z / (2.f * atlas.extent.z)};
348 };
349 PbrSurface candidate = surface;
350 candidate.vegetationExtras.texture = extras.texture;
351 candidate.vegetationExtras.coords = coords(extras);
352 candidate.vegetationExtras.usage.fill(1);
353 candidate.vegetationColors.texture = colors.texture;
354 candidate.vegetationColors.coords = coords(colors);
355 candidate.vegetationColors.usage.fill(1);
356 candidate.vegetationMotion.texture = motion.texture;
357 candidate.vegetationMotion.noise = runtime.motionNoise;
358 candidate.vegetationMotion.coords = coords(motion);
359 candidate.vegetationMotion.globalDirection = runtime.motionDirection;
360 candidate.vegetationMotion.worldOrigin = runtime.worldOrigin;
361 candidate.vegetationMotion.time = runtime.time;
362 candidate.vegetationMotion.globalBending = runtime.globalBending;
363 candidate.vegetationMotion.globalBranch = runtime.globalBranch;
364 candidate.vegetationMotion.globalFlutter = runtime.globalFlutter;
365 candidate.vegetationMotion.noiseTiling = runtime.noiseTiling;
366 candidate.vegetationMotion.fadeDistance = runtime.motionFadeDistance;
367 candidate.vegetationMotion.mode =
369 candidate.vegetationMotion.usage.fill(1);
370 candidate.vegetationAlpha.noise = runtime.fadeNoise;
371 candidate.vegetationAlpha.cameraFadeMin = runtime.cameraFadeMin;
372 candidate.vegetationAlpha.cameraFadeMax = runtime.cameraFadeMax;
373 candidate.vegetationAlpha.noiseTiling = runtime.fadeNoiseTiling;
374 candidate.vegetationVertex.texture = vertex.texture;
375 candidate.vegetationVertex.coords = coords(vertex);
376 candidate.vegetationVertex.source = PbrVegetationDeformationSource::GpuFields;
377 candidate.vegetationVertex.usage.fill(1);
378 auto valid = validatePbrSurface(candidate);
379 if (!valid) return valid;
380 surface = std::move(candidate);
381 return Result<void>::success();
382}
383} // namespace
384
387 const VegetationMotionGpuAtlas& motion, const VegetationVertexGpuAtlas& vertex,
388 const VegetationGpuRuntime& runtime) {
389 return bindVegetationGpuFieldsAtRevision(surface, field.revision(), extras, colors, motion, vertex, runtime);
390}
391
393 const VegetationGpuFieldSet& set, const VegetationGpuRuntime& runtime) {
394 return bindVegetationGpuFields(surface, field, set.extras, set.colors, set.motion, set.vertex, runtime);
395}
396
398 std::uint64_t expectedRevision, const VegetationGpuRuntime& runtime) {
399 return bindVegetationGpuFieldsAtRevision(surface, expectedRevision, set.extras, set.colors, set.motion, set.vertex,
400 runtime);
401}
402
403} // namespace eve::graphics
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
std::map< std::string, Var > values
std::string message
wgpu::PopErrorScopeStatus status
std::int32_t first
std::uint32_t height
std::uint32_t width
size_t offset
std::array< float, 3 > position
bool valid
bool finite
TileLayer * layer
uint8_t * pixels
std::vector< float > colors
float dz
float dx
uint32_t index
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
Structured status and zero or more diagnostics for an operation.
Definition Status.h:68
Texture / mesh / shader / canvas creation and release.
virtual Result< Texture * > newTextureArrayRgba16f(uint32_t width, uint32_t height, uint32_t layers, std::span< const uint16_t > rgbaHalf)
Upload a sampled 2D array of tightly packed linear RGBA16F texels.
virtual bool releaseTexture(Texture *texture)=0
Release texture.
Backend-independent, owning vegetation influence field. Single writer; concurrent const evaluation is...
eve::Diagnostic Diagnostic
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
Definition Animation.h:25
VegetationGpuAtlas VegetationColorsGpuAtlas
Result< void > releaseVegetationGpuFieldSet(IResourceFactory &factory, VegetationGpuFieldSet &set)
Release every texture still present in a detached field set. Successful entries are nulled....
VegetationGpuAtlas VegetationVertexGpuAtlas
VegetationGpuAtlas VegetationMotionGpuAtlas
VegetationChannel
Independent vegetation field channels; values use linear color and world units.
Result< VegetationGpuFieldSet > uploadVegetationGpuFieldSet(IResourceFactory &factory, const VegetationField &field, glm::vec3 center, glm::vec3 extent, uint32_t width, uint32_t height, bool snapToTexel)
Bake and upload all four nine-layer projections as one publication. Earlier uploads are released if a...
Result< VegetationGpuAtlas > uploadAtlas(IResourceFactory &factory, const VegetationField &field, VegetationChannel channel, glm::vec3 center, glm::vec3 extent, uint32_t width, uint32_t height, bool snapToTexel)
Result< void > validatePbrSurface(const PbrSurface &s)
Validate finite material factors and sampler enums without changing the input.
Definition PbrSurface.cpp:4
Result< void > bindVegetationGpuFields(PbrSurface &surface, const VegetationField &field, const VegetationExtrasGpuAtlas &extras, const VegetationColorsGpuAtlas &colors, const VegetationMotionGpuAtlas &motion, const VegetationVertexGpuAtlas &vertex, const VegetationGpuRuntime &runtime)
Atomically bind four coherent GPU projections to an owning PBR parameter snapshot.
Result< VegetationExtrasGpuAtlas > uploadVegetationExtrasAtlas(IResourceFactory &factory, const VegetationField &field, glm::vec3 center, glm::vec3 extent, uint32_t width, uint32_t height, bool snapToTexel)
Bake and atomically upload all nine Extras layers as a linear RGBA16F 2D array.
Result< VegetationVertexGpuAtlas > uploadVegetationVertexAtlas(IResourceFactory &factory, const VegetationField &field, glm::vec3 center, glm::vec3 extent, uint32_t width, uint32_t height, bool snapToTexel)
Bake and atomically upload all nine Vertex layers as a linear RGBA16F 2D array.
VegetationGpuAtlas VegetationExtrasGpuAtlas
Result< VegetationMotionGpuAtlas > uploadVegetationMotionAtlas(IResourceFactory &factory, const VegetationField &field, glm::vec3 center, glm::vec3 extent, uint32_t width, uint32_t height, bool snapToTexel)
Bake and atomically upload all nine Motion layers as a linear RGBA16F 2D array.
Result< VegetationColorsGpuAtlas > uploadVegetationColorsAtlas(IResourceFactory &factory, const VegetationField &field, glm::vec3 center, glm::vec3 extent, uint32_t width, uint32_t height, bool snapToTexel)
Bake and atomically upload all nine Colors layers as a linear RGBA16F 2D array. Ownership,...
Result< int > invalid(std::string message)
Invalid.
Owning material parameter snapshot; texture pointers remain borrowed. Base color/metallic/roughness c...
Definition PbrSurface.h:218
One owning vegetation channel in row-major X/Z order at texel centers. Channel atlases may use indepe...
Immutable GPU projection of nine layers from one vegetation field channel. The source field remains a...
Coherent detached projections for all four vegetation channels. Textures are factory-owned....
Runtime-owned global inputs shared by one coherent vegetation field binding. Time is explicit for det...