diff --git a/external/vcpkg b/external/vcpkg index ce35b1a..f9a99aa 160000 --- a/external/vcpkg +++ b/external/vcpkg @@ -1 +1 @@ -Subproject commit ce35b1a53aac26d7fcdb8ee1ef7a8e4eea02d27b +Subproject commit f9a99aa79c1309001e36572fa42d7d8edebfc451 diff --git a/res/shaders/BasePass.slang b/res/shaders/BasePass.slang index dde24a6..06e0e46 100644 --- a/res/shaders/BasePass.slang +++ b/res/shaders/BasePass.slang @@ -65,12 +65,12 @@ float4 fragmentMain(in FragmentParameter params) : SV_Target float shadowFactor = 0.0f; int count = 0; int range = 1; - //for (int x = -range; x <= range; ++x) { - // for (int y = -range; y <= range; ++y) { + for (int x = -range; x <= range; ++x) { + for (int y = -range; y <= range; ++y) { float shadow = 1.0f; if (currentDepth > 0.0 && currentDepth < 1.0) { - float dist = pShadowMapping.shadowMaps[cascadeIndex].Sample(pShadowMapping.shadowSampler, float3(shadowCoord.xy, i)).r; + float dist = pShadowMapping.shadowMaps[cascadeIndex].Sample(pShadowMapping.shadowSampler, float3(shadowCoord.xy + float2(dx * x, dy * y), i)).r; if (shadowCoord.w > 0 && currentDepth < dist) { shadow = 0; @@ -78,16 +78,16 @@ float4 fragmentMain(in FragmentParameter params) : SV_Target } shadowFactor += shadow; count++; - // } - //} + } + } result = (shadowFactor / count) * pLightEnv.directionalLights[i].illuminate(lightingParams, brdf); } for (uint i = 0; i < pLightEnv.numPointLights; ++i) { - //uint lightIndex = pLightCullingData.lightIndexList[startOffset + i]; - //result += pLightEnv.pointLights[i].illuminate(lightingParams, brdf); + uint lightIndex = pLightCullingData.lightIndexList[startOffset + i]; + result += pLightEnv.pointLights[lightIndex].illuminate(lightingParams, brdf); } - //result += brdf.evaluateAmbient(lightingParams.viewDir_WS); + result += brdf.evaluateAmbient(lightingParams.viewDir_WS); return float4(result, brdf.getAlpha()); } \ No newline at end of file diff --git a/res/shaders/ComputeFrustums.slang b/res/shaders/ComputeFrustums.slang index e6ad1f3..f90a1f2 100644 --- a/res/shaders/ComputeFrustums.slang +++ b/res/shaders/ComputeFrustums.slang @@ -1,4 +1,5 @@ import Common; +import Frustum; import DispatchParams; struct ComputeShaderInput diff --git a/res/shaders/DepthCullingTask.slang b/res/shaders/DepthCullingTask.slang index f122494..0f0f811 100644 --- a/res/shaders/DepthCullingTask.slang +++ b/res/shaders/DepthCullingTask.slang @@ -2,6 +2,7 @@ import Common; import Scene; import Bounding; import DepthCommon; +import Frustum; groupshared MeshPayload p; groupshared uint head; diff --git a/res/shaders/LightCulling.slang b/res/shaders/LightCulling.slang index 5c832c2..fb80a91 100644 --- a/res/shaders/LightCulling.slang +++ b/res/shaders/LightCulling.slang @@ -1,6 +1,7 @@ import Common; import DispatchParams; import LightEnv; +import Frustum; struct ComputeShaderInput { diff --git a/res/shaders/lib/Bounding.slang b/res/shaders/lib/Bounding.slang index 19ab0b3..c9d6964 100644 --- a/res/shaders/lib/Bounding.slang +++ b/res/shaders/lib/Bounding.slang @@ -1,4 +1,5 @@ import Common; +import Frustum; struct BoundingSphere { diff --git a/res/shaders/lib/DispatchParams.slang b/res/shaders/lib/DispatchParams.slang index 67aa603..fafa5ce 100644 --- a/res/shaders/lib/DispatchParams.slang +++ b/res/shaders/lib/DispatchParams.slang @@ -1,4 +1,5 @@ import Common; +import Frustum; struct DispatchParams { diff --git a/res/shaders/lib/LightEnv.slang b/res/shaders/lib/LightEnv.slang index e8c559c..d72d614 100644 --- a/res/shaders/lib/LightEnv.slang +++ b/res/shaders/lib/LightEnv.slang @@ -1,5 +1,6 @@ import Common; import MaterialParameter; +import Frustum; interface ILightEnv { @@ -81,6 +82,8 @@ interface IBRDF float3 evaluateAmbient(float3 viewDir_WS); float getAlpha(); float3 getEmissive(); + float getRoughness(); + float getMetallic(); [mutating] void setNormal(float3 n); }; @@ -134,6 +137,8 @@ struct Phong : IBRDF { return emissive; } + float getRoughness() { return pow(2.0 / (shininess + 2.0), 0.25); } + float getMetallic() { return 0; } [mutating] void setNormal(float3 n) { normal = n; } }; @@ -187,6 +192,8 @@ struct BlinnPhong : IBRDF { return emissive; } + float getRoughness() { return pow(2.0 / (shininess + 2.0), 0.25); } + float getMetallic() { return 0; } [mutating] void setNormal(float3 n) { normal = n; } }; @@ -240,6 +247,8 @@ struct CelShading : IBRDF { return emissive; } + float getRoughness() { return 1.0; } + float getMetallic() { return 0; } [mutating] void setNormal(float3 n) { normal = n; } }; @@ -364,5 +373,7 @@ struct CookTorrance : IBRDF { return emissive; } + float getRoughness() { return roughness; } + float getMetallic() { return metallic; } [mutating] void setNormal(float3 n) { normal = n; } }; diff --git a/res/shaders/raytracing/ClosestHit.slang b/res/shaders/raytracing/ClosestHit.slang index 24ecb3f..d3f80c7 100644 --- a/res/shaders/raytracing/ClosestHit.slang +++ b/res/shaders/raytracing/ClosestHit.slang @@ -19,20 +19,17 @@ import MATERIAL_FILE_NAME; // Direct Illumination: Next Event Estimation over any present lights for (int i = spheres.length(); i-->0;) { Sphere ls = spheres[i]; - if (all(equal(ls.e, vec3(0)))) continue; // skip non-emissive spheres + if (all(equal(ls.e, vec3(0)))) continue; // skip non-emissive spheres vec3 xc = ls.geo.xyz - x; vec3 sw = normalize(xc), su = normalize(cross((abs(sw.x)>.1 ? vec3(0,1,0) : vec3(1,0,0)), sw)), sv = cross(sw,su); float cos_a_max = sqrt(float(1 - ls.geo.w*ls.geo.w / dot(xc,xc))); float cos_a = 1 - rnd.x + rnd.x*cos_a_max, sin_a = sqrt(1 - cos_a*cos_a); float phi = 2 * pi * rnd.y; vec3 l = normalize(su*cos(phi)*sin_a + sv*sin(phi)*sin_a + sw*cos_a); // sampled direction towards light - if (intersect(Ray(x,l), matls, paramsls, sphereId, triId) && sphereId == i) { // test if shadow ray hits this light source - float omega = 2 * pi * (1-cos_a_max); - if(useAtt) { - float tau = exp(-kt * length(x - paramsls.x)); - result += max(dot(l,nl),0) * ls.e * omega * tau; - } else { - result += accmat / pi * max(dot(l,nl),0) * ls.e * omega; // brdf term obj.c.xyz already in accmat, 1/pi for brdf + if (intersect(Ray(x,l), matls, paramsls, sphereId, triId) && sphereId == i) { // test if shadow ray hits this light + source float omega = 2 * pi * (1-cos_a_max); if(useAtt) { float tau = exp(-kt * length(x - paramsls.x)); result += max(dot(l,nl),0) * + ls.e * omega * tau; } else { result += accmat / pi * max(dot(l,nl),0) * ls.e * omega; // brdf term obj.c.xyz already in accmat, 1/pi + for brdf } } } @@ -78,11 +75,10 @@ const static float3 fogEmm = float3(0, 0.01, 0.01); const static float eps = 1e-5; [shader("closesthit")] -void closestHit(inout RayPayload hitValue, in BuiltInTriangleIntersectionAttributes attr) -{ +void closestHit(inout RayPayload hitValue, in BuiltInTriangleIntersectionAttributes attr) { hitValue.hit = true; // todo: replace with anyhit shader - if(hitValue.anyHit) + if (hitValue.anyHit) return; const float3 barycentricCoords = float3(1.0f - attr.barycentrics.x - attr.barycentrics.y, attr.barycentrics.x, attr.barycentrics.y); @@ -111,112 +107,167 @@ void closestHit(inout RayPayload hitValue, in BuiltInTriangleIntersectionAttribu hitValue.params = params.getLightingParameter(); hitValue.materialParams = params.getMaterialParameter(); - + LightingParameter lightingParams = hitValue.params; lightingParams.viewDir_WS = -WorldRayDirection(); let brdf = Material.prepare(hitValue.materialParams); float3 normal_WS = brdf.getNormal(); - float3 normalLight_WS = dot(normal_WS,WorldRayDirection())<0 ? normal_WS : -normal_WS; + float3 normalLight_WS = dot(normal_WS, WorldRayDirection()) < 0 ? normal_WS : -normal_WS; float3 intersection_WS = lightingParams.position_WS; - + hitValue.depth++; float3 rnd = rand01(hitValue.rndSeed); - //float kt = ka + ks; - //float s = -log(rnd.z) / kt; - //if (s < RayTCurrent()) { + // float kt = ka + ks; + // float s = -log(rnd.z) / kt; + // if (s < RayTCurrent()) { // float3 xs = WorldRayOrigin() + s * WorldRayDirection(); - // float p = kt * rnd.z; - // if (hitValue.depth > 5) { - // if (rnd.z >= p) return; - // else brdf.baseColor /= p; - // } - // float3 ldirect = nextEventEstimation(brdf.baseColor, WorldRayDirection(), xs, -WorldRayDirection(), kt, true, rnd); - // hitValue.light += (fogEmm + ks * ldirect) / kt; - // accmat *= ks / kt; - // rayDesc.Origin = xs; - // rayDesc.Direction = float3( - // cos(2*PI*rnd.x)*sqrt(1-rnd.y*rnd.y), - // sin(2*PI*rnd.x)*sqrt(1-rnd.y*rnd.y), - // rnd.y - // ); - // TraceRay(scene, 0, 0xff, 0, 0, rayDesc); - //} + // float p = kt * rnd.z; + // if (hitValue.depth > 5) { + // if (rnd.z >= p) return; + // else brdf.baseColor /= p; + // } + // float3 ldirect = nextEventEstimation(brdf.baseColor, WorldRayDirection(), xs, -WorldRayDirection(), kt, true, rnd); + // hitValue.light += (fogEmm + ks * ldirect) / kt; + // accmat *= ks / kt; + // rayDesc.Origin = xs; + // rayDesc.Direction = float3( + // cos(2*PI*rnd.x)*sqrt(1-rnd.y*rnd.y), + // sin(2*PI*rnd.x)*sqrt(1-rnd.y*rnd.y), + // rnd.y + // ); + // TraceRay(scene, 0, 0xff, 0, 0, rayDesc); + // } float p = max(max(brdf.getBaseColor().x, brdf.getBaseColor().y), brdf.getBaseColor().z); - if(hitValue.depth > 5) { - if (rnd.z >= p) return; + if (hitValue.depth > 5) { + if (rnd.z >= p) + return; } hitValue.light += brdf.getEmissive() * hitValue.emissive + brdf.evaluateAmbient(lightingParams.viewDir_WS); //-- Ideal DIFFUSE reflection - //if(bool(useNEE)) { + // if(bool(useNEE)) { // accrad += nextEventEstimation(accmat, r.d, params.x, params.nl, kt, false, rnd); //} - for(uint i = 0; i < pLightEnv.numDirectionalLights; ++i) { - float3 x = intersection_WS; - float3 l = -pLightEnv.directionalLights[i].direction.xyz; - RayDesc rayDesc; - rayDesc.TMax = 10000.0f; - rayDesc.TMin = eps; - rayDesc.Origin = x; - rayDesc.Direction = l; - RayPayload payload; - payload.depth = hitValue.depth; - payload.emissive = 1; - payload.anyHit = true; - payload.hit = false; - payload.rndSeed = hitValue.rndSeed; - TraceRay(pRayTracingParams.scene, RAY_FLAG_CULL_BACK_FACING_TRIANGLES | RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, 0xff, 0, 0, 0, rayDesc, payload); + // Only trace shadow rays for direct lighting at shallow depths + if (hitValue.depth <= 3) { + for (uint i = 0; i < pLightEnv.numDirectionalLights; ++i) { + float3 x = intersection_WS; + float3 l = -pLightEnv.directionalLights[i].direction.xyz; + RayDesc rayDesc; + rayDesc.TMax = 10000.0f; + rayDesc.TMin = eps; + rayDesc.Origin = x; + rayDesc.Direction = l; + RayPayload payload; + payload.depth = hitValue.depth; + payload.emissive = 1; + payload.anyHit = true; + payload.hit = false; + payload.rndSeed = hitValue.rndSeed; + TraceRay(pRayTracingParams.scene, RAY_FLAG_CULL_BACK_FACING_TRIANGLES | RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, 0xff, 0, 0, 0, + rayDesc, payload); - // we have missed all geometry, so directional light is affecting us - if(!payload.hit) { - hitValue.light += pLightEnv.directionalLights[i].illuminate(lightingParams, brdf); + // we have missed all geometry, so directional light is affecting us + if (!payload.hit) { + hitValue.light += pLightEnv.directionalLights[i].illuminate(lightingParams, brdf); + } } - } - for(uint i = 0; i < pLightEnv.numPointLights; ++i) { - RayPayload payload; - payload.rndSeed = hitValue.rndSeed; - float3 x = intersection_WS; - float3 l = pLightEnv.pointLights[i].position_WS.xyz - intersection_WS; - if(length(l) > pLightEnv.pointLights[i].colorRange.w) { - continue; + for (uint i = 0; i < pLightEnv.numPointLights; ++i) { + RayPayload payload; + payload.rndSeed = hitValue.rndSeed; + float3 x = intersection_WS; + float3 l = pLightEnv.pointLights[i].position_WS.xyz - intersection_WS; + if (length(l) > pLightEnv.pointLights[i].colorRange.w) { + continue; + } + RayDesc rayDesc; + rayDesc.TMax = 1.0f; + rayDesc.TMin = eps; + rayDesc.Origin = x; + rayDesc.Direction = l; + TraceRay(pRayTracingParams.scene, RAY_FLAG_CULL_BACK_FACING_TRIANGLES | RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, 0xff, 0, 0, 0, + rayDesc, payload); + + // hitting only after the light + if (!payload.hit) { + hitValue.light += pLightEnv.pointLights[i].illuminate(lightingParams, brdf); + } } - RayDesc rayDesc; - rayDesc.TMax = 1.0f; - rayDesc.TMin = eps; - rayDesc.Origin = x; - rayDesc.Direction = l; - TraceRay(pRayTracingParams.scene, RAY_FLAG_CULL_BACK_FACING_TRIANGLES | RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, 0xff, 0, 0, 0, rayDesc, payload); - - // hitting only after the light - if(!payload.hit) { - hitValue.light += pLightEnv.pointLights[i].illuminate(lightingParams, brdf); - } - } - // Indirect Illumination: cosine-weighted importance sampling - if(hitValue.depth < 1) { - float r1 = 2 * PI * rnd.x, r2 = rnd.y, r2s = sqrt(r2); - float3 w = normalLight_WS; - float3 u = normalize(cross(abs(w.x)>0.1 ? float3(0,1,0) : float3(1,0,0), w)); - float3 v = cross(w,u); + } // end depth <= 2 direct lighting guard + // Indirect Illumination: probabilistic diffuse/specular importance sampling + if (hitValue.depth < 3) { + float3 V = -WorldRayDirection(); + float3 N = normalLight_WS; + float rough = brdf.getRoughness(); + float met = brdf.getMetallic(); + float3 F0 = lerp(float3(0.04), brdf.getBaseColor(), met); + float NdotV = max(dot(N, V), 0.001); + float3 F = F0 + (1 - F0) * pow(1 - NdotV, 5.0); + float specProb = clamp((F.x + F.y + F.z) / 3.0, 0.1, 0.9); + RayDesc rayDesc; rayDesc.TMax = 10000.0f; rayDesc.TMin = eps; rayDesc.Origin = intersection_WS; - rayDesc.Direction = normalize(u*cos(r1)*r2s + v * sin(r1)*r2s + w * sqrt(1 - r2)); - RayPayload payload; - payload.light = float3(0); - payload.emissive = 1; - payload.hit = false; - //payload.emissive = 0; // in the next bounce, consider reflective part only! - payload.depth = hitValue.depth; - payload.anyHit = false; - payload.rndSeed = hitValue.rndSeed + 1; - TraceRay(pRayTracingParams.scene, RAY_FLAG_CULL_BACK_FACING_TRIANGLES, 0xff, 0, 0, 0, rayDesc, payload); - float bias = dot(normalLight_WS, rayDesc.Direction); - hitValue.light += brdf.evaluate(-WorldRayDirection(), rayDesc.Direction, payload.light / bias); - } - //hitValue.light /= p; -} \ No newline at end of file + float3 bounceWeight = float3(0); + bool validBounce = true; + + if (rnd.z < specProb) { + // GGX importance sampling for specular reflection + float a = max(rough * rough, 0.001); + float a2 = a * a; + float phi = 2.0 * PI * rnd.x; + float cosTheta = sqrt((1.0 - rnd.y) / (1.0 + (a2 - 1.0) * rnd.y)); + float sinTheta = sqrt(1.0 - cosTheta * cosTheta); + + float3 u = normalize(cross(abs(N.x) > 0.1 ? float3(0, 1, 0) : float3(1, 0, 0), N)); + float3 v = cross(N, u); + float3 H = normalize(u * cos(phi) * sinTheta + v * sin(phi) * sinTheta + N * cosTheta); + float3 L = reflect(-V, H); + rayDesc.Direction = L; + + float NdotL = dot(N, L); + float NdotH = max(dot(N, H), 0.0); + float VdotH = max(dot(V, H), 0.0); + + if (NdotL <= 0) { + validBounce = false; + } else { + // Smith G term + float k = (rough + 1); + k = (k * k) / 8; + float G = (NdotV / (NdotV * (1 - k) + k)) * (NdotL / (NdotL * (1 - k) + k)); + // Fresnel at half-vector + float3 Fh = F0 + (max(float3(1.0 - rough), F0) - F0) * pow(clamp(1 - VdotH, 0, 1), 5.0); + // Monte Carlo weight: F * G * VdotH / (NdotH * NdotV * specProb) + bounceWeight = Fh * G * VdotH / (NdotH * NdotV * specProb + 0.0001); + } + } else { + // Cosine-weighted importance sampling for diffuse + float r1 = 2 * PI * rnd.x, r2 = rnd.y, r2s = sqrt(r2); + float3 w = N; + float3 u = normalize(cross(abs(w.x) > 0.1 ? float3(0, 1, 0) : float3(1, 0, 0), w)); + float3 v = cross(w, u); + rayDesc.Direction = normalize(u * cos(r1) * r2s + v * sin(r1) * r2s + w * sqrt(1 - r2)); + // Diffuse weight: kd * baseColor / (1 - specProb) + float3 kd = (1 - F) * (1 - met); + bounceWeight = kd * brdf.getBaseColor() / (1 - specProb); + } + + if (validBounce) { + RayPayload payload; + payload.light = float3(0); + payload.hit = false; + payload.emissive = 0; + payload.depth = hitValue.depth; + payload.anyHit = false; + payload.rndSeed = hitValue.rndSeed + 1; + TraceRay(pRayTracingParams.scene, RAY_FLAG_CULL_BACK_FACING_TRIANGLES, 0xff, 0, 0, 0, rayDesc, payload); + hitValue.light += bounceWeight * payload.light; + } + } + // hitValue.light /= p; +} diff --git a/src/Engine/Graphics/Vulkan/Graphics.cpp b/src/Engine/Graphics/Vulkan/Graphics.cpp index 08baaa9..37aecf2 100644 --- a/src/Engine/Graphics/Vulkan/Graphics.cpp +++ b/src/Engine/Graphics/Vulkan/Graphics.cpp @@ -819,7 +819,7 @@ void Graphics::pickPhysicalDevice() { shaderFloatControls = true; } } - rayTracingEnabled = false && rayTracingPipelineSupport && accelerationStructureSupport && hostOperationsSupport && deviceAddressSupport && + rayTracingEnabled = rayTracingPipelineSupport && accelerationStructureSupport && hostOperationsSupport && deviceAddressSupport && descriptorIndexingSupport && spirv14Support && shaderFloatControls; }