Skip to content

Commit

Permalink
Fix z-fighting with reverse-Z projection
Browse files Browse the repository at this point in the history
  • Loading branch information
Nelarius committed Apr 22, 2024
1 parent 4f58b1b commit 51dbf12
Show file tree
Hide file tree
Showing 11 changed files with 58 additions and 53 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
25 changes: 15 additions & 10 deletions src/pt/deferred_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,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 @@ -249,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 @@ -720,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 @@ -849,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 @@ -886,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 @@ -1524,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 @@ -1540,7 +1545,7 @@ void DeferredRenderer::LightingPass::render(

{
const Uniforms uniforms{
inverseViewProjectionMat,
inverseViewReverseZProjectionMatrix,
glm::vec4(cameraPosition, 1.f),
glm::vec2(fbsize.x, fbsize.y),
exposure,
Expand Down
4 changes: 2 additions & 2 deletions src/pt/deferred_renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ struct DeferredRendererDescriptor

struct RenderDescriptor
{
glm::mat4 viewProjectionMatrix;
glm::mat4 viewReverseZProjectionMatrix;
glm::vec3 cameraPosition;
Sky sky;
Extent2u framebufferSize;
Expand Down Expand Up @@ -175,7 +175,7 @@ class DeferredRenderer

struct Uniforms
{
glm::mat4 inverseViewProjection;
glm::mat4 inverseViewReverseZProjectionMat;
glm::vec4 cameraPosition;
glm::vec2 framebufferSize;
float exposure;
Expand Down
2 changes: 1 addition & 1 deletion src/pt/deferred_renderer_debug_pass.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fn fsMain(in: VertexOutput) -> @location(0) vec4f {
} else if c.x < 0.666 {
return textureLoad(gbufferNormal, idx, 0);
} else {
let d = vec3(1.0) - textureLoad(gbufferDepth, idx, 0);
let d = textureLoad(gbufferDepth, idx, 0);
let x = d;
let a = 0.1;
return vec4((1.0 + a) * x / (x + vec3(a)), 1.0);
Expand Down
6 changes: 3 additions & 3 deletions src/pt/deferred_renderer_lighting_pass.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct SkyState {
};

struct Uniforms {
inverseViewProjectionMat: mat4x4f,
inverseViewReverseZProjectionMat: mat4x4f,
cameraEye: vec4f,
framebufferSize: vec2f,
exposure: f32,
Expand Down Expand Up @@ -112,7 +112,7 @@ fn fsMain(in: VertexOutput) -> @location(0) vec4f {
let textureIdx = vec2u(floor(uv * uniforms.framebufferSize));
var rng = initRng(textureIdx, vec2u(uniforms.framebufferSize), uniforms.frameCount);
let depthSample = textureLoad(gbufferDepth, textureIdx, 0);
if depthSample == 1.0 {
if depthSample == 0.0 {
let world = worldFromUv(uv, depthSample);
let v = normalize(world - uniforms.cameraEye.xyz);
let s = skyState.sunDirection;
Expand All @@ -138,7 +138,7 @@ fn fsMain(in: VertexOutput) -> @location(0) vec4f {
@must_use
fn worldFromUv(uv: vec2f, depthSample: f32) -> vec3f {
let ndc = vec4(2.0 * vec2(uv.x, 1.0 - uv.y) - vec2(1.0), depthSample, 1.0);
let worldInvW = uniforms.inverseViewProjectionMat * ndc;
let worldInvW = uniforms.inverseViewReverseZProjectionMat * ndc;
let world = worldInvW / worldInvW.w;
return world.xyz;
}
Expand Down
34 changes: 27 additions & 7 deletions src/pt/fly_camera_controller.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "fly_camera_controller.hpp"

#include <GLFW/glfw3.h>
#include <glm/gtc/matrix_transform.hpp>

#include <algorithm>
#include <cassert>
Expand All @@ -20,14 +21,33 @@ Camera FlyCameraController::getCamera() const
aspectRatio(mWindowSize));
}

glm::mat4 FlyCameraController::viewProjectionMatrix() const
glm::mat4 FlyCameraController::viewReverseZProjectionMatrix() const
{
const auto orientation = cameraOrientation();
return generateViewProjectionMatrix(
mPosition,
mPosition + mFocusDistance * orientation.forward,
mVfov,
aspectRatio(mWindowSize));
const auto orientation = cameraOrientation();
const glm::vec3 origin = mPosition;
const glm::vec3 lookAt = mPosition + mFocusDistance * orientation.forward;

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);

// clang-format off
const glm::mat4 reverseZ = glm::mat4(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, -1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 1.0f);
// clang-format on

const float near = 0.1f;
const float far = 1000.0f;
const glm::mat4 project =
glm::perspective(mVfov.asRadians(), aspectRatio(mWindowSize), near, far);

const glm::mat4 view = glm::lookAt(origin, lookAt, up);

return reverseZ * project * view;
}

void FlyCameraController::lookAt(const glm::vec3& p)
Expand Down
2 changes: 1 addition & 1 deletion src/pt/fly_camera_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct FlyCameraController
FlyCameraController() = default;

Camera getCamera() const;
glm::mat4 viewProjectionMatrix() const;
glm::mat4 viewReverseZProjectionMatrix() const;

void lookAt(const glm::vec3& p);
void update(GLFWwindow* window, float dt);
Expand Down
6 changes: 2 additions & 4 deletions src/pt/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,10 +457,9 @@ try
break;
case RendererType_Deferred:
{
const glm::mat4 viewProjectionMat = appState.cameraController.viewProjectionMatrix();
NLRS_ASSERT(appState.ui.exposureStops >= 0);
const nlrs::RenderDescriptor renderDesc{
viewProjectionMat,
appState.cameraController.viewReverseZProjectionMatrix(),
appState.cameraController.position(),
nlrs::Sky{
appState.ui.skyTurbidity,
Expand All @@ -477,10 +476,9 @@ try
}
case RendererType_Debug:
{
const glm::mat4 viewProjectionMat = appState.cameraController.viewProjectionMatrix();
deferredRenderer.renderDebug(
gpuContext,
viewProjectionMat,
appState.cameraController.viewReverseZProjectionMatrix(),
nlrs::Extent2f(windowResolution),
textureBlitter.textureView());
break;
Expand Down
8 changes: 4 additions & 4 deletions src/pt/shader_source.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,7 @@ fn fsMain(in: VertexOutput) -> @location(0) vec4f {
} else if c.x < 0.666 {
return textureLoad(gbufferNormal, idx, 0);
} else {
let d = vec3(1.0) - textureLoad(gbufferDepth, idx, 0);
let d = textureLoad(gbufferDepth, idx, 0);
let x = d;
let a = 0.1;
return vec4((1.0 + a) * x / (x + vec3(a)), 1.0);
Expand Down Expand Up @@ -780,7 +780,7 @@ struct SkyState {
};
struct Uniforms {
inverseViewProjectionMat: mat4x4f,
inverseViewReverseZProjectionMat: mat4x4f,
cameraEye: vec4f,
framebufferSize: vec2f,
exposure: f32,
Expand Down Expand Up @@ -867,7 +867,7 @@ fn fsMain(in: VertexOutput) -> @location(0) vec4f {
let textureIdx = vec2u(floor(uv * uniforms.framebufferSize));
var rng = initRng(textureIdx, vec2u(uniforms.framebufferSize), uniforms.frameCount);
let depthSample = textureLoad(gbufferDepth, textureIdx, 0);
if depthSample == 1.0 {
if depthSample == 0.0 {
let world = worldFromUv(uv, depthSample);
let v = normalize(world - uniforms.cameraEye.xyz);
let s = skyState.sunDirection;
Expand All @@ -893,7 +893,7 @@ fn fsMain(in: VertexOutput) -> @location(0) vec4f {
@must_use
fn worldFromUv(uv: vec2f, depthSample: f32) -> vec3f {
let ndc = vec4(2.0 * vec2(uv.x, 1.0 - uv.y) - vec2(1.0), depthSample, 1.0);
let worldInvW = uniforms.inverseViewProjectionMat * ndc;
let worldInvW = uniforms.inverseViewReverseZProjectionMat * ndc;
let world = worldInvW / worldInvW.w;
return world.xyz;
}
Expand Down

0 comments on commit 51dbf12

Please sign in to comment.