package pass import ( "github.com/vkngwrapper/core/v2/core1_0" "zworld/engine/object" "zworld/engine/object/light" "zworld/engine/renderapi" "zworld/engine/renderapi/cache" "zworld/engine/renderapi/color" "zworld/engine/renderapi/command" "zworld/engine/renderapi/framebuffer" "zworld/engine/renderapi/renderpass" "zworld/engine/renderapi/renderpass/attachment" "zworld/engine/renderapi/vertex" "zworld/engine/renderapi/vulkan" ) const LightingSubpass renderpass.Name = "lighting" type DeferredLightPass struct { app vulkan.App target vulkan.Target gbuffer GeometryBuffer ssao vulkan.Target quad vertex.Mesh pass renderpass.T light LightShader fbuf framebuffer.Array samplers []cache.SamplerCache shadows []*ShadowCache lightbufs []*LightBuffer lightQuery *object.Query[light.T] } func NewDeferredLightingPass( app vulkan.App, target vulkan.Target, gbuffer GeometryBuffer, shadows Shadow, occlusion vulkan.Target, ) *DeferredLightPass { pass := renderpass.New(app.Device(), renderpass.Args{ Name: "Deferred Lighting", ColorAttachments: []attachment.Color{ { Name: OutputAttachment, Image: attachment.FromImageArray(target.Surfaces()), Samples: 0, LoadOp: core1_0.AttachmentLoadOpClear, StoreOp: core1_0.AttachmentStoreOpStore, InitialLayout: 0, FinalLayout: core1_0.ImageLayoutShaderReadOnlyOptimal, Clear: color.T{}, Blend: attachment.BlendAdditive, }, }, Subpasses: []renderpass.Subpass{ { Name: LightingSubpass, ColorAttachments: []attachment.Name{OutputAttachment}, }, }, }) fbuf, err := framebuffer.NewArray(target.Frames(), app.Device(), "deferred-lighting", target.Width(), target.Height(), pass) if err != nil { panic(err) } quad := vertex.ScreenQuad("geometry-pass-quad") lightsh := NewLightShader(app, pass, gbuffer, occlusion) samplers := make([]cache.SamplerCache, target.Frames()) lightbufs := make([]*LightBuffer, target.Frames()) shadowmaps := make([]*ShadowCache, target.Frames()) for i := range lightbufs { samplers[i] = cache.NewSamplerCache(app.Textures(), lightsh.Descriptors(i).Shadow) shadowmaps[i] = NewShadowCache(samplers[i], shadows.Shadowmap) lightbufs[i] = NewLightBuffer(256) } return &DeferredLightPass{ target: target, gbuffer: gbuffer, app: app, quad: quad, light: lightsh, pass: pass, fbuf: fbuf, shadows: shadowmaps, lightbufs: lightbufs, lightQuery: object.NewQuery[light.T](), } } func (p *DeferredLightPass) Record(cmds command.Recorder, args renderapi.Args, scene object.Component) { camera := CameraFromArgs(args) desc := p.light.Descriptors(args.Frame) desc.Camera.Set(camera) lightbuf := p.lightbufs[args.Frame] shadows := p.shadows[args.Frame] lightbuf.Reset() // todo: perform frustum culling on light volumes lights := p.lightQuery.Reset().Collect(scene) for _, lit := range lights { lightbuf.Store(lit.LightData(shadows)) } lightbuf.Flush(desc.Lights) shadows.Flush() quad := p.app.Meshes().Fetch(p.quad) cmds.Record(func(cmd command.Buffer) { cmd.CmdBeginRenderPass(p.pass, p.fbuf[args.Frame]) p.light.Bind(cmd, args.Frame) quad.Draw(cmd, 0) cmd.CmdEndRenderPass() }) } func (p *DeferredLightPass) Name() string { return "Deferred Lighting" } func (p *DeferredLightPass) Destroy() { for _, cache := range p.samplers { cache.Destroy() } p.samplers = nil p.lightbufs = nil p.fbuf.Destroy() p.fbuf = nil p.pass.Destroy() p.pass = nil p.light.Destroy() p.light = nil }