浏览代码

HDRenderLoop: Add area light support, first draft

/main
Sebastien Lagarde 8 年前
当前提交
caf80e26
共有 7 个文件被更改,包括 643 次插入114 次删除
  1. 44
      Assets/ScriptableRenderLoop/HDRenderLoop/Shaders/Lighting/LightDefinition.cs
  2. 135
      Assets/ScriptableRenderLoop/HDRenderLoop/Shaders/Material/Lit/Lit.hlsl
  3. 32
      Assets/ScriptableRenderLoop/ShaderLibrary/CommonLighting.hlsl
  4. 145
      Assets/ScriptableRenderLoop/ShaderLibrary/ImageBasedLighting.hlsl
  5. 218
      Assets/ScriptableRenderLoop/ShaderLibrary/Sampling.hlsl
  6. 174
      Assets/ScriptableRenderLoop/ShaderLibrary/AreaLighting.hlsl
  7. 9
      Assets/ScriptableRenderLoop/ShaderLibrary/AreaLighting.hlsl.meta

44
Assets/ScriptableRenderLoop/HDRenderLoop/Shaders/Lighting/LightDefinition.cs


};
[GenerateHLSL]
public enum AreaShapeType
{
Rectangle,
Line,
// Currently not supported in real time (just use for reference)
Sphere,
Disk,
Hemisphere,
Cylindre
};
[GenerateHLSL]
public float invSqrAttenuationRadius;
public Vector3 color;
public AreaShapeType shapeType;
public Vector3 forward;
public float diffuseScale;
public Vector3 up;
public float specularScale;
public Vector3 right;
public float shadowDimmer;
Matrix4x4 lightVerts;
public Vector2 size;
public float twoSided;
public float unused;
};
[GenerateHLSL]

public Vector3 positionWS;
public EnvShapeType shapeType;
public Matrix4x4 worldToLocal; // No scale
public Vector3 forward;
public int sliceIndex;
public Vector3 up;
public float blendDistance;
public Vector3 right;
public float unused0;
public int sliceIndex;
public float unused1;
public float blendDistance;
public float unused2;
};
[GenerateHLSL]

135
Assets/ScriptableRenderLoop/HDRenderLoop/Shaders/Material/Lit/Lit.hlsl


#ifndef UNITY_MATERIAL_LIT_INCLUDED
// Upgrade NOTE: excluded shader from OpenGL ES 2.0 because it uses non-square matrices
#pragma exclude_renderers gles
#define UNITY_MATERIAL_LIT_INCLUDED
//-----------------------------------------------------------------------------

// TODO: How can I declare a sampler for this one that is bilinear filtering
// TODO: This one should be set into a constant Buffer at pass frequency (with _Screensize)
UNITY_DECLARE_TEX2D(_PreIntegratedFGD);
UNITY_DECLARE_TEX2D(_LtcGGXMatrix);
UNITY_DECLARE_TEX2D(_LtcGGXMagnitude);
// For image based lighting, a part of the BSDF is pre-integrated.
// This is done both for specular and diffuse (in case of DisneyDiffuse)

// TODO: if we want we can store ambient occlusion here from SSAO pass for example that can be use for IBL specular occlusion
// float ambientOcclusion; // Feed from an ambient occlusion buffer
// area light
float3x3 minV;
float ltcGGXMagnitude;
};
PreLightData GetPreLightData(float3 V, float3 positionWS, Coordinate coord, BSDFData bsdfData)

// #if SHADERPASS == SHADERPASS_GBUFFER
// preLightData.ambientOcclusion = _AmbientOcclusion.Load(uint3(coord.unPositionSS, 0)).x;
// #endif
// Area light specific
// UVs for sampling the LUTs
// TODO: Test with fastAcos
float theta = acos(dot(bsdfData.normalWS, V));
// Scale and bias for the current precomputed table
float2 uv = 0.0078125 + 0.984375 * float2(bsdfData.perceptualRoughness, theta * INV_HALF_PI);
// Get the inverse LTC matrix for GGX
// Note we load the matrix transpose (avoid to have to transpose it in shader)
preLightData.minV = 0.0;
preLightData.minV._m22 = 1.0;
preLightData.minV._m00_m02_m11_m20 = UNITY_SAMPLE_TEX2D_LOD(_LtcGGXMatrix, uv);
preLightData.ltcGGXMagnitude = UNITY_SAMPLE_TEX2D_LOD(_LtcGGXMagnitude, uv).w;
return preLightData;
}

}
//-----------------------------------------------------------------------------
// Reference code for image based lighting
// EvaluateBSDF_Area - Reference
//-----------------------------------------------------------------------------
// We calculate area reference light with the area integral rather than the solid angle one.
void evaluateAreaLightReference(float3 V, float3 positionWS, AreaLightData lightData, BSDFData bsdfData,
out float4 diffuseLighting,
out float4 specularLighting,
uint sampleCount = 512)
{
// Add some jittering on Hammersley2d
float2 randNum = InitRandom(V.xy * 0.5 + 0.5);
// Accumulate this light locally, then add on at the end (avoids any accidental multiplies of in/out accumulated variables)
float3 accBottom = float3(0.0, 0.0, 0.0);
float3 accTop = float3(0.0, 0.0, 0.0);
for (uint i = 0; i < sampleCount; ++i)
{
float3 P = float3(0.0, 0.0, 0.0); // Sample light point. Random point on the light shape in local space.
float3 Ns = float3(0.0, 0.0, 0.0); // Unit surface normal at P
float lightPdf = 0.0; // Pdf of the light sample
float2 u = Hammersley2d(i, sampleCount);
u = frac(u + randNum + 0.5);
float4x4 localToWorld = float4x4(float4(lightData.right, 0.0), float4(lightData.up, 0.0), float4(lightData.forward, 0.0), float4(light.positionWS, 1.0));
if (areaLightType == AREASHAPETYPE_SPHERE)
sampleSphere(u, localToWorld, lightData.size.x, lightPdf, P, Ns);
else if (areaLightType == AREASHAPETYPE_HEMISPHERE)
sampleHemisphere(u, localToWorld, lightData.size.x, lightPdf, P, Ns);
else if (areaLightType == AREASHAPETYPE_CYLINDER)
sampleCylinder(u, localToWorld, lightData.size.x, lightData.size.y, lightPdf, P, Ns);
else if (areaLightType == AREASHAPETYPE_RECTANGLE)
sampleRectangle(u, localToWorld, lightData.size.x, lightData.size.y, lightPdf, P, Ns);
else if (areaLightType == AREASHAPETYPE_DISK)
sampleDisk(u, localToWorld, lightData.size.x, lightPdf, P, Ns);
else if (areaLightType == AREASHAPETYPE_LINE)
// sampleLine(u, localToWorld, areaLight.lightRadius0, lightPdf, P, Ns);
; // TODO
// Get distance
float3 unL = P - worldPos;
float sqrDist = dot(unL, unL);
float3 L = normalize(unL);
float illuminance = saturate(dot(Ns, -L)) * saturate(dot(bsdfData.normalWS, L)) / (sqrDist * lightPdf);
float3 localDiffuse = float3(0.0, 0.0, 0.0);
float3 localSpecular = float3(0.0, 0.0, 0.0);
if (illuminance > 0.0)
{
// TODO
// BSDF(V, L, data localDiffuse, localSpecular);
localDiffuse *= illuminance;
localSpecular *= illuminance;
}
accBottom += localDiffuse;
accTop += localSpecular;
}
// Generate the final values to accumulate
float3 localBottom = (accBottom / float(sampleCount)) * light.color * light.diffuseScale;
float3 localTop = (accTop / float(sampleCount)) * light.color * light.specularScale;
// Accumulate this light into in/out accumulation variables
outBottom += localBottom;
outTop += localTop;
}
//-----------------------------------------------------------------------------
// EvaluateBSDF_Area
//-----------------------------------------------------------------------------
void EvaluateBSDF_Area( float3 V, float3 positionWS, PreLightData prelightData, AreaLightData lightData, BSDFData bsdfData,
out float4 diffuseLighting,
out float4 specularLighting)
{
// Can be precomputed - light.LightVerts is a tab of 4 vertices representing the light quad
// TODO: can be simplify as a rect, with extend
float4x3 L = lightData.lightVerts - float4x3(positionWS, positionWS, positionWS, positionWS);
// TODO: Can we get early out based on diffuse computation ? (if all point are clip)
diffuseLighting = float4(0.0f, 0.0f, 0.0f, 1.0f);
specularLighting = float4(0.0f, 0.0f, 0.0f, 1.0f);
// TODO: Fresnel is missing here but should be present
specularLighting.rgb = LTCEvaluate(V, bsdfData.normalWS, prelightData.minV, L) prelightData.ltcGGXMagnitude;
//#ifdef DIFFUSE_LAMBERT_BRDF
// Lambert diffuse term (here it should be Disney)
float3x3 identity = 0;
identity._m00_m11_m22 = 1.0;
diffuseLighting.rgb = LTCEvaluate(V, bsdfData.normalWS, identity, L) * bsdfData.diffuseColor;
//#else
// TODO: Disney
//#endif
// Divide all by 2 PI as it is Lambert integration for diffuse
diffuseLighting.rgb *= lightData.color / (2 * PI);
specularLighting.rgb *= lightData.color / (2 * PI);
// TODO: current area light code doesn't take into account artist attenuation radius!
}
//-----------------------------------------------------------------------------
// EvaluateBSDF_Env - Reference
// ----------------------------------------------------------------------------
// Ref: Moving Frostbite to PBR (Appendix A)

if (lightData.shapeType == ENVSHAPETYPE_BOX)
{
// worldToLocal assume no scaling
float4x4 worldToLocal = transpose(float4x4(float4(lightData.right, 0.0), float4(lightData.up, 0.0), float4(lightData.forward, 0.0), float4(light.positionWS, 1.0)));
float3 positionLS = mul(lightData.worldToLocal, float4(positionWS, 1.0)).xyz;
float3 rayLS = mul((float3x3)lightData.worldToLocal, rayWS);
float3 boxOuterDistance = lightData.innerDistance + float3(lightData.blendDistance, lightData.blendDistance, lightData.blendDistance);

32
Assets/ScriptableRenderLoop/ShaderLibrary/CommonLighting.hlsl


return (1 - perceptualSmoothness);
}
//-----------------------------------------------------------------------------
// Get local frame
//-----------------------------------------------------------------------------
// generate an orthonormalBasis from 3d unit vector.
void GetLocalFrame(float3 N, out float3 tangentX, out float3 tangentY)
{
float3 upVector = abs(N.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0);
tangentX = normalize(cross(upVector, N));
tangentY = cross(N, tangentX);
}
// TODO: test
/*
// http://orbit.dtu.dk/files/57573287/onb_frisvad_jgt2012.pdf
void GetLocalFrame(float3 N, out float3 tangentX, out float3 tangentY)
{
if (N.z < -0.999) // Handle the singularity
{
tangentX = float3(0.0, -1.0, 0.0);
tangentY = float3(-1.0, 0.0, 0.0);
return ;
}
float a = 1.0 / (1.0 + N.z);
float b = -N.x * N.y * a;
tangentX = float3(1.0f - N.x * N.x * a , b, -N.x);
tangentY = float3(b, 1.0f - N.y * N.y * a, -N.y);
}
*/
#endif // UNITY_COMMON_LIGHTING_INCLUDED

145
Assets/ScriptableRenderLoop/ShaderLibrary/ImageBasedLighting.hlsl


#include "CommonLighting.hlsl"
#include "BSDF.hlsl"
#include "Sampling.hlsl"
// Sample generator
// Util image based lighting
// Ref: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
uint ReverseBits32(uint bits)
// TODO: We need to change this hard limit!
#define UNITY_SPECCUBE_LOD_STEPS (6)
float perceptualRoughnessToMipmapLevel(float perceptualRoughness)
#if 0 // Shader model 5
return reversebits(bits);
// TODO: Clean a bit this code
// CAUTION: remap from Morten may work only with offline convolution, see impact with runtime convolution!
// For now disabled
#if 0
float m = PerceptualRoughnessToRoughness(perceptualRoughness); // m is the real roughness parameter
const float fEps = 1.192092896e-07F; // smallest such that 1.0+FLT_EPSILON != 1.0 (+1e-4h is NOT good here. is visibly very wrong)
float n = (2.0 / max(fEps, m*m)) - 2.0; // remap to spec power. See eq. 21 in --> https://dl.dropboxusercontent.com/u/55891920/papers/mm_brdf.pdf
n /= 4.0; // remap from n_dot_h formulatino to n_dot_r. See section "Pre-convolved Cube Maps vs Path Tracers" --> https://s3.amazonaws.com/docs.knaldtech.com/knald/1.0.0/lys_power_drops.html
perceptualRoughness = pow(2.0 / (n + 2.0), 0.25); // remap back to square root of real roughness (0.25 include both the sqrt root of the conversion and sqrt for going from roughness to perceptualRoughness)
bits = ( bits << 16) | ( bits >> 16);
bits = ((bits & 0x00ff00ff) << 8) | ((bits & 0xff00ff00) >> 8);
bits = ((bits & 0x0f0f0f0f) << 4) | ((bits & 0xf0f0f0f0) >> 4);
bits = ((bits & 0x33333333) << 2) | ((bits & 0xcccccccc) >> 2);
bits = ((bits & 0x55555555) << 1) | ((bits & 0xaaaaaaaa) >> 1);
return bits;
// MM: came up with a surprisingly close approximation to what the #if 0'ed out code above does.
perceptualRoughness = perceptualRoughness * (1.7 - 0.7 * perceptualRoughness);
}
float RadicalInverse_VdC(uint bits)
{
return float(ReverseBits32(bits)) * 2.3283064365386963e-10; // 0x100000000
return perceptualRoughness * UNITY_SPECCUBE_LOD_STEPS;
float2 Hammersley2d(uint i, uint maxSampleCount)
// Ref: See "Moving Frostbite to PBR" Listing 22
// This formulation is for GGX only (with smith joint visibility or regular)
float3 GetSpecularDominantDir(float3 N, float3 R, float roughness)
return float2(float(i) / float(maxSampleCount), RadicalInverse_VdC(i));
}
float Hash(uint s)
{
s = s ^ 2747636419u;
s = s * 2654435769u;
s = s ^ (s >> 16);
s = s * 2654435769u;
s = s ^ (s >> 16);
s = s * 2654435769u;
return float(s) / 4294967295.0;
}
float2 InitRandom(float2 input)
{
float2 r;
r.x = Hash(uint(input.x * 4294967295.0));
r.y = Hash(uint(input.y * 4294967295.0));
return r;
float a = 1.0 - roughness;
float lerpFactor = a * (sqrt(a) + roughness);
// The result is not normalized as we fetch in a cubemap
return lerp(N, R, lerpFactor);
// Util
// Anisotropic image based lighting
// generate an orthonormalBasis from 3d unit vector.
void GetLocalFrame(float3 N, out float3 tangentX, out float3 tangentY)
// To simulate the streching of highlight at grazing angle for IBL we shrink the roughness
// which allow to fake an anisotropic specular lobe.
// Ref: http://www.frostbite.com/2015/08/stochastic-screen-space-reflections/ - slide 84
float AnisotropicStrechAtGrazingAngle(float roughness, float perceptualRoughness, float NdotV)
float3 upVector = abs(N.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0);
tangentX = normalize(cross(upVector, N));
tangentY = cross(N, tangentX);
return roughness * lerp(saturate(NdotV * 2.0), 1.0, perceptualRoughness);
// TODO: test
/*
// http://orbit.dtu.dk/files/57573287/onb_frisvad_jgt2012.pdf
void GetLocalFrame(float3 N, out float3 tangentX, out float3 tangentY)
{
if (N.z < -0.999) // Handle the singularity
{
tangentX = float3(0.0, -1.0, 0.0);
tangentY = float3(-1.0, 0.0, 0.0);
return ;
}
float a = 1.0 / (1.0 + N.z);
float b = -N.x * N.y * a;
tangentX = float3(1.0f - N.x * N.x * a , b, -N.x);
tangentY = float3(b, 1.0f - N.y * N.y * a, -N.y);
}
*/
// Sampling
// Importance sampling BSDF functions
// ----------------------------------------------------------------------------
void ImportanceSampleCosDir(float2 u,

}
return float4(acc * (1.0 / accWeight), 1.0);
}
// TODO: We need to change this hard limit!
#define UNITY_SPECCUBE_LOD_STEPS (6)
float perceptualRoughnessToMipmapLevel(float perceptualRoughness)
{
// TODO: Clean a bit this code
// CAUTION: remap from Morten may work only with offline convolution, see impact with runtime convolution!
// For now disabled
#if 0
float m = PerceptualRoughnessToRoughness(perceptualRoughness); // m is the real roughness parameter
const float fEps = 1.192092896e-07F; // smallest such that 1.0+FLT_EPSILON != 1.0 (+1e-4h is NOT good here. is visibly very wrong)
float n = (2.0 / max(fEps, m*m)) - 2.0; // remap to spec power. See eq. 21 in --> https://dl.dropboxusercontent.com/u/55891920/papers/mm_brdf.pdf
n /= 4.0; // remap from n_dot_h formulatino to n_dot_r. See section "Pre-convolved Cube Maps vs Path Tracers" --> https://s3.amazonaws.com/docs.knaldtech.com/knald/1.0.0/lys_power_drops.html
perceptualRoughness = pow(2.0 / (n + 2.0), 0.25); // remap back to square root of real roughness (0.25 include both the sqrt root of the conversion and sqrt for going from roughness to perceptualRoughness)
#else
// MM: came up with a surprisingly close approximation to what the #if 0'ed out code above does.
perceptualRoughness = perceptualRoughness * (1.7 - 0.7 * perceptualRoughness);
#endif
return perceptualRoughness * UNITY_SPECCUBE_LOD_STEPS;
}
// Ref: See "Moving Frostbite to PBR" Listing 22
// This formulation is for GGX only (with smith joint visibility or regular)
float3 GetSpecularDominantDir(float3 N, float3 R, float roughness)
{
float a = 1.0 - roughness;
float lerpFactor = a * (sqrt(a) + roughness);
// The result is not normalized as we fetch in a cubemap
return lerp(N, R, lerpFactor);
}
//-----------------------------------------------------------------------------
// Anisotropic image based lighting
//-----------------------------------------------------------------------------
// To simulate the streching of highlight at grazing angle for IBL we shrink the roughness
// which allow to fake an anisotropic specular lobe.
// Ref: http://www.frostbite.com/2015/08/stochastic-screen-space-reflections/ - slide 84
float AnisotropicStrechAtGrazingAngle(float roughness, float perceptualRoughness, float NdotV)
{
return roughness * lerp(saturate(NdotV * 2.0), 1.0, perceptualRoughness);
}
#endif // UNITY_IMAGE_BASED_LIGHTING_INCLUDED

218
Assets/ScriptableRenderLoop/ShaderLibrary/Sampling.hlsl


#ifndef UNITY_SAMPLING_INCLUDED
#define UNITY_SAMPLING_INCLUDED
//-----------------------------------------------------------------------------
// Sample generator
//-----------------------------------------------------------------------------
// Ref: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
uint ReverseBits32(uint bits)
{
#if 0 // Shader model 5
return reversebits(bits);
#else
bits = (bits << 16) | (bits >> 16);
bits = ((bits & 0x00ff00ff) << 8) | ((bits & 0xff00ff00) >> 8);
bits = ((bits & 0x0f0f0f0f) << 4) | ((bits & 0xf0f0f0f0) >> 4);
bits = ((bits & 0x33333333) << 2) | ((bits & 0xcccccccc) >> 2);
bits = ((bits & 0x55555555) << 1) | ((bits & 0xaaaaaaaa) >> 1);
return bits;
#endif
}
float RadicalInverse_VdC(uint bits)
{
return float(ReverseBits32(bits)) * 2.3283064365386963e-10; // 0x100000000
}
float2 Hammersley2d(uint i, uint maxSampleCount)
{
return float2(float(i) / float(maxSampleCount), RadicalInverse_VdC(i));
}
float Hash(uint s)
{
s = s ^ 2747636419u;
s = s * 2654435769u;
s = s ^ (s >> 16);
s = s * 2654435769u;
s = s ^ (s >> 16);
s = s * 2654435769u;
return float(s) / 4294967295.0;
}
float2 InitRandom(float2 input)
{
float2 r;
r.x = Hash(uint(input.x * 4294967295.0));
r.y = Hash(uint(input.y * 4294967295.0));
return r;
}
//-----------------------------------------------------------------------------
// Sampling function
//-----------------------------------------------------------------------------
// Reference : Monte Carlo techniques for direct lighting calculations http://www.cs.virginia.edu/~jdl/bib/globillum/mis/shirley96.pdf + PBRT
float3 UniformSampleSphere(float u1, float u2)
{
float phi = TWO_PI * u2;
float cosTheta = 1.0 - 2.0 * u1;
float sinTheta = sqrt(max(0.0, 1.0 - cosTheta * cosTheta));
return float3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta); // our light point backward (-Z)
}
float3 UniformSampleHemisphere(float u1, float u2)
{
float phi = TWO_PI * u2;
float cosTheta = u1;
float sinTheta = sqrt(max(0.0, 1.0 - cosTheta * cosTheta));
return float3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta); // our light point backward (-Z)
}
float3 UniformSampleDisk(float u1, float u2)
{
float r = sqrt(u1);
float phi = TWO_PI * u2;
return float3(r * cos(phi), r * sin(phi), 0); // generate in the XY plane as light point backward (-Z)
}
void SampleSphere( float2 u,
float4x4 localToWorld,
float radius,
out float lightPdf,
out float3 P,
out float3 Ns)
{
float u1 = u.x;
float u2 = u.y;
// Random point at light surface
Ns = uniformSampleSphere(u1, u2);
// Transform point on unit sphere to world space
P = radius * Ns + localToWorld[3].xyz;
// pdf is just the inverse of the area
lightPdf = 1.0 / (FOUR_PI * radius * radius);
}
void SampleHemisphere( float2 u,
float4x4 localToWorld,
float radius,
out float lightPdf,
out float3 P,
out float3 Ns)
{
float u1 = u.x;
float u2 = u.y;
// Random point at light surface
Ns = -uniformSampleHemisphere(u1, u2); // We want the y down hemisphere
P = radius * Ns;
// Transform to world space
P = mul(float4(P, 1.0), localToWorld).xyz;
Ns = mul(Ns, convertFloat3x3(localToWorld));
// pdf is just the inverse of the area
lightPdf = 1.0 / (TWO_PI * radius * radius);
}
// Note: The cylinder has no end caps (i.e. no disk on the side)
void SampleCylinder(float2 u,
float4x4 localToWorld,
float radius,
float width,
out float lightPdf,
out float3 P,
out float3 Ns)
{
float u1 = u.x;
float u2 = u.y;
// Random point at light surface
float t = (u1 - 0.5) * width;
float theta = 2.0 * PI * u2;
float cosTheta = cos(theta);
float sinTheta = sin(theta);
P = float3(t, radius * cosTheta, radius * sinTheta); // Cylinder are align on the left axis in Frosbite
Ns = normalize(float3(0.0, cosTheta, sinTheta));
// Transform to world space
P = mul(float4(P, 1.0), localToWorld).xyz;
Ns = mul(Ns, convertFloat3x3(localToWorld));
// pdf is just the inverse of the area
lightPdf = 1.0 / (TWO_PI * radius * width);
}
void SampleCapsule( float2 u,
float4x4 localToWorld,
float radius,
float width,
int passIt,
out float lightPdf,
out float3 P,
out float3 Ns)
{
// Capsules are sampled in two times:
// - Pass 0: Cylinder
// - Pass 1: Hemisphere caps
if (passIt == 0)
{
sampleCylinder(u, localToWorld, radius, width, lightPdf, P, Ns);
}
else
{
float u1 = u.x;
float u2 = u.y;
// Random point at light surface
Ns = uniformSampleSphere(u1, u2);
P = radius * Ns;
// Split the sphere into two hemisphere and shift each hemisphere on one side of the cylinder
P.x += (Ns.x > 0.0 ? 1.0 : -1.0) * 0.5 * width;
// Transform to world space
P = mul(float4(P, 1.0), localToWorld).xyz;
Ns = mul(Ns, convertFloat3x3(localToWorld));
// pdf is just the inverse of the area
lightPdf = 1.0 / (FOUR_PI * radius * radius);
}
}
void SampleRectangle( float2 u,
float4x4 localToWorld,
float width,
float height,
out float lightPdf,
out float3 P,
out float3 Ns)
{
// Random point at light surface
P = float3((u.x - 0.5) * width, (u.y - 0.5) * height, 0);
Ns = float3(0, 0, -1); // By default our rectangle light point backward
// Transform to world space
P = mul(float4(P, 1.0), localToWorld).xyz;
Ns = mul(Ns, convertFloat3x3(localToWorld));
// pdf is just the inverse of the area
lightPdf = 1.0 / (width * height);
}
void SampleDisk(float2 u,
float4x4 localToWorld,
float radius,
out float lightPdf,
out float3 P,
out float3 Ns)
{
// Random point at light surface
P = uniformSampleDisk(u.x, u.y) * radius;
Ns = float3(0.0, 0.0, -1.0);
// Transform to world space
P = mul(float4(P, 1.0), localToWorld).xyz;
Ns = mul(Ns, convertFloat3x3(localToWorld));
// pdf is just the inverse of the area
lightPdf = 1.0 / (PI * radius * radius);
}
#endif // UNITY_SAMPLING_INCLUDED

174
Assets/ScriptableRenderLoop/ShaderLibrary/AreaLighting.hlsl


#ifndef UNITY_AREA_LIGHTING_INCLUDED
// Upgrade NOTE: excluded shader from OpenGL ES 2.0 because it uses non-square matrices
#pragma exclude_renderers gles
#define UNITY_AREA_LIGHTING_INCLUDED
float IntegrateEdge(float3 v1, float3 v2)
{
float3 cosTheta = dot(v1, v2);
cosTheta = clamp(cosTheta, -0.9999, 0.9999);
// TODO: Experiment with fastAcos
float3 theta = acos(cosTheta);
float3 res = cross(v1, v2).z * theta / sin(theta);
return res;
}
// Baum's equation
// Expects non-normalized vertex positions
float PolygonRadiance(float4x3 L, bool twoSided)
{
// 1. ClipQuadToHorizon
// detect clipping config
uint config = 0;
if (L[0].z > 0) config += 1;
if (L[1].z > 0) config += 2;
if (L[2].z > 0) config += 4;
if (L[3].z > 0) config += 8;
// The fifth vertex for cases when clipping cuts off one corner.
// Due to a compiler bug, copying L into a vector array with 5 rows
// messes something up, so we need to stick with the matrix + the L4 vertex.
float3 L4 = L[3];
// This switch is surprisingly fast. Tried replacing it with a lookup array of vertices.
// Even though that replaced the switch with just some indexing and no branches, it became
// way, way slower - mem fetch stalls?
uint n = 0;
switch (config)
{
case 0: // clip all
break;
case 1: // V1 clip V2 V3 V4
n = 3;
L[1] = -L[1].z * L[0] + L[0].z * L[1];
L[2] = -L[3].z * L[0] + L[0].z * L[3];
break;
case 2: // V2 clip V1 V3 V4
n = 3;
L[0] = -L[0].z * L[1] + L[1].z * L[0];
L[2] = -L[2].z * L[1] + L[1].z * L[2];
break;
case 3: // V1 V2 clip V3 V4
n = 4;
L[2] = -L[2].z * L[1] + L[1].z * L[2];
L[3] = -L[3].z * L[0] + L[0].z * L[3];
break;
case 4: // V3 clip V1 V2 V4
n = 3;
L[0] = -L[3].z * L[2] + L[2].z * L[3];
L[1] = -L[1].z * L[2] + L[2].z * L[1];
break;
case 5: // V1 V3 clip V2 V4: impossible
break;
case 6: // V2 V3 clip V1 V4
n = 4;
L[0] = -L[0].z * L[1] + L[1].z * L[0];
L[3] = -L[3].z * L[2] + L[2].z * L[3];
break;
case 7: // V1 V2 V3 clip V4
n = 5;
L4 = -L[3].z * L[0] + L[0].z * L[3];
L[3] = -L[3].z * L[2] + L[2].z * L[3];
break;
case 8: // V4 clip V1 V2 V3
n = 3;
L[0] = -L[0].z * L[3] + L[3].z * L[0];
L[1] = -L[2].z * L[3] + L[3].z * L[2];
L[2] = L[3];
break;
case 9: // V1 V4 clip V2 V3
n = 4;
L[1] = -L[1].z * L[0] + L[0].z * L[1];
L[2] = -L[2].z * L[3] + L[3].z * L[2];
break;
case 10: // V2 V4 clip V1 V3: impossible
break;
case 11: // V1 V2 V4 clip V3
n = 5;
L[3] = -L[2].z * L[3] + L[3].z * L[2];
L[2] = -L[2].z * L[1] + L[1].z * L[2];
break;
case 12: // V3 V4 clip V1 V2
n = 4;
L[1] = -L[1].z * L[2] + L[2].z * L[1];
L[0] = -L[0].z * L[3] + L[3].z * L[0];
break;
case 13: // V1 V3 V4 clip V2
n = 5;
L[3] = L[2];
L[2] = -L[1].z * L[2] + L[2].z * L[1];
L[1] = -L[1].z * L[0] + L[0].z * L[1];
break;
case 14: // V2 V3 V4 clip V1
n = 5;
L4 = -L[0].z * L[3] + L[3].z * L[0];
L[0] = -L[0].z * L[1] + L[1].z * L[0];
break;
case 15: // V1 V2 V3 V4
n = 4;
break;
}
if (n == 0)
return 0;
if (n == 3)
L[3] = L[0];
if (n == 4)
L4 = L[0];
// 2. Project onto sphere
L[0] = normalize(L[0]);
L[1] = normalize(L[1]);
L[2] = normalize(L[2]);
L[3] = normalize(L[3]);
L4 = normalize(L4);
// 3. Integrate
float sum = 0;
sum += IntegrateEdge(L[0], L[1]);
sum += IntegrateEdge(L[1], L[2]);
sum += IntegrateEdge(L[2], L[3]);
if (n >= 4)
sum += IntegrateEdge(L[3], L4);
if (n == 5)
sum += IntegrateEdge(L4, L[0]);
return twoSided > 0.0 ? abs(sum) : max(0.0, sum);
}
float LTCEvaluate(float3 V, float3 N, float3x3 minV, float4x3 L)
{
// Construct local orthonormal basis around N, aligned with N
float3x3 basis;
basis[0] = normalize(V - N * dot(V, N));
basis[1] = normalize(cross(N, basis[0]));
basis[2] = N;
// rotate area light in local basis
minV = mul(transpose(basis), minV);
L = mul(L, minV);
// Polygon radiance in transformed configuration - specular
return PolygonRadiance(L);
}
#endif // UNITY_AREA_LIGHTING_INCLUDED

9
Assets/ScriptableRenderLoop/ShaderLibrary/AreaLighting.hlsl.meta


fileFormatVersion: 2
guid: 2af09bb13e6547242913c40dfb0e50f9
timeCreated: 1476726533
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存