Skip to content

Commit

Permalink
Add sky pass
Browse files Browse the repository at this point in the history
  • Loading branch information
Nelarius committed Mar 29, 2024
1 parent 510d6e2 commit 92ae60c
Show file tree
Hide file tree
Showing 9 changed files with 605 additions and 65 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ set(WGSL_SHADER_FILES
reference_path_tracer.wgsl
texture_blit.wgsl
hybrid_renderer_gbuffer_pass.wgsl
hybrid_renderer_debug_pass.wgsl)
hybrid_renderer_debug_pass.wgsl
hybrid_renderer_sky_pass.wgsl)

set(SHADER_SOURCE_HEADER_FILE src/pt/shader_source.hpp)

Expand Down
72 changes: 72 additions & 0 deletions src/pt/aligned_sky_state.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#pragma once

#include <common/assert.hpp>
#include <common/units/angle.hpp>

#include <glm/glm.hpp>
#include <hw-skymodel/hw_skymodel.h>

#include <array>
#include <cstring>
#include <numbers>

namespace nlrs
{
struct Sky
{
float turbidity = 1.0f;
std::array<float, 3> albedo = {1.0f, 1.0f, 1.0f};
float sunZenithDegrees = 30.0f;
float sunAzimuthDegrees = 0.0f;

bool operator==(const Sky&) const noexcept = default;
};

// A 16-byte aligned sky state for the hw-skymodel library. Matches the layout of the following WGSL
// struct:
//
// struct SkyState {
// params: array<f32, 27>,
// skyRadiances: array<f32, 3>,
// solarRadiances: array<f32, 3>,
// sunDirection: vec3<f32>,
// };
struct AlignedSkyState
{
float params[27]; // offset: 0
float skyRadiances[3]; // offset: 27
float solarRadiances[3]; // offset: 30
float padding1[3]; // offset: 33
glm::vec3 sunDirection; // offset: 36
float padding2; // offset: 39

inline AlignedSkyState(const Sky& sky)
: params{0},
skyRadiances{0},
solarRadiances{0},
padding1{0.f, 0.f, 0.f},
sunDirection(0.f),
padding2(0.0f)
{
const float sunZenith = Angle::degrees(sky.sunZenithDegrees).asRadians();
const float sunAzimuth = Angle::degrees(sky.sunAzimuthDegrees).asRadians();

sunDirection = glm::normalize(glm::vec3(
std::sin(sunZenith) * std::cos(sunAzimuth),
std::cos(sunZenith),
-std::sin(sunZenith) * std::sin(sunAzimuth)));

const sky_params skyParams{
.elevation = 0.5f * std::numbers::pi_v<float> - sunZenith,
.turbidity = sky.turbidity,
.albedo = {sky.albedo[0], sky.albedo[1], sky.albedo[2]}};

sky_state skyState;
NLRS_ASSERT(sky_state_new(&skyParams, &skyState) == sky_state_result_success);

std::memcpy(params, skyState.params, sizeof(skyState.params));
std::memcpy(skyRadiances, skyState.sky_radiances, sizeof(skyState.sky_radiances));
std::memcpy(solarRadiances, skyState.solar_radiances, sizeof(skyState.solar_radiances));
}
};
} // namespace nlrs
247 changes: 243 additions & 4 deletions src/pt/hybrid_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ HybridRenderer::HybridRenderer(
mGbufferBindGroupLayout(),
mGbufferBindGroup(),
mGbufferPass(gpuContext, rendererDesc),
mDebugPass()
mDebugPass(),
mSkyPass(gpuContext)
{
{
const std::array<WGPUTextureFormat, 1> depthFormats{
Expand Down Expand Up @@ -169,8 +170,10 @@ HybridRenderer::~HybridRenderer()

void HybridRenderer::render(
const GpuContext& gpuContext,
const WGPUTextureView textureView,
const glm::mat4& viewProjectionMat)
const glm::mat4& viewProjectionMat,
const glm::vec3& cameraPosition,
const Sky& sky,
const WGPUTextureView textureView)
{
wgpuDeviceTick(gpuContext.device);

Expand All @@ -190,7 +193,7 @@ void HybridRenderer::render(
mAlbedoTextureView,
mNormalTextureView);

mDebugPass.render(mGbufferBindGroup, encoder, textureView);
mSkyPass.render(gpuContext, viewProjectionMat, cameraPosition, sky, encoder, textureView);

const WGPUCommandBuffer cmdBuffer = [encoder]() {
const WGPUCommandBufferDescriptor cmdBufferDesc{
Expand Down Expand Up @@ -1020,4 +1023,240 @@ void HybridRenderer::DebugPass::resize(const GpuContext& gpuContext, const Exten
wgpuQueueWriteBuffer(
gpuContext.queue, mUniformBuffer.ptr(), 0, &uniformData.x, sizeof(Extent2<float>));
}

HybridRenderer::SkyPass::SkyPass(const GpuContext& gpuContext)
: mCurrentSky{},
mVertexBuffer{
gpuContext.device,
"Sky vertex buffer",
GpuBufferUsage::Vertex | GpuBufferUsage::CopyDst,
std::span<const float[2]>(quadVertexData)},
mSkyStateBuffer{
gpuContext.device,
"Sky state buffer",
GpuBufferUsage::ReadOnlyStorage | GpuBufferUsage::CopyDst,
sizeof(AlignedSkyState)},
mSkyStateBindGroup{},
mUniformBuffer{
gpuContext.device,
"Sky uniform buffer",
GpuBufferUsage::Uniform | GpuBufferUsage::CopyDst,
sizeof(Uniforms)},
mUniformBindGroup{},
mPipeline(nullptr)
{
{
const AlignedSkyState skyState{mCurrentSky};
wgpuQueueWriteBuffer(
gpuContext.queue, mSkyStateBuffer.ptr(), 0, &skyState, sizeof(AlignedSkyState));
}

const GpuBindGroupLayout skyStateBindGroupLayout{
gpuContext.device,
"Sky pass sky state bind group layout",
mSkyStateBuffer.bindGroupLayoutEntry(0, WGPUShaderStage_Fragment, sizeof(AlignedSkyState))};

mSkyStateBindGroup = GpuBindGroup{
gpuContext.device,
"Sky pass sky state bind group",
skyStateBindGroupLayout.ptr(),
mSkyStateBuffer.bindGroupEntry(0)};

const GpuBindGroupLayout uniformBindGroupLayout{
gpuContext.device,
"Sky passs uniform bind group layout",
mUniformBuffer.bindGroupLayoutEntry(0, WGPUShaderStage_Fragment, sizeof(Uniforms))};

mUniformBindGroup = GpuBindGroup{
gpuContext.device,
"Sky pass uniform bind group",
uniformBindGroupLayout.ptr(),
mUniformBuffer.bindGroupEntry(0)};

{
// Pipeline layout

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

const WGPUPipelineLayoutDescriptor pipelineLayoutDesc{
.nextInChain = nullptr,
.label = "Sky pass pipeline layout",
.bindGroupLayoutCount = std::size(bindGroupLayouts),
.bindGroupLayouts = bindGroupLayouts,
};

const WGPUPipelineLayout pipelineLayout =
wgpuDeviceCreatePipelineLayout(gpuContext.device, &pipelineLayoutDesc);

// Vertex layout

const WGPUVertexAttribute vertexAttributes[] = {WGPUVertexAttribute{
.format = WGPUVertexFormat_Float32x2,
.offset = 0,
.shaderLocation = 0,
}};

const WGPUVertexBufferLayout vertexBufferLayout{
.arrayStride = sizeof(float[2]),
.stepMode = WGPUVertexStepMode_Vertex,
.attributeCount = std::size(vertexAttributes),
.attributes = vertexAttributes,
};

// Shader module

const WGPUShaderModule shaderModule = [&gpuContext]() -> WGPUShaderModule {
const WGPUShaderModuleWGSLDescriptor wgslDesc = {
.chain =
WGPUChainedStruct{
.next = nullptr,
.sType = WGPUSType_ShaderModuleWGSLDescriptor,
},
.code = HYBRID_RENDERER_SKY_PASS_SOURCE,
};

const WGPUShaderModuleDescriptor moduleDesc{
.nextInChain = &wgslDesc.chain,
.label = "Sky pass shader",
};

return wgpuDeviceCreateShaderModule(gpuContext.device, &moduleDesc);
}();
NLRS_ASSERT(shaderModule != nullptr);

// Fragment state

const WGPUBlendState blendState{
.color =
WGPUBlendComponent{
.operation = WGPUBlendOperation_Add,
.srcFactor = WGPUBlendFactor_One,
.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha,
},
.alpha =
WGPUBlendComponent{
.operation = WGPUBlendOperation_Add,
.srcFactor = WGPUBlendFactor_Zero,
.dstFactor = WGPUBlendFactor_One,
},
};

const WGPUColorTargetState colorTargets[] = {WGPUColorTargetState{
.nextInChain = nullptr,
.format = Window::SWAP_CHAIN_FORMAT,
.blend = &blendState,
.writeMask = WGPUColorWriteMask_All}};

const WGPUFragmentState fragmentState{
.nextInChain = nullptr,
.module = shaderModule,
.entryPoint = "fsMain",
.constantCount = 0,
.constants = nullptr,
.targetCount = std::size(colorTargets),
.targets = colorTargets,
};

// Pipeline

const WGPURenderPipelineDescriptor pipelineDesc{
.nextInChain = nullptr,
.label = "Sky pass render pipeline",
.layout = pipelineLayout,
.vertex =
WGPUVertexState{
.nextInChain = nullptr,
.module = shaderModule,
.entryPoint = "vsMain",
.constantCount = 0,
.constants = nullptr,
.bufferCount = 1,
.buffers = &vertexBufferLayout,
},
.primitive =
WGPUPrimitiveState{
.nextInChain = nullptr,
.topology = WGPUPrimitiveTopology_TriangleList,
.stripIndexFormat = WGPUIndexFormat_Undefined,
.frontFace = WGPUFrontFace_CCW,
.cullMode = WGPUCullMode_Back,
},
.depthStencil = nullptr,
.multisample =
WGPUMultisampleState{
.nextInChain = nullptr,
.count = 1,
.mask = ~0u,
.alphaToCoverageEnabled = false,
},
.fragment = &fragmentState,
};

mPipeline = wgpuDeviceCreateRenderPipeline(gpuContext.device, &pipelineDesc);
wgpuPipelineLayoutRelease(pipelineLayout);
}
}

HybridRenderer::SkyPass::~SkyPass()
{
renderPipelineSafeRelease(mPipeline);
mPipeline = nullptr;
}

void HybridRenderer::SkyPass::render(
const GpuContext& gpuContext,
const glm::mat4& viewProjectionMat,
const glm::vec3& cameraPosition,
const Sky& sky,
const WGPUCommandEncoder cmdEncoder,
const WGPUTextureView textureView)
{
if (mCurrentSky != sky)
{
mCurrentSky = sky;
const AlignedSkyState skyState{sky};
wgpuQueueWriteBuffer(
gpuContext.queue, mSkyStateBuffer.ptr(), 0, &skyState, sizeof(AlignedSkyState));
}

{
const Uniforms uniforms{glm::inverse(viewProjectionMat), glm::vec4(cameraPosition, 1.f)};
wgpuQueueWriteBuffer(
gpuContext.queue, mUniformBuffer.ptr(), 0, &uniforms, sizeof(Uniforms));
}

const WGPURenderPassEncoder renderPass = [cmdEncoder, textureView]() -> WGPURenderPassEncoder {
const WGPURenderPassColorAttachment colorAttachment{
.nextInChain = nullptr,
.view = textureView,
.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED,
.resolveTarget = nullptr,
.loadOp = WGPULoadOp_Clear,
.storeOp = WGPUStoreOp_Store,
.clearValue = WGPUColor{0.0, 0.0, 0.0, 1.0},
};

const WGPURenderPassDescriptor renderPassDesc{
.nextInChain = nullptr,
.label = "Sky pass render pass",
.colorAttachmentCount = 1,
.colorAttachments = &colorAttachment,
.depthStencilAttachment = nullptr,
.occlusionQuerySet = nullptr,
.timestampWrites = nullptr,
};

return wgpuCommandEncoderBeginRenderPass(cmdEncoder, &renderPassDesc);
}();
NLRS_ASSERT(renderPass != nullptr);

wgpuRenderPassEncoderSetPipeline(renderPass, mPipeline);
wgpuRenderPassEncoderSetBindGroup(renderPass, 0, mSkyStateBindGroup.ptr(), 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPass, 1, mUniformBindGroup.ptr(), 0, nullptr);
wgpuRenderPassEncoderSetVertexBuffer(
renderPass, 0, mVertexBuffer.ptr(), 0, mVertexBuffer.byteSize());
wgpuRenderPassEncoderDraw(renderPass, 6, 1, 0, 0);
wgpuRenderPassEncoderEnd(renderPass);
}
} // namespace nlrs
Loading

0 comments on commit 92ae60c

Please sign in to comment.