5#include "graphics/shaders/pbr_surface_frag_spv.inc"
6#include "graphics/shaders/pbr_surface_vert_spv.inc"
12using detail::PbrUniformGpu;
13vk::SamplerAddressMode addressMode(uint32_t
value) {
14 return value == 33071 ? vk::SamplerAddressMode::eClampToEdge
15 :
value == 33648 ? vk::SamplerAddressMode::eMirroredRepeat
16 : vk::SamplerAddressMode::eRepeat;
18vk::Pipeline createPbrPipeline(vkb::Device&
device, vk::PipelineLayout
layout,
const vkb::BuiltRenderPass& pass,
20 bool alphaToCoverage) {
21 ShaderModulePair
shaders(
device, embeddedSpirv(pbr_surface_vert_spv), embeddedSpirv(pbr_surface_frag_spv));
22 auto input = vkb::VertexInputStateBuilder();
23 input.addInputBinding<MeshVertex>();
28 input.input_attributes.emplace_back(0, 0, vk::Format::eR32G32B32Sfloat,
29 uint32_t(offsetof(MeshVertex,
pos)));
30 input.input_attributes.emplace_back(1, 0, vk::Format::eR32G32B32Sfloat,
31 uint32_t(offsetof(MeshVertex,
normal)));
32 input.input_attributes.emplace_back(2, 0, vk::Format::eR32G32Sfloat,
33 uint32_t(offsetof(MeshVertex,
uv)));
34 input.input_attributes.emplace_back(3, 0, vk::Format::eR16G16B16A16Uint,
35 uint32_t(offsetof(MeshVertex, joints)));
36 input.input_attributes.emplace_back(4, 0, vk::Format::eR32G32B32A32Sfloat,
37 uint32_t(offsetof(MeshVertex,
weights)));
40 for (uint32_t i = 0; i < 11; ++i) {
41 input.input_bindings.emplace_back(i + 1, uint32_t(2 *
sizeof(
float)), vk::VertexInputRate::eVertex);
42 input.input_attributes.emplace_back(i + 5, i + 1, vk::Format::eR32G32Sfloat, 0);
44 auto attachment = makeBlendAttachment(
blend);
46 vk::PipelineColorBlendStateCreateInfo
color{};
47 color.attachmentCount = 1;
48 color.pAttachments = &attachment;
49 vk::PipelineMultisampleStateCreateInfo multisampling{};
50 multisampling.rasterizationSamples = samples;
51 multisampling.alphaToCoverageEnable = alphaToCoverage && samples != vk::SampleCountFlagBits::e1;
52 return device.createPipeline()
55 .setVertexInputState(
input)
56 .setDynamicStatesViewportScissor()
57 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f,
60 : vk::CullModeFlagBits::eBack),
61 vk::FrontFace::eCounterClockwise)
62 .setMultisampler(multisampling)
63 .setDepthStencil(true, depthWrite, vk::
CompareOp::eLess)
64 .setColorBlending(
color)
72 vk::DescriptorSet
set{};
77 std::map<std::tuple<VkRenderPass, uint32_t, size_t>, vk::Pipeline>
pipelines;
78 std::map<std::array<uint32_t, 4>, vk::Sampler>
samplers;
79 std::vector<std::vector<std::unique_ptr<Draw>>>
frames;
82 for (
auto&
draw : frame)
91void Graphics::destroyPbrResources() { pbrResources_.reset(); }
98 if (!result)
return result;
125 const auto requireKind = [](
Texture* texture,
const char* role,
bool array,
bool volume,
129 if (!gpu || gpu->isCube || gpu->isVolume !=
volume || (!
volume && gpu->isArray != array) ||
130 (array && std::uint32_t(texture->
layers) < minimumLayers))
136 auto ready = requireKind(
binding.texture,
"PBR texture",
false,
false);
137 if (!ready)
return ready;
140 auto ready = requireKind(
binding.texture,
"PBR detail texture",
false,
false);
141 if (!ready)
return ready;
143 for (
auto [texture, role,
layer] :
144 std::array<std::tuple<Texture*, const char*, std::uint32_t>, 4>{
149 auto ready = requireKind(texture, role,
true,
false,
layer + 1u);
150 if (!ready)
return ready;
153 if (!ready)
return ready;
155 if (!ready)
return ready;
156 pbrSurface_ = *surface;
160 if (!pbrResources_) {
161 pbrResources_.reset(
new PbrResources());
162 auto&
r = *pbrResources_;
164 const auto both = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment;
165 const auto fragment = vk::ShaderStageFlagBits::eFragment;
166 const auto vertex = vk::ShaderStageFlagBits::eVertex;
167 std::array<vk::DescriptorSetLayoutBinding, 14>
bindings{
168 {{0, vk::DescriptorType::eUniformBuffer, 1, both},
169 {1, vk::DescriptorType::eCombinedImageSampler, 11, fragment},
170 {3, vk::DescriptorType::eStorageBuffer, 1, vertex},
171 {4, vk::DescriptorType::eCombinedImageSampler, 1, fragment},
172 {5, vk::DescriptorType::eUniformBuffer, 1, fragment},
173 {6, vk::DescriptorType::eCombinedImageSampler, 1, fragment},
174 {7, vk::DescriptorType::eCombinedImageSampler, 3, fragment},
175 {8, vk::DescriptorType::eCombinedImageSampler, 1, fragment},
176 {9, vk::DescriptorType::eCombinedImageSampler, 1, fragment},
177 {10, vk::DescriptorType::eCombinedImageSampler, 1, vertex},
178 {11, vk::DescriptorType::eCombinedImageSampler, 1, vertex},
179 {12, vk::DescriptorType::eCombinedImageSampler, 1, vertex},
180 {13, vk::DescriptorType::eCombinedImageSampler, 1, fragment},
181 {2, vk::DescriptorType::eStorageBuffer, 1, vertex}}};
182 vk::DescriptorSetLayoutCreateInfo
info{};
185 r.setLayout =
device->createDescriptorSetLayout(
info);
186 r.layout = createPipelineLayout(device,
r.setLayout);
188 auto&
r = *pbrResources_;
189 auto& fslots = currentMesh3dFrameSlots();
191 auto frame = currentFrameSlot();
192 if (
r.frames.size() <= frame)
r.frames.resize(frame + 1);
193 auto& draws =
r.frames[frame];
194 const auto index = fslots.drawIndex++;
195 while (draws.size() <=
index) draws.emplace_back(std::make_unique<PbrResources::Draw>());
198 std::array<vk::DescriptorPoolSize, 3> sizes{{{vk::DescriptorType::eUniformBuffer, 2},
199 {vk::DescriptorType::eStorageBuffer, 2},
200 {vk::DescriptorType::eCombinedImageSampler, 22}}};
201 vk::DescriptorPoolCreateInfo
info{};
203 info.poolSizeCount = 3;
204 info.pPoolSizes = sizes.data();
206 vk::DescriptorSetAllocateInfo alloc{};
207 alloc.descriptorPool =
draw.pool;
208 alloc.descriptorSetCount = 1;
209 alloc.pSetLayouts = &
r.setLayout;
210 draw.set =
device->allocateDescriptorSets(alloc).front();
212 const auto&
s = *pbrSurface_;
216 u.view = mesh3dFrameUbo.
view;
217 u.camera = glm::vec4(glm::vec3(mesh3dFrameUbo.
cameraPos),
s.anisotropyRotation);
219 u.ambient = glm::vec4(glm::vec3(mesh3dLighting.
ambient),
s.anisotropyStrength);
220 u.material = {mesh3dMetallic, mesh3dRoughness, float(
int(mesh3dSurfaceMode)),
221 std::clamp(mesh3dAlphaCutoff +
s.vegetationColor.globalAlphaThresholdOffset, 0.f, 1.f)};
224 u.emissive = {
s.emissive[0],
s.emissive[1],
s.emissive[2],
s.emissiveStrength};
225 u.specular = {
s.specularColor[0],
s.specularColor[1],
s.specularColor[2],
s.specularFactor};
226 u.coat = {
s.clearcoatFactor,
s.clearcoatRoughness,
s.clearcoatNormalScale,
s.ior};
227 u.colorMaskSecondary = {
s.colorMaskSecondary[0],
s.colorMaskSecondary[1],
s.colorMaskSecondary[2],
228 float(
s.normalMode)};
229 u.colorMaskParams = {
s.colorMaskEnabled ? 1.f : 0.f,
s.colorMaskMin,
s.colorMaskMax,
s.albedoTextureStrength};
230 const auto& trans =
s.translucency;
231 u.translucencyColor = {trans.color[0], trans.color[1], trans.color[2], trans.intensity};
232 u.translucencyParams = {trans.strength, trans.normalDistortion, trans.scattering, trans.direct};
233 u.translucencyLighting = {trans.ambient, trans.shadow, trans.maskAmount, trans.globalIntensity * trans.overlay};
234 u.translucencyMask = {trans.maskMinimum, trans.maskMaximum, 0, 0};
235 u.motionHighlightColor = {
s.motionHighlightColor[0],
s.motionHighlightColor[1],
s.motionHighlightColor[2],
236 s.vegetationColor.overlaySubsurface};
237 u.vegetationOverlay = {
s.vegetationColor.overlayColor[0],
s.vegetationColor.overlayColor[1],
238 s.vegetationColor.overlayColor[2],
s.vegetationColor.overlay};
239 u.vegetationWetness = {
s.vegetationColor.wetness,
s.vegetationColor.overlayNormalScale,
240 s.vegetationColor.wetnessNormalScale,
s.vegetationColor.overlaySmoothness};
241 u.vegetationStageParams = {
s.vegetationColor.overlayVariation,
s.vegetationColor.overlayProjection,
242 s.vegetationColor.vertexOcclusionAlpha,
s.vegetationColor.wetnessContrast};
243 u.vegetationFieldColor = {
s.vegetationColor.fieldColor[0],
s.vegetationColor.fieldColor[1],
244 s.vegetationColor.fieldColor[2],
s.vegetationColor.fieldColor[3]};
245 u.vegetationColorParams = {
s.vegetationColor.colorsCoverage,
s.vegetationColor.colorsIntensity,
246 s.vegetationColor.colorsMask,
s.vegetationColor.colorsVariation};
247 u.vegetationOcclusionParams = {
s.vegetationColor.vertexOcclusionMinimum,
s.vegetationColor.vertexOcclusionMaximum,
249 u.vegetationOcclusionColor = {
s.vegetationColor.vertexOcclusionColor[0],
s.vegetationColor.vertexOcclusionColor[1],
250 s.vegetationColor.vertexOcclusionColor[2],
251 float(
s.vegetationColor.backfaceNormalMode)};
252 u.vegetationGlobalMasks = {
s.vegetationColor.globalColorMaskMinimum,
s.vegetationColor.globalColorMaskMaximum,
253 s.vegetationColor.globalOverlayMaskMinimum,
s.vegetationColor.globalOverlayMaskMaximum};
254 const auto&
d =
s.vegetationDetail;
255 u.detailUv = {
d.uvScale[0],
d.uvScale[1],
d.uvOffset[0],
d.uvOffset[1]};
256 u.detailColor = {
d.color[0],
d.color[1],
d.color[2],
d.normalBlendValue};
257 u.detailColorTwo = {
d.colorTwo[0],
d.colorTwo[1],
d.colorTwo[2],
d.colorTwo[3]};
258 u.detailValues = {
d.value,
d.normalValue,
d.albedoValue,
d.metallicValue};
259 u.detailMaterial = {
d.occlusionValue,
d.smoothnessValue,
d.blendMinimum,
d.blendMaximum};
260 u.detailMasks = {
d.maskMinimum,
d.maskMaximum,
d.meshMinimum,
d.meshMaximum};
261 u.detailInfo = {
d.uvMode,
d.colorMode,
d.blendMode,
d.alphaMode};
262 uint32_t detailPresent = 0;
263 for (
size_t i = 0; i <
d.textures.size(); ++i)
264 if (
d.textures[i].texture) detailPresent |= 1u << i;
265 u.detailInfo2 = {
d.maskMode,
d.meshMode,
d.inverseUvScale ? 1u : 0
u, detailPresent};
266 const auto& extras =
s.vegetationExtras;
267 u.extrasCoords = {extras.coords[0], extras.coords[1], extras.coords[2], extras.coords[3]};
268 u.extrasFallback = {extras.fallback[0], extras.fallback[1], extras.fallback[2], extras.fallback[3]};
269 u.extrasUsage0 = {extras.usage[0], extras.usage[1], extras.usage[2], extras.usage[3]};
270 u.extrasUsage1 = {extras.usage[4], extras.usage[5], extras.usage[6], extras.usage[7]};
271 u.extrasUsage2 = {extras.usage[8], 0, 0, 0};
272 u.extrasInfo = {extras.layer, extras.usePivotPosition ? 1u : 0
u, extras.texture ? 1u : 0
u,
273 s.vegetationAlpha.enabled ? 1u : 0
u};
274 u.extrasUsage2.y =
s.vegetationAlpha.global;
275 u.extrasUsage2.z =
s.vegetationAlpha.variation;
276 u.extrasUsage2.w =
s.vegetationAlpha.detailFade ? 1.f : 0.f;
277 u.vegetationAlphaFade = {
s.vegetationAlpha.glancing,
s.vegetationAlpha.camera,
s.vegetationAlpha.constant,
278 s.vegetationAlpha.noiseTiling};
279 u.vegetationAlphaCamera = {
s.vegetationAlpha.cameraFadeMin,
s.vegetationAlpha.cameraFadeMax, 0, 0};
280 u.vegetationEmission = {
s.vegetationEmission.minimum,
s.vegetationEmission.maximum,
s.vegetationEmission.phase,
281 s.vegetationEmission.enabled ?
s.vegetationEmission.global : -1.f};
282 u.vegetationGradientOne = {
s.vegetationGradient.colorOne[0],
s.vegetationGradient.colorOne[1],
283 s.vegetationGradient.colorOne[2],
284 s.vegetationGradient.enabled ?
s.vegetationGradient.minimum : -1.f};
285 u.vegetationGradientTwo = {
s.vegetationGradient.colorTwo[0],
s.vegetationGradient.colorTwo[1],
286 s.vegetationGradient.colorTwo[2],
s.vegetationGradient.maximum};
287 const auto&
colors =
s.vegetationColors;
292 u.colorsUsage2 = {
colors.usage[8], 0, 0, 0};
294 const auto& vertex =
s.vegetationVertex;
295 u.vertexCoords = {vertex.coords[0], vertex.coords[1], vertex.coords[2], vertex.coords[3]};
296 u.vertexFallback = {vertex.fallback[0], vertex.fallback[1], vertex.fallback[2], vertex.fallback[3]};
297 u.vertexUsage0 = {vertex.usage[0], vertex.usage[1], vertex.usage[2], vertex.usage[3]};
298 u.vertexUsage1 = {vertex.usage[4], vertex.usage[5], vertex.usage[6], vertex.usage[7]};
299 u.vertexUsage2 = {vertex.usage[8], 0, 0, 0};
300 u.vertexSize = {vertex.globalSize, vertex.sizeFadeStart, vertex.sizeFadeEnd, vertex.distanceFadeBias};
301 u.vertexInfo = {vertex.layer, uint32_t(vertex.source), 0
u, 0
u};
302 const auto& motion =
s.vegetationMotion;
303 u.motionCoords = {motion.coords[0], motion.coords[1], motion.coords[2], motion.coords[3]};
304 u.motionFallback = {motion.fallback[0], motion.fallback[1], motion.fallback[2], motion.fallback[3]};
305 u.motionUsage0 = {motion.usage[0], motion.usage[1], motion.usage[2], motion.usage[3]};
306 u.motionUsage1 = {motion.usage[4], motion.usage[5], motion.usage[6], motion.usage[7]};
307 u.motionUsage2 = {motion.usage[8], 0, 0, 0};
308 u.motionGlobal0 = {motion.globalDirection[0], motion.globalDirection[1], motion.worldOrigin[0],
309 motion.worldOrigin[1]};
310 u.motionGlobal1 = {motion.worldOrigin[2], motion.dynamicMode, motion.rigidity, motion.facing};
311 u.motionTime = {float(motion.time), 0, 0, 0};
312 u.motionBending = {motion.bending, motion.bendingSpeed, motion.bendingScale, motion.bendingVariation};
313 u.motionBranch = {motion.branch, motion.rolling, motion.branchSpeed, motion.branchScale};
314 u.motionBranch2 = {motion.branchVariation, motion.globalBending, motion.globalBranch, motion.globalFlutter};
315 u.motionFlutter = {motion.flutter, motion.flutterSpeed, motion.flutterScale, motion.flutterVariation};
316 u.motionControl = {motion.noiseTiling, motion.interaction, motion.interactionMask, motion.fadeDistance};
317 u.motionPerspective = {motion.perspectivePush, motion.perspectiveNoise, motion.perspectiveAngle, 0};
318 u.motionInfo = {motion.layer, uint32_t(motion.mode), 0
u, 0
u};
319 const bool skinned =
mesh->hasGpuSkinning() &&
mesh->getSkinPaletteCount() > 0;
320 u.misc = {
s.normalScale,
s.occlusionStrength,
s.unlit ? 1.f : 0.f,
321 skinned ? float(
mesh->getSkinPaletteCount()) : 0.f};
322 for (
int i = 0; i < std::min(mesh3dLighting.
count, 8); ++i)
u.lights[i] = mesh3dLighting.
lights[i];
323 u.lights[0].color.w = mesh3dEnvTexture ? mesh3dEnvIntensity : 0;
324 std::map<uint32_t, size_t> offsets;
326 std::vector<float> coordinates(
size_t(
mesh->getVertexCount()) * 2, 0.f);
327 std::array<vk::DescriptorImageInfo, 11> images;
328 std::array<vk::DescriptorImageInfo, 3> detailImages;
329 for (
size_t i = 0; i < 11; ++i) {
330 const auto&
b =
s.textures[i];
331 uint32_t
offset = UINT32_MAX;
334 if (
values.empty() &&
b.texcoord != 0)
throw Exception(
"PBR texture references missing mesh UV channel");
336 auto found = offsets.find(
b.texcoord);
337 if (
found == offsets.end()) {
338 size_t start = coordinates.size() / 2;
339 offsets.emplace(
b.texcoord,
start);
340 coordinates.insert(coordinates.end(),
values.begin(),
values.end());
346 u.uvTransform[i] = {
b.offset[0],
b.offset[1],
b.scale[0],
b.scale[1]};
347 u.uvInfo[i] = {
b.rotation,
offset,
b.texture ? 1.f : 0.f,
b.srgbDecode ? 1.f : 0.f};
348 auto* tex =
static_cast<GpuTexture*
>((
b.texture ?
b.texture : whiteTexture)->gpuHandle);
349 if (tex->isCube)
throw Exception(
"PBR material texture must be two-dimensional");
350 const std::array<uint32_t, 4>
key{
b.wrapS,
b.wrapT,
b.minFilter,
b.magFilter};
352 if (
found ==
r.samplers.end()) {
353 vk::SamplerCreateInfo
info{};
354 info.magFilter =
b.magFilter == 9728 ? vk::Filter::eNearest : vk::Filter::eLinear;
355 info.minFilter = (
b.minFilter == 9728 ||
b.minFilter == 9984 ||
b.minFilter == 9986) ? vk::Filter::eNearest
357 info.mipmapMode =
b.minFilter >= 9986 ? vk::SamplerMipmapMode::eLinear : vk::SamplerMipmapMode::eNearest;
358 info.addressModeU = addressMode(
b.wrapS);
359 info.addressModeV = addressMode(
b.wrapT);
360 info.addressModeW = vk::SamplerAddressMode::eRepeat;
361 info.maxLod =
b.minFilter >= 9984 ? VK_LOD_CLAMP_NONE : 0;
364 images[i] = {
found->second, tex->imageView(), vk::ImageLayout::eShaderReadOnlyOptimal};
366 for (
size_t i = 0; i <
d.textures.size(); ++i) {
367 const auto&
b =
d.textures[i];
368 auto* tex =
static_cast<GpuTexture*
>((
b.texture ?
b.texture : whiteTexture)->gpuHandle);
369 if (tex->isCube)
throw Exception(
"PBR detail texture must be two-dimensional");
370 const std::array<uint32_t, 4>
key{
b.wrapS,
b.wrapT,
b.minFilter,
b.magFilter};
372 if (
found ==
r.samplers.end()) {
373 vk::SamplerCreateInfo
info{};
374 info.magFilter =
b.magFilter == 9728 ? vk::Filter::eNearest : vk::Filter::eLinear;
375 info.minFilter = (
b.minFilter == 9728 ||
b.minFilter == 9984 ||
b.minFilter == 9986) ? vk::Filter::eNearest
377 info.mipmapMode =
b.minFilter >= 9986 ? vk::SamplerMipmapMode::eLinear : vk::SamplerMipmapMode::eNearest;
378 info.addressModeU = addressMode(
b.wrapS);
379 info.addressModeV = addressMode(
b.wrapT);
380 info.addressModeW = vk::SamplerAddressMode::eRepeat;
381 info.maxLod =
b.minFilter >= 9984 ? VK_LOD_CLAMP_NONE : 0;
384 detailImages[i] = {
found->second, tex->imageView(), vk::ImageLayout::eShaderReadOnlyOptimal};
386 if (!extras.texture && !defaultExtrasArray) {
387 const std::array<uint16_t, 4> neutral{0x3c00u, 0
u, 0
u, 0x3c00u};
389 if (!
created.ok())
throw Exception(
"PBR could not create its neutral Extras array");
390 defaultExtrasArray =
created.value();
392 Texture* extrasTexture = extras.texture ? extras.texture : defaultExtrasArray;
393 auto* extrasGpu =
static_cast<GpuTexture*
>(extrasTexture->gpuHandle);
394 if (!extrasGpu->isArray || (extras.texture && extrasTexture->layers < 9))
395 throw Exception(
"PBR Extras texture must be a nine-layer 2D array");
396 vk::DescriptorImageInfo extrasImage{extrasGpu->sampler, extrasGpu->imageView(),
397 vk::ImageLayout::eShaderReadOnlyOptimal};
398 if (!
colors.texture && !defaultColorsArray) {
399 const std::array<uint16_t, 4> neutral{0x3c00u, 0x3c00u, 0x3c00u, 0
u};
401 if (!
created.ok())
throw Exception(
"PBR could not create its neutral Colors array");
402 defaultColorsArray =
created.value();
405 auto* colorsGpu =
static_cast<GpuTexture*
>(colorsTexture->gpuHandle);
406 if (!colorsGpu->isArray || (
colors.texture && colorsTexture->layers < 9))
407 throw Exception(
"PBR Colors texture must be a nine-layer 2D array");
408 vk::DescriptorImageInfo colorsImage{colorsGpu->sampler, colorsGpu->imageView(),
409 vk::ImageLayout::eShaderReadOnlyOptimal};
410 if (!vertex.texture && !defaultVertexArray) {
411 const std::array<uint16_t, 4> neutral{0
u, 0
u, 0
u, 0x3c00u};
413 if (!
created.ok())
throw Exception(
"PBR could not create its neutral Vertex array");
414 defaultVertexArray =
created.value();
416 Texture* vertexTexture = vertex.texture ? vertex.texture : defaultVertexArray;
417 auto* vertexGpu =
static_cast<GpuTexture*
>(vertexTexture->gpuHandle);
418 if (!vertexGpu->isArray || (vertex.texture && vertexTexture->layers < 9))
419 throw Exception(
"PBR Vertex texture must be a nine-layer 2D array");
423 throw Exception(
"PBR GPU vegetation deformation requires an unskinned object-mode mesh");
424 vk::DescriptorImageInfo vertexImage{vertexGpu->sampler, vertexGpu->imageView(),
425 vk::ImageLayout::eShaderReadOnlyOptimal};
426 if (!motion.texture && !defaultMotionArray) {
427 const std::array<uint16_t, 4> neutral{0x3c00u, 0
u, 0x3800u, 0
u};
429 if (!
created.ok())
throw Exception(
"PBR could not create its neutral Motion array");
430 defaultMotionArray =
created.value();
432 Texture* motionTexture = motion.texture ? motion.texture : defaultMotionArray;
433 auto* motionGpu =
static_cast<GpuTexture*
>(motionTexture->gpuHandle);
434 if (!motionGpu->isArray || (motion.texture && motionTexture->layers < 9))
435 throw Exception(
"PBR Motion texture must be a nine-layer 2D array");
436 vk::DescriptorImageInfo motionImage{motionGpu->sampler, motionGpu->imageView(),
437 vk::ImageLayout::eShaderReadOnlyOptimal};
438 Texture* noiseTexture = motion.noise ? motion.noise : whiteTexture;
439 auto* noiseGpu =
static_cast<GpuTexture*
>(noiseTexture->gpuHandle);
440 if (noiseGpu->isArray || noiseGpu->isCube)
throw Exception(
"PBR motion noise must be two-dimensional");
441 vk::DescriptorImageInfo noiseImage{noiseGpu->sampler, noiseGpu->imageView(),
442 vk::ImageLayout::eShaderReadOnlyOptimal};
443 if (!defaultVegetationFadeNoise) {
444 const std::array<uint8_t, 4> zero{0, 0, 0, 255};
446 if (!
created.ok())
throw Exception(
"PBR could not create its neutral vegetation fade volume");
447 defaultVegetationFadeNoise =
created.value();
449 Texture* fadeNoiseTexture =
s.vegetationAlpha.noise ?
s.vegetationAlpha.noise : defaultVegetationFadeNoise;
450 auto* fadeNoiseGpu =
static_cast<GpuTexture*
>(fadeNoiseTexture->gpuHandle);
451 if (!fadeNoiseGpu->isVolume)
throw Exception(
"PBR vegetation fade noise must be three-dimensional");
452 vk::DescriptorImageInfo fadeNoiseImage{fadeNoiseGpu->sampler, fadeNoiseGpu->imageView(),
453 vk::ImageLayout::eShaderReadOnlyOptimal};
454 if (coordinates.empty()) coordinates.resize(2);
456 const auto& tangents =
mesh->importedTangents();
457 const auto& bitangents =
mesh->importedBitangents();
458 if (tangents.size() !=
size_t(
mesh->getVertexCount()) * 3 || bitangents.size() != tangents.size())
459 throw Exception(
"PBR authored tangent frame does not match mesh vertices");
460 if (coordinates.size() > UINT32_MAX || tangents.size() > (UINT32_MAX - coordinates.size()) / 2)
461 throw Exception(
"PBR tangent stream exceeds addressable range");
462 u.tangentInfo = {uint32_t(coordinates.size()), 1, 0, 0};
463 for (
size_t i = 0; i < tangents.size(); i += 3) {
464 coordinates.insert(coordinates.end(), tangents.begin() + i, tangents.begin() + i + 3);
465 coordinates.insert(coordinates.end(), bitangents.begin() + i, bitangents.begin() + i + 3);
468 const auto highlights =
mesh->motionHighlights();
469 if (!highlights.empty()) {
470 auto valid =
mesh->validateMotionHighlights(highlights);
471 if (!
valid)
throw Exception(
"PBR motion highlights do not match mesh vertices");
472 if (coordinates.size() > UINT32_MAX || highlights.size() > UINT32_MAX - coordinates.size())
473 throw Exception(
"PBR highlight stream exceeds addressable range");
474 u.tangentInfo.z = uint32_t(coordinates.size());
476 coordinates.insert(coordinates.end(), highlights.begin(), highlights.end());
478 const auto vegetationFactors =
mesh->vegetationFactors();
479 if (!vegetationFactors.empty()) {
480 auto valid =
mesh->validateVegetationFactors(vegetationFactors);
481 if (!
valid)
throw Exception(
"PBR vegetation factors do not match mesh vertices");
482 if (coordinates.size() > UINT32_MAX || vegetationFactors.size() > UINT32_MAX - coordinates.size())
483 throw Exception(
"PBR vegetation factor stream exceeds addressable range");
484 u.vegetationInfo = {uint32_t(coordinates.size()), 1,
s.vegetationColor.invertVertexOcclusion ? 1u : 0
u,
485 s.vegetationColor.invertVertexOcclusionColors ? 1u : 0
u};
486 coordinates.insert(coordinates.end(), vegetationFactors.begin(), vegetationFactors.end());
488 const auto deformationFactors =
mesh->vegetationDeformationFactors();
489 if (!deformationFactors.empty()) {
490 auto valid =
mesh->validateVegetationDeformationFactors(deformationFactors);
491 if (!
valid)
throw Exception(
"PBR vegetation deformation factors do not match mesh vertices");
492 if (coordinates.size() > UINT32_MAX || deformationFactors.size() > UINT32_MAX - coordinates.size())
493 throw Exception(
"PBR vegetation deformation stream exceeds addressable range");
494 u.vertexInfo.z = uint32_t(coordinates.size());
496 coordinates.insert(coordinates.end(), deformationFactors.begin(), deformationFactors.end());
499 throw Exception(
"PBR GPU vegetation motion requires a nine-float rest-deformation stream");
500 const auto upload = [&](vkb::GenericBuffer&
buffer, vk::BufferUsageFlags
usage,
const void* data,
size_t bytes) {
501 if ((
usage & vk::BufferUsageFlagBits::eStorageBuffer) &&
502 bytes >
device.physical_device.properties.limits.maxStorageBufferRange)
503 throw Exception(
"PBR mesh storage exceeds device maxStorageBufferRange");
507 upload(
draw.uniform, vk::BufferUsageFlagBits::eUniformBuffer, &
u,
sizeof(
u));
508 upload(
draw.uv, vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eStorageBuffer,
509 coordinates.data(), coordinates.size() *
sizeof(
float));
510 const glm::mat4 identity(1);
511 upload(
draw.skin, vk::BufferUsageFlagBits::eStorageBuffer, skinned ?
mesh->skinPalette().data() : &identity[0][0],
512 skinned ?
mesh->skinPalette().
size() * sizeof(float) : sizeof(identity));
513 auto shadow = mesh3dShadows.
ubo;
514 if (!mesh3dShadows.
active) {
518 shadow.bias.z = mesh3dShadowReceive ? 1.f : 0.f;
519 upload(
draw.shadow, vk::BufferUsageFlagBits::eUniformBuffer, &shadow,
sizeof(shadow));
520 ensureDefaultEnvCubemap();
521 auto* env =
static_cast<GpuTexture*
>((mesh3dEnvTexture ? mesh3dEnvTexture : defaultEnvCubemap)->gpuHandle);
522 if (!env->isCube)
throw Exception(
"PBR environment must be a cubemap");
523 vk::DescriptorImageInfo envInfo{env->sampler, env->imageView(), vk::ImageLayout::eShaderReadOnlyOptimal};
524 vk::DescriptorImageInfo shadowInfo{shadowSampler, currentShadowArrayView(),
525 currentShadowMap().image.currentLayout()};
526 std::array<vk::DescriptorBufferInfo, 4> buffers{{{
draw.uniform.buffer, 0,
sizeof(
u)},
527 {
draw.skin.buffer, 0,
draw.skin.size},
528 {
draw.shadow.buffer, 0,
sizeof(shadow)},
529 {
draw.uv.buffer, 0,
draw.uv.size}}};
530 std::array<vk::WriteDescriptorSet, 14> writes{};
531 const std::array<uint32_t, 14>
bindings{0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 2};
532 for (
size_t i = 0; i < writes.size(); ++i) {
533 writes[i].dstSet =
draw.set;
535 writes[i].descriptorCount = 1;
537 for (
auto [write,
buffer] :
std::array<
std::pair<int, int>, 4>{{{0, 0}, {2, 1}, {4, 2}, {13, 3}}}) {
538 writes[write].descriptorType =
539 (write == 2 || write == 13) ? vk::DescriptorType::eStorageBuffer : vk::DescriptorType::eUniformBuffer;
540 writes[write].pBufferInfo = &buffers[
buffer];
542 writes[1].descriptorType = writes[3].descriptorType = writes[5].descriptorType = writes[6].descriptorType =
543 writes[7].descriptorType = writes[8].descriptorType = writes[9].descriptorType = writes[10].descriptorType =
544 writes[11].descriptorType = writes[12].descriptorType = vk::DescriptorType::eCombinedImageSampler;
545 writes[1].descriptorCount = 11;
546 writes[1].pImageInfo = images.data();
547 writes[3].pImageInfo = &envInfo;
548 writes[5].pImageInfo = &shadowInfo;
549 writes[6].descriptorCount = 3;
550 writes[6].pImageInfo = detailImages.data();
551 writes[7].pImageInfo = &extrasImage;
552 writes[8].pImageInfo = &colorsImage;
553 writes[9].pImageInfo = &vertexImage;
554 writes[10].pImageInfo = &motionImage;
555 writes[11].pImageInfo = &noiseImage;
556 writes[12].pImageInfo = &fadeNoiseImage;
557 device->updateDescriptorSets(writes, {});
558 const auto& rp = offscreen3DPassOpen ? (offscreen3DHDRActive ? hdrOffscreen3DRenderPass : offscreen3DRenderPass)
560 const auto samples = offscreen3DPassOpen ? vk::SampleCountFlagBits::e1 : activeSceneSamples();
563 const bool depthWrite = !transparent || mesh3dSurfaceDepthWrite;
568 const size_t pipelineVariant =
569 size_t(
blend) * 12u + (depthWrite ? 6u : 0
u) + (alphaToCoverage ? 3u : 0
u) + size_t(resolvedCull) - 1u;
570 auto key = std::make_tuple(VkRenderPass(rp.handle), uint32_t(samples), pipelineVariant);
572 if (
found ==
r.pipelines.end())
574 .emplace(
key, createPbrPipeline(device,
r.layout, rp, samples,
blend, depthWrite, resolvedCull,
577 auto& cb = currentPresentCb();
578 cb.bindPipeline(vk::PipelineBindPoint::eGraphics,
found->second);
579 cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
r.layout, 0, 1, &
draw.set, 0,
nullptr);
580 std::array<vk::Buffer, 11> uvBuffers;
581 uvBuffers.fill(
draw.uv.buffer);
582 std::array<vk::DeviceSize, 11> uvOffsets{};
583 for (
size_t i = 0; i < uvOffsets.size(); ++i)
584 if (
u.uvInfo[i].offset != UINT32_MAX) uvOffsets[i] = vk::DeviceSize(
u.uvInfo[i].offset) * 2 *
sizeof(float);
585 cb.bindVertexBuffers(1, uvBuffers, uvOffsets);
586 drawIndexedMesh(cb, *
static_cast<GpuMesh*
>(
mesh->gpuHandle));
587 lastMesh3dPipeline = vk::Pipeline{};
588 lastMesh3dClusteredPipeline = vk::Pipeline{};
std::map< std::string, Var > values
vk::UniqueSampler sampler
std::vector< std::weak_ptr< DeviceBytes > > resources
std::vector< std::shared_ptr< DeviceBytes > > bindings
std::unique_ptr< gpgpu::GpuBuffer > buffer
std::unordered_map< std::string, ShaderEntry > shaders
eve::action::ActionVfxBinding binding
std::vector< float > colors
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.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
GPU mesh handle (+ optional CPU morph targets).
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Result< void > setMesh3DPbrSurface(const PbrSurface *surface) override
Sets the mesh 3 d pbr surface.
Result< Texture * > newTextureArrayRgba16f(uint32_t width, uint32_t height, uint32_t layers, std::span< const uint16_t > rgbaHalf) override
Creates a texture array rgba 16 f. @ownership Caller deletes unless documented otherwise.
Result< Texture * > newTexture3DRgba8(uint32_t width, uint32_t height, uint32_t depth, std::span< const uint8_t > rgba) override
Creates a texture 3 d rgba 8. @ownership Caller deletes unless documented otherwise.
PbrCullMode
Raster face culling requested by a PBR material.
eve::Color Color
RGBA color used by every graphics draw call. Lives inside eve::graphics so including a graphics heade...
Result< void > validatePbrSurface(const PbrSurface &s)
Validate finite material factors and sampler enums without changing the input.
eve::BlendMode BlendMode
Compatibility alias for the shared 2D blend mode.
CompareOp
CompareOp public API.
Light3DGpu lights[kMaxLights]
Owning material parameter snapshot; texture pointers remain borrowed. Base color/metallic/roughness c...
PbrVegetationMotion vegetationMotion
PbrVegetationExtras vegetationExtras
std::array< PbrTextureBinding, std::size_t(PbrTextureSlot::Count)> textures
PbrVegetationVertex vegetationVertex
PbrVegetationColors vegetationColors
PbrVegetationDetail vegetationDetail
PbrVegetationAlpha vegetationAlpha
std::array< PbrTextureBinding, 3 > textures
vkb::GenericBuffer shadow
vkb::GenericBuffer uniform
std::map< std::array< uint32_t, 4 >, vk::Sampler > samplers
vk::PipelineLayout layout
std::vector< std::vector< std::unique_ptr< Draw > > > frames
std::map< std::tuple< VkRenderPass, uint32_t, size_t >, vk::Pipeline > pipelines
vk::DescriptorSetLayout setLayout