16struct DeferredLightingUBO {
22 glm::vec4
ambient{0.12f, 0.12f, 0.14f, 0.f};
26static_assert(
sizeof(DeferredLightingUBO) == 224,
"DeferredLightingUBO must match WGSL DeferredFrame");
30void Graphics::destroyDeferredLightingResources() {
31 deferredLightingPipeline = {};
32 deferredLightingPipelineLayout = {};
33 deferredLightingSetLayout = {};
34 deferredLightingNearestSampler = {};
35 deferredLightingPending_ =
false;
38void Graphics::createDeferredLightingPipeline() {
39 if (deferredLightingPipeline || !device)
return;
41 BindGroupLayoutBuilder
b;
42 b.buffer(0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform,
true,
sizeof(DeferredLightingUBO));
43 b.texture(1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float, wgpu::TextureViewDimension::e2D);
44 b.texture(2, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float, wgpu::TextureViewDimension::e2D);
45 b.texture(3, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float, wgpu::TextureViewDimension::e2D);
46 b.texture(4, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float, wgpu::TextureViewDimension::e2D);
47 b.texture(5, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float, wgpu::TextureViewDimension::e2D);
48 b.texture(6, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Depth, wgpu::TextureViewDimension::e2D);
49 b.buffer(7, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::ReadOnlyStorage,
false,
51 b.buffer(8, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::ReadOnlyStorage,
false,
52 sizeof(ClusterTableEntry));
53 b.buffer(9, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::ReadOnlyStorage,
false,
sizeof(uint32_t));
54 b.buffer(10, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform,
true,
sizeof(ShadowUBO));
55 b.texture(11, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Depth, wgpu::TextureViewDimension::e2DArray);
56 b.sampler(12, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering);
57 b.sampler(13, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Comparison);
58 deferredLightingSetLayout =
b.build(device,
"eve_deferred_lighting");
60 WGPUBindGroupLayout layouts[1] = {deferredLightingSetLayout.Get()};
61 WGPUPipelineLayoutDescriptor pl{};
62 pl.label =
sv(
"eve_deferred_lighting_layout");
63 pl.bindGroupLayoutCount = 1;
64 pl.bindGroupLayouts = layouts;
65 deferredLightingPipelineLayout =
66 device.CreatePipelineLayout(
reinterpret_cast<const wgpu::PipelineLayoutDescriptor*
>(&pl));
71 WGPUColorTargetState
target{};
72 target.format = sceneColorFormat;
73 target.writeMask = WGPUColorWriteMask_All;
75 WGPUFragmentState fs{};
76 fs.module = fragModule.Get();
77 fs.entryPoint =
sv(
"fs_main");
81 WGPUDepthStencilState ds{};
82 ds.format = WGPUTextureFormat_Depth32Float;
83 ds.depthWriteEnabled = WGPUOptionalBool_True;
86 ds.depthCompare = WGPUCompareFunction_Always;
88 WGPURenderPipelineDescriptor pd{};
89 pd.label =
sv(
"eve_deferred_lighting");
90 pd.layout = deferredLightingPipelineLayout.Get();
91 pd.vertex.module = vertModule.Get();
92 pd.vertex.entryPoint =
sv(
"vs_main");
93 pd.vertex.bufferCount = 0;
95 pd.depthStencil = &ds;
96 pd.primitive.topology = WGPUPrimitiveTopology_TriangleList;
97 pd.primitive.frontFace = WGPUFrontFace_CCW;
98 pd.primitive.cullMode = WGPUCullMode_None;
99 pd.primitive.stripIndexFormat = WGPUIndexFormat_Undefined;
101 pd.multisample.count = 1;
102 pd.multisample.mask = 0xFFFFFFFFu;
103 deferredLightingPipeline =
104 device.CreateRenderPipeline(
reinterpret_cast<const wgpu::RenderPipelineDescriptor*
>(&pd));
106 if (!deferredLightingNearestSampler) {
107 WGPUSamplerDescriptor sd{};
108 sd.label =
sv(
"eve_deferred_nearest");
109 sd.addressModeU = WGPUAddressMode_ClampToEdge;
110 sd.addressModeV = WGPUAddressMode_ClampToEdge;
111 sd.addressModeW = WGPUAddressMode_ClampToEdge;
112 sd.magFilter = WGPUFilterMode_Nearest;
113 sd.minFilter = WGPUFilterMode_Nearest;
114 sd.mipmapFilter = WGPUMipmapFilterMode_Nearest;
115 sd.maxAnisotropy = 1.f;
116 deferredLightingNearestSampler = device.CreateSampler(
reinterpret_cast<const wgpu::SamplerDescriptor*
>(&sd));
121 if (!device || !initialized)
return;
122 if (gbufferSlots.empty() || gbufferWidth <= 0 || gbufferHeight <= 0)
return;
123 deferredLightingPending_ =
true;
126void Graphics::ensureDeferredLightingClusteredUpload() {
127 if (mesh3dClustered.
active) {
128 uploadClusteredLighting(mesh3dClustered);
131 std::vector<ClusteredLightGpu> dirs;
133 if (glm::length(
dir) > 1e-4f) {
139 ClusteredLightingUpload empty =
141 std::max(1, sceneColorHeight), 1.f, mesh3dLighting.
ambient);
146void Graphics::flushDeferredLighting(wgpu::RenderPassEncoder pass) {
147 if (!deferredLightingPending_)
return;
148 deferredLightingPending_ =
false;
149 if (!pass || !device || gbufferSlots.empty())
return;
150 if (sceneColorSamples != 1)
return;
152 createShadowResources();
153 createDeferredLightingPipeline();
154 if (!deferredLightingPipeline || !shadowDepthArray)
return;
156 ensureDeferredLightingClusteredUpload();
157 ClusteredStorage& storage = clusteredStorage[currentFrameSlot()];
158 if (!storage.lights || !storage.table || !storage.indices)
return;
160 GbufferSlot& slot = gbufferSlots[lastGbufferSlot < gbufferSlots.size() ? lastGbufferSlot : currentFrameSlot()];
161 if (!slot.normalView || !slot.depthView)
return;
163 DeferredLightingUBO ubo{};
164 ubo.invViewProj = glm::inverse(mesh3dViewProj);
165 ubo.view = mesh3dView;
166 ubo.cameraPos = glm::vec4(mesh3dCameraPos, 0.f);
167 ubo.ambient = mesh3dLighting.
ambient;
170 ubo.lightDir = glm::vec4(
dir, glm::length(
dir) > 1e-4f ? 1.f : 0.f);
173 if (mesh3dClustered.
active) {
174 ubo.gridInfo = mesh3dClustered.
gridInfo;
175 ubo.clipInfo = mesh3dClustered.
clipInfo;
176 ubo.ambient = mesh3dClustered.
ambient;
178 ubo.lightColor = glm::vec4(glm::vec3(mesh3dClustered.
primaryColor), 0.f);
179 ubo.view = mesh3dClustered.
view;
181 ubo.clipInfo.z = float(std::max(1, sceneColorWidth > 0 ? sceneColorWidth : gbufferWidth));
182 ubo.clipInfo.w = float(std::max(1, sceneColorHeight > 0 ? sceneColorHeight : gbufferHeight));
184 auto& uboArena = currentUboArena();
185 ensureUboArena(uboArena, uboArena.used + 512);
186 const uint32_t frameOffset = uboArena.alloc(
sizeof(DeferredLightingUBO), 256);
187 const uint32_t shadowOffset = uboArena.alloc(
sizeof(ShadowUBO), 256);
188 queue.WriteBuffer(uboArena.buffer, frameOffset, &ubo,
sizeof(ubo));
189 ShadowUBO shadowUbo = mesh3dShadows.
ubo;
190 if (!mesh3dShadows.
active) shadowUbo.
bias.y = 0.f;
191 queue.WriteBuffer(uboArena.buffer, shadowOffset, &shadowUbo,
sizeof(shadowUbo));
193 WGPUBindGroupEntry
entries[14]{};
195 entries[0].buffer = uboArena.buffer.Get();
196 entries[0].offset = frameOffset;
197 entries[0].size =
sizeof(DeferredLightingUBO);
200 entries[1].textureView = slot.normalView.Get();
202 entries[2].textureView = slot.depthColorView.Get();
204 entries[3].textureView = slot.albedoView.Get();
206 entries[4].textureView = slot.pbrParamsView.Get();
208 entries[5].textureView = slot.emissiveView.Get();
210 entries[6].textureView = slot.depthView.Get();
213 entries[7].buffer = storage.lights.Get();
214 entries[7].size = storage.lightsCap;
216 entries[8].buffer = storage.table.Get();
217 entries[8].size = storage.tableCap;
219 entries[9].buffer = storage.indices.Get();
220 entries[9].size = storage.indicesCap;
223 entries[10].buffer = uboArena.buffer.Get();
224 entries[10].offset = shadowOffset;
225 entries[10].size =
sizeof(ShadowUBO);
228 entries[11].textureView = shadowDepthArray->
view.Get();
230 entries[12].sampler = deferredLightingNearestSampler.Get();
234 WGPUBindGroupDescriptor bgd{};
235 bgd.label =
sv(
"eve_deferred_lighting_bg");
236 bgd.layout = deferredLightingSetLayout.Get();
239 wgpu::BindGroup bg = device.CreateBindGroup(
reinterpret_cast<const wgpu::BindGroupDescriptor*
>(&bgd));
241 const uint32_t offsets[2] = {frameOffset, shadowOffset};
242 pass.SetPipeline(deferredLightingPipeline);
243 pass.SetBindGroup(0, bg, 2, offsets);
244 pass.Draw(3, 1, 0, 0);
std::unordered_map< std::string, QuestRuntime > entries
void setMesh3DClusteredLighting(const ClusteredLightingUpload &upload) override
Sets the mesh 3 d clustered lighting.
void drawDeferredLighting() override
Draws deferred lighting.
WGPUStringView sv(const char *s)
Build a WGPUStringView from a C string (null-safe, length auto-computed).
const char * kDeferredLightingVertWgsl
Immutable deferred-lighting vertex WGSL. @borrowed Static storage; valid for the process lifetime.
wgpu::ShaderModule makeWgslModule(wgpu::Device &dev, const char *wgsl)
Create an owning shader module; retain it until pipeline creation completes.
const char * kDeferredLightingFragWgsl
Immutable clustered deferred lighting fragment WGSL (core metallic-roughness PBR + CSM + clustered po...
ClusteredLightingUpload buildClusteredLighting(const std::vector< ClusteredLightGpu > &points, const std::vector< ClusteredLightGpu > &dirs, const glm::mat4 &view, float nearZ, float farZ, int screenW, int screenH, float fovYRad, const glm::vec4 &ambient)
Build clustered tables for point lights in view space.
Light3DGpu ClusteredLightGpu
GPU light packing for mesh3d / PBR (std140-friendly).
Light3DGpu lights[kMaxLights]