zworld/engine/render/pass/deferred_lighting.go
2024-01-14 22:56:06 +08:00

147 lines
3.6 KiB
Go

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
}