Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
64c01a0
Working build
stan4dbunny Jan 23, 2025
ee7e3b0
Figured out how to render to a texture and blit it
stan4dbunny Jan 24, 2025
a18cb76
reflection debug view almost fixed for path-tracer
stan4dbunny Feb 5, 2025
35f172f
path-tracer reflection view done?
stan4dbunny Feb 5, 2025
c0fc62e
added option DEBUG_REFLECTIONS to gui that just shows reflections, an…
stan4dbunny Feb 5, 2025
2809196
the same but actually works this time
stan4dbunny Feb 5, 2025
0fcc940
before adding png
stan4dbunny Feb 10, 2025
9ae8a76
png screenshots supported
stan4dbunny Feb 10, 2025
3f05068
Can screendump GI-1.1 reflection debug view now!!
stan4dbunny Feb 10, 2025
c3410f8
flip script works more automatically
stan4dbunny Feb 11, 2025
f5af2d1
comment
stan4dbunny Feb 12, 2025
57d14e3
started working on mutual sampling
stan4dbunny Feb 17, 2025
5318e39
everything red...
stan4dbunny Feb 18, 2025
a92037c
started on importance/brdf sampling
stan4dbunny Feb 20, 2025
74b6de4
?
stan4dbunny Feb 26, 2025
ee23b66
...
stan4dbunny Mar 7, 2025
3d69273
cleaned up screenshot code
stan4dbunny Mar 10, 2025
901ba09
cleaned up
stan4dbunny Mar 10, 2025
13d8e90
fixed merge?
stan4dbunny Mar 10, 2025
7de13ac
cleanup
stan4dbunny Mar 10, 2025
eee09e3
fix errors?
stan4dbunny Mar 10, 2025
d2efe33
fixed errors, for real?
stan4dbunny Mar 10, 2025
755ee57
png works now
stan4dbunny Mar 11, 2025
7e253cf
pt glossy reflection
stan4dbunny Mar 24, 2025
d23e38e
new branch
stan4dbunny Mar 20, 2025
6c992df
multibounce + fixed pt reflection view
stan4dbunny Apr 29, 2025
7733cbe
screenshot works again
stan4dbunny Apr 29, 2025
2999b21
option to turn off sss
stan4dbunny Apr 29, 2025
8cb40ed
..
stan4dbunny Apr 29, 2025
c1762b6
reflections * albedo
stan4dbunny May 2, 2025
de2493d
cornell
stan4dbunny May 5, 2025
5a86259
multibounce fixed?
stan4dbunny May 26, 2025
3d8186e
fixed for real
stan4dbunny May 26, 2025
e68b1ff
cleaned up
stan4dbunny May 29, 2025
9498430
final
stan4dbunny May 29, 2025
1c2d60b
cleaned up
stan4dbunny May 29, 2025
f07886e
cleaned up comments
stan4dbunny Jun 5, 2025
8a9f779
fixed
stan4dbunny Jun 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/core/src/capsaicin/capsaicin_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,17 @@ class CapsaicinInternal
*/
void resetRenderState() const noexcept;

void dumpBuffer(char const *file_path, GfxTexture dump_buffer);
void saveImage(GfxBuffer dump_buffer, uint32_t dump_buffer_width, uint32_t dump_buffer_height,
char const *file_path);
void saveEXR(GfxBuffer dump_buffer, uint32_t dump_buffer_width, uint32_t dump_buffer_height,
char const *exr_file_path);
void saveJPG(GfxBuffer dump_buffer, uint32_t dump_buffer_width, uint32_t dump_buffer_height,
char const *jpg_file_path);
void savePNG(GfxBuffer dump_buffer, uint32_t dump_buffer_width,
uint32_t dump_buffer_height, char const *png_file_path);
void dumpCamera(char const *file_path, CameraMatrices const &camera_matrices, float camera_jitter_x,
float camera_jitter_y);
/**
* Reset any internal events to their default state.
*/
Expand Down Expand Up @@ -969,6 +980,8 @@ class CapsaicinInternal
uint32_t dumpBufferHeight, std::filesystem::path const &filePath);
void saveJPG(GfxBuffer const &dumpBuffer, DXGI_FORMAT bufferFormat, uint32_t dumpBufferWidth,
uint32_t dumpBufferHeight, std::filesystem::path const &filePath) const;
void savePNG(GfxBuffer const &dumpBuffer, DXGI_FORMAT bufferFormat, uint32_t dumpBufferWidth,
uint32_t dumpBufferHeight, std::filesystem::path const &filePath) const;
void dumpCamera(CameraMatrices const &cameraMatrices, float cameraJitterX, float cameraJitterY,
std::filesystem::path const &filePath) const;

Expand Down
73 changes: 72 additions & 1 deletion src/core/src/capsaicin/capsaicin_internal_dump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ void CapsaicinInternal::dumpDebugView(std::filesystem::path const &filePath, std
// instead of the raw AOV data
auto extension = filePath.extension().string();
std::ranges::transform(extension, extension.begin(), tolower);
if (extension == ".jpg" || extension == ".jpeg")
if (extension == ".jpg" || extension == ".jpeg" || extension == ".png")
{
dump_buffer = currentView;
}
Expand All @@ -199,6 +199,7 @@ void CapsaicinInternal::dumpDebugView(std::filesystem::path const &filePath, std
}
}

// clang-format off
void CapsaicinInternal::dumpCamera(std::filesystem::path const &filePath, bool const jittered) const
{
dumpCamera(camera_matrices_[jittered], jittered ? camera_jitter_.x : 0.F,
Expand Down Expand Up @@ -236,6 +237,10 @@ void CapsaicinInternal::saveImage(GfxBuffer const &dumpBuffer, const DXGI_FORMAT
{
saveJPG(dumpBuffer, bufferFormat, dumpBufferWidth, dumpBufferHeight, filePath);
}
else if (extension == ".png")
{
savePNG(dumpBuffer, bufferFormat, dumpBufferWidth, dumpBufferHeight, filePath);
}
else if (extension == ".exr")
{
saveEXR(dumpBuffer, bufferFormat, dumpBufferWidth, dumpBufferHeight, filePath);
Expand Down Expand Up @@ -455,6 +460,72 @@ void CapsaicinInternal::saveJPG(GfxBuffer const &dumpBuffer, const DXGI_FORMAT b
}
}

void CapsaicinInternal::savePNG(GfxBuffer const &dumpBuffer, const DXGI_FORMAT bufferFormat, uint32_t const dumpBufferWidth,
uint32_t const dumpBufferHeight, std::filesystem::path const &filePath) const
{
// Image
void const *bufferData = gfxBufferGetData(gfx_, dumpBuffer);
uint32_t const imageWidth = dumpBufferWidth;
uint32_t const imageHeight = dumpBufferHeight;
uint32_t const imagePixelCount = dumpBufferWidth * dumpBufferHeight;
uint32_t const channelCount = GetNumChannels(bufferFormat);
uint32_t const bitsPerChannel = GetBitsPerPixel(bufferFormat) / channelCount;

if (bitsPerChannel == 8 && channelCount == 3)
{
int ret = stbi_write_png(filePath.string().c_str(), static_cast<int32_t>(imageWidth),
static_cast<int32_t>(imageHeight), 3, bufferData, imageWidth * 3);
if (ret == 0)
{
GFX_PRINT_ERROR(kGfxResult_InternalError, "Can't save '%s'", filePath.string().c_str());
}
}
else
{
std::vector<unsigned char> imageData(static_cast<size_t>(imageWidth) * imageHeight * 3);
auto quantize = [&]<typename T>(T const *dumpBufferData) {
for (size_t pixelIndex = 0; pixelIndex < imagePixelCount; ++pixelIndex)
{
imageData[3 * pixelIndex + 0] =
ConvertType<uint8_t, T>(dumpBufferData[channelCount * pixelIndex + 0]);
imageData[3 * pixelIndex + 1] =
channelCount > 1 ? ConvertType<uint8_t, T>(dumpBufferData[channelCount * pixelIndex + 1])
: 0;
imageData[3 * pixelIndex + 2] =
channelCount > 2 ? ConvertType<uint8_t, T>(dumpBufferData[channelCount * pixelIndex + 2])
: 0;
}
};

if (bool const isFloatFormat = IsFormatFloat(bufferFormat); bitsPerChannel == 32 && isFloatFormat)
{
quantize(static_cast<float const *>(bufferData));
}
else if (bitsPerChannel == 32)
{
quantize(static_cast<uint32_t const *>(bufferData));
}
else if (bitsPerChannel == 16 && isFloatFormat)
{
quantize(static_cast<Half const *>(bufferData));
}
else if (bitsPerChannel == 16)
{
quantize(static_cast<uint16_t const *>(bufferData));
}
else if (bitsPerChannel == 8)
{
quantize(static_cast<uint8_t const *>(bufferData));
}

int ret = stbi_write_png(filePath.string().c_str(), static_cast<int32_t>(imageWidth),
static_cast<int32_t>(imageHeight), 3, imageData.data(), imageWidth * 3);
if (ret == 0)
{
GFX_PRINT_ERROR(kGfxResult_InternalError, "Can't save '%s'", filePath.string().c_str());
}
}
}
void CapsaicinInternal::dumpCamera(CameraMatrices const &cameraMatrices, float const cameraJitterX,
float const cameraJitterY, std::filesystem::path const &filePath) const
{
Expand Down
128 changes: 124 additions & 4 deletions src/core/src/geometry/path_tracing.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,38 @@ void shadeLightHit(RayDesc ray, MaterialBRDF material, float3 normal, float3 vie
#endif // DISABLE_NON_NEE
}

/**
* Calculate any radiance from a hit light.
* @param ray The traced ray that hit a surface.
* @param material Material data describing BRDF of surface.
* @param normal Shading normal vector at current position.
* @param viewDirection Outgoing ray view direction.
* @param throughput The current paths combined throughput.
* @param lightPDF The PDF of sampling the returned light direction.
* @param radianceLi The radiance visible along sampled light.
* @param selectedLight The light that was selected for sampling.
* @param [in,out] radiance The combined radiance. Any new radiance is added to the existing value and returned.
* @param firstHit True if this is the first hit
*/
void shadeLightHit(RayDesc ray, MaterialBRDF material, float3 normal, float3 viewDirection, float3 throughput,
float lightPDF, float3 radianceLi, Light selectedLight, inout float3 radiance, bool firstHit)
{
#ifdef DISABLE_NON_NEE
float3 sampleReflectance = evaluateBRDF(material, normal, viewDirection, ray.Direction);
radiance += throughput * sampleReflectance * radianceLi / lightPDF.xxx;
#else
// Evaluate BRDF for new light direction and calculate combined PDF for current sample
float3 sampleReflectance;
float samplePDF = sampleBRDFPDFAndEvalute(material, normal, viewDirection, ray.Direction, sampleReflectance, firstHit);
if (samplePDF != 0.0f)
{
bool deltaLight = isDeltaLight(selectedLight);
float weight = (!deltaLight) ? heuristicMIS(lightPDF, samplePDF) : 1.0f;
radiance += throughput * sampleReflectance * radianceLi * (weight / lightPDF).xxx;
}
#endif // DISABLE_NON_NEE
}

/**
* Calculates a new light ray direction from a surface by sampling the scenes lighting.
* @tparam RNG The type of random number sampler to be used.
Expand Down Expand Up @@ -303,6 +335,56 @@ void sampleLightsNEE(MaterialBRDF material, inout StratifiedSampler randomStrati
}
}

/**
* Calculates radiance from a new light ray direction from a surface by sampling the scenes lighting.
* @tparam RNG The type of random number sampler to be used.
* @param material Material data describing BRDF of surface.
* @param randomStratified Random number sampler used to sample light.
* @param lightSampler Light sampler.
* @param position Current position on surface.
* @param normal Shading normal vector at current position.
* @param geometryNormal Surface normal vector at current position.
* @param viewDirection Outgoing ray view direction.
* @param throughput The current paths combined throughput.
* @param [in,out] radiance The combined radiance. Any new radiance is added to the existing value and returned.
* @param firstHit True if this is the first hit.
*/
void sampleLightsNEEFirstHitInfo(MaterialBRDF material, inout StratifiedSampler randomStratified, LightSampler lightSampler,
float3 position, float3 normal, float3 geometryNormal, float3 viewDirection, float3 throughput, inout pathPayload radiance, bool firstHit)
{
// Get sampled light direction
float lightPDF;
RayDesc ray;
float3 radianceLi;
Light selectedLight;
if (!sampleLightsNEEDirection(material, randomStratified, lightSampler, position, normal, geometryNormal, viewDirection, ray, lightPDF, radianceLi, selectedLight))
{
return;
}

// Trace shadow ray
#if USE_INLINE_RT
ShadowRayQuery rayShadowQuery = TraceRay < ShadowRayQuery > (ray);
bool hit = rayShadowQuery.CommittedStatus() == COMMITTED_NOTHING;
#else
ShadowRayPayload payload = {false};
TraceRay(g_Scene, SHADOW_RAY_FLAGS, 0xFFu, 1, 0, 1, ray, payload);
bool hit = payload.visible;
#endif

// If nothing was hit then we have hit the light
if (hit)
{
// Add lighting contribution
#ifdef USE_CUSTOM_HIT_FUNCTIONS
shadeLightHitCustom(ray, material, normal, viewDirection, throughput, lightPDF, radianceLi, selectedLight, radiance);
#else
shadeLightHit(ray, material, normal, viewDirection, throughput, lightPDF, radianceLi, selectedLight, radiance, firstHit);
#endif
}
}


/**
* Calculate the next segment along a path after a valid surface hit.
* @param materialBRDF The material on the hit surface.
Expand All @@ -323,10 +405,25 @@ bool pathNext(MaterialBRDF materialBRDF, inout StratifiedSampler randomStratifie
inout LightSampler lightSampler, uint currentBounce, uint minBounces, uint maxBounces, float3 normal,
float3 geometryNormal, float3 viewDirection, inout float3 throughput, out float3 rayDirection, out float samplePDF)
{
// Sample BRDF to get next ray direction
float3 sampleReflectance;
rayDirection = sampleBRDF(materialBRDF, randomStratified, normal, viewDirection, sampleReflectance, samplePDF);
bool specularSampled;

#ifdef DEBUG_REFLECTIONS
if (currentBounce == 0)
{
materialBRDF.F0 = float3(1, 1, 1);
}
#endif
rayDirection = sampleBRDF(materialBRDF, randomStratified, normal, viewDirection, sampleReflectance, samplePDF, specularSampled);

#ifdef DEBUG_REFLECTIONS
// If we decide to bounce diffusely on the first bounce, we terminate the ray
if (!specularSampled && currentBounce == 0)
{
return false;
}
#endif

// Prevent tracing directions below the surface
if (dot(geometryNormal, rayDirection) <= 0.0f || samplePDF == 0.0f)
{
Expand Down Expand Up @@ -382,6 +479,16 @@ bool pathHit(inout RayDesc ray, HitInfo hitData, IntersectData iData, inout Stra
return false;
}

#ifdef DEBUG_REFLECTIONS
// If the first hit is too rough, then we won't get a reflection, so we terminate the ray
if (currentBounce == 0)
{
MaterialEvaluated materialEvaluated = MakeMaterialEvaluated(iData.material, iData.uv);
if (materialEvaluated.roughness > 0.6f)
return false;
}
#endif

float3 viewDirection = -ray.Direction;
// Stop if surface normal places ray behind surface (note surface normal != geometric normal)
// Currently disabled due to incorrect normals generated by normal mapping when not using displacement/parallax
Expand All @@ -391,6 +498,8 @@ bool pathHit(inout RayDesc ray, HitInfo hitData, IntersectData iData, inout Stra
//}

MaterialBRDF materialBRDF = MakeMaterialBRDF(iData.material, iData.uv);


#ifdef DISABLE_ALBEDO_MATERIAL
// Disable material albedo if requested
if (currentBounce == 0)
Expand All @@ -409,8 +518,13 @@ bool pathHit(inout RayDesc ray, HitInfo hitData, IntersectData iData, inout Stra
# endif // DISABLE_DIRECT_LIGHTING
{
// Sample a single light
sampleLightsNEE(materialBRDF, randomStratified, lightSampler, iData.position,
iData.normal, iData.geometryNormal, viewDirection, throughput, radiance);
bool firstHit;
if (currentBounce == 0)
firstHit = true;
else
firstHit = false;
sampleLightsNEEFirstHitInfo(materialBRDF, randomStratified, lightSampler, iData.position,
iData.normal, iData.geometryNormal, viewDirection, throughput, radiance, firstHit);
}
#endif // DISABLE_NEE

Expand Down Expand Up @@ -465,6 +579,12 @@ void tracePath(RayDesc ray, inout StratifiedSampler randomStratified, inout Ligh
// Check for valid intersection
if (rayQuery.CommittedStatus() == COMMITTED_NOTHING)
{
#ifdef DEBUG_REFLECTIONS
//No sky light for reflection debug
if (bounce == 0)
return;
#endif

# ifdef USE_CUSTOM_HIT_FUNCTIONS
shadePathMissCustom(ray, bounce, lightSampler, normal, samplePDF, throughput, radiance);
# else
Expand Down
3 changes: 3 additions & 0 deletions src/core/src/lights/lights_shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ THE SOFTWARE.
#ifndef LIGHTS_SHARED_H
#define LIGHTS_SHARED_H

#ifdef _WIN32
#include <algorithm>
#endif
#include "../gpu_shared.h"

enum LightType
Expand Down
21 changes: 20 additions & 1 deletion src/core/src/materials/material_evaluation.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,30 @@ float3 evaluateBRDFDiffuse(MaterialBRDF material, float dotHV, float dotNL)
float3 diffuse = evaluateLambert(material.albedo);

// Add the weight of the diffuse compensation term to prevent excessive brightness compared to specular
diffuse *= diffuseCompensation(fresnel(0.04f.xxx, dotHV), dotHV);
diffuse *= diffuseCompensationTerm(fresnel(0.04f.xxx, dotHV), dotHV);
float3 brdf = diffuse * saturate(dotNL);
return brdf;
}

/**
* Evaluate the diffuse component of the BRDF.
* @param material Material data describing BRDF.
* @param normal The normal of the surface
* @param viewDirection The direction from the hit point towards the view point
* @param lightDirection The direction from the hit point towards the light
* @return The calculated reflectance.
*/
float3 evaluateBRDFDiffuse(MaterialBRDF material, float3 normal, float3 viewDirection, float3 lightDirection)
{
// Calculate shading angles
float dotNL = clamp(dot(normal, lightDirection), -1.0f, 1.0f);
// Calculate half vector
float3 halfVector = normalize(viewDirection + lightDirection);
float dotHV = saturate(dot(halfVector, viewDirection));

return evaluateBRDFDiffuse(material, dotHV, dotNL);
}

#ifndef DISABLE_SPECULAR_MATERIALS
/**
* Evaluate the specular component of the BRDF.
Expand Down
Loading