Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 9 additions & 8 deletions atlas/graphics/deferred.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ buildGPUAreaLights(const std::vector<AreaLight *> &lights, int maxCount) {
#ifdef METAL
void Window::enableGlobalIllumination() {
usesGlobalIllumination = true;
useSSR = true;
ddgiSystem = std::make_shared<photon::GlobalIllumination>();
ddgiSystem->sampleNormalMaps = false;
ddgiSystem->init();
Expand Down Expand Up @@ -627,7 +628,9 @@ void Window::deferredRendering(
if (usesGlobalIllumination && ddgiSystem != nullptr &&
ddgiSystem->probeSpace != nullptr &&
ddgiSystem->irradianceMap != nullptr &&
ddgiSystem->irradianceMap->texture != nullptr) {
ddgiSystem->irradianceMap->texture != nullptr &&
ddgiSystem->distanceMap != nullptr &&
ddgiSystem->distanceMap->texture != nullptr) {
lightPipeline->setUniform3f("ps.origin",
ddgiSystem->probeSpace->originWorldSpace.x,
ddgiSystem->probeSpace->originWorldSpace.y,
Expand All @@ -653,12 +656,10 @@ void Window::deferredRendering(

std::shared_ptr<opal::Texture> ddgiIrradianceTexture =
ddgiSystem->irradianceMap->texture;
if (ddgiSystem->irradianceMapPrev != nullptr &&
ddgiSystem->irradianceMapPrev->texture != nullptr &&
ddgiSystem->frameIndex <= 1) {
ddgiIrradianceTexture = ddgiSystem->irradianceMapPrev->texture;
}
std::shared_ptr<opal::Texture> ddgiDistanceTexture =
ddgiSystem->distanceMap->texture;
lightPipeline->bindTexture("irradianceMap", ddgiIrradianceTexture, 16);
lightPipeline->bindTexture("ddgiDistanceMap", ddgiDistanceTexture, 17);
} else {
lightPipeline->setUniform3f("ps.origin", 0.0f, 0.0f, 0.0f);
lightPipeline->setUniform3f("ps.spacing", 1.0f, 1.0f, 1.0f);
Expand All @@ -667,6 +668,8 @@ void Window::deferredRendering(
lightPipeline->setUniform4f("ps.atlasParams", 0.0f, 0.0f, 0.0f, 0.0f);
lightPipeline->bindTexture2D("irradianceMap",
fallbackIrradianceTexture->textureID, 16);
lightPipeline->bindTexture2D("ddgiDistanceMap",
fallbackIrradianceTexture->textureID, 17);
}
#endif

Expand Down Expand Up @@ -972,8 +975,6 @@ void Window::deferredRendering(
lightPipeline->bindTextureCubemap(
"skybox", fallbackSkyboxTexture->textureID, boundTextures);
}
lightPipeline->setUniformBool(
"useIBL", scene->skybox != nullptr && scene->skybox->cubemap.id != 0);
boundTextures++;

lightPipeline->setUniform1f(
Expand Down
524 changes: 391 additions & 133 deletions include/atlas/core/default_shaders.h

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions include/photon/illuminate.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ class GlobalIllumination {
std::shared_ptr<Texture> irradianceMap;
/** @brief Previous irradiance atlas for temporal blending. */
std::shared_ptr<Texture> irradianceMapPrev;
std::shared_ptr<Texture> distanceMap;
std::shared_ptr<Texture> distanceMapPrev;
/** @brief Compute program that writes probe irradiance tiles to the atlas.
*/
std::shared_ptr<ShaderProgram> giWriteShader;
Expand All @@ -171,6 +173,12 @@ class GlobalIllumination {
/** @brief Byte capacity currently allocated for probeRadianceBuffer. */
int probeRadianceCapacity = 0;

std::shared_ptr<opal::Buffer> triangleBuffer;
std::shared_ptr<opal::Buffer> materialBuffer;
std::shared_ptr<opal::PrimitiveAccelerationStructure> sceneBLAS;
std::shared_ptr<opal::InstanceAccelerationStructure> sceneTLAS;
bool accelerationStructureDirty = false;

/** @brief Active probe-space definition used for DDGI dispatch. */
std::shared_ptr<ProbeSpace> probeSpace;

Expand Down
208 changes: 179 additions & 29 deletions photon/gi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,44 @@
#ifdef METAL
namespace {
constexpr int kDdgiMaterialTextureUnitStart = 10;
constexpr int kDdgiMaxMaterialTextures = 24;
constexpr int kDdgiMaxMaterialTextures = 48;
constexpr int kDdgiSkyboxTextureUnit = 60;
constexpr int kDdgiPreviousIrradianceTextureUnit = 61;

std::shared_ptr<opal::Texture> createDdgiFallbackSkyboxTexture() {
constexpr unsigned char value[4] = {0, 0, 0, 255};
const unsigned char *faces[6] = {value, value, value, value, value, value};
auto texture = opal::Texture::create(
opal::TextureType::TextureCubeMap, opal::TextureFormat::Rgba8, 1, 1,
opal::TextureDataFormat::Rgba, nullptr, 1);
texture->setFilterMode(opal::TextureFilterMode::Linear,
opal::TextureFilterMode::Linear);
texture->setWrapMode(opal::TextureAxis::S,
opal::TextureWrapMode::ClampToEdge);
texture->setWrapMode(opal::TextureAxis::T,
opal::TextureWrapMode::ClampToEdge);
texture->setWrapMode(opal::TextureAxis::R,
opal::TextureWrapMode::ClampToEdge);
for (int face = 0; face < 6; ++face) {
texture->updateFace(face, faces[face], 1, 1,
opal::TextureDataFormat::Rgba);
}
return texture;
}

void clearDdgiTexture(const std::shared_ptr<Texture> &texture) {
if (texture == nullptr || texture->texture == nullptr ||
texture->creationData.width <= 0 || texture->creationData.height <= 0) {
return;
}
std::vector<float> zeros(
static_cast<size_t>(texture->creationData.width) *
static_cast<size_t>(texture->creationData.height) * 4,
0.0f);
texture->texture->updateData(zeros.data(), texture->creationData.width,
texture->creationData.height,
opal::TextureDataFormat::Rgba);
}

int registerMaterialTextureSlot(
const Texture &texture,
Expand Down Expand Up @@ -251,6 +288,19 @@ void photon::GlobalIllumination::init() {
Texture::create(512, 512, opal::TextureFormat::Rgba16F,
opal::TextureDataFormat::Rgba, TextureType::Color));

distanceMap = std::make_shared<Texture>(
Texture::create(512, 512, opal::TextureFormat::Rgba16F,
opal::TextureDataFormat::Rgba, TextureType::Color));

distanceMapPrev = std::make_shared<Texture>(
Texture::create(512, 512, opal::TextureFormat::Rgba16F,
opal::TextureDataFormat::Rgba, TextureType::Color));

clearDdgiTexture(irradianceMap);
clearDdgiTexture(irradianceMapPrev);
clearDdgiTexture(distanceMap);
clearDdgiTexture(distanceMapPrev);

giPipeline = opal::Pipeline::create();
giPipeline->setShaderProgram(giWriteShader->shader);
giPipeline->setComputeThreadgroupSize(8, 8, 1);
Expand Down Expand Up @@ -299,7 +349,9 @@ void photon::GlobalIllumination::updateProbeLayout() {
probeSpace->probeResolution, probeSpace->textureBorderSize);
if (hasCachedLayoutSignature && cachedLayoutSignature == layoutSignature &&
probeRadianceBuffer != nullptr && irradianceMap != nullptr &&
irradianceMapPrev != nullptr) {
irradianceMapPrev != nullptr && distanceMap != nullptr &&
distanceMapPrev != nullptr && triangleBuffer != nullptr &&
materialBuffer != nullptr && sceneBLAS != nullptr) {
return;
}
cachedLayoutSignature = layoutSignature;
Expand All @@ -311,6 +363,14 @@ void photon::GlobalIllumination::updateProbeLayout() {
materials.reserve(ddgiObjects.size());
std::unordered_map<uint64_t, int> textureSlots;

for (auto *object : ddgiObjects) {
if (object == nullptr || !object->canUseDeferredRendering()) {
continue;
}
findTextureSlotForType(object->textures, TextureType::Color,
materialTextures, textureSlots);
}

for (auto *object : ddgiObjects) {
if (object == nullptr || !object->canUseDeferredRendering()) {
continue;
Expand All @@ -323,11 +383,7 @@ void photon::GlobalIllumination::updateProbeLayout() {

const auto &indices = object->indices;
const bool useIndexBuffer = indices.size() >= 3;
glm::mat4 model(1.0f);
model = glm::translate(model, object->getPosition().toGlm());
model *=
glm::mat4_cast(glm::normalize(object->getRotation().toGlmQuat()));
model = glm::scale(model, object->getScale().toGlm());
glm::mat4 model = object->model;
glm::mat3 linearMatrix = glm::mat3(model);
glm::mat3 normalMatrix = glm::transpose(glm::inverse(glm::mat3(model)));

Expand Down Expand Up @@ -458,6 +514,48 @@ void photon::GlobalIllumination::updateProbeLayout() {
}
}

if (hasGeometry) {
glm::vec3 rawExtent = glm::max(boundsMax - boundsMin, glm::vec3(0.0f));
spacing = std::max(spacing, std::max(rawExtent.x, std::max(rawExtent.y,
rawExtent.z)) /
15.0f);
}

std::vector<opal::PrimitiveVertex> accelerationVertices;
std::vector<uint32_t> accelerationIndices;
accelerationVertices.reserve(triangles.size() * 3);
accelerationIndices.reserve(triangles.size() * 3);
for (const auto &triangle : triangles) {
const glm::vec4 positions[3] = {triangle.v0, triangle.v1, triangle.v2};
for (const auto &position : positions) {
opal::PrimitiveVertex vertex{};
vertex.position[0] = position.x;
vertex.position[1] = position.y;
vertex.position[2] = position.z;
accelerationIndices.push_back(
static_cast<uint32_t>(accelerationVertices.size()));
accelerationVertices.push_back(vertex);
}
}
if (!accelerationVertices.empty()) {
sceneBLAS = opal::PrimitiveAccelerationStructure::create(
accelerationVertices, accelerationIndices);
sceneTLAS.reset();
accelerationStructureDirty = true;
triangleBuffer = opal::Buffer::create(
opal::BufferUsage::ShaderRead,
triangles.size() * sizeof(DDGITriangle), triangles.data());
materialBuffer = opal::Buffer::create(
opal::BufferUsage::ShaderRead,
materials.size() * sizeof(DDGIMaterial), materials.data());
} else {
sceneBLAS.reset();
sceneTLAS.reset();
triangleBuffer.reset();
materialBuffer.reset();
accelerationStructureDirty = false;
}

float layoutPad = spacing * 0.25f;
Position3d minWs = hasGeometry ? Position3d(boundsMin.x - layoutPad,
boundsMin.y - layoutPad,
Expand Down Expand Up @@ -498,7 +596,10 @@ void photon::GlobalIllumination::updateProbeLayout() {
static_cast<int>(std::ceil(std::sqrt((float)totalProbeCount))), 1, 64);

probeSpace->originWorldSpace = minWs;
probeSpace->spacing = Position3d(spacing, spacing, spacing);
probeSpace->spacing = Position3d(
Nx > 1 ? extent.x / static_cast<float>(Nx - 1) : spacing,
Ny > 1 ? extent.y / static_cast<float>(Ny - 1) : spacing,
Nz > 1 ? extent.z / static_cast<float>(Nz - 1) : spacing);
probeSpace->probeCount = Vector3((float)Nx, (float)Ny, (float)Nz);
probeSpace->probesPerRow = probesPerRow;

Expand All @@ -522,7 +623,8 @@ void photon::GlobalIllumination::updateProbeLayout() {

const int atlasW = std::max(1, probeSpace->atlasWidth());
const int atlasH = std::max(1, probeSpace->atlasHeight());
bool needCreate = !irradianceMap || !irradianceMapPrev;
bool needCreate = !irradianceMap || !irradianceMapPrev || !distanceMap ||
!distanceMapPrev;
bool sizeChanged =
!needCreate && (irradianceMap->creationData.width != atlasW ||
irradianceMap->creationData.height != atlasH);
Expand All @@ -535,6 +637,16 @@ void photon::GlobalIllumination::updateProbeLayout() {
irradianceMapPrev = std::make_shared<Texture>(
Texture::create(atlasW, atlasH, opal::TextureFormat::Rgba16F,
opal::TextureDataFormat::Rgba, TextureType::Color));
distanceMap = std::make_shared<Texture>(
Texture::create(atlasW, atlasH, opal::TextureFormat::Rgba16F,
opal::TextureDataFormat::Rgba, TextureType::Color));
distanceMapPrev = std::make_shared<Texture>(
Texture::create(atlasW, atlasH, opal::TextureFormat::Rgba16F,
opal::TextureDataFormat::Rgba, TextureType::Color));
clearDdgiTexture(irradianceMap);
clearDdgiTexture(irradianceMapPrev);
clearDdgiTexture(distanceMap);
clearDdgiTexture(distanceMapPrev);
}

int effectiveRaysPerProbe = std::max(1, raysPerProbe);
Expand All @@ -560,19 +672,36 @@ void photon::GlobalIllumination::render(
probeRadianceBuffer == nullptr || irradianceMap == nullptr ||
irradianceMapPrev == nullptr || irradianceMap->texture == nullptr ||
irradianceMapPrev->texture == nullptr ||
distanceMap == nullptr || distanceMapPrev == nullptr ||
distanceMap->texture == nullptr || distanceMapPrev->texture == nullptr ||
triangleBuffer == nullptr || materialBuffer == nullptr ||
sceneBLAS == nullptr ||
copySrcFramebuffer == nullptr || copyDstFramebuffer == nullptr) {
return;
}

if (accelerationStructureDirty) {
commandBuffer->buildPrimitiveAccelerationStructure(sceneBLAS);
opal::AccelerationStructureInstance instance{};
instance.blas = sceneBLAS;
instance.transform = glm::mat4(1.0f);
instance.instanceId = 0;
instance.mask = 0xFF;
instance.cullDisable = true;
sceneTLAS = opal::InstanceAccelerationStructure::create({instance});
commandBuffer->buildInstanceAccelerationStructure(sceneTLAS);
accelerationStructureDirty = false;
}
if (sceneTLAS == nullptr || !sceneTLAS->isBuilt) {
return;
}

const uint totalProbes =
static_cast<uint>(std::max(1, this->probeSpace->totalProbes()));
const uint requestedRays =
static_cast<uint>(std::max(1, this->raysPerProbe));
const uint effectiveRays = std::max(1u, requestedRays);
uint updateStride = static_cast<uint>(std::max(1, this->probeUpdateStride));
if (frameIndex < static_cast<int>(updateStride) + 2) {
updateStride = 1u;
}
uint updateOffset =
(updateStride > 1u)
? static_cast<uint>(std::max(0, frameIndex)) % updateStride
Expand All @@ -597,6 +726,11 @@ void photon::GlobalIllumination::render(
auto copy = opal::ResolveAction::createForColorAttachment(
copySrcFramebuffer, copyDstFramebuffer, 0);
commandBuffer->performResolve(copy);
copySrcFramebuffer->attachTexture(distanceMap->texture, 0);
copyDstFramebuffer->attachTexture(distanceMapPrev->texture, 0);
copy = opal::ResolveAction::createForColorAttachment(
copySrcFramebuffer, copyDstFramebuffer, 0);
commandBuffer->performResolve(copy);

// Perform Ray Tracing
giRaytracingPipeline->bindShaderReadWriteBuffer("probeRadianceOut",
Expand Down Expand Up @@ -754,23 +888,8 @@ void photon::GlobalIllumination::render(
areaLights.empty() ? sizeof(GPUAreaLight)
: areaLights.size() * sizeof(GPUAreaLight));

DDGITriangle fallbackTriangle{};
DDGIMaterial fallbackMaterial{};
const void *triangleData =
triangles.empty() ? static_cast<const void *>(&fallbackTriangle)
: static_cast<const void *>(triangles.data());
const size_t triangleSize = triangles.empty()
? sizeof(DDGITriangle)
: triangles.size() * sizeof(DDGITriangle);
const void *materialData =
materials.empty() ? static_cast<const void *>(&fallbackMaterial)
: static_cast<const void *>(materials.data());
const size_t materialSize = materials.empty()
? sizeof(DDGIMaterial)
: materials.size() * sizeof(DDGIMaterial);
giRaytracingPipeline->bindBufferData("tris", triangleData, triangleSize);
giRaytracingPipeline->bindBufferData("materials", materialData,
materialSize);
giRaytracingPipeline->bindBuffer("tris", triangleBuffer, 1);
giRaytracingPipeline->bindBuffer("materials", materialBuffer, 2);

struct GPUSceneCounts {
uint32_t triCount;
Expand Down Expand Up @@ -805,6 +924,31 @@ void photon::GlobalIllumination::render(
kDdgiMaterialTextureUnitStart + i);
}

static std::shared_ptr<opal::Texture> fallbackSkybox = nullptr;
if (fallbackSkybox == nullptr) {
fallbackSkybox = createDdgiFallbackSkyboxTexture();
}
std::shared_ptr<opal::Texture> skyboxTexture = fallbackSkybox;
int useSkybox = 0;
glm::vec3 skyColor(0.12f, 0.14f, 0.18f);
if (scene != nullptr) {
auto skybox = scene->getSkybox();
if (skybox != nullptr && skybox->cubemap.texture != nullptr) {
skyboxTexture = skybox->cubemap.texture;
useSkybox = 1;
} else if (scene->atmosphere.isEnabled()) {
Color atmosphereColor = scene->atmosphere.getLightColor();
skyColor = glm::vec3(atmosphereColor.r, atmosphereColor.g,
atmosphereColor.b) *
std::max(scene->atmosphere.getLightIntensity(), 0.0f);
}
}
giRaytracingPipeline->bindTexture("skybox", skyboxTexture,
kDdgiSkyboxTextureUnit);
giRaytracingPipeline->bindTexture("previousIrradiance",
irradianceMapPrev->texture,
kDdgiPreviousIrradianceTextureUnit);

giRaytracingPipeline->setUniform3f(
"ps.origin", probeSpace->originWorldSpace.x,
probeSpace->originWorldSpace.y, probeSpace->originWorldSpace.z);
Expand Down Expand Up @@ -837,15 +981,21 @@ void photon::GlobalIllumination::render(
static_cast<int>(updateStride));
giRaytracingPipeline->setUniform1i("rt.probeUpdateCount",
static_cast<int>(activeProbeCount));
giRaytracingPipeline->setUniform3f("rt.skyColor", skyColor.x, skyColor.y,
skyColor.z);
giRaytracingPipeline->setUniform1i("rt.useSkybox", useSkybox);

commandBuffer->bindPipeline(giRaytracingPipeline);
commandBuffer->bindInstanceAccelerationStructure(sceneTLAS, 10);
commandBuffer->dispatch(totalRays, 1, 1);

commandBuffer->computeBarrier();

// Write to irradiance texture
giPipeline->bindTexture("outTexture", irradianceMap->texture, 0);
giPipeline->bindTexture("prevTexture", irradianceMapPrev->texture, 1);
giPipeline->bindTexture("outDistance", distanceMap->texture, 2);
giPipeline->bindTexture("prevDistance", distanceMapPrev->texture, 3);

giPipeline->bindBuffer("probeRadiance", this->probeRadianceBuffer);

Expand Down
Loading
Loading