Examples/DeferredShading: Add god rays (WIP)
This commit is contained in:
parent
a8fefeb833
commit
a6b8caa5ba
|
|
@ -0,0 +1,95 @@
|
||||||
|
[layout(std140)]
|
||||||
|
struct ViewerData
|
||||||
|
{
|
||||||
|
projectionMatrix: mat4<f32>,
|
||||||
|
invProjectionMatrix: mat4<f32>,
|
||||||
|
viewMatrix: mat4<f32>,
|
||||||
|
invViewMatrix: mat4<f32>,
|
||||||
|
viewProjMatrix: mat4<f32>,
|
||||||
|
invViewProjMatrix: mat4<f32>,
|
||||||
|
renderTargetSize: vec2<f32>,
|
||||||
|
invRenderTargetSize: vec2<f32>,
|
||||||
|
eyePosition: vec3<f32>
|
||||||
|
}
|
||||||
|
|
||||||
|
[layout(std140)]
|
||||||
|
struct Settings
|
||||||
|
{
|
||||||
|
exposure: f32,
|
||||||
|
decay: f32,
|
||||||
|
density: f32,
|
||||||
|
weight: f32,
|
||||||
|
lightPosition: vec2<f32>, //< Switch to world position
|
||||||
|
}
|
||||||
|
|
||||||
|
const SampleCount: i32 = 200;
|
||||||
|
|
||||||
|
external
|
||||||
|
{
|
||||||
|
[set(0), binding(0)] viewerData: uniform<ViewerData>,
|
||||||
|
[set(0), binding(1)] settings: uniform<Settings>,
|
||||||
|
[set(0), binding(2)] occluderTexture: sampler2D<f32>
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FragIn
|
||||||
|
{
|
||||||
|
[builtin(fragcoord)] fragcoord: vec4<f32>,
|
||||||
|
[location(0)] uv: vec2<f32>
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FragOut
|
||||||
|
{
|
||||||
|
[location(0)] color: vec4<f32>
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VertIn
|
||||||
|
{
|
||||||
|
[location(0)] pos: vec2<f32>,
|
||||||
|
[location(1)] uv: vec2<f32>
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VertOut
|
||||||
|
{
|
||||||
|
[builtin(position)] position: vec4<f32>,
|
||||||
|
[location(0)] uv: vec2<f32>
|
||||||
|
}
|
||||||
|
|
||||||
|
[entry(frag)]
|
||||||
|
fn main(input: FragIn) -> FragOut
|
||||||
|
{
|
||||||
|
let deltaUV = input.uv - settings.lightPosition;
|
||||||
|
deltaUV *= 1.0 / f32(SampleCount) * settings.density;
|
||||||
|
let illuminationDecay = 1.0;
|
||||||
|
|
||||||
|
let uv = input.uv;
|
||||||
|
|
||||||
|
let outputColor = vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
while (i < SampleCount)
|
||||||
|
{
|
||||||
|
uv -= deltaUV;
|
||||||
|
let sample = occluderTexture.Sample(uv);
|
||||||
|
|
||||||
|
sample *= illuminationDecay * settings.weight;
|
||||||
|
outputColor += sample;
|
||||||
|
|
||||||
|
illuminationDecay *= settings.decay;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let output: FragOut;
|
||||||
|
output.color = outputColor;
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
[entry(vert)]
|
||||||
|
fn main(input: VertIn) -> VertOut
|
||||||
|
{
|
||||||
|
let output: VertOut;
|
||||||
|
output.position = vec4<f32>(input.pos, 0.0, 1.0);
|
||||||
|
output.uv = input.uv;
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
@ -455,7 +455,7 @@ int main()
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Nz::ShaderBinding>> bloomBlendShaderBinding(BloomSubdivisionCount);
|
std::vector<std::shared_ptr<Nz::ShaderBinding>> bloomBlendShaderBinding(BloomSubdivisionCount);
|
||||||
|
|
||||||
// Fullscreen data
|
// Gamma correction
|
||||||
|
|
||||||
Nz::RenderPipelineLayoutInfo fullscreenPipelineLayoutInfo;
|
Nz::RenderPipelineLayoutInfo fullscreenPipelineLayoutInfo;
|
||||||
|
|
||||||
|
|
@ -475,6 +475,71 @@ int main()
|
||||||
|
|
||||||
fullscreenPipelineInfo.shaderModules.push_back(device->InstantiateShaderModule(Nz::ShaderStageType::Fragment | Nz::ShaderStageType::Vertex, Nz::ShaderLanguage::NazaraShader, resourceDir / "gamma.nzsl", {}));
|
fullscreenPipelineInfo.shaderModules.push_back(device->InstantiateShaderModule(Nz::ShaderStageType::Fragment | Nz::ShaderStageType::Vertex, Nz::ShaderLanguage::NazaraShader, resourceDir / "gamma.nzsl", {}));
|
||||||
|
|
||||||
|
// God rays
|
||||||
|
|
||||||
|
Nz::RenderPipelineLayoutInfo godraysPipelineLayoutInfo;
|
||||||
|
|
||||||
|
godraysPipelineLayoutInfo.bindings = {
|
||||||
|
{
|
||||||
|
{
|
||||||
|
0, 0,
|
||||||
|
Nz::ShaderBindingType::UniformBuffer,
|
||||||
|
Nz::ShaderStageType::Fragment,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0, 1,
|
||||||
|
Nz::ShaderBindingType::UniformBuffer,
|
||||||
|
Nz::ShaderStageType::Fragment,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0, 2,
|
||||||
|
Nz::ShaderBindingType::Texture,
|
||||||
|
Nz::ShaderStageType::Fragment,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Nz::RenderPipelineInfo godraysPipelineInfo;
|
||||||
|
godraysPipelineInfo.primitiveMode = Nz::PrimitiveMode::TriangleList;
|
||||||
|
godraysPipelineInfo.pipelineLayout = device->InstantiateRenderPipelineLayout(godraysPipelineLayoutInfo);
|
||||||
|
godraysPipelineInfo.vertexBuffers.push_back({
|
||||||
|
0,
|
||||||
|
fullscreenVertexDeclaration
|
||||||
|
});
|
||||||
|
|
||||||
|
godraysPipelineInfo.shaderModules.push_back(device->InstantiateShaderModule(Nz::ShaderStageType::Fragment | Nz::ShaderStageType::Vertex, Nz::ShaderLanguage::NazaraShader, resourceDir / "god_rays.nzsl", {}));
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::RenderPipeline> godraysPipeline = device->InstantiateRenderPipeline(godraysPipelineInfo);
|
||||||
|
|
||||||
|
Nz::FieldOffsets godraysFieldOffsets(Nz::StructLayout::Std140);
|
||||||
|
std::size_t gr_exposureOffset = godraysFieldOffsets.AddField(Nz::StructFieldType::Float1);
|
||||||
|
std::size_t gr_decayOffset = godraysFieldOffsets.AddField(Nz::StructFieldType::Float1);
|
||||||
|
std::size_t gr_densityOffset = godraysFieldOffsets.AddField(Nz::StructFieldType::Float1);
|
||||||
|
std::size_t gr_weightOffset = godraysFieldOffsets.AddField(Nz::StructFieldType::Float1);
|
||||||
|
std::size_t gr_lightPositionOffset = godraysFieldOffsets.AddField(Nz::StructFieldType::Float2);
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::ShaderBinding> godRaysShaderBinding = godraysPipelineInfo.pipelineLayout->AllocateShaderBinding(0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
uniformExposure = 0.0034f;
|
||||||
|
uniformDecay = 1.0f;
|
||||||
|
uniformDensity = 0.84f;
|
||||||
|
uniformWeight = 5.65f;
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::vector<Nz::UInt8> godRaysData(godraysFieldOffsets.GetSize());
|
||||||
|
Nz::AccessByOffset<float&>(godRaysData.data(), gr_exposureOffset) = 0.0034f;
|
||||||
|
Nz::AccessByOffset<float&>(godRaysData.data(), gr_decayOffset) = 0.9f;
|
||||||
|
Nz::AccessByOffset<float&>(godRaysData.data(), gr_densityOffset) = 0.84f;
|
||||||
|
Nz::AccessByOffset<float&>(godRaysData.data(), gr_weightOffset) = 5.65f;
|
||||||
|
Nz::AccessByOffset<Nz::Vector2f&>(godRaysData.data(), gr_lightPositionOffset) = Nz::Vector2f(0.5f, 0.1f);
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::AbstractBuffer> godRaysUBO = device->InstantiateBuffer(Nz::BufferType::Uniform);
|
||||||
|
godRaysUBO->Initialize(godRaysData.size(), Nz::BufferUsage::DeviceLocal | Nz::BufferUsage::Dynamic);
|
||||||
|
godRaysUBO->Fill(godRaysData.data(), 0, godRaysData.size());
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::ShaderBinding> godRaysBlitShaderBinding;
|
||||||
|
|
||||||
|
|
||||||
const std::shared_ptr<const Nz::VertexDeclaration>& lightingVertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_UV);
|
const std::shared_ptr<const Nz::VertexDeclaration>& lightingVertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_UV);
|
||||||
|
|
||||||
|
|
@ -614,6 +679,8 @@ int main()
|
||||||
std::size_t bloomOutput;
|
std::size_t bloomOutput;
|
||||||
std::vector<std::size_t> bloomTextures(BloomSubdivisionCount * 2);
|
std::vector<std::size_t> bloomTextures(BloomSubdivisionCount * 2);
|
||||||
std::size_t lightOutput;
|
std::size_t lightOutput;
|
||||||
|
std::size_t occluderTexture;
|
||||||
|
std::size_t godRaysTexture;
|
||||||
|
|
||||||
std::size_t toneMappingOutput;
|
std::size_t toneMappingOutput;
|
||||||
|
|
||||||
|
|
@ -672,6 +739,16 @@ int main()
|
||||||
Nz::PixelFormat::RGBA16F
|
Nz::PixelFormat::RGBA16F
|
||||||
});
|
});
|
||||||
|
|
||||||
|
occluderTexture = graph.AddAttachment({
|
||||||
|
"Occluder texture",
|
||||||
|
Nz::PixelFormat::RGBA8,
|
||||||
|
});
|
||||||
|
|
||||||
|
godRaysTexture = graph.AddAttachment({
|
||||||
|
"God rays texture",
|
||||||
|
Nz::PixelFormat::RGBA16F
|
||||||
|
});
|
||||||
|
|
||||||
bloomOutput = graph.AddAttachmentProxy("Bloom output", lightOutput);
|
bloomOutput = graph.AddAttachmentProxy("Bloom output", lightOutput);
|
||||||
|
|
||||||
unsigned int bloomSize = 50'000;
|
unsigned int bloomSize = 50'000;
|
||||||
|
|
@ -812,6 +889,44 @@ int main()
|
||||||
forwardPass.SetDepthStencilInput(depthBuffer1);
|
forwardPass.SetDepthStencilInput(depthBuffer1);
|
||||||
forwardPass.SetDepthStencilOutput(depthBuffer2);
|
forwardPass.SetDepthStencilOutput(depthBuffer2);
|
||||||
|
|
||||||
|
|
||||||
|
Nz::FramePass& occluderPass = graph.AddPass("Occluder pass");
|
||||||
|
occluderPass.SetCommandCallback([&](Nz::CommandBufferBuilder& builder, const Nz::Recti& renderArea)
|
||||||
|
{
|
||||||
|
builder.SetScissor(renderArea);
|
||||||
|
builder.SetViewport(renderArea);
|
||||||
|
|
||||||
|
builder.BindShaderBinding(0, *skyboxShaderBinding);
|
||||||
|
|
||||||
|
builder.BindIndexBuffer(*cubeMeshGfx->GetIndexBuffer(0));
|
||||||
|
builder.BindVertexBuffer(0, *cubeMeshGfx->GetVertexBuffer(0));
|
||||||
|
builder.BindPipeline(*skyboxPipeline);
|
||||||
|
|
||||||
|
builder.DrawIndexed(Nz::SafeCast<Nz::UInt32>(cubeMeshGfx->GetIndexCount(0)));
|
||||||
|
});
|
||||||
|
|
||||||
|
occluderPass.AddOutput(occluderTexture);
|
||||||
|
occluderPass.SetClearColor(0, Nz::Color::Black);
|
||||||
|
occluderPass.SetDepthStencilInput(depthBuffer1);
|
||||||
|
|
||||||
|
Nz::FramePass& godraysPass = graph.AddPass("Light scattering pass");
|
||||||
|
godraysPass.SetCommandCallback([&](Nz::CommandBufferBuilder& builder, const Nz::Recti& renderArea)
|
||||||
|
{
|
||||||
|
builder.SetScissor(renderArea);
|
||||||
|
builder.SetViewport(renderArea);
|
||||||
|
|
||||||
|
builder.BindShaderBinding(0, *godRaysShaderBinding);
|
||||||
|
|
||||||
|
builder.BindPipeline(*godraysPipeline);
|
||||||
|
builder.BindVertexBuffer(0, *fullscreenVertexBuffer);
|
||||||
|
|
||||||
|
builder.Draw(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
godraysPass.AddInput(occluderTexture);
|
||||||
|
godraysPass.AddOutput(godRaysTexture);
|
||||||
|
|
||||||
|
|
||||||
Nz::FramePass& bloomBrightPass = graph.AddPass("Bloom pass - extract bright pixels");
|
Nz::FramePass& bloomBrightPass = graph.AddPass("Bloom pass - extract bright pixels");
|
||||||
bloomBrightPass.SetCommandCallback([&](Nz::CommandBufferBuilder& builder, const Nz::Recti& renderArea)
|
bloomBrightPass.SetCommandCallback([&](Nz::CommandBufferBuilder& builder, const Nz::Recti& renderArea)
|
||||||
{
|
{
|
||||||
|
|
@ -893,6 +1008,10 @@ int main()
|
||||||
builder.BindShaderBinding(0, *bloomBlendShaderBinding[i]);
|
builder.BindShaderBinding(0, *bloomBlendShaderBinding[i]);
|
||||||
builder.Draw(3);
|
builder.Draw(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// God rays
|
||||||
|
builder.BindShaderBinding(0, *godRaysBlitShaderBinding);
|
||||||
|
builder.Draw(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
bloomBlendPass.SetExecutionCallback([&]
|
bloomBlendPass.SetExecutionCallback([&]
|
||||||
|
|
@ -901,6 +1020,7 @@ int main()
|
||||||
});
|
});
|
||||||
|
|
||||||
bloomBlendPass.AddInput(lightOutput);
|
bloomBlendPass.AddInput(lightOutput);
|
||||||
|
bloomBlendPass.AddInput(godRaysTexture);
|
||||||
bloomBlendPass.SetReadInput(0, false);
|
bloomBlendPass.SetReadInput(0, false);
|
||||||
|
|
||||||
for (std::size_t i = 0; i < BloomSubdivisionCount; ++i)
|
for (std::size_t i = 0; i < BloomSubdivisionCount; ++i)
|
||||||
|
|
@ -1004,6 +1124,7 @@ int main()
|
||||||
float rotationSpeed = ComputeLightAnimationSpeed(viewerPos);
|
float rotationSpeed = ComputeLightAnimationSpeed(viewerPos);
|
||||||
|
|
||||||
auto& whiteLight = spotLights.emplace_back();
|
auto& whiteLight = spotLights.emplace_back();
|
||||||
|
whiteLight.color = Nz::Color(100, 100, 255);
|
||||||
whiteLight.radius = 5.f;
|
whiteLight.radius = 5.f;
|
||||||
whiteLight.position = AnimateLightPosition(viewerPos, rotationSpeed, -elapsedTime);
|
whiteLight.position = AnimateLightPosition(viewerPos, rotationSpeed, -elapsedTime);
|
||||||
whiteLight.direction = AnimateLightDirection(camQuat * Nz::Vector3f::Forward(), rotationSpeed, -elapsedTime);
|
whiteLight.direction = AnimateLightDirection(camQuat * Nz::Vector3f::Forward(), rotationSpeed, -elapsedTime);
|
||||||
|
|
@ -1231,6 +1352,33 @@ int main()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frame.PushForRelease(std::move(godRaysShaderBinding));
|
||||||
|
|
||||||
|
godRaysShaderBinding = godraysPipelineInfo.pipelineLayout->AllocateShaderBinding(0);
|
||||||
|
godRaysShaderBinding->Update({
|
||||||
|
{
|
||||||
|
0,
|
||||||
|
Nz::ShaderBinding::UniformBufferBinding {
|
||||||
|
viewerInstance.GetViewerBuffer().get(),
|
||||||
|
0, viewerInstance.GetViewerBuffer()->GetSize()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
1,
|
||||||
|
Nz::ShaderBinding::UniformBufferBinding {
|
||||||
|
godRaysUBO.get(),
|
||||||
|
0, godRaysUBO->GetSize()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
2,
|
||||||
|
Nz::ShaderBinding::TextureBinding {
|
||||||
|
bakedGraph.GetAttachmentTexture(occluderTexture).get(),
|
||||||
|
textureSampler.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
frame.PushForRelease(std::move(toneMappingShaderBinding));
|
frame.PushForRelease(std::move(toneMappingShaderBinding));
|
||||||
|
|
||||||
toneMappingShaderBinding = fullscreenPipelineInfoViewer.pipelineLayout->AllocateShaderBinding(0);
|
toneMappingShaderBinding = fullscreenPipelineInfoViewer.pipelineLayout->AllocateShaderBinding(0);
|
||||||
|
|
@ -1251,6 +1399,33 @@ int main()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frame.PushForRelease(std::move(godRaysBlitShaderBinding));
|
||||||
|
|
||||||
|
godRaysBlitShaderBinding = bloomBlendPipelineInfo.pipelineLayout->AllocateShaderBinding(0);
|
||||||
|
godRaysBlitShaderBinding->Update({
|
||||||
|
{
|
||||||
|
0,
|
||||||
|
Nz::ShaderBinding::UniformBufferBinding {
|
||||||
|
viewerInstance.GetViewerBuffer().get(),
|
||||||
|
0, viewerInstance.GetViewerBuffer()->GetSize()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/*{
|
||||||
|
1,
|
||||||
|
Nz::ShaderBinding::TextureBinding {
|
||||||
|
bakedGraph.GetAttachmentTexture(lightOutput).get(),
|
||||||
|
textureSampler.get()
|
||||||
|
}
|
||||||
|
},*/
|
||||||
|
{
|
||||||
|
2,
|
||||||
|
Nz::ShaderBinding::TextureBinding {
|
||||||
|
bakedGraph.GetAttachmentTexture(godRaysTexture).get(),
|
||||||
|
textureSampler.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
frame.PushForRelease(std::move(finalBlitBinding));
|
frame.PushForRelease(std::move(finalBlitBinding));
|
||||||
|
|
||||||
finalBlitBinding = fullscreenPipelineInfo.pipelineLayout->AllocateShaderBinding(0);
|
finalBlitBinding = fullscreenPipelineInfo.pipelineLayout->AllocateShaderBinding(0);
|
||||||
|
|
@ -1279,6 +1454,7 @@ int main()
|
||||||
|
|
||||||
viewerInstance.UpdateBuffers(uploadPool, builder);
|
viewerInstance.UpdateBuffers(uploadPool, builder);
|
||||||
|
|
||||||
|
// Update light buffer
|
||||||
if (!spotLights.empty() && (lightUpdate || lightAnimation))
|
if (!spotLights.empty() && (lightUpdate || lightAnimation))
|
||||||
{
|
{
|
||||||
auto& lightDataAllocation = uploadPool.Allocate(alignedSpotLightSize * spotLights.size());
|
auto& lightDataAllocation = uploadPool.Allocate(alignedSpotLightSize * spotLights.size());
|
||||||
|
|
@ -1309,6 +1485,26 @@ int main()
|
||||||
builder.CopyBuffer(lightDataAllocation, lightUbo.get());
|
builder.CopyBuffer(lightDataAllocation, lightUbo.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update light scattering buffer
|
||||||
|
{
|
||||||
|
Nz::Vector4f pos(0.f, 200.f, 0.f, 1.f);
|
||||||
|
pos = viewerInstance.GetViewMatrix() * pos;
|
||||||
|
pos = viewerInstance.GetProjectionMatrix() * pos;
|
||||||
|
pos /= pos.w;
|
||||||
|
|
||||||
|
Nz::Vector2f& lightPosition = Nz::AccessByOffset<Nz::Vector2f&>(godRaysData.data(), gr_lightPositionOffset);
|
||||||
|
lightPosition = Nz::Vector2f(pos.x * 0.5f + 0.5f, pos.y * 0.5f + 0.5f);
|
||||||
|
lightPosition.x = Nz::Clamp(std::abs(lightPosition.x), -0.5f, 1.5f);
|
||||||
|
lightPosition.y = Nz::Clamp(std::abs(lightPosition.y), -0.5f, 1.5f);
|
||||||
|
|
||||||
|
auto& lightScatteringAllocation = uploadPool.Allocate(godRaysData.size());
|
||||||
|
Nz::UInt8* dataPtr = static_cast<Nz::UInt8*>(lightScatteringAllocation.mappedPtr);
|
||||||
|
|
||||||
|
std::memcpy(dataPtr, godRaysData.data(), godRaysData.size());
|
||||||
|
|
||||||
|
builder.CopyBuffer(lightScatteringAllocation, godRaysUBO.get());
|
||||||
|
}
|
||||||
|
|
||||||
matUpdate = spaceshipMatPass->Update(frame, builder) || matUpdate;
|
matUpdate = spaceshipMatPass->Update(frame, builder) || matUpdate;
|
||||||
matUpdate = planeMatPass->Update(frame, builder) || matUpdate;
|
matUpdate = planeMatPass->Update(frame, builder) || matUpdate;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue