浏览代码

Implement a very basic ray marcher for homogeneous volumes

/sample_game
Evgenii Golubev 7 年前
当前提交
adcfcfd6
共有 6 个文件被更改,包括 154 次插入46 次删除
  1. 13
      Assets/ScriptableRenderPipeline/Core/ShaderLibrary/VolumeRendering.hlsl
  2. 43
      Assets/ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipeline.cs
  3. 8
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePass.cs
  4. 10
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/Volumetrics/HomogeneousFog.cs
  5. 6
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/Volumetrics/HomogeneousFog.cs.hlsl
  6. 120
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/Volumetrics/Resources/VolumetricLighting.compute

13
Assets/ScriptableRenderPipeline/Core/ShaderLibrary/VolumeRendering.hlsl


return exp(-opticalDepth);
}
float IsotropicPhaseFunction()
{
return INV_FOUR_PI;
}
float HenyeyGreensteinPhaseFunction(float asymmetry, float LdotD)
{
float g = asymmetry;
// Note: the factor before pow() is a constant, and could therefore be factored out.
return (INV_FOUR_PI * (1 - g * g)) * pow(abs(1 + g * g - 2 * g * LdotD), -1.5);
}
#endif // UNITY_VOLUME_RENDERING_INCLUDED

43
Assets/ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipeline.cs


m_DebugDisplaySettings.RegisterDebug();
m_DebugFullScreenTempRT = HDShaderIDs._DebugFullScreenTexture;
if (asset.tileSettings.enableClustered)
{
Debug.Assert(asset.tileSettings.enableTileAndCluster);
m_VolumetricLightingKernel = m_VolumetricLightingCS.FindKernel("VolumetricLightingClustered");
}
else
{
m_VolumetricLightingKernel = m_VolumetricLightingCS.FindKernel("VolumetricLightingAllLights");
}
s_UnboundedVolumeData = new ComputeBuffer(1, System.Runtime.InteropServices.Marshal.SizeOf(typeof(VolumeProperties)));
}

foreach (HomogeneousFog fogComponent in fogComponents)
{
if (fogComponent.volumeParameters.IsVolumeUnbounded())
if (fogComponent.enabled && fogComponent.volumeParameters.IsVolumeUnbounded())
{
unboundedFogComponent = fogComponent;
break;

if (unboundedFogComponent == null) { return; }
List<VolumeProperties> unboundedVolumeProperties = new List<VolumeProperties>();
unboundedVolumeProperties.Add(unboundedFogComponent.volumeParameters.GetProperties());
using (new Utilities.ProfilingSample("VolumetricLighting", cmd))
{
bool enableClustered = m_Asset.tileSettings.enableClustered && m_Asset.tileSettings.enableTileAndCluster;
// TODO: probably unnecessary to update the buffer every frame.
s_UnboundedVolumeData.SetData<VolumeProperties>(unboundedVolumeProperties);
m_VolumetricLightingKernel = m_VolumetricLightingCS.FindKernel(enableClustered ? "VolumetricLightingClustered"
: "VolumetricLightingAllLights");
List<VolumeProperties> unboundedVolumeProperties = new List<VolumeProperties>();
unboundedVolumeProperties.Add(unboundedFogComponent.volumeParameters.GetProperties());
hdCamera.SetupComputeShader(m_VolumetricLightingCS, cmd);
// TODO: probably unnecessary to update the buffer every frame.
s_UnboundedVolumeData.SetData(unboundedVolumeProperties);
// TODO: replace strings with nameIDs.
cmd.SetComputeBufferParam( m_VolumetricLightingCS, m_VolumetricLightingKernel, "_UnboundedVolume", s_UnboundedVolumeData);
cmd.SetComputeTextureParam(m_VolumetricLightingCS, m_VolumetricLightingKernel, "_LightingTexture", m_CameraColorBufferRT);
cmd.SetComputeTextureParam(m_VolumetricLightingCS, m_VolumetricLightingKernel, HDShaderIDs._DepthTexture, GetDepthTexture());
hdCamera.SetupComputeShader(m_VolumetricLightingCS, cmd);
// TODO: replace strings with nameIDs.
cmd.SetComputeBufferParam( m_VolumetricLightingCS, m_VolumetricLightingKernel, "_UnboundedVolume", s_UnboundedVolumeData);
cmd.SetComputeTextureParam(m_VolumetricLightingCS, m_VolumetricLightingKernel, "_LightingTexture", m_CameraColorBufferRT);
cmd.SetComputeTextureParam(m_VolumetricLightingCS, m_VolumetricLightingKernel, HDShaderIDs._DepthTexture, GetDepthTexture());
// Pass clustered light data (if present) into the compute shader.
m_LightLoop.PushGlobalParams(hdCamera.camera, cmd, m_VolumetricLightingCS, m_VolumetricLightingKernel, true);
cmd.SetComputeIntParam(m_VolumetricLightingCS, HDShaderIDs._UseTileLightList, 0);
// Perform the SSS filtering pass which fills 'm_CameraFilteringBufferRT'.
cmd.DispatchCompute(m_VolumetricLightingCS, m_VolumetricLightingKernel, ((int)hdCamera.screenSize.x + 15) / 16, ((int)hdCamera.screenSize.y + 15) / 16, 1);
cmd.DispatchCompute(m_VolumetricLightingCS, m_VolumetricLightingKernel, ((int)hdCamera.screenSize.x + 15) / 16, ((int)hdCamera.screenSize.y + 15) / 16, 1);
}
}
// Render material that are forward opaque only (like eye), this include unlit material

8
Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePass.cs


s_LightVolumeDataBuffer.SetData(m_lightList.lightVolumes);
}
private void BindGlobalParams(CommandBuffer cmd, Camera camera)
private void BindGlobalParams(CommandBuffer cmd, Camera camera, bool forceClustered)
SetGlobalBuffer(HDShaderIDs.g_vLightListGlobal, !usingFptl ? s_PerVoxelLightLists : s_LightList); // opaques list (unless MSAA possibly)
SetGlobalBuffer(HDShaderIDs.g_vLightListGlobal, (forceClustered || !usingFptl) ? s_PerVoxelLightLists : s_LightList); // opaques list (unless MSAA possibly)
SetGlobalTexture(HDShaderIDs._CookieTextures, m_CookieTexArray.GetTexCache());
SetGlobalTexture(HDShaderIDs._CookieCubeTextures, m_CubeCookieTexArray.GetTexCache());

}
}
private void PushGlobalParams(Camera camera, CommandBuffer cmd, ComputeShader computeShader, int kernelIndex)
public void PushGlobalParams(Camera camera, CommandBuffer cmd, ComputeShader computeShader, int kernelIndex, bool forceClustered = false)
{
using (new Utilities.ProfilingSample("Push Global Parameters", cmd))
{

SetGlobalPropertyRedirect(computeShader, kernelIndex, cmd);
BindGlobalParams(cmd, camera);
BindGlobalParams(cmd, camera, forceClustered);
SetGlobalPropertyRedirect(null, 0, null);
}
}

10
Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/Volumetrics/HomogeneousFog.cs


public struct VolumeProperties
{
public Vector3 scattering;
public float anisotropy;
public float asymmetry;
public Vector3 extinction;
public float unused;
}

public Bounds bounds; // Position and dimensions in meters
public Vector3 albedo; // Single scattering albedo [0, 1]
public Vector3 meanFreePath; // In meters [0.01, inf]
public float anisotropy; // [-1, 1]
public float asymmetry; // [-1, 1]; 0 = isotropic
public VolumeParameters()
{

anisotropy = 0.0f;
asymmetry = 0.0f;
}
public bool IsVolumeUnbounded()

meanFreePath.y = Mathf.Max(meanFreePath.y, 0.01f);
meanFreePath.z = Mathf.Max(meanFreePath.z, 0.01f);
anisotropy = Mathf.Clamp(anisotropy, -1.0f, 1.0f);
asymmetry = Mathf.Clamp(asymmetry, -1.0f, 1.0f);
}
public VolumeProperties GetProperties()

properties.scattering = GetScatteringCoefficient();
properties.anisotropy = anisotropy;
properties.asymmetry = asymmetry;
properties.extinction = GetExtinctionCoefficient();
return properties;

6
Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/Volumetrics/HomogeneousFog.cs.hlsl


struct VolumeProperties
{
float3 scattering;
float anisotropy;
float asymmetry;
float3 extinction;
float unused;
};

{
return value.scattering;
}
float GetAnisotropy(VolumeProperties value)
float GetAsymmetry(VolumeProperties value)
return value.anisotropy;
return value.asymmetry;
}
float3 GetExtinction(VolumeProperties value)
{

120
Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/Volumetrics/Resources/VolumetricLighting.compute


#pragma enable_d3d11_debug_symbols
#include "../../../ShaderPass/ShaderPass.cs.hlsl"
#define SHADERPASS SHADERPASS_VOLUMETRIC_LIGHTING
#define GROUP_SIZE_1D 16
#define GROUP_SIZE_2D (GROUP_SIZE_1D * GROUP_SIZE_1D)

struct Ray
{
float3 originWS;
float3 directionWS; // Not normalized
float maxLength;
float3 directionWS; // Normalized
float maxLength; // In meters
// Integrates the volume along the ray to
float3 IntegrateVolume() {return 0;}
// Computes the in-scattered radiance along the ray.
float3 PerformIntegration(PositionInputs posInput, Ray ray, uint numSteps)
{
float3 scattering = _UnboundedVolume[0].scattering;
float3 extinction = _UnboundedVolume[0].extinction;
float asymmetry = _UnboundedVolume[0].asymmetry;
LightLoopContext context;
// ZERO_INITIALIZE(LightLoopContext, context);
context.shadowContext = InitShadowContext();
float maxDepthVS = posInput.depthVS;
float du = rcp(numSteps); // Compile-time constant
float u0 = 0.5 * du; // Compile-time constant
float dt = du * ray.maxLength;
float3 radiance = 0;
for (uint i = 0; i < numSteps; i++)
{
float u = u0 + i * du; // [0, 1]
float t = u * ray.maxLength; // [0, ray.maxLength]
float3 samplePositionWS = ray.originWS + t * ray.directionWS;
// if (featureFlags & LIGHTFEATUREFLAGS_PUNCTUAL)
{
uint punctualLightCount;
#ifdef LIGHTLOOP_TILE_PASS
uint punctualLightStart;
posInput.depthVS = u * maxDepthVS;
GetCountAndStart(posInput, LIGHTCATEGORY_PUNCTUAL, punctualLightStart, punctualLightCount);
#else
punctualLightCount = _PunctualLightCount;
#endif
for (uint j = 0; j < punctualLightCount; ++j)
{
#ifdef LIGHTLOOP_TILE_PASS
uint punctualLightIndex = FetchIndex(punctualLightStart, j);
#else
uint punctualLightIndex = j;
#endif
// Fetch the light.
LightData lightData = _LightDatas[punctualLightIndex];
float3 L = lightData.positionWS - samplePositionWS;
// Compute the distance to the light, and the associated transmittance values.
float dist2 = dot(L, L);
float dist = sqrt(dist2);
float rcpDist = rsqrt(dist2);
float LdotD = dot(L, ray.directionWS) * rcpDist;
float phase = HenyeyGreensteinPhaseFunction(asymmetry, LdotD);
float3 lightT = Transmittance(OpticalDepthHomogeneous(extinction, dist));
float3 sampleT = Transmittance(OpticalDepthHomogeneous(extinction, t));
float shadow = 1;
[branch] if (lightData.shadowIndex >= 0)
{
// TODO: make projector lights cast shadows.
float3 offset = float3(0.0, 0.0, 0.0); // GetShadowPosOffset(nDotL, normal);
float4 L_dist = { L * rcpDist, dist };
shadow = GetPunctualShadowAttenuation(context.shadowContext, samplePositionWS + offset, 0, lightData.shadowIndex, L_dist, posInput.unPositionSS);
shadow = lerp(1.0, shadow, lightData.shadowDimmer);
}
// Compute the amount of in-scattered radiance.
radiance += (dt * scattering * phase * shadow * rcpDist * rcpDist) * (lightT * sampleT) * lightData.color;
}
}
}
return radiance;
}
[numthreads(GROUP_SIZE_2D, 1, 1)]
void VolumetricLighting(uint2 groupId : SV_GroupID,

uint2 localCoord = DecodeMorton2D(mortonCode);
uint2 tileAnchor = groupId * GROUP_SIZE_1D;
uint2 pixelCoord = tileAnchor + localCoord;
uint2 tileCoord = pixelCoord / GetTileSize();
PositionInputs posInput = GetPositionInput(pixelCoord, _ScreenSize.zw);
UpdatePositionInput(LOAD_TEXTURE2D(_DepthTexture, pixelCoord).r, _InvViewProjMatrix, _ViewProjMatrix, posInput);
if (pixelCoord.x >= (uint)_ScreenSize.x || pixelCoord.y >= (uint)_ScreenSize.y) { return; }
Ray ray;
PositionInputs posInput = GetPositionInput(pixelCoord, _ScreenSize.zw, tileCoord);
UpdatePositionInput(max(LOAD_TEXTURE2D(_DepthTexture, pixelCoord).r, 0.02), _InvViewProjMatrix, _ViewProjMatrix, posInput);
// Note: the camera ray does not start on the the near (image) plane.
Ray cameraRay;
// Note: the camera ray does not start on the the near (camera sensor) plane.
ray.originWS = GetCurrentViewPosition();
ray.directionWS = posInput.positionWS - ray.originWS;
ray.maxLength = length(ray.directionWS);
cameraRay.originWS = GetCurrentViewPosition();
cameraRay.directionWS = posInput.positionWS - cameraRay.originWS;
cameraRay.maxLength = sqrt(dot(cameraRay.directionWS, cameraRay.directionWS));
cameraRay.directionWS *= rsqrt(dot(cameraRay.directionWS, cameraRay.directionWS)); //Normalize
float3 rayT = Transmittance(OpticalDepthHomogeneous(_UnboundedVolume[0].extinction, cameraRay.maxLength));
// TODO: make this a parameter controllable from C#.
const float maxSamplingDistance = 64.0f; // In meters
float distanceScale = max(1, maxSamplingDistance / cameraRay.maxLength);
float3 transmittance = Transmittance(OpticalDepthHomogeneous(_UnboundedVolume[0].extinction, ray.maxLength));
cameraRay.maxLength *= distanceScale;
posInput.depthVS *= distanceScale;
float3 inL = PerformIntegration(posInput, cameraRay, 64);
_LightingTexture[pixelCoord] = float4(transmittance * _LightingTexture[pixelCoord].rgb, _LightingTexture[pixelCoord].a);
_LightingTexture[pixelCoord] = float4(rayT * _LightingTexture[pixelCoord].rgb + inL, _LightingTexture[pixelCoord].a);
}
正在加载...
取消
保存