浏览代码

Merge pull request #395 from Unity-Technologies/Add-multibounce-approximation-on-AO-

Add multibounce approximation on ao
/main
GitHub 7 年前
当前提交
f14653df
共有 4 个文件被更改,包括 132 次插入85 次删除
  1. 15
      ScriptableRenderPipeline/Core/ShaderLibrary/CommonLighting.hlsl
  2. 23
      ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePass.hlsl
  3. 142
      ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePassLoop.hlsl
  4. 37
      ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.hlsl

15
ScriptableRenderPipeline/Core/ShaderLibrary/CommonLighting.hlsl


}
// Ref: Moving Frostbite to PBR - Gotanda siggraph 2011
float GetSpecularOcclusion(float NdotV, float ambientOcclusion, float roughness)
// Return specular occlusion based on ambient occlusion (usually get from SSAO) and view/roughness info
float SpecularOcclusionFromAmbientOcclusion(float NdotV, float ambientOcclusion, float roughness)
}
// ref: Practical Realtime Strategies for Accurate Indirect Occlusion
// Update ambient occlusion to colored ambient occlusion based on statitics of how light is bouncing in an object and with the albedo of the object
float3 GTAOMultiBounce(float visibility, float3 albedo)
{
float3 a = 2.0404 * albedo - 0.3324;
float3 b = -4.7951 * albedo + 0.6417;
float3 c = 2.7552 * albedo + 0.6903;
float x = visibility;
return max(x, ((x * a + b) * x + c) * x);
}
//-----------------------------------------------------------------------------

23
ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePass.hlsl


struct LightLoopContext
{
// Visible from Material
float ambientOcclusion;
// Visible from Material - these values are expected in any LightLoopContext
float indirectAmbientOcclusion; // Ambient occlusion use for indirect lighting (reflection probe, baked diffuse lighting)
float directAmbientOcclusion; // Ambient occlusion use for direct lighting (directional, punctual, area)
// Not visible from Material (user should not use these properties in Material)
// Not visible from Material (user should not use these properties in Material file)
};
// Store all the accumulated lighting produce by the light loop
struct LightLoopAccumulatedLighting
{
float3 dirDiffuseLighting;
float3 dirSpecularLighting;
float3 punctualDiffuseLighting;
float3 punctualSpecularLighting;
float3 areaDiffuseLighting;
float3 areaSpecularLighting;
float3 envDiffuseLighting;
float3 envSpecularLighting;
};
//-----------------------------------------------------------------------------

142
ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePassLoop.hlsl


totalIblWeight = saturate(accumulatedWeight);
weight.y -= saturate(accumulatedWeight - totalIblWeight);
// TODO: We do'nt use ibl diffuse lighting currently, let here in case users want to use it.
// iblDiffuseLighting = lerp(iblDiffuseLighting, localDiffuseLighting, weight.x);
iblDiffuseLighting = lerp(iblDiffuseLighting, localDiffuseLighting, weight.x);
void LightLoop( float3 V, PositionInputs posInput, PreLightData prelightData, BSDFData bsdfData, float3 bakeDiffuseLighting, uint featureFlags,
void LightLoop( float3 V, PositionInputs posInput, PreLightData preLightData, BSDFData bsdfData, float3 bakeDiffuseLighting, uint featureFlags,
out float3 diffuseLighting,
out float3 specularLighting)
{

// We store inverse AO so neutral is black. So either we sample inside or outside the texture it return 0 in case of neutral
context.ambientOcclusion = 1.0 - LOAD_TEXTURE2D(_AmbientOcclusionTexture, posInput.unPositionSS).x;
context.indirectAmbientOcclusion = 1.0 - LOAD_TEXTURE2D(_AmbientOcclusionTexture, posInput.unPositionSS).x;
context.directAmbientOcclusion = lerp(1.0, context.indirectAmbientOcclusion, _AmbientOcclusionDirectLightStrenght);
diffuseLighting = float3(0.0, 0.0, 0.0);
specularLighting = float3(0.0, 0.0, 0.0);
// This struct is use to store accumulated lighting, summation is done in PostEvaluateBSDF
LightLoopAccumulatedLighting accLighting;
ZERO_INITIALIZE(LightLoopAccumulatedLighting, accLighting);
uint i = 0; // Declare once to avoid the D3D11 compiler warning.

{
float3 localDiffuseLighting, localSpecularLighting;
EvaluateBSDF_Directional(context, V, posInput, prelightData, _DirectionalLightDatas[i], bsdfData,
EvaluateBSDF_Directional(context, V, posInput, preLightData, _DirectionalLightDatas[i], bsdfData,
diffuseLighting += localDiffuseLighting;
specularLighting += localSpecularLighting;
accLighting.dirDiffuseLighting += localDiffuseLighting;
accLighting.dirSpecularLighting += localSpecularLighting;
}
}

{
float3 localDiffuseLighting, localSpecularLighting;
EvaluateBSDF_Punctual( context, V, posInput, prelightData, _LightDatas[FetchIndex(punctualLightStart, i)], bsdfData,
EvaluateBSDF_Punctual( context, V, posInput, preLightData, _LightDatas[FetchIndex(punctualLightStart, i)], bsdfData,
diffuseLighting += localDiffuseLighting;
specularLighting += localSpecularLighting;
accLighting.punctualDiffuseLighting += localDiffuseLighting;
accLighting.punctualSpecularLighting += localSpecularLighting;
}
#else

float3 localDiffuseLighting, localSpecularLighting;
EvaluateBSDF_Punctual( context, V, posInput, prelightData, _LightDatas[i], bsdfData,
EvaluateBSDF_Punctual( context, V, posInput, preLightData, _LightDatas[i], bsdfData,
diffuseLighting += localDiffuseLighting;
specularLighting += localSpecularLighting;
accLighting.punctualDiffuseLighting += localDiffuseLighting;
accLighting.punctualSpecularLighting += localSpecularLighting;
}
#endif

{
float3 localDiffuseLighting, localSpecularLighting;
#ifdef LIGHTLOOP_TILE_PASS
uint areaLightStart;

while (i < areaLightCount && lightType == GPULIGHTTYPE_LINE)
{
EvaluateBSDF_Area( context, V, posInput, prelightData, _LightDatas[areaIndex], bsdfData, GPULIGHTTYPE_LINE,
float3 localDiffuseLighting, localSpecularLighting;
EvaluateBSDF_Area( context, V, posInput, preLightData, _LightDatas[areaIndex], bsdfData, GPULIGHTTYPE_LINE,
diffuseLighting += localDiffuseLighting;
specularLighting += localSpecularLighting;
accLighting.areaDiffuseLighting += localDiffuseLighting;
accLighting.areaSpecularLighting += localSpecularLighting;
i++;
areaIndex = i < areaLightCount ? FetchIndex(areaLightStart, i) : 0;

while (i < areaLightCount && lightType == GPULIGHTTYPE_RECTANGLE)
{
EvaluateBSDF_Area( context, V, posInput, prelightData, _LightDatas[areaIndex], bsdfData, GPULIGHTTYPE_RECTANGLE,
float3 localDiffuseLighting, localSpecularLighting;
EvaluateBSDF_Area( context, V, posInput, preLightData, _LightDatas[areaIndex], bsdfData, GPULIGHTTYPE_RECTANGLE,
diffuseLighting += localDiffuseLighting;
specularLighting += localSpecularLighting;
accLighting.areaDiffuseLighting += localDiffuseLighting;
accLighting.areaSpecularLighting += localSpecularLighting;
i++;
areaIndex = i < areaLightCount ? FetchIndex(areaLightStart, i) : 0;

#else
EvaluateBSDF_Area( context, V, posInput, prelightData, _LightDatas[i], bsdfData, _LightDatas[i].lightType,
float3 localDiffuseLighting, localSpecularLighting;
EvaluateBSDF_Area( context, V, posInput, preLightData, _LightDatas[i], bsdfData, _LightDatas[i].lightType,
diffuseLighting += localDiffuseLighting;
specularLighting += localSpecularLighting;
accLighting.areaDiffuseLighting += localDiffuseLighting;
accLighting.areaSpecularLighting += localSpecularLighting;
float3 iblDiffuseLighting = float3(0.0, 0.0, 0.0);
float3 iblSpecularLighting = float3(0.0, 0.0, 0.0);
float totalIblWeight = 0.0; // Max: 1
// Reflection probes are sorted by volume (in the increasing order).
if (featureFlags & LIGHTFEATUREFLAGS_ENV)
if (featureFlags & LIGHTFEATUREFLAGS_ENV || featureFlags & LIGHTFEATUREFLAGS_SKY)
float3 localDiffuseLighting, localSpecularLighting;
float2 weight;
context.sampleReflection = SINGLE_PASS_CONTEXT_SAMPLE_REFLECTION_PROBES;
#ifdef LIGHTLOOP_TILE_PASS
uint envLightStart;
uint envLightCount;
GetCountAndStart(posInput, LIGHTCATEGORY_ENV, envLightStart, envLightCount);
#else
uint envLightCount = _EnvLightCount;
#endif
float totalIblWeight = 0.0; // Max: 1
// Note: In case of IBL we are sorted from smaller to bigger projected solid angle bounds. We are not sorted by type so we can't do a 'while' approach like for area light.
for (i = 0; i < envLightCount && totalIblWeight < 1.0; ++i)
// Reflection probes are sorted by volume (in the increasing order).
if (featureFlags & LIGHTFEATUREFLAGS_ENV)
float3 localDiffuseLighting, localSpecularLighting;
float2 weight;
context.sampleReflection = SINGLE_PASS_CONTEXT_SAMPLE_REFLECTION_PROBES;
uint envLightIndex = FetchIndex(envLightStart, i);
uint envLightStart;
uint envLightCount;
GetCountAndStart(posInput, LIGHTCATEGORY_ENV, envLightStart, envLightCount);
uint envLightIndex = i;
uint envLightCount = _EnvLightCount;
EvaluateBSDF_Env(context, V, posInput, prelightData, _EnvLightDatas[envLightIndex], bsdfData, localDiffuseLighting, localSpecularLighting, weight);
applyWeigthedIblLighting(localDiffuseLighting, localSpecularLighting, weight, iblDiffuseLighting, iblSpecularLighting, totalIblWeight);
// Note: In case of IBL we are sorted from smaller to bigger projected solid angle bounds. We are not sorted by type so we can't do a 'while' approach like for area light.
for (i = 0; i < envLightCount && totalIblWeight < 1.0; ++i)
{
#ifdef LIGHTLOOP_TILE_PASS
uint envLightIndex = FetchIndex(envLightStart, i);
#else
uint envLightIndex = i;
#endif
EvaluateBSDF_Env(context, V, posInput, preLightData, _EnvLightDatas[envLightIndex], bsdfData, localDiffuseLighting, localSpecularLighting, weight);
applyWeigthedIblLighting(localDiffuseLighting, localSpecularLighting, weight, accLighting.envDiffuseLighting, accLighting.envSpecularLighting, totalIblWeight);
}
}
if (featureFlags & LIGHTFEATUREFLAGS_SKY)
{
// Only apply the sky IBL if the sky texture is available, and if we haven't yet accumulated enough IBL lighting.
if (_EnvLightSkyEnabled && totalIblWeight < 1.0)
if (featureFlags & LIGHTFEATUREFLAGS_SKY)
float3 localDiffuseLighting, localSpecularLighting;
float2 weight;
// Only apply the sky IBL if the sky texture is available, and if we haven't yet accumulated enough IBL lighting.
if (_EnvLightSkyEnabled && totalIblWeight < 1.0)
{
float3 localDiffuseLighting, localSpecularLighting;
float2 weight;
// The sky is a single cubemap texture separate from the reflection probe texture array (different resolution and compression)
context.sampleReflection = SINGLE_PASS_CONTEXT_SAMPLE_SKY;
EnvLightData envLightSky = InitSkyEnvLightData(0); // The sky data are generated on the fly so the compiler can optimize the code
EvaluateBSDF_Env(context, V, posInput, prelightData, envLightSky, bsdfData, localDiffuseLighting, localSpecularLighting, weight);
applyWeigthedIblLighting(localDiffuseLighting, localSpecularLighting, weight, iblDiffuseLighting, iblSpecularLighting, totalIblWeight);
// The sky is a single cubemap texture separate from the reflection probe texture array (different resolution and compression)
context.sampleReflection = SINGLE_PASS_CONTEXT_SAMPLE_SKY;
EnvLightData envLightSky = InitSkyEnvLightData(0); // The sky data are generated on the fly so the compiler can optimize the code
EvaluateBSDF_Env(context, V, posInput, preLightData, envLightSky, bsdfData, localDiffuseLighting, localSpecularLighting, weight);
applyWeigthedIblLighting(localDiffuseLighting, localSpecularLighting, weight, accLighting.envDiffuseLighting, accLighting.envSpecularLighting, totalIblWeight);
}
// Apply ambient occlusion on direct lighting based on strenght factor
diffuseLighting *= lerp(1.0, context.ambientOcclusion, _AmbientOcclusionDirectLightStrenght);
diffuseLighting += iblDiffuseLighting;
specularLighting += iblSpecularLighting;
// Apply GI at the same time as reflection
// Add indirect diffuse + emissive (if any) - Ambient occlusion is multiply by emissive which is wrong but not a big deal
diffuseLighting += bakeDiffuseLighting * context.ambientOcclusion;
// Also Apply indiret diffuse (GI)
// PostEvaluateBSDF will perform any operation wanted by the material and sum everything into diffuseLighting and specularLighting
PostEvaluateBSDF( context, preLightData, bsdfData, accLighting, bakeDiffuseLighting,
diffuseLighting, specularLighting);
ApplyDebug(context, posInput.positionWS, diffuseLighting, specularLighting);
}

37
ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.hlsl


// EvaluateBSDF_Directional (supports directional and box projector lights)
//-----------------------------------------------------------------------------
float4 EvaluateCookie_Directional(LightLoopContext context, DirectionalLightData lightData,
float4 EvaluateCookie_Directional(LightLoopContext lightLoopContext, DirectionalLightData lightData,
float3 lighToSample)
{
// Compute the NDC position (in [-1, 1]^2) by projecting 'positionWS' onto the near plane.

// We let the sampler handle tiling or clamping to border.
// Note: tiling (the repeat mode) is not currently supported.
float4 cookie = SampleCookie2D(context, coord, lightData.cookieIndex);
float4 cookie = SampleCookie2D(lightLoopContext, coord, lightData.cookieIndex);
cookie.a = isInBounds ? cookie.a : 0;

// EvaluateBSDF_Punctual (supports spot, point and projector lights)
//-----------------------------------------------------------------------------
float4 EvaluateCookie_Punctual(LightLoopContext context, LightData lightData,
float4 EvaluateCookie_Punctual(LightLoopContext lightLoopContext, LightData lightData,
float3 lighToSample)
{
int lightType = lightData.lightType;

[branch] if (lightType == GPULIGHTTYPE_POINT)
{
cookie = SampleCookieCube(context, positionLS, lightData.cookieIndex);
cookie = SampleCookieCube(lightLoopContext, positionLS, lightData.cookieIndex);
}
else
{

float2 coord = positionNDC * 0.5 + 0.5;
// We let the sampler handle clamping to border.
cookie = SampleCookie2D(context, coord, lightData.cookieIndex);
cookie = SampleCookie2D(lightLoopContext, coord, lightData.cookieIndex);
cookie.a = isInBounds ? cookie.a : 0;
}

F = Sqr(-F * bsdfData.coatCoverage + 1.0);
}
// TODO: we must always perform a weight calculation as due to tiled rendering we need to smooth out cubemap at boundaries.
// So goal is to split into two category and have an option to say if we parallax correct or not.
// Apply specular occlusion on it
specularLighting *= bsdfData.specularOcclusion * GetSpecularOcclusion(preLightData.NdotV, lightLoopContext.ambientOcclusion, bsdfData.roughness);
//-----------------------------------------------------------------------------
// PostEvaluateBSDF
// ----------------------------------------------------------------------------
void PostEvaluateBSDF( LightLoopContext lightLoopContext, PreLightData preLightData, BSDFData bsdfData, LightLoopAccumulatedLighting accLighting, float3 bakeDiffuseLighting,
out float3 diffuseLighting, out float3 specularLighting)
{
// Add indirect diffuse + emissive (if any) - Ambient occlusion is multiply by emissive which is wrong but not a big deal
bakeDiffuseLighting *= GTAOMultiBounce(lightLoopContext.indirectAmbientOcclusion, bsdfData.diffuseColor);
float specularOcclusion = GetSpecularOcclusionFromAmbientOcclusion(preLightData.NdotV, lightLoopContext.indirectAmbientOcclusion, bsdfData.roughness);
// Try to mimic multibounce with specular color. Not the point of the original formula but ok result.
accLighting.envSpecularLighting *= bsdfData.specularOcclusion * GTAOMultiBounce(specularOcclusion, bsdfData.fresnel0);
// TODO: we could call a function like PostBSDF that will apply albedo and divide by PI once for the loop
// envDiffuseLighting is not used in our case
diffuseLighting = (accLighting.dirDiffuseLighting + accLighting.punctualDiffuseLighting + accLighting.areaDiffuseLighting) * GTAOMultiBounce(lightLoopContext.directAmbientOcclusion, bsdfData.diffuseColor) + bakeDiffuseLighting;
specularLighting = accLighting.dirSpecularLighting + accLighting.punctualSpecularLighting + accLighting.areaSpecularLighting + accLighting.envSpecularLighting;
}
#endif // #ifdef HAS_LIGHTLOOP
正在加载...
取消
保存