From 9f7916b177968fd759c86c68710d53bee76dfe6c Mon Sep 17 00:00:00 2001 From: Max Van den Eynde Date: Sun, 19 Jul 2026 19:05:13 +0200 Subject: [PATCH] Solved an issue with skyboxes and path tracing --- atlas/application/window.cpp | 3 + include/atlas/core/default_shaders.h | 110 +++++++++++++++++++------- opal/command_buffer.cpp | 31 ++++---- photon/path_tracing.cpp | 41 ++++++++-- shaders/metal/path_tracing/path.metal | 69 +++++++++++++--- tests/path-tracing/main.ascene | 10 +-- 6 files changed, 201 insertions(+), 63 deletions(-) diff --git a/atlas/application/window.cpp b/atlas/application/window.cpp index 0aba4367..a094d0e1 100644 --- a/atlas/application/window.cpp +++ b/atlas/application/window.cpp @@ -1872,6 +1872,9 @@ bool Window::stepFrame() { } if (!editorControlsRenderedInScenePass) { + if (this->usePathTracing) { + renderEditorGrid(commandBuffer); + } renderEditorOverlays(commandBuffer); } diff --git a/include/atlas/core/default_shaders.h b/include/atlas/core/default_shaders.h index 72532f51..db84f3d8 100644 --- a/include/atlas/core/default_shaders.h +++ b/include/atlas/core/default_shaders.h @@ -6398,7 +6398,8 @@ struct Material { float transmittance; float ior; - float _pad2[2]; + float reflectivity; + float _pad2; }; struct MeshData { @@ -6475,7 +6476,12 @@ struct SceneData { float indirectStrength; float ambientIntensity; uint materialTextureCount; - uint _pad0; + uint atmosphereEnabled; + float atmosphereSunSize; + float3 atmosphereSunDirection; + float atmosphereSunIntensity; + float3 atmosphereSunColor; + float _pad0; }; float pow5(float x) { @@ -6514,7 +6520,8 @@ uint seedBase(uint2 gid, uint w, uint frame, uint sampleIndex) { constexpr sampler skyboxSampler(coord::normalized, address::clamp_to_edge, filter::linear, mip_filter::linear); -float3 skyColor(float3 dir, float intensity, texturecube skybox) { +float3 skyColor(float3 dir, float intensity, texturecube skybox, + constant SceneData &sceneData) { float3 sampleDir = dir; float len2 = dot(sampleDir, sampleDir); if (len2 > 1e-10) { @@ -6523,6 +6530,33 @@ float3 skyColor(float3 dir, float intensity, texturecube skybox) { sampleDir = float3(0.0, 1.0, 0.0); } float3 sky = skybox.sample(skyboxSampler, sampleDir).xyz; + if (sceneData.atmosphereEnabled != 0 && + sceneData.atmosphereSunDirection.y > -0.15) { + float3 sunDirection = sceneData.atmosphereSunDirection; + float sunDirectionLength = dot(sunDirection, sunDirection); + sunDirection = sunDirectionLength > 1e-10 + ? sunDirection * rsqrt(sunDirectionLength) + : float3(0.0, 1.0, 0.0); + float sunDot = dot(sampleDir, sunDirection); + float sizeAdjust = + 1.0 - (sceneData.atmosphereSunSize - 1.0) * 0.001; + float sunSize = 0.9995 * sizeAdjust; + float sunGlowSize = + 0.998 * (1.0 - (sceneData.atmosphereSunSize - 1.0) * 0.003); + float sunHaloSize = + 0.99 * (1.0 - (sceneData.atmosphereSunSize - 1.0) * 0.015); + float sunDisk = smoothstep(sunSize - 0.0002, sunSize, sunDot); + float sunGlow = + smoothstep(sunGlowSize, sunSize, sunDot) * (1.0 - sunDisk); + float sunHalo = smoothstep(sunHaloSize, sunSize, sunDot) * + (1.0 - smoothstep(sunSize, sunGlowSize, sunDot)); + float horizonFade = smoothstep( + -0.15, 0.05, sceneData.atmosphereSunDirection.y); + float sunIntensity = max(sceneData.atmosphereSunIntensity, 0.05); + sky += sceneData.atmosphereSunColor * + (sunDisk * 5.0 + sunGlow * 0.5 + sunHalo) * horizonFade * + sunIntensity; + } float scale = intensity > 0.0 ? intensity : 1.0; return sky * scale; } @@ -6587,7 +6621,8 @@ constexpr sampler materialTexSampler(coord::normalized, address::repeat, texture2d materialTexture32, \ texture2d materialTexture33, \ texture2d materialTexture34, \ - texture2d materialTexture35, \ + texture2d materialTexture35, )", +R"( \ texture2d materialTexture36, \ texture2d materialTexture37, \ texture2d materialTexture38, \ @@ -6609,8 +6644,7 @@ constexpr sampler materialTexSampler(coord::normalized, address::repeat, materialTexture16, materialTexture17, materialTexture18, \ materialTexture19, materialTexture20, materialTexture21, \ materialTexture22, materialTexture23, materialTexture24, \ - materialTexture25, materi)", -R"(alTexture26, materialTexture27, \ + materialTexture25, materialTexture26, materialTexture27, \ materialTexture28, materialTexture29, materialTexture30, \ materialTexture31, materialTexture32, materialTexture33, \ materialTexture34, materialTexture35, materialTexture36, \ @@ -6720,7 +6754,8 @@ float4 sampleMaterialTexture(int textureIndex, float2 uv, return materialTexture22.sample(materialTexSampler, uv); case 23: return materialTexture23.sample(materialTexSampler, uv); - case 24: + case 24:)", +R"( return materialTexture24.sample(materialTexSampler, uv); case 25: return materialTexture25.sample(materialTexSampler, uv); @@ -6763,8 +6798,7 @@ float4 sampleMaterialTexture(int textureIndex, float2 uv, case 44: return materialTexture44.sample(materialTexSampler, uv); case 45: - return m)", -R"(aterialTexture45.sample(materialTexSampler, uv); + return materialTexture45.sample(materialTexSampler, uv); case 46: return materialTexture46.sample(materialTexSampler, uv); case 47: @@ -6917,7 +6951,8 @@ bool isOccludedPointLight(PointLight light, float3 P, float3 N, float3(r * cos(phi), r * sin(phi), z) * lightRadius; float3 sampledLightPos = light.position + sphereOffset; - float3 toLight = sampledLightPos - P; + float3 toL)", +R"(ight = sampledLightPos - P; float dist2 = max(dot(toLight, toLight), 0.001); float dist = sqrt(dist2); float3 L = toLight / dist; @@ -6957,8 +6992,7 @@ bool isOccludedAreaLight(AreaLight light, float3 P, float3 N, float dist2 = max(dot(toLight, toLight), 1e-4); float dist = sqrt(dist2); float3 L = toLight / dist; - return i)", -R"(sOccluded(isect, sceneAS, P, N, L, dist - 0.001); + return isOccluded(isect, sceneAS, P, N, L, dist - 0.001); } // --------------------------------------------------------------------------- @@ -7102,7 +7136,8 @@ float3 evalDirectLightingPBR(intersector isect, float dist = max(length(toLight), 1e-4); float3 L = toLight / dist; float lightRange = max(pointLights[i].range, 1e-4); - float minDist = max(lightRange * 0.08, 0.15); + float minDist = max)", +R"((lightRange * 0.08, 0.15); float distSq = dist * dist + minDist * minDist; float rangeFade = 1.0 - smoothstep(lightRange * 0.75, lightRange, dist); float atten = rangeFade / max(distSq, 1e-4); @@ -7134,8 +7169,7 @@ float3 evalDirectLightingPBR(intersector isect, float lightRange = max(spotLights[i].range, 1e-4); float minDist = max(lightRange * 0.08, 0.15); float distSq = dist * dist + minDist * minDist; - float rangeFade = 1.0 - smoothstep(lightRang)", -R"(e * 0.75, lightRange, dist); + float rangeFade = 1.0 - smoothstep(lightRange * 0.75, lightRange, dist); float atten = rangeFade / max(distSq, 1e-4); float intensity = max(spotLights[i].intensity, 0.0) * atten * spot; float3 c = evalPBR(albedo, metallic, roughness, N, V, L, @@ -7218,7 +7252,7 @@ float3 sampleRadiance(uint2 gid, uint sampleIndex, uint w, for (uint step = 0; step < 8; ++step) { if (hit.type == intersection_type::none) { - return skyColor(surfaceRay.direction, 0.0, skybox); + return skyColor(surfaceRay.direction, 0.0, skybox, sceneData); } uint instanceIndex = hit.instance_id; @@ -7274,10 +7308,11 @@ float3 sampleRadiance(uint2 gid, uint sampleIndex, uint w, } if (!foundOpaqueSurface) { - return skyColor(surfaceRay.direction, 0.0, skybox); + return skyColor(surfaceRay.direction, 0.0, skybox, sceneData); } - float3 N = resolveShadingNormal(mat, texUV, localN, localT, localB, inst, + float3 N = resolveShadingNormal(mat, texUV, localN, localT, localB, inst)", +R"(, sceneData.materialTextureCount, PT_MATERIAL_TEXTURE_ARGS); float3 P = surfaceRay.origin + surfaceRay.direction * hit.distance; @@ -7296,6 +7331,7 @@ float3 sampleRadiance(uint2 gid, uint sampleIndex, uint w, resolveMaterialParameters(mat, texUV, sceneData.materialTextureCount, PT_MATERIAL_TEXTURE_ARGS, albedo, metallic, roughness, ao, emissive, ior, transmittance); + float reflectivity = clamp(mat.reflectivity, 0.0, 1.0); float sssStrength = clamp(1.0 - mat.albedo.w, 0.0, 1.0) * (1.0 - metallic); float sssThickness = mix(0.25, 1.75, ao); @@ -7307,7 +7343,9 @@ float3 sampleRadiance(uint2 gid, uint sampleIndex, uint w, float3 indirect = float3(0.0); if (sceneData.maxBounces > 0) { - float3 F0 = mix(float3(0.04), albedo, metallic); + float3 baseF0 = mix(float3(0.04), albedo, metallic); + float3 reflectedColor = mix(float3(1.0), albedo, metallic); + float3 F0 = mix(baseF0, reflectedColor, reflectivity); float3 F_approx = F_Schlick(max(dot(N, V), 0.0), F0); float dielectricF0 = pow((ior - 1.0) / (ior + 1.0), 2.0); @@ -7317,8 +7355,10 @@ float3 sampleRadiance(uint2 gid, uint sampleIndex, uint w, (1.0 - metallic) * dielectricSpec; float transmitProb = transmittance * (1.0 - metallic) * (1.0 - dielectricSpec); - float diffuseProb = (1.0 - metallic) * (1.0 - transmittance)", -R"(); + float diffuseProb = (1.0 - metallic) * (1.0 - transmittance); + specProb = mix(specProb, 1.0, reflectivity); + transmitProb *= 1.0 - reflectivity; + diffuseProb *= 1.0 - reflectivity; float probSum = max(specProb + transmitProb + diffuseProb, 1e-4); specProb /= probSum; transmitProb /= probSum; @@ -7333,8 +7373,10 @@ R"(); float3 brdfWeight; float chooseSplit = rand(rng); bool choseTransmission = false; + bool choseSpecular = false; if (specProb > 1e-4 && chooseSplit < specProb) { + choseSpecular = true; float2 u = float2(rand(rng), rand(rng)); float3 localH = sampleGGX(u, max(roughness, 0.001)); float3 H_world = normalize(basis * localH); @@ -7459,7 +7501,8 @@ R"(); bool enteringShell = dot(tN, tV) > 0.0; float etaShell = enteringShell ? (1.0 / tIor) : tIor; float3 faceNShell = enteringShell ? tN : -tN; - float3 refractShell = refract(-tV, faceNShell, etaShell); + float)", +R"(3 refractShell = refract(-tV, faceNShell, etaShell); if (length(refractShell) < 1e-5) { refractShell = reflect(-tV, faceNShell); } @@ -7474,9 +7517,14 @@ R"(); } if (resolvedBounceHit.type == intersection_type::none) { + float bounceStrength = choseSpecular + ? mix(sceneData.indirectStrength, 1.0, + max(metallic, reflectivity)) + : sceneData.indirectStrength; indirect = brdfWeight * - skyColor(resolvedBounceRay.direction, 0.0, skybox) * - sceneData.indirectStrength; + skyColor(resolvedBounceRay.direction, 0.0, skybox, + sceneData) * + bounceStrength; } else { uint bi = resolvedBounceHit.instance_id; uint bp = resolvedBounceHit.primitive_id; @@ -7505,8 +7553,7 @@ R"(); float3 bLocalT = normalizeOr(float3(vertices[bj0].tangent) * bb0 + float3(vertices[bj1].tangent) * bb1 + - )", -R"( float3(vertices[bj2].tangent) * bb2, + float3(vertices[bj2].tangent) * bb2, float3(1.0, 0.0, 0.0)); float3 bLocalB = normalizeOr(float3(vertices[bj0].bitangent) * bb0 + @@ -7546,8 +7593,12 @@ R"( float3(vertices[bj2].tangent) * bb2, float3 bAmbient = bAlbedo * max(sceneData.ambientIntensity, 0.0) * (1.0 - bMetallic) * bAo; + float bounceStrength = choseSpecular + ? mix(sceneData.indirectStrength, 1.0, + max(metallic, reflectivity)) + : sceneData.indirectStrength; indirect = brdfWeight * (bAmbient + bounceDirect + bEmissive) * - sceneData.indirectStrength; + bounceStrength; } indirect = clampLuminance(indirect, 16.0); @@ -7646,7 +7697,8 @@ kernel void main0(texture2d outTex [[texture(0)]], soft = soft * soft / max(bloomKnee * 4.0, 0.00001); - float contribution = + float c)", +R"(ontribution = max(brightness - bloomThreshold, soft) / max(brightness, 0.00001); @@ -7657,7 +7709,7 @@ kernel void main0(texture2d outTex [[texture(0)]], } )", }; -static const AtlasPackedShaderSource PATH = {PATH_PARTS, 7}; +static const AtlasPackedShaderSource PATH = {PATH_PARTS, 8}; static const char* const POINT_DEPTH_FRAG_PARTS[] = { R"(#include diff --git a/opal/command_buffer.cpp b/opal/command_buffer.cpp index 2aeb0cd4..5a720aad 100644 --- a/opal/command_buffer.cpp +++ b/opal/command_buffer.cpp @@ -865,8 +865,8 @@ void bindComputeTextures(const std::shared_ptr &pipeline, } auto &pipelineState = metal::pipelineState(pipeline.get()); - std::array desiredTextures{}; - std::array desiredSamplers{}; + std::array desiredTextures{}; + std::array desiredSamplers{}; desiredTextures.fill(nullptr); desiredSamplers.fill(nullptr); @@ -881,11 +881,13 @@ void bindComputeTextures(const std::shared_ptr &pipeline, if (textureState.texture == nullptr) { continue; } - if (textureState.sampler == nullptr) { - metal::rebuildTextureSampler(texture.get(), device); - } desiredTextures[static_cast(unit)] = textureState.texture; - desiredSamplers[static_cast(unit)] = textureState.sampler; + if (unit < static_cast(desiredSamplers.size())) { + if (textureState.sampler == nullptr) { + metal::rebuildTextureSampler(texture.get(), device); + } + desiredSamplers[static_cast(unit)] = textureState.sampler; + } } if (pipeline->shaderProgram != nullptr) { @@ -907,20 +909,21 @@ void bindComputeTextures(const std::shared_ptr &pipeline, if (fallbackState.texture == nullptr) { continue; } - if (fallbackState.sampler == nullptr) { - metal::rebuildTextureSampler(fallback.get(), device); - } desiredTextures[unitIndex] = fallbackState.texture; - desiredSamplers[unitIndex] = fallbackState.sampler; + if (unitIndex < desiredSamplers.size()) { + if (fallbackState.sampler == nullptr) { + metal::rebuildTextureSampler(fallback.get(), device); + } + desiredSamplers[unitIndex] = fallbackState.sampler; + } } } - constexpr size_t kMaxComputeTextureUnits = 16; - const size_t computeUnitCount = - std::min(desiredTextures.size(), kMaxComputeTextureUnits); - for (size_t unit = 0; unit < computeUnitCount; ++unit) { + for (size_t unit = 0; unit < desiredTextures.size(); ++unit) { encoder->setTexture(desiredTextures[unit], static_cast(unit)); + } + for (size_t unit = 0; unit < desiredSamplers.size(); ++unit) { encoder->setSamplerState(desiredSamplers[unit], static_cast(unit)); } diff --git a/photon/path_tracing.cpp b/photon/path_tracing.cpp index ca174444..3308fd75 100644 --- a/photon/path_tracing.cpp +++ b/photon/path_tracing.cpp @@ -229,7 +229,8 @@ void photon::PathTracing::buildAccelerationStructure( float transmittance; float ior; - float _pad2[2]; + float reflectivity; + float _pad2; }; struct MeshData { @@ -362,6 +363,8 @@ void photon::PathTracing::buildAccelerationStructure( data.emissiveColor[2] = object->material.emissiveColor.b; data.ior = object->material.ior; data.transmittance = object->material.transmittance; + data.reflectivity = object->material.reflectivity; + data._pad2 = 0.0f; const bool useNormalMap = object->material.useNormalMap && sampleNormalMaps; const float normalStrength = std::max( @@ -663,12 +666,25 @@ void photon::PathTracing::render( glm::vec3 directionalLightColor(1.0f, 1.0f, 1.0f); float directionalLightIntensity = 0.0f; float ambientIntensity = 0.0f; + glm::vec3 atmosphereSunDirection(0.0f, 1.0f, 0.0f); + glm::vec3 atmosphereSunColor(1.0f, 0.95f, 0.8f); + float atmosphereSunIntensity = 0.0f; + float atmosphereSunSize = 1.0f; + int atmosphereEnabled = 0; Scene *scene = (Window::mainWindow != nullptr) ? Window::mainWindow->getCurrentScene() : nullptr; if (scene != nullptr) { + if (scene->atmosphere.isEnabled()) { + atmosphereEnabled = 1; + atmosphereSunDirection = scene->atmosphere.getSunAngle().toGlm(); + Color sunColor = scene->atmosphere.sunColor; + atmosphereSunColor = glm::vec3(sunColor.r, sunColor.g, sunColor.b); + atmosphereSunIntensity = scene->atmosphere.getLightIntensity(); + atmosphereSunSize = scene->atmosphere.sunSize; + } ambientIntensity = scene->isAutomaticAmbientEnabled() ? scene->getAutomaticAmbientIntensity() : scene->getAmbientIntensity(); @@ -731,6 +747,18 @@ void photon::PathTracing::render( directionalLightIntensity); pathTracingPipeline->setUniform1f("sceneData.ambientIntensity", ambientIntensity); + pathTracingPipeline->setUniform1i("sceneData.atmosphereEnabled", + atmosphereEnabled); + pathTracingPipeline->setUniform1f("sceneData.atmosphereSunSize", + atmosphereSunSize); + pathTracingPipeline->setUniform3f( + "sceneData.atmosphereSunDirection", atmosphereSunDirection.x, + atmosphereSunDirection.y, atmosphereSunDirection.z); + pathTracingPipeline->setUniform1f("sceneData.atmosphereSunIntensity", + atmosphereSunIntensity); + pathTracingPipeline->setUniform3f( + "sceneData.atmosphereSunColor", atmosphereSunColor.x, + atmosphereSunColor.y, atmosphereSunColor.z); pathTracingPipeline->setUniform1i("sceneData.numPointLights", pointLightCount); pathTracingPipeline->setUniform1i("sceneData.numSpotLights", @@ -757,15 +785,16 @@ void photon::PathTracing::render( if (fallbackSkyboxTexture == nullptr) { fallbackSkyboxTexture = createFallbackSkyboxTexture(); } - auto skyboxTextureId = fallbackSkyboxTexture->textureID; + std::shared_ptr skyboxTexture = fallbackSkyboxTexture; if (scene != nullptr) { auto skybox = scene->getSkybox(); - if (skybox != nullptr && skybox->cubemap.id != 0) { - skyboxTextureId = skybox->cubemap.id; + if (skybox != nullptr && skybox->cubemap.texture != nullptr) { + skyboxTexture = skybox->cubemap.texture; } } - pathTracingPipeline->bindTextureCubemap("skybox", skyboxTextureId, - kPathTracerSkyboxTextureUnit); + pathTracingPipeline->bindTexture("skybox", skyboxTexture, + kPathTracerSkyboxTextureUnit); + auto skyboxTextureId = skyboxTexture->textureID; bool lightChanged = cachedDirectionalLightCount != directionalLightCount || glm::length(cachedDirectionalLightDirection - diff --git a/shaders/metal/path_tracing/path.metal b/shaders/metal/path_tracing/path.metal index 440d4a3a..7d75c23b 100644 --- a/shaders/metal/path_tracing/path.metal +++ b/shaders/metal/path_tracing/path.metal @@ -27,7 +27,8 @@ struct Material { float transmittance; float ior; - float _pad2[2]; + float reflectivity; + float _pad2; }; struct MeshData { @@ -104,7 +105,12 @@ struct SceneData { float indirectStrength; float ambientIntensity; uint materialTextureCount; - uint _pad0; + uint atmosphereEnabled; + float atmosphereSunSize; + float3 atmosphereSunDirection; + float atmosphereSunIntensity; + float3 atmosphereSunColor; + float _pad0; }; float pow5(float x) { @@ -143,7 +149,8 @@ uint seedBase(uint2 gid, uint w, uint frame, uint sampleIndex) { constexpr sampler skyboxSampler(coord::normalized, address::clamp_to_edge, filter::linear, mip_filter::linear); -float3 skyColor(float3 dir, float intensity, texturecube skybox) { +float3 skyColor(float3 dir, float intensity, texturecube skybox, + constant SceneData &sceneData) { float3 sampleDir = dir; float len2 = dot(sampleDir, sampleDir); if (len2 > 1e-10) { @@ -152,6 +159,33 @@ float3 skyColor(float3 dir, float intensity, texturecube skybox) { sampleDir = float3(0.0, 1.0, 0.0); } float3 sky = skybox.sample(skyboxSampler, sampleDir).xyz; + if (sceneData.atmosphereEnabled != 0 && + sceneData.atmosphereSunDirection.y > -0.15) { + float3 sunDirection = sceneData.atmosphereSunDirection; + float sunDirectionLength = dot(sunDirection, sunDirection); + sunDirection = sunDirectionLength > 1e-10 + ? sunDirection * rsqrt(sunDirectionLength) + : float3(0.0, 1.0, 0.0); + float sunDot = dot(sampleDir, sunDirection); + float sizeAdjust = + 1.0 - (sceneData.atmosphereSunSize - 1.0) * 0.001; + float sunSize = 0.9995 * sizeAdjust; + float sunGlowSize = + 0.998 * (1.0 - (sceneData.atmosphereSunSize - 1.0) * 0.003); + float sunHaloSize = + 0.99 * (1.0 - (sceneData.atmosphereSunSize - 1.0) * 0.015); + float sunDisk = smoothstep(sunSize - 0.0002, sunSize, sunDot); + float sunGlow = + smoothstep(sunGlowSize, sunSize, sunDot) * (1.0 - sunDisk); + float sunHalo = smoothstep(sunHaloSize, sunSize, sunDot) * + (1.0 - smoothstep(sunSize, sunGlowSize, sunDot)); + float horizonFade = smoothstep( + -0.15, 0.05, sceneData.atmosphereSunDirection.y); + float sunIntensity = max(sceneData.atmosphereSunIntensity, 0.05); + sky += sceneData.atmosphereSunColor * + (sunDisk * 5.0 + sunGlow * 0.5 + sunHalo) * horizonFade * + sunIntensity; + } float scale = intensity > 0.0 ? intensity : 1.0; return sky * scale; } @@ -843,7 +877,7 @@ float3 sampleRadiance(uint2 gid, uint sampleIndex, uint w, for (uint step = 0; step < 8; ++step) { if (hit.type == intersection_type::none) { - return skyColor(surfaceRay.direction, 0.0, skybox); + return skyColor(surfaceRay.direction, 0.0, skybox, sceneData); } uint instanceIndex = hit.instance_id; @@ -899,7 +933,7 @@ float3 sampleRadiance(uint2 gid, uint sampleIndex, uint w, } if (!foundOpaqueSurface) { - return skyColor(surfaceRay.direction, 0.0, skybox); + return skyColor(surfaceRay.direction, 0.0, skybox, sceneData); } float3 N = resolveShadingNormal(mat, texUV, localN, localT, localB, inst, @@ -921,6 +955,7 @@ float3 sampleRadiance(uint2 gid, uint sampleIndex, uint w, resolveMaterialParameters(mat, texUV, sceneData.materialTextureCount, PT_MATERIAL_TEXTURE_ARGS, albedo, metallic, roughness, ao, emissive, ior, transmittance); + float reflectivity = clamp(mat.reflectivity, 0.0, 1.0); float sssStrength = clamp(1.0 - mat.albedo.w, 0.0, 1.0) * (1.0 - metallic); float sssThickness = mix(0.25, 1.75, ao); @@ -932,7 +967,9 @@ float3 sampleRadiance(uint2 gid, uint sampleIndex, uint w, float3 indirect = float3(0.0); if (sceneData.maxBounces > 0) { - float3 F0 = mix(float3(0.04), albedo, metallic); + float3 baseF0 = mix(float3(0.04), albedo, metallic); + float3 reflectedColor = mix(float3(1.0), albedo, metallic); + float3 F0 = mix(baseF0, reflectedColor, reflectivity); float3 F_approx = F_Schlick(max(dot(N, V), 0.0), F0); float dielectricF0 = pow((ior - 1.0) / (ior + 1.0), 2.0); @@ -943,6 +980,9 @@ float3 sampleRadiance(uint2 gid, uint sampleIndex, uint w, float transmitProb = transmittance * (1.0 - metallic) * (1.0 - dielectricSpec); float diffuseProb = (1.0 - metallic) * (1.0 - transmittance); + specProb = mix(specProb, 1.0, reflectivity); + transmitProb *= 1.0 - reflectivity; + diffuseProb *= 1.0 - reflectivity; float probSum = max(specProb + transmitProb + diffuseProb, 1e-4); specProb /= probSum; transmitProb /= probSum; @@ -957,8 +997,10 @@ float3 sampleRadiance(uint2 gid, uint sampleIndex, uint w, float3 brdfWeight; float chooseSplit = rand(rng); bool choseTransmission = false; + bool choseSpecular = false; if (specProb > 1e-4 && chooseSplit < specProb) { + choseSpecular = true; float2 u = float2(rand(rng), rand(rng)); float3 localH = sampleGGX(u, max(roughness, 0.001)); float3 H_world = normalize(basis * localH); @@ -1098,9 +1140,14 @@ float3 sampleRadiance(uint2 gid, uint sampleIndex, uint w, } if (resolvedBounceHit.type == intersection_type::none) { + float bounceStrength = choseSpecular + ? mix(sceneData.indirectStrength, 1.0, + max(metallic, reflectivity)) + : sceneData.indirectStrength; indirect = brdfWeight * - skyColor(resolvedBounceRay.direction, 0.0, skybox) * - sceneData.indirectStrength; + skyColor(resolvedBounceRay.direction, 0.0, skybox, + sceneData) * + bounceStrength; } else { uint bi = resolvedBounceHit.instance_id; uint bp = resolvedBounceHit.primitive_id; @@ -1169,8 +1216,12 @@ float3 sampleRadiance(uint2 gid, uint sampleIndex, uint w, float3 bAmbient = bAlbedo * max(sceneData.ambientIntensity, 0.0) * (1.0 - bMetallic) * bAo; + float bounceStrength = choseSpecular + ? mix(sceneData.indirectStrength, 1.0, + max(metallic, reflectivity)) + : sceneData.indirectStrength; indirect = brdfWeight * (bAmbient + bounceDirect + bEmissive) * - sceneData.indirectStrength; + bounceStrength; } indirect = clampLuminance(indirect, 16.0); diff --git a/tests/path-tracing/main.ascene b/tests/path-tracing/main.ascene index f2c25464..8f83af34 100644 --- a/tests/path-tracing/main.ascene +++ b/tests/path-tracing/main.ascene @@ -14,9 +14,9 @@ "orthoSize": 5.0, "orthographic": false, "position": [ - 2.644721508026123, - 1.1444071531295776, - 3.9767873287200928 + -2.715547800064087, + 1.4940226078033447, + 4.527331352233887 ], "target": [ -0.3924787640571594, @@ -26,14 +26,14 @@ }, "environment": { "atmosphere": { - "enabled": false, + "enabled": true, "globalLight": { "castsShadows": true, "enabled": false, "shadowResolution": 4096 } }, - "atmosphereSky": false, + "atmosphereSky": true, "automaticAmbient": false }, "id": "main_scene",