Skip to content

Commit

Permalink
Merge branch 'deferred-direct-light'
Browse files Browse the repository at this point in the history
  • Loading branch information
Nelarius committed Apr 22, 2024
2 parents 23fab5d + 51dbf12 commit 2f59399
Show file tree
Hide file tree
Showing 12 changed files with 902 additions and 106 deletions.
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -39,6 +39,9 @@ An interactive experimental pathtracer, implemented using WebGPU via the [Dawn](
- [Sky Dome Appearance Project](https://cgg.mff.cuni.cz/projects/SkylightModelling/)
- The original source of the sky and solar disk model
- The following [Rust implementation](https://github.com/phoekz/hw-skymodel) serves as the basis of the much simplified C code used in this project.
- Depth precision and reverse Z projection
- [Depth Precision Visualized](https://www.reedbeta.com/blog/depth-precision-visualized/)
- [Reverse Z (and why it's so awesome)](https://tomhultonharrop.com/mathematics/graphics/2023/08/06/reverse-z.html)

## Build

Expand Down
15 changes: 0 additions & 15 deletions src/common/camera.cpp
Expand Up @@ -49,19 +49,4 @@ Ray generateCameraRay(const Camera& camera, const float u, const float v)

return Ray{.origin = origin, .direction = glm::normalize(direction)};
}

glm::mat4 generateViewProjectionMatrix(
glm::vec3 origin,
glm::vec3 lookAt,
Angle vfov,
float aspectRatio)
{
const glm::vec3 forward = glm::normalize(lookAt - origin);
const glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f);
const glm::vec3 right = glm::normalize(glm::cross(forward, worldUp));
const glm::vec3 up = glm::cross(right, forward);

return glm::perspective(vfov.asRadians(), aspectRatio, 0.1f, 1000.0f) *
glm::lookAt(origin, lookAt, up);
}
} // namespace nlrs
6 changes: 0 additions & 6 deletions src/common/camera.hpp
Expand Up @@ -32,10 +32,4 @@ Camera createCamera(
// (u, v) are in [0, 1] range, where (0, 0) is the lower left corner and (1, 1) is the upper right
// corner.
Ray generateCameraRay(const Camera& camera, float u, float v);

glm::mat4 generateViewProjectionMatrix(
glm::vec3 origin,
glm::vec3 lookAt,
Angle vfov,
float aspectRatio);
} // namespace nlrs
172 changes: 152 additions & 20 deletions src/pt/deferred_renderer.cpp
@@ -1,4 +1,5 @@
#include "gpu_context.hpp"
#include "gpu_limits.hpp"
#include "deferred_renderer.hpp"
#include "shader_source.hpp"
#include "webgpu_utils.hpp"
Expand All @@ -14,7 +15,7 @@ namespace nlrs
{
namespace
{
const WGPUTextureFormat DEPTH_TEXTURE_FORMAT = WGPUTextureFormat_Depth24Plus;
const WGPUTextureFormat DEPTH_TEXTURE_FORMAT = WGPUTextureFormat_Depth32Float;
const WGPUTextureFormat ALBEDO_TEXTURE_FORMAT = WGPUTextureFormat_BGRA8Unorm;
const WGPUTextureFormat NORMAL_TEXTURE_FORMAT = WGPUTextureFormat_RGBA16Float;

Expand Down Expand Up @@ -184,7 +185,13 @@ DeferredRenderer::DeferredRenderer(
}

mDebugPass = DebugPass{gpuContext, mGbufferBindGroupLayout, rendererDesc.framebufferSize};
mLightingPass = LightingPass{gpuContext, mGbufferBindGroupLayout};
mLightingPass = LightingPass{
gpuContext,
mGbufferBindGroupLayout,
rendererDesc.sceneBvhNodes,
rendererDesc.scenePositionAttributes,
rendererDesc.sceneVertexAttributes,
rendererDesc.sceneBaseColorTextures};
}

DeferredRenderer::~DeferredRenderer()
Expand Down Expand Up @@ -227,7 +234,7 @@ void DeferredRenderer::render(const GpuContext& gpuContext, const RenderDescript
offsetof(TimestampsLayout, gbufferPassStart) / TimestampsLayout::MEMBER_SIZE);
mGbufferPass.render(
gpuContext,
renderDesc.viewProjectionMatrix,
renderDesc.viewReverseZProjectionMatrix,
encoder,
mDepthTextureView,
mAlbedoTextureView,
Expand All @@ -242,8 +249,9 @@ void DeferredRenderer::render(const GpuContext& gpuContext, const RenderDescript
mQuerySet,
offsetof(TimestampsLayout, lightingPassStart) / TimestampsLayout::MEMBER_SIZE);
{
const glm::mat4 inverseViewProjectionMat = glm::inverse(renderDesc.viewProjectionMatrix);
const Extent2f framebufferSize = Extent2f(renderDesc.framebufferSize);
const glm::mat4 inverseViewProjectionMat =
glm::inverse(renderDesc.viewReverseZProjectionMatrix);
const Extent2f framebufferSize = Extent2f(renderDesc.framebufferSize);
mLightingPass.render(
gpuContext,
mGbufferBindGroup,
Expand Down Expand Up @@ -498,13 +506,13 @@ DeferredRenderer::GbufferPass::GbufferPass(
return buffers;
}()),
mBaseColorTextureIndices(
rendererDesc.baseColorTextureIndices.begin(),
rendererDesc.baseColorTextureIndices.end()),
rendererDesc.modelBaseColorTextureIndices.begin(),
rendererDesc.modelBaseColorTextureIndices.end()),
mBaseColorTextures([&gpuContext, &rendererDesc]() -> std::vector<GpuTexture> {
std::vector<GpuTexture> textures;
std::transform(
rendererDesc.baseColorTextures.begin(),
rendererDesc.baseColorTextures.end(),
rendererDesc.sceneBaseColorTextures.begin(),
rendererDesc.sceneBaseColorTextures.end(),
std::back_inserter(textures),
[&gpuContext](const Texture& texture) -> GpuTexture {
const auto dimensions = texture.dimensions();
Expand Down Expand Up @@ -713,7 +721,7 @@ DeferredRenderer::GbufferPass::GbufferPass(
.nextInChain = nullptr,
.format = DEPTH_TEXTURE_FORMAT,
.depthWriteEnabled = true,
.depthCompare = WGPUCompareFunction_Less,
.depthCompare = WGPUCompareFunction_Greater,
.stencilFront = DEFAULT_STENCIL_FACE_STATE,
.stencilBack = DEFAULT_STENCIL_FACE_STATE,
.stencilReadMask = 0, // stencil masks deactivated by setting to zero
Expand Down Expand Up @@ -842,14 +850,18 @@ DeferredRenderer::GbufferPass::~GbufferPass()

void DeferredRenderer::GbufferPass::render(
const GpuContext& gpuContext,
const glm::mat4& viewProjectionMat,
const glm::mat4& viewReverseZProjectionMatrix,
const WGPUCommandEncoder cmdEncoder,
const WGPUTextureView depthTextureView,
const WGPUTextureView albedoTextureView,
const WGPUTextureView normalTextureView)
{
wgpuQueueWriteBuffer(
gpuContext.queue, mUniformBuffer.ptr(), 0, &viewProjectionMat[0][0], sizeof(glm::mat4));
gpuContext.queue,
mUniformBuffer.ptr(),
0,
&viewReverseZProjectionMatrix[0][0],
sizeof(glm::mat4));

const WGPURenderPassEncoder renderPassEncoder = [cmdEncoder,
depthTextureView,
Expand Down Expand Up @@ -879,9 +891,9 @@ void DeferredRenderer::GbufferPass::render(
.view = depthTextureView,
.depthLoadOp = WGPULoadOp_Clear,
.depthStoreOp = WGPUStoreOp_Store,
.depthClearValue = 1.0f,
.depthClearValue = 0.0f,
.depthReadOnly = false,
.stencilLoadOp = WGPULoadOp_Undefined, // ops must not be set if no stencil aspect
.stencilLoadOp = WGPULoadOp_Undefined,
.stencilStoreOp = WGPUStoreOp_Undefined,
.stencilClearValue = 0,
.stencilReadOnly = true,
Expand Down Expand Up @@ -1177,8 +1189,12 @@ void DeferredRenderer::DebugPass::render(
}

DeferredRenderer::LightingPass::LightingPass(
const GpuContext& gpuContext,
const GpuBindGroupLayout& gbufferBindGroupLayout)
const GpuContext& gpuContext,
const GpuBindGroupLayout& gbufferBindGroupLayout,
std::span<const BvhNode> sceneBvhNodes,
std::span<const PositionAttribute> scenePositionAttributes,
std::span<const VertexAttributes> sceneVertexAttributes,
std::span<const Texture> sceneBaseColorTextures)
: mCurrentSky{},
mVertexBuffer{
gpuContext.device,
Expand All @@ -1197,6 +1213,24 @@ DeferredRenderer::LightingPass::LightingPass(
GpuBufferUsage::Uniform | GpuBufferUsage::CopyDst,
sizeof(Uniforms)},
mUniformBindGroup{},
mBvhNodeBuffer{
gpuContext.device,
"BVH node buffer",
GpuBufferUsage::ReadOnlyStorage | GpuBufferUsage::CopyDst,
std::span<const BvhNode>(sceneBvhNodes)},
mPositionAttributesBuffer{
gpuContext.device,
"Position attribute buffer",
GpuBufferUsage::ReadOnlyStorage | GpuBufferUsage::CopyDst,
std::span<const PositionAttribute>(scenePositionAttributes)},
mVertexAttributesBuffer{
gpuContext.device,
"Vertex attribute buffer",
GpuBufferUsage::ReadOnlyStorage | GpuBufferUsage::CopyDst,
std::span<const VertexAttributes>(sceneVertexAttributes)},
mTextureDescriptorBuffer{},
mTextureBuffer{},
mBvhBindGroup{},
mPipeline(nullptr)
{
{
Expand Down Expand Up @@ -1227,13 +1261,96 @@ DeferredRenderer::LightingPass::LightingPass(
uniformBindGroupLayout.ptr(),
mUniformBuffer.bindGroupEntry(0)};

{
struct TextureDescriptor
{
std::uint32_t width, height, offset;
};
// Ensure matches layout of `TextureDescriptor` definition in shader.
std::vector<TextureDescriptor> textureDescriptors;
textureDescriptors.reserve(sceneBaseColorTextures.size());

std::vector<Texture::BgraPixel> textureData;
textureData.reserve((2 << 28) / sizeof(Texture::BgraPixel));

// Texture descriptors and texture data need to appended in the order of
// sceneBaseColorTextures. The vertex attribute's `textureIdx` indexes into that array, and
// we want to use the same indices to index into the texture descriptor array.

for (const Texture& baseColorTexture : sceneBaseColorTextures)
{
const auto dimensions = baseColorTexture.dimensions();
const auto pixels = baseColorTexture.pixels();

const std::uint32_t width = dimensions.width;
const std::uint32_t height = dimensions.height;
const std::uint32_t offset = static_cast<std::uint32_t>(textureData.size());

textureData.resize(textureData.size() + pixels.size());
std::memcpy(
textureData.data() + offset,
pixels.data(),
pixels.size() * sizeof(Texture::BgraPixel));

textureDescriptors.push_back({width, height, offset});
}

mTextureDescriptorBuffer = GpuBuffer(
gpuContext.device,
"texture descriptor buffer",
GpuBufferUsage::ReadOnlyStorage | GpuBufferUsage::CopyDst,
std::span<const TextureDescriptor>(textureDescriptors));

const std::size_t textureDataNumBytes = textureData.size() * sizeof(Texture::BgraPixel);
const std::size_t maxStorageBufferBindingSize =
static_cast<std::size_t>(REQUIRED_LIMITS.maxStorageBufferBindingSize);
if (textureDataNumBytes > maxStorageBufferBindingSize)
{
throw std::runtime_error(fmt::format(
"Texture buffer size ({}) exceeds "
"maxStorageBufferBindingSize ({}).",
textureDataNumBytes,
maxStorageBufferBindingSize));
}

mTextureBuffer = GpuBuffer(
gpuContext.device,
"texture buffer",
GpuBufferUsage::ReadOnlyStorage | GpuBufferUsage::CopyDst,
std::span<const Texture::BgraPixel>(textureData));
}

const GpuBindGroupLayout bvhBindGroupLayout{
gpuContext.device,
"Scene bind group layout",
std::array<WGPUBindGroupLayoutEntry, 5>{
mBvhNodeBuffer.bindGroupLayoutEntry(0, WGPUShaderStage_Fragment),
mPositionAttributesBuffer.bindGroupLayoutEntry(1, WGPUShaderStage_Fragment),
mVertexAttributesBuffer.bindGroupLayoutEntry(2, WGPUShaderStage_Fragment),
mTextureDescriptorBuffer.bindGroupLayoutEntry(3, WGPUShaderStage_Fragment),
mTextureBuffer.bindGroupLayoutEntry(4, WGPUShaderStage_Fragment),
}};

mBvhBindGroup = GpuBindGroup{
gpuContext.device,
"Lighting pass BVH bind group",
bvhBindGroupLayout.ptr(),
std::array<WGPUBindGroupEntry, 5>{
mBvhNodeBuffer.bindGroupEntry(0),
mPositionAttributesBuffer.bindGroupEntry(1),
mVertexAttributesBuffer.bindGroupEntry(2),
mTextureDescriptorBuffer.bindGroupEntry(3),
mTextureBuffer.bindGroupEntry(4),
}};

{
// Pipeline layout

const WGPUBindGroupLayout bindGroupLayouts[] = {
skyStateBindGroupLayout.ptr(),
uniformBindGroupLayout.ptr(),
gbufferBindGroupLayout.ptr()};
gbufferBindGroupLayout.ptr(),
bvhBindGroupLayout.ptr()};

const WGPUPipelineLayoutDescriptor pipelineLayoutDesc{
.nextInChain = nullptr,
Expand Down Expand Up @@ -1370,8 +1487,15 @@ DeferredRenderer::LightingPass::LightingPass(LightingPass&& other) noexcept
mSkyStateBindGroup = std::move(other.mSkyStateBindGroup);
mUniformBuffer = std::move(other.mUniformBuffer);
mUniformBindGroup = std::move(other.mUniformBindGroup);
mBvhNodeBuffer = std::move(other.mBvhNodeBuffer);
mPositionAttributesBuffer = std::move(other.mPositionAttributesBuffer);
mVertexAttributesBuffer = std::move(other.mVertexAttributesBuffer);
mTextureDescriptorBuffer = std::move(other.mTextureDescriptorBuffer);
mTextureBuffer = std::move(other.mTextureBuffer);
mBvhBindGroup = std::move(other.mBvhBindGroup);
mPipeline = other.mPipeline;
other.mPipeline = nullptr;
mFrameCount = other.mFrameCount;
}
}

Expand All @@ -1386,9 +1510,16 @@ DeferredRenderer::LightingPass& DeferredRenderer::LightingPass::operator=(
mSkyStateBindGroup = std::move(other.mSkyStateBindGroup);
mUniformBuffer = std::move(other.mUniformBuffer);
mUniformBindGroup = std::move(other.mUniformBindGroup);
mBvhNodeBuffer = std::move(other.mBvhNodeBuffer);
mPositionAttributesBuffer = std::move(other.mPositionAttributesBuffer);
mVertexAttributesBuffer = std::move(other.mVertexAttributesBuffer);
mTextureDescriptorBuffer = std::move(other.mTextureDescriptorBuffer);
mTextureBuffer = std::move(other.mTextureBuffer);
mBvhBindGroup = std::move(other.mBvhBindGroup);
renderPipelineSafeRelease(mPipeline);
mPipeline = other.mPipeline;
other.mPipeline = nullptr;
mFrameCount = other.mFrameCount;
}
return *this;
}
Expand All @@ -1398,7 +1529,7 @@ void DeferredRenderer::LightingPass::render(
const GpuBindGroup& gbufferBindGroup,
const WGPUCommandEncoder cmdEncoder,
const WGPUTextureView targetTextureView,
const glm::mat4& inverseViewProjectionMat,
const glm::mat4& inverseViewReverseZProjectionMatrix,
const glm::vec3& cameraPosition,
const Extent2f& fbsize,
const Sky& sky,
Expand All @@ -1414,11 +1545,11 @@ void DeferredRenderer::LightingPass::render(

{
const Uniforms uniforms{
inverseViewProjectionMat,
inverseViewReverseZProjectionMatrix,
glm::vec4(cameraPosition, 1.f),
glm::vec2(fbsize.x, fbsize.y),
exposure,
0.f};
mFrameCount++};
wgpuQueueWriteBuffer(
gpuContext.queue, mUniformBuffer.ptr(), 0, &uniforms, sizeof(Uniforms));
}
Expand Down Expand Up @@ -1453,6 +1584,7 @@ void DeferredRenderer::LightingPass::render(
wgpuRenderPassEncoderSetBindGroup(renderPass, 0, mSkyStateBindGroup.ptr(), 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPass, 1, mUniformBindGroup.ptr(), 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPass, 2, gbufferBindGroup.ptr(), 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPass, 3, mBvhBindGroup.ptr(), 0, nullptr);
wgpuRenderPassEncoderSetVertexBuffer(
renderPass, 0, mVertexBuffer.ptr(), 0, mVertexBuffer.byteSize());
wgpuRenderPassEncoderDraw(renderPass, 6, 1, 0, 0);
Expand Down

0 comments on commit 2f59399

Please sign in to comment.