Skip to content

Commit

Permalink
don't pass world around everywhere in integrators
Browse files Browse the repository at this point in the history
  • Loading branch information
ashpil committed Jul 10, 2024
1 parent 10bef7a commit b9f5891
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 99 deletions.
46 changes: 23 additions & 23 deletions shaders/hrtsystem/integrator.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ float3 estimateDirect(RaytracingAccelerationStructure accel, Frame frame, Light
}

// selects a shading normal based on the most preferred normal that is plausible
Frame selectFrame(World world, MeshAttributes attrs, uint materialIdx, float3 outgoingDirWs) {
const Frame textureFrame = getTextureFrame(world, materialIdx, attrs.texcoord, attrs.frame);
Frame selectFrame(MeshAttributes attrs, Material material, float3 outgoingDirWs) {
const Frame textureFrame = material.getTextureFrame(attrs.texcoord, attrs.frame);
const bool frontfacing = dot(attrs.triangleFrame.n, outgoingDirWs) > 0;
Frame shadingFrame;
if ((frontfacing && dot(outgoingDirWs, textureFrame.n) > 0) || (!frontfacing && -dot(outgoingDirWs, textureFrame.n) > 0)) {
Expand Down Expand Up @@ -104,18 +104,18 @@ struct PathTracingIntegrator : Integrator {
for (Intersection its = Intersection::find(scene.tlas, ray); its.hit(); its = Intersection::find(scene.tlas, ray)) {

// decode mesh attributes and material from intersection
const uint instanceID = scene.world.instances[its.instanceIndex].instanceID();
const MeshAttributes attrs = MeshAttributes::lookupAndInterpolate(scene.world, its.instanceIndex, its.geometryIndex, its.primitiveIndex, its.barycentrics).inWorld(scene.world, its.instanceIndex);
const PolymorphicBSDF material = PolymorphicBSDF::load(scene.world, scene.world.materialIdx(instanceID, its.geometryIndex), attrs.texcoord);
const Material material = scene.world.material(its.instanceIndex, its.geometryIndex);
const PolymorphicBSDF bsdf = PolymorphicBSDF::load(material, attrs.texcoord);

const float3 outgoingDirWs = -ray.Direction;
const Frame shadingFrame = selectFrame(scene.world, attrs, scene.world.materialIdx(instanceID, its.geometryIndex), outgoingDirWs);
const Frame shadingFrame = selectFrame(attrs, material, outgoingDirWs);
const float3 outgoingDirSs = shadingFrame.worldToFrame(outgoingDirWs);

// collect light from emissive meshes
// lights only emit from front face
if (dot(outgoingDirWs, attrs.triangleFrame.n) > 0.0) {
const float3 emissiveLight = getEmissive(scene.world, scene.world.materialIdx(instanceID, its.geometryIndex), attrs.texcoord);
const float3 emissiveLight = material.getEmissive(attrs.texcoord);
const float areaPdf = scene.meshLights.areaPdf(its.instanceIndex, its.geometryIndex, its.primitiveIndex);
if (meshSamplesPerBounce == 0 || bounceCount == 0 || isLastMaterialDelta || areaPdf == 0) {
// add emissive light at point if light not explicitly sampled or initial bounce
Expand All @@ -139,30 +139,30 @@ struct PathTracingIntegrator : Integrator {
throughput /= pSurvive;
}

const bool isCurrentMaterialDelta = material.isDelta();
const bool isCurrentMaterialDelta = bsdf.isDelta();

if (!isCurrentMaterialDelta) {
// accumulate direct light samples from env map
for (uint directCount = 0; directCount < envSamplesPerBounce; directCount++) {
float2 rand = float2(rng.getFloat(), rng.getFloat());
accumulatedColor += throughput * estimateDirectMISLight(scene.tlas, shadingFrame, scene.envMap, material, outgoingDirSs, attrs.position, attrs.triangleFrame.n, rand, envSamplesPerBounce, 1) / envSamplesPerBounce;
accumulatedColor += throughput * estimateDirectMISLight(scene.tlas, shadingFrame, scene.envMap, bsdf, outgoingDirSs, attrs.position, attrs.triangleFrame.n, rand, envSamplesPerBounce, 1) / envSamplesPerBounce;
}

// accumulate direct light samples from emissive meshes
for (uint directCount = 0; directCount < meshSamplesPerBounce; directCount++) {
float2 rand = float2(rng.getFloat(), rng.getFloat());
accumulatedColor += throughput * estimateDirectMISLight(scene.tlas, shadingFrame, scene.meshLights, material, outgoingDirSs, attrs.position, attrs.triangleFrame.n, rand, meshSamplesPerBounce, 1) / meshSamplesPerBounce;
accumulatedColor += throughput * estimateDirectMISLight(scene.tlas, shadingFrame, scene.meshLights, bsdf, outgoingDirSs, attrs.position, attrs.triangleFrame.n, rand, meshSamplesPerBounce, 1) / meshSamplesPerBounce;
}
}

// sample direction for next bounce
const BSDFSample sample = material.sample(outgoingDirSs, float2(rng.getFloat(), rng.getFloat()));
const BSDFSample sample = bsdf.sample(outgoingDirSs, float2(rng.getFloat(), rng.getFloat()));
if (sample.pdf == 0.0) return accumulatedColor; // in a perfect world this would never happen

// set up info for next bounce
ray.Direction = shadingFrame.frameToWorld(sample.dirFs);
ray.Origin = offsetAlongNormal(attrs.position, faceForward(attrs.triangleFrame.n, ray.Direction));
throughput *= material.eval(sample.dirFs, outgoingDirSs) * abs(Frame::cosTheta(sample.dirFs)) / sample.pdf;
throughput *= bsdf.eval(sample.dirFs, outgoingDirSs) * abs(Frame::cosTheta(sample.dirFs)) / sample.pdf;
bounceCount += 1;
isLastMaterialDelta = isCurrentMaterialDelta;
lastMaterialPdf = sample.pdf;
Expand Down Expand Up @@ -209,37 +209,37 @@ struct DirectLightIntegrator : Integrator {
Intersection its = Intersection::find(scene.tlas, initialRay);
if (its.hit()) {
// decode mesh attributes and material from intersection
const uint instanceID = scene.world.instances[its.instanceIndex].instanceID();
const MeshAttributes attrs = MeshAttributes::lookupAndInterpolate(scene.world, its.instanceIndex, its.geometryIndex, its.primitiveIndex, its.barycentrics).inWorld(scene.world, its.instanceIndex);
const PolymorphicBSDF material = PolymorphicBSDF::load(scene.world, scene.world.materialIdx(instanceID, its.geometryIndex), attrs.texcoord);
const Material material = scene.world.material(its.instanceIndex, its.geometryIndex);
const PolymorphicBSDF bsdf = PolymorphicBSDF::load(material, attrs.texcoord);

const float3 outgoingDirWs = -initialRay.Direction;
const Frame shadingFrame = selectFrame(scene.world, attrs, scene.world.materialIdx(instanceID, its.geometryIndex), outgoingDirWs);
const Frame shadingFrame = selectFrame(attrs, material, outgoingDirWs);
const float3 outgoingDirSs = shadingFrame.worldToFrame(outgoingDirWs);

// collect light from emissive meshes
accumulatedColor += getEmissive(scene.world, scene.world.materialIdx(instanceID, its.geometryIndex), attrs.texcoord);
accumulatedColor += material.getEmissive(attrs.texcoord);

const bool isMaterialDelta = material.isDelta();
const bool isMaterialDelta = bsdf.isDelta();

if (!isMaterialDelta) {
// accumulate direct light samples from env map
for (uint directCount = 0; directCount < envSamples; directCount++) {
float2 rand = float2(rng.getFloat(), rng.getFloat());
accumulatedColor += estimateDirectMISLight(scene.tlas, shadingFrame, scene.envMap, material, outgoingDirSs, attrs.position, attrs.triangleFrame.n, rand, envSamples, brdfSamples) / envSamples;
accumulatedColor += estimateDirectMISLight(scene.tlas, shadingFrame, scene.envMap, bsdf, outgoingDirSs, attrs.position, attrs.triangleFrame.n, rand, envSamples, brdfSamples) / envSamples;
}

// accumulate direct light samples from emissive meshes
for (uint directCount = 0; directCount < meshSamples; directCount++) {
float2 rand = float2(rng.getFloat(), rng.getFloat());
accumulatedColor += estimateDirectMISLight(scene.tlas, shadingFrame, scene.meshLights, material, outgoingDirSs, attrs.position, attrs.triangleFrame.n, rand, meshSamples, brdfSamples) / meshSamples;
accumulatedColor += estimateDirectMISLight(scene.tlas, shadingFrame, scene.meshLights, bsdf, outgoingDirSs, attrs.position, attrs.triangleFrame.n, rand, meshSamples, brdfSamples) / meshSamples;
}
}

for (uint brdfSampleCount = 0; brdfSampleCount < brdfSamples; brdfSampleCount++) {
const BSDFSample sample = material.sample(outgoingDirSs, float2(rng.getFloat(), rng.getFloat()));
const BSDFSample sample = bsdf.sample(outgoingDirSs, float2(rng.getFloat(), rng.getFloat()));
if (sample.pdf > 0.0) {
const float3 throughput = material.eval(outgoingDirSs, sample.dirFs) * abs(Frame::cosTheta(sample.dirFs)) / sample.pdf;
const float3 throughput = bsdf.eval(outgoingDirSs, sample.dirFs) * abs(Frame::cosTheta(sample.dirFs)) / sample.pdf;
if (all(throughput != 0)) {
RayDesc ray;
ray.TMin = 0;
Expand All @@ -249,8 +249,8 @@ struct DirectLightIntegrator : Integrator {
Intersection its = Intersection::find(scene.tlas, ray);
if (its.hit()) {
// decode mesh attributes and material from intersection
MeshAttributes attrs = MeshAttributes::lookupAndInterpolate(scene.world, its.instanceIndex, its.geometryIndex, its.primitiveIndex, its.barycentrics).inWorld(scene.world, its.instanceIndex);
float3 emissiveLight = getEmissive(scene.world, scene.world.materialIdx(scene.world.instances[its.instanceIndex].instanceID(), its.geometryIndex), attrs.texcoord);
const MeshAttributes attrs = MeshAttributes::lookupAndInterpolate(scene.world, its.instanceIndex, its.geometryIndex, its.primitiveIndex, its.barycentrics).inWorld(scene.world, its.instanceIndex);
const float3 emissiveLight = scene.world.material(its.instanceIndex, its.geometryIndex).getEmissive(attrs.texcoord);

// collect light from emissive meshes
// lights only emit from front face
Expand All @@ -272,7 +272,7 @@ struct DirectLightIntegrator : Integrator {
} else {
LightEval l = scene.envMap.eval(ray.Direction);
if (l.pdf > 0) {
float weight = powerHeuristic(brdfSamples, sample.pdf, envSamples, l.pdf);
const float weight = powerHeuristic(brdfSamples, sample.pdf, envSamples, l.pdf);
accumulatedColor += throughput * scene.envMap.incomingRadiance(ray.Direction) * weight / brdfSamples;
}
}
Expand Down
4 changes: 1 addition & 3 deletions shaders/hrtsystem/light.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,11 @@ struct TriangleLight: Light {
}

LightSample sample(float3 positionWs, float3 triangleNormalDirWs, float2 rand) {
const uint instanceID = world.instances[instanceIndex].instanceID();

const float2 barycentrics = squareToTriangle(rand);
const MeshAttributes attrs = MeshAttributes::lookupAndInterpolate(world, instanceIndex, geometryIndex, primitiveIndex, barycentrics).inWorld(world, instanceIndex);

LightSample lightSample;
lightSample.radiance = getEmissive(world, world.materialIdx(instanceID, geometryIndex), attrs.texcoord);
lightSample.radiance = world.material(instanceIndex, geometryIndex).getEmissive(attrs.texcoord);
lightSample.dirWs = normalize(attrs.position - positionWs);
lightSample.pdf = areaMeasureToSolidAngleMeasure(attrs.position, positionWs, lightSample.dirWs, attrs.triangleFrame.n) / MeshAttributes::triangleArea(world, instanceIndex, geometryIndex, primitiveIndex);

Expand Down
87 changes: 50 additions & 37 deletions shaders/hrtsystem/material.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,50 @@

#include "../utils/math.hlsl"
#include "../utils/mappings.hlsl"
#include "world.hlsl"

float3 decodeNormal(float2 rg) {
rg = rg * 2 - 1;
return float3(rg, sqrt(1.0 - saturate(dot(rg, rg)))); // saturate due to float/compression annoyingness
}

float3 tangentNormalToWorld(float3 normalTangentSpace, Frame tangentFrame) {
return normalize(tangentFrame.frameToWorld(normalTangentSpace)).xyz;
}

Frame createTextureFrame(float3 normalWorldSpace, Frame tangentFrame) {
Frame textureFrame = tangentFrame;
textureFrame.n = normalWorldSpace;
textureFrame.reorthogonalize();

return textureFrame;
}

enum class BSDFType : uint {
Glass,
Lambert,
PerfectMirror,
StandardPBR,
};

struct Material {
uint normal;
uint emissive;

// find appropriate thing to decode from address using `type`
BSDFType type;
uint64_t addr;

Frame getTextureFrame(float2 texcoords, Frame tangentFrame) {
const float2 rg = dTextures[NonUniformResourceIndex(normal)].SampleLevel(dTextureSampler, texcoords, 0).rg;
const float3 normalTangentSpace = decodeNormal(rg);
const float3 normalWorldSpace = tangentNormalToWorld(normalTangentSpace, tangentFrame);
return createTextureFrame(normalWorldSpace, tangentFrame);
}

float3 getEmissive(float2 texcoords) {
return dTextures[NonUniformResourceIndex(emissive)].SampleLevel(dTextureSampler, texcoords, 0).rgb;
}
};

// all code below expects stuff to be in the reflection frame

Expand Down Expand Up @@ -397,13 +440,12 @@ struct PolymorphicBSDF : BSDF {
uint64_t addr;
float2 texcoords;

static PolymorphicBSDF load(World world, uint materialIdx, float2 texcoords) {
const Material materialData = world.materials[NonUniformResourceIndex(materialIdx)];
PolymorphicBSDF material;
material.type = materialData.type;
material.addr = materialData.materialAddress;
material.texcoords = texcoords;
return material;
static PolymorphicBSDF load(Material material, float2 texcoords) {
PolymorphicBSDF bsdf;
bsdf.type = material.type;
bsdf.addr = material.addr;
bsdf.texcoords = texcoords;
return bsdf;
}

float pdf(float3 w_i, float3 w_o) {
Expand Down Expand Up @@ -487,32 +529,3 @@ struct PolymorphicBSDF : BSDF {
}
};

float3 decodeNormal(float2 rg) {
rg = rg * 2 - 1;
return float3(rg, sqrt(1.0 - saturate(dot(rg, rg)))); // saturate due to float/compression annoyingness
}

float3 tangentNormalToWorld(float3 normalTangentSpace, Frame tangentFrame) {
return normalize(tangentFrame.frameToWorld(normalTangentSpace)).xyz;
}

Frame createTextureFrame(float3 normalWorldSpace, Frame tangentFrame) {
Frame textureFrame = tangentFrame;
textureFrame.n = normalWorldSpace;
textureFrame.reorthogonalize();

return textureFrame;
}

Frame getTextureFrame(World world, uint materialIndex, float2 texcoords, Frame tangentFrame) {
const Material data = world.materials[NonUniformResourceIndex(materialIndex)];
const float2 rg = dTextures[NonUniformResourceIndex(data.normal)].SampleLevel(dTextureSampler, texcoords, 0).rg;
const float3 normalTangentSpace = decodeNormal(rg);
const float3 normalWorldSpace = tangentNormalToWorld(normalTangentSpace, tangentFrame);
return createTextureFrame(normalWorldSpace, tangentFrame);
}

float3 getEmissive(World world, uint materialIndex, float2 texcoords) {
Material data = world.materials[NonUniformResourceIndex(materialIndex)];
return dTextures[NonUniformResourceIndex(data.emissive)].SampleLevel(dTextureSampler, texcoords, 0).rgb;
}
3 changes: 1 addition & 2 deletions shaders/hrtsystem/mesh_sampling/power.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ void main(uint3 dispatchXYZ: SV_DispatchThreadID) {
for (uint j = 0; j < samples_per_dim; j++) {
const float2 barycentrics = squareToTriangle(float2(i, j) / float(samples_per_dim));
const MeshAttributes attrs = MeshAttributes::lookupAndInterpolate(world, pushConsts.instanceIndex, pushConsts.geometryIndex, srcPrimitive, barycentrics);
const uint instanceID = world.instances[pushConsts.instanceIndex].instanceID();
total_emissive += luminance(getEmissive(world, world.materialIdx(instanceID, pushConsts.geometryIndex), attrs.texcoord));
total_emissive += luminance(world.material(pushConsts.instanceIndex, pushConsts.geometryIndex).getEmissive(attrs.texcoord));
}
}

Expand Down
Loading

0 comments on commit b9f5891

Please sign in to comment.