载入中...
搜索中...
未找到
GraphicsDeferredLighting.cpp
浏览该文件的文档.
1// Hybrid clustered deferred lighting (Phase D): WebGPU parity with the Vulkan
2// fullscreen pass. drawDeferredLighting() stages work; flushDeferredLighting()
3// runs at the start of the scene-color pass after GBuffer has been written.
5#include "graphics/Shadow.h"
9
10#include <algorithm>
11#include <cstring>
12
13namespace eve::graphics::webgpu {
14namespace {
15
16struct DeferredLightingUBO {
17 glm::mat4 invViewProj{1.f};
18 glm::mat4 view{1.f};
19 glm::vec4 lightDir{0.4f, 1.f, 0.3f, 0.f};
20 glm::vec4 lightColor{1.f, 1.f, 1.f, 0.f};
21 glm::vec4 cameraPos{0.f, 0.f, 3.f, 0.f};
22 glm::vec4 ambient{0.12f, 0.12f, 0.14f, 0.f};
23 glm::vec4 gridInfo{16.f, 9.f, 24.f, 0.f};
24 glm::vec4 clipInfo{0.1f, 100.f, 1.f, 1.f};
25};
26static_assert(sizeof(DeferredLightingUBO) == 224, "DeferredLightingUBO must match WGSL DeferredFrame");
27
28} // namespace
29
30void Graphics::destroyDeferredLightingResources() {
31 deferredLightingPipeline = {};
32 deferredLightingPipelineLayout = {};
33 deferredLightingSetLayout = {};
34 deferredLightingNearestSampler = {};
35 deferredLightingPending_ = false;
36}
37
38void Graphics::createDeferredLightingPipeline() {
39 if (deferredLightingPipeline || !device) return;
40
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,
50 sizeof(ClusteredLightGpu));
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");
59
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));
67
68 wgpu::ShaderModule vertModule = makeWgslModule(device, kDeferredLightingVertWgsl);
69 wgpu::ShaderModule fragModule = makeWgslModule(device, kDeferredLightingFragWgsl);
70
71 WGPUColorTargetState target{};
72 target.format = sceneColorFormat;
73 target.writeMask = WGPUColorWriteMask_All;
74
75 WGPUFragmentState fs{};
76 fs.module = fragModule.Get();
77 fs.entryPoint = sv("fs_main");
78 fs.targetCount = 1;
79 fs.targets = &target;
80
81 WGPUDepthStencilState ds{};
82 ds.format = WGPUTextureFormat_Depth32Float;
83 ds.depthWriteEnabled = WGPUOptionalBool_True;
84 // Always pass; fragment writes frag_depth from GBuffer hwDepth so
85 // transparent Forward+ can depth-test against deferred opaques.
86 ds.depthCompare = WGPUCompareFunction_Always;
87
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;
94 pd.fragment = &fs;
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;
100 // Hybrid compile() forces MSAA off; deferred lighting is 1x only.
101 pd.multisample.count = 1;
102 pd.multisample.mask = 0xFFFFFFFFu;
103 deferredLightingPipeline =
104 device.CreateRenderPipeline(reinterpret_cast<const wgpu::RenderPipelineDescriptor*>(&pd));
105
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));
117 }
118}
119
121 if (!device || !initialized) return;
122 if (gbufferSlots.empty() || gbufferWidth <= 0 || gbufferHeight <= 0) return;
123 deferredLightingPending_ = true;
124}
125
126void Graphics::ensureDeferredLightingClusteredUpload() {
127 if (mesh3dClustered.active) {
128 uploadClusteredLighting(mesh3dClustered);
129 return;
130 }
131 std::vector<ClusteredLightGpu> dirs;
132 const glm::vec3 dir = glm::vec3(mesh3dLighting.lights[0].posRadius);
133 if (glm::length(dir) > 1e-4f) {
135 d.posRadius = glm::vec4(glm::normalize(dir), 0.f);
136 d.color = mesh3dLighting.lights[0].color;
137 dirs.push_back(d);
138 }
139 ClusteredLightingUpload empty =
140 buildClusteredLighting({}, dirs, mesh3dView, mesh3dNear, mesh3dFar, std::max(1, sceneColorWidth),
141 std::max(1, sceneColorHeight), /*fovYRad*/ 1.f, mesh3dLighting.ambient);
142 empty.active = true;
144}
145
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;
151
152 createShadowResources();
153 createDeferredLightingPipeline();
154 if (!deferredLightingPipeline || !shadowDepthArray) return;
155
156 ensureDeferredLightingClusteredUpload();
157 ClusteredStorage& storage = clusteredStorage[currentFrameSlot()];
158 if (!storage.lights || !storage.table || !storage.indices) return;
159
160 GbufferSlot& slot = gbufferSlots[lastGbufferSlot < gbufferSlots.size() ? lastGbufferSlot : currentFrameSlot()];
161 if (!slot.normalView || !slot.depthView) return;
162
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;
168 {
169 const glm::vec3 dir = glm::vec3(mesh3dLighting.lights[0].posRadius);
170 ubo.lightDir = glm::vec4(dir, glm::length(dir) > 1e-4f ? 1.f : 0.f);
171 ubo.lightColor = mesh3dLighting.lights[0].color;
172 }
173 if (mesh3dClustered.active) {
174 ubo.gridInfo = mesh3dClustered.gridInfo;
175 ubo.clipInfo = mesh3dClustered.clipInfo;
176 ubo.ambient = mesh3dClustered.ambient;
177 ubo.lightDir = mesh3dClustered.primaryDir;
178 ubo.lightColor = glm::vec4(glm::vec3(mesh3dClustered.primaryColor), 0.f);
179 ubo.view = mesh3dClustered.view;
180 }
181 ubo.clipInfo.z = float(std::max(1, sceneColorWidth > 0 ? sceneColorWidth : gbufferWidth));
182 ubo.clipInfo.w = float(std::max(1, sceneColorHeight > 0 ? sceneColorHeight : gbufferHeight));
183
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));
192
193 WGPUBindGroupEntry entries[14]{};
194 entries[0].binding = 0;
195 entries[0].buffer = uboArena.buffer.Get();
196 entries[0].offset = frameOffset;
197 entries[0].size = sizeof(DeferredLightingUBO);
198
199 entries[1].binding = 1;
200 entries[1].textureView = slot.normalView.Get();
201 entries[2].binding = 2;
202 entries[2].textureView = slot.depthColorView.Get();
203 entries[3].binding = 3;
204 entries[3].textureView = slot.albedoView.Get();
205 entries[4].binding = 4;
206 entries[4].textureView = slot.pbrParamsView.Get();
207 entries[5].binding = 5;
208 entries[5].textureView = slot.emissiveView.Get();
209 entries[6].binding = 6;
210 entries[6].textureView = slot.depthView.Get();
211
212 entries[7].binding = 7;
213 entries[7].buffer = storage.lights.Get();
214 entries[7].size = storage.lightsCap;
215 entries[8].binding = 8;
216 entries[8].buffer = storage.table.Get();
217 entries[8].size = storage.tableCap;
218 entries[9].binding = 9;
219 entries[9].buffer = storage.indices.Get();
220 entries[9].size = storage.indicesCap;
221
222 entries[10].binding = 10;
223 entries[10].buffer = uboArena.buffer.Get();
224 entries[10].offset = shadowOffset;
225 entries[10].size = sizeof(ShadowUBO);
226
227 entries[11].binding = 11;
228 entries[11].textureView = shadowDepthArray->view.Get();
229 entries[12].binding = 12;
230 entries[12].sampler = deferredLightingNearestSampler.Get();
231 entries[13].binding = 13;
232 entries[13].sampler = shadowDepthArray->sampler.Get();
233
234 WGPUBindGroupDescriptor bgd{};
235 bgd.label = sv("eve_deferred_lighting_bg");
236 bgd.layout = deferredLightingSetLayout.Get();
237 bgd.entryCount = 14;
238 bgd.entries = entries;
239 wgpu::BindGroup bg = device.CreateBindGroup(reinterpret_cast<const wgpu::BindGroupDescriptor*>(&bgd));
240
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);
245}
246
247} // namespace eve::graphics::webgpu
LogicalId target
std::unordered_map< std::string, QuestRuntime > entries
MeleePoint3 b
Definition MeleeHit.cpp:41
float d
glm::mat4 view
V3 dir
Definition TreeMesh.cpp:150
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).
Definition wgpu_types.h:20
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.
Definition wgpu_types.h:34
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).
Definition Light.h:161
Light3DGpu lights[kMaxLights]
Definition Light.h:175
glm::vec4 lightColor
glm::vec4 ambient
glm::vec4 lightDir
glm::mat4 invViewProj
glm::vec4 gridInfo
glm::vec4 cameraPos
glm::vec4 clipInfo