From 2903aaeea0f4426495b27418948b9fed8732c232 Mon Sep 17 00:00:00 2001 From: Areloch Date: Fri, 22 Feb 2019 08:12:03 -0600 Subject: [PATCH] WIP of the rewrite of blend/projection to follow BSF's math approach. --- .../source/renderInstance/renderProbeMgr.cpp | 10 +- .../advanced/reflectionProbeArrayP.hlsl | 224 +++++++++--------- 2 files changed, 127 insertions(+), 107 deletions(-) diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index 561437e41..36a66698b 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -655,7 +655,15 @@ void RenderProbeMgr::render( SceneRenderState *state ) for (U32 i = 0; i < mEffectiveProbeCount; i++) { - contribColors[i] = Point4F(RandomGen.randF(0, 1), RandomGen.randF(0, 1), RandomGen.randF(0, 1),1); + //we're going to cheat here a little for consistent debugging behavior. The first 3 probes will always have R G and then B for their colors, every other will be random + if (i == 0) + contribColors[i] = Point4F(1, 0, 0, 1); + else if (i == 1) + contribColors[i] = Point4F(0, 1, 0, 1); + else if (i == 2) + contribColors[i] = Point4F(0, 0, 1, 1); + else + contribColors[i] = Point4F(RandomGen.randF(0, 1), RandomGen.randF(0, 1), RandomGen.randF(0, 1),1); } mProbeArrayEffect->setShaderConst("$probeContribColors", contribColors); diff --git a/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl b/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl index 1360c029c..04dc0e8bf 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl @@ -33,11 +33,25 @@ uniform float4 probeConfigData[MAX_PROBES]; //r,g,b/mode,radius,atten uniform float4 probeContribColors[MAX_PROBES]; #endif +struct ProbeData +{ + float3 wsPosition; + float radius; + float3 boxExtents; + float attenuation; + float4x4 worldToLocal; + uint cubemapIdx; + uint type; //box = 0, sphere = 1 + float contribution; + float pad; +}; + // Box Projected IBL Lighting // Based on: http://www.gamedev.net/topic/568829-box-projected-cubemap-environment-mapping/ // and https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ -float3 boxProject(float3 wsPosition, float3 wsEyeRay, float3 reflectDir, float3 boxWSPos, float3 boxMin, float3 boxMax) +/*float3 boxProject(float3 wsPosition, float3 wsEyeRay, float3 reflectDir, float3 boxWSPos, float3 boxMin, float3 boxMax) { + float3 positionLS = mul(worldToObjArray[id], float4(wsEyeRay, 1.0)).xyz; //float3 rayLS = mul(worldToObjArray[id], float4(wsEyeRay, 1.0)).xyz; //float3 reflCameraLS = mul(worldToObjArray[id], float4(reflectDir), 1.0)).xyz; @@ -51,18 +65,54 @@ float3 boxProject(float3 wsPosition, float3 wsEyeRay, float3 reflectDir, float3 float3 posonbox = offset + nrdir * dist; return posonbox - boxWSPos; -} +}*/ -float3 iblBoxDiffuse( Surface surface, int id) +float3 defineSphereSpaceInfluence(Surface surface, ProbeData probe, float3 wsEyeRay, out float contribution) { - float3 cubeN = boxProject(surface.P, surface.V, surface.R, inRefPosArray[id].xyz, bbMinArray[id].xyz, bbMaxArray[id].xyz); - return TORQUE_TEXCUBEARRAYLOD(irradianceCubemapAR,cubeN,id,surface.roughness*cubeMips).xyz; + contribution = 0; + return float3(0, 0, 0); } -float3 iblBoxSpecular(Surface surface, TORQUE_SAMPLER2D(brdfTexture), int id) +float3 defineBoxSpaceInfluence(Surface surface, ProbeData probe, float3 wsEyeRay, out float contribution) +{ + float3 lsPos = mul(probe.worldToLocal, float4(surface.P, 1.0)).xyz; + float3 lsDir = mul(probe.worldToLocal, float4(wsEyeRay, 0)).xyz; + + float3 lsInvDir = float3(1 / lsDir.x, 1 / lsDir.y, 1 / lsDir.z); + + float3 intersectsMax = lsInvDir - lsPos * lsInvDir; + float3 intersectsMin = -lsInvDir - lsPos * lsInvDir; + + float3 positiveIntersections = max(intersectsMax, intersectsMin); + float intersectDist = min(positiveIntersections.x, min(positiveIntersections.y, positiveIntersections.z)); + + float3 wsIntersectPosition = surface.P + intersectDist * wsEyeRay; + float3 lookupDir = wsIntersectPosition - probe.wsPosition; + + float3 reducedExtents = probe.boxExtents - float3(probe.attenuation, probe.attenuation, probe.attenuation); + + float distToBox = length(max(max(-reducedExtents - (lsPos * probe.boxExtents), 0), (lsPos * probe.boxExtents) - reducedExtents)); + + float normalizedDistance = distToBox / 50; //50 is a random BS number. was probe.attenuation + + float t = saturate(3.3333 - 3.3333 * normalizedDistance); + contribution = t * t * (3.0 - 2.0 * t); + + return lookupDir; +} + +float4 iblBoxDiffuse( Surface surface, ProbeData probe, float3 dir) +{ + float lod = surface.roughness*cubeMips; + float3 color = TORQUE_TEXCUBEARRAYLOD(irradianceCubemapAR, dir, probe.cubemapIdx, lod).xyz; + + return float4(color * probe.contribution, ceil(probe.contribution)); +} + +float4 iblBoxSpecular(Surface surface, ProbeData probe, float3 dir) { // BRDF - float2 brdf = TORQUE_TEX2DLOD(brdfTexture, float4(surface.roughness, surface.NdotV,0.0,0.0)).xy; + float2 brdf = TORQUE_TEX2DLOD(BRDFTexture, float4(surface.roughness, surface.NdotV,0.0,0.0)).xy; // Radiance (Specular) #if DEBUGVIZ_SPECCUBEMAP == 0 @@ -71,27 +121,9 @@ float3 iblBoxSpecular(Surface surface, TORQUE_SAMPLER2D(brdfTexture), int id) float lod = 0; #endif - float3 cubeR = boxProject(surface.P, surface.V, surface.R, inRefPosArray[id].xyz, bbMinArray[id].xyz, bbMaxArray[id].xyz); + float3 color = TORQUE_TEXCUBEARRAYLOD(cubeMapAR, dir, probe.cubemapIdx, lod).xyz * (brdf.x + brdf.y); - float3 radiance = TORQUE_TEXCUBEARRAYLOD(cubeMapAR,cubeR,id,lod).xyz * (brdf.x + brdf.y); - - return radiance; -} - -float defineBoxSpaceInfluence(Surface surface, int id) -{ - float3 surfPosLS = mul( worldToObjArray[id], float4(surface.P,1.0)).xyz; - - float3 boxMinLS = inProbePosArray[id].xyz-(float3(1,1,1)*probeConfigData[id].g); - float3 boxMaxLS = inProbePosArray[id].xyz+(float3(1,1,1)*probeConfigData[id].g); - - float boxOuterRange = length(boxMaxLS - boxMinLS); - float boxInnerRange = boxOuterRange / probeConfigData[id].b; - - float3 localDir = float3(abs(surfPosLS.x), abs(surfPosLS.y), abs(surfPosLS.z)); - localDir = (localDir - boxInnerRange) / (boxOuterRange - boxInnerRange); - - return max(localDir.x, max(localDir.y, localDir.z)) * -1; + return float4(color * probe.contribution, 1); } float4 main( PFXVertToPix IN ) : SV_TARGET @@ -111,67 +143,65 @@ float4 main( PFXVertToPix IN ) : SV_TARGET int i = 0; - float blendVal[MAX_PROBES]; - float blendFactor[MAX_PROBES]; - float blendSum = 0; - float blendFacSum = 0; - float invBlendSum = 0; + float3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness); - for (i = 0; i < numProbes; i++) - { - if (probeConfigData[i].r) - { - float3 L = inProbePosArray[i].xyz - surface.P; - blendVal[i] = 1.0 - length(L) / probeConfigData[i].g; - } - else - { - blendVal[i] = defineBoxSpaceInfluence(surface, i); - } - blendVal[i] = saturate(blendVal[i]); - blendSum += blendVal[i]; - invBlendSum += (1.0f - blendVal[i]); - } + //energy conservation + float3 kD = 1.0.xxx - F; + kD *= 1.0 - surface.metalness; - // Weight0 = normalized NDF, inverted to have 1 at center, 0 at boundary. - // And as we invert, we need to divide by Num-1 to stay normalized (else sum is > 1). - // respect constraint B. - // Weight1 = normalized inverted NDF, so we have 1 at center, 0 at boundary - // and respect constraint A. + float4 irradiance = float4(0, 0, 0, 0); + float4 specular = float4(0, 0, 0, 0); - for (i = 0; i < numProbes; i++) - { - if (numProbes>1) - { - blendFactor[i] = ((1.0f -blendVal[i] / blendSum)) / (numProbes - 1); - blendFactor[i] *= ((1.0f -blendVal[i]) / invBlendSum); - blendFacSum += blendFactor[i]; - } - else - { - blendFactor[i] = blendVal[i]; - blendFacSum = blendVal[i]; - } - } + float contributingProbeCount = 1; - // Normalize blendVal -#if DEBUGVIZ_ATTENUATION == 0 //this can likely be removed when we fix the above normalization behavior - if (blendFacSum == 0.0f) // Possible with custom weight - { - blendFacSum = 1.0f; - } + float contribSum = 0; + +#if DEBUGVIZ_CONTRIB == 1 + float contribVal[MAX_PROBES]; #endif - float invBlendSumWeighted = 1.0f / blendFacSum; + //Process prooooobes for (i = 0; i < numProbes; ++i) { - blendFactor[i] *= invBlendSumWeighted; + ProbeData probe; + + probe.wsPosition = inProbePosArray[i].xyz; + probe.radius = probeConfigData[i].g; + probe.boxExtents = length(bbMaxArray[i].xyz - bbMinArray[i].xyz); + probe.attenuation = probeConfigData[i].b; + probe.worldToLocal = worldToObjArray[i]; + probe.cubemapIdx = i; + probe.type = probeConfigData[i].r; + probe.contribution = 0; + + if (probe.type == 0) //box + { + float3 reflDir = defineBoxSpaceInfluence(surface, probe, IN.wsEyeRay, probe.contribution); + + float4 irr = iblBoxDiffuse(surface, probe, reflDir); + + float3 spec = iblBoxSpecular(surface, probe, reflDir).rgb * F; + + irradiance += irr; + specular += float4(spec,1); + + contributingProbeCount += irr.a; //if we have any contribution to this pixel, our a should be at a 1, which we'll ultimately utilize to average the total + + contribSum += probe.contribution; + +#if DEBUGVIZ_CONTRIB == 1 + contribVal[i] = probe.contribution; +#endif + } } - //return float4(blendFactor[0], blendFactor[0], blendFactor[0], 1); + //Average these bad boys out + //irradiance /= contributingProbeCount; + //specular /= contributingProbeCount; + #if DEBUGVIZ_ATTENUATION == 1 - return float4(blendFacSum, blendFacSum, blendFacSum, 1); + return float4(contribSum, contribSum, contribSum, 1); #endif #if DEBUGVIZ_CONTRIB == 1 @@ -179,10 +209,10 @@ float4 main( PFXVertToPix IN ) : SV_TARGET float3 finalContribColor = float3(0, 0, 0); for (i = 0; i < numProbes; ++i) { - if (blendFactor[i] == 0) + if (contribVal[i] == 0) continue; - finalContribColor += blendFactor[i] * probeContribColors[i].rgb; + finalContribColor += contribVal[i] * probeContribColors[i].rgb; } return float4(finalContribColor, 1); @@ -190,44 +220,26 @@ float4 main( PFXVertToPix IN ) : SV_TARGET #if DEBUGVIZ_SPECCUBEMAP == 0 && DEBUGVIZ_DIFFCUBEMAP == 0 - float3 irradiance = float3(0,0,0); - float3 specular = float3(0,0,0); - float3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness); - //energy conservation - float3 kD = 1.0.xxx - F; - kD *= 1.0 - surface.metalness; - for (i = 0; i < numProbes; ++i) + /*for (i = 0; i < numProbes; ++i) { - if (blendFactor[i] == 0) + if (blendVal[i] == 0) continue; - irradiance += blendFactor[i]*iblBoxDiffuse(surface, i); + irradiance += blendVal[i]*iblBoxDiffuse(surface, i); - specular += blendFactor[i]*F*iblBoxSpecular(surface, TORQUE_SAMPLER2D_MAKEARG(BRDFTexture),i); - } + specular += blendVal[i]*F*iblBoxSpecular(surface, TORQUE_SAMPLER2D_MAKEARG(BRDFTexture),i); + }*/ //final diffuse color - float3 diffuse = kD * irradiance * surface.baseColor.rgb; - float4 finalColor = float4(diffuse + specular * surface.ao, blendFacSum); + float3 diffuse = kD * irradiance.rgb * surface.baseColor.rgb; + float4 finalColor = float4(diffuse + specular.rgb * surface.ao, contribSum); - return finalColor; + return finalColor; #elif DEBUGVIZ_SPECCUBEMAP == 1 && DEBUGVIZ_DIFFCUBEMAP == 0 - float3 cubeColor = float3(0, 0, 0); - for (i = 0; i < numProbes; ++i) - { - cubeColor += blendFactor[i] * iblBoxSpecular(surface, TORQUE_SAMPLER2D_MAKEARG(BRDFTexture), i); - } - - return float4(cubeColor,1); + return float4(specular.rgb, 1); #elif DEBUGVIZ_DIFFCUBEMAP == 1 - float3 cubeColor = float3(0, 0, 0); - for (i = 0; i < numProbes; ++i) - { - cubeColor += blendFactor[i] * iblBoxDiffuse(surface, i); - } - - return float4(cubeColor, 1); + return float4(irradiance.rgb, 1); #endif }