浏览代码

Merge pull request #4 from Unity-Technologies/AreaLight

Arealight
/main
GitHub 8 年前
当前提交
ba628504
共有 14 个文件被更改,包括 825 次插入185 次删除
  1. 19
      Assets/ScriptableRenderLoop/HDRenderLoop/HDRenderLoop.cs
  2. 42
      Assets/ScriptableRenderLoop/HDRenderLoop/Shaders/Lighting/LightDefinition.cs
  3. 113
      Assets/ScriptableRenderLoop/HDRenderLoop/Shaders/Lighting/LightDefinition.cs.hlsl
  4. 236
      Assets/ScriptableRenderLoop/HDRenderLoop/Shaders/Material/Lit/Lit.hlsl
  5. 2
      Assets/ScriptableRenderLoop/HDRenderLoop/Shaders/Material/Material.hlsl
  6. 18
      Assets/ScriptableRenderLoop/ShaderLibrary/Common.hlsl
  7. 35
      Assets/ScriptableRenderLoop/ShaderLibrary/CommonLighting.hlsl
  8. 145
      Assets/ScriptableRenderLoop/ShaderLibrary/ImageBasedLighting.hlsl
  9. 191
      Assets/ScriptableRenderLoop/ShaderLibrary/Sampling.hlsl
  10. 173
      Assets/ScriptableRenderLoop/ShaderLibrary/AreaLighting.hlsl
  11. 9
      Assets/ScriptableRenderLoop/ShaderLibrary/AreaLighting.hlsl.meta
  12. 9
      Assets/ScriptableRenderLoop/fptl/RegularForwardLightingTemplate.hlsl.meta
  13. 9
      Assets/ScriptableRenderLoop/fptl/RegularForwardLightingUtils.hlsl.meta
  14. 9
      Assets/ScriptableRenderLoop/fptl/RegularForwardReflectionTemplate.hlsl.meta

19
Assets/ScriptableRenderLoop/HDRenderLoop/HDRenderLoop.cs


l.color.Set(lightColorR, lightColorG, lightColorB);
// Light direction is opposite to the forward direction
l.forward = -light.light.transform.forward;
// CAUTION: For IES as we inverse forward maybe this will need rotation.
l.forward = light.light.transform.forward; // Note: Light direction is oriented backward (-Z)
l.up = light.light.transform.up;
l.right = light.light.transform.right;

if (probe.texture == null)
continue;
var l = new EnvLightData
{
positionWS = probe.localToWorld.GetColumn(3),
shapeType = EnvShapeType.None
};
var l = new EnvLightData();
l.positionWS = probe.localToWorld.GetColumn(3);
l.shapeType = EnvShapeType.None;
if (probe.boxProjection != 0)
{

// Caution should only be rigid transform, no scale
l.worldToLocal = probe.localToWorld.transpose;
l.right = probe.localToWorld.GetColumn(0);
l.up = probe.localToWorld.GetColumn(1);
l.forward = probe.localToWorld.GetColumn(2);
l.innerDistance = probe.bounds.extents;
l.sliceIndex = m_cubeReflTexArray.FetchSlice(probe.texture);

42
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,
Cylinder
};
[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;
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]

113
Assets/ScriptableRenderLoop/HDRenderLoop/Shaders/Lighting/LightDefinition.cs.hlsl


//
//
// UnityEngine.ScriptableRenderLoop.AreaShapeType: static fields
//
#define AREASHAPETYPE_RECTANGLE (0)
#define AREASHAPETYPE_LINE (1)
#define AREASHAPETYPE_SPHERE (2)
#define AREASHAPETYPE_DISK (3)
#define AREASHAPETYPE_HEMISPHERE (4)
#define AREASHAPETYPE_CYLINDER (5)
//
// UnityEngine.ScriptableRenderLoop.EnvShapeType: static fields
//
#define ENVSHAPETYPE_NONE (0)

struct AreaLightData
{
float3 positionWS;
float invSqrAttenuationRadius;
float3 color;
int shapeType;
float3 forward;
float diffuseScale;
float3 up;
float specularScale;
float3 right;
float shadowDimmer;
float2 size;
float twoSided;
float unused;
};
// Generated from UnityEngine.ScriptableRenderLoop.EnvLightData

float3 positionWS;
int shapeType;
float4x4 worldToLocal;
float3 forward;
int sliceIndex;
float3 up;
float blendDistance;
float3 right;
float unused0;
int sliceIndex;
float unused1;
float blendDistance;
float unused2;
};
// Generated from UnityEngine.ScriptableRenderLoop.PlanarLightData

{
return value.positionWS;
}
float GetInvSqrAttenuationRadius(AreaLightData value)
{
return value.invSqrAttenuationRadius;
}
float3 GetColor(AreaLightData value)
{
return value.color;
}
int GetShapeType(AreaLightData value)
{
return value.shapeType;
}
float3 GetForward(AreaLightData value)
{
return value.forward;
}
float GetDiffuseScale(AreaLightData value)
{
return value.diffuseScale;
}
float3 GetUp(AreaLightData value)
{
return value.up;
}
float GetSpecularScale(AreaLightData value)
{
return value.specularScale;
}
float3 GetRight(AreaLightData value)
{
return value.right;
}
float GetShadowDimmer(AreaLightData value)
{
return value.shadowDimmer;
}
float2 GetSize(AreaLightData value)
{
return value.size;
}
float GetTwoSided(AreaLightData value)
{
return value.twoSided;
}
float GetUnused(AreaLightData value)
{
return value.unused;
}
//
// Accessors for UnityEngine.ScriptableRenderLoop.EnvLightData

{
return value.shapeType;
}
float4x4 GetWorldToLocal(EnvLightData value)
float3 GetForward(EnvLightData value)
return value.worldToLocal;
return value.forward;
}
int GetSliceIndex(EnvLightData value)
{
return value.sliceIndex;
}
float3 GetUp(EnvLightData value)
{
return value.up;
}
float GetBlendDistance(EnvLightData value)
{
return value.blendDistance;
}
float3 GetRight(EnvLightData value)
{
return value.right;
}
float GetUnused0(EnvLightData value)
{
return value.unused0;
int GetSliceIndex(EnvLightData value)
float GetUnused1(EnvLightData value)
return value.sliceIndex;
return value.unused1;
float GetBlendDistance(EnvLightData value)
float GetUnused2(EnvLightData value)
return value.blendDistance;
return value.unused2;
}
//

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


// SurfaceData is define in Lit.cs which generate Lit.cs.hlsl
#include "Lit.cs.hlsl"
// Reference Lambert diffuse / GGX Specular for IBL and area lights
//#define LIT_DISPLAY_REFERENCE
// TODO: Check if anisotropy with a dynamic if on anisotropy > 0 is performant. Because it may mean we always calculate both isotrpy and anisotropy case.
// Maybe we should always calculate anisotropy in case of standard ? Don't think the compile can optimize correctly.

// 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)

// 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, 0);
preLightData.ltcGGXMagnitude = UNITY_SAMPLE_TEX2D_LOD(_LtcGGXMagnitude, uv, 0).w;
return preLightData;
}

}
//-----------------------------------------------------------------------------
// BSDF share between area light (reference) and punctual light
//-----------------------------------------------------------------------------
void BSDF( float3 V, float3 L, float3 positionWS, PreLightData prelightData, BSDFData bsdfData,
out float3 diffuseLighting,
out float3 specularLighting)
{
float3 H = normalize(V + L);
float LdotH = saturate(dot(L, H));
float NdotH = saturate(dot(bsdfData.normalWS, H));
float NdotL = saturate(dot(bsdfData.normalWS, L));
float3 F = F_Schlick(bsdfData.fresnel0, LdotH);
float Vis;
float D;
// TODO: this way of handling aniso may not be efficient, or maybe with material classification, need to check perf here
// Maybe always using aniso maybe a win ?
if (bsdfData.materialId == MATERIALID_LIT_ANISO)
{
float TdotL = saturate(dot(bsdfData.tangentWS, L));
float BdotL = saturate(dot(bsdfData.bitangentWS, L));
#ifdef USE_BSDF_PRE_LAMBDAV
Vis = V_SmithJointGGXAnisoLambdaV( prelightData.TdotV, prelightData.BdotV, prelightData.NdotV, TdotL, BdotL, NdotL,
bsdfData.roughnessT, bsdfData.roughnessB, prelightData.anisoGGXlambdaV);
#else
Vis = V_SmithJointGGXAniso( prelightData.TdotV, prelightData.BdotV, prelightData.NdotV, TdotL, BdotL, NdotL,
bsdfData.roughnessT, bsdfData.roughnessB);
#endif
float TdotH = saturate(dot(bsdfData.tangentWS, H));
float BdotH = saturate(dot(bsdfData.bitangentWS, H));
D = D_GGXAnisoDividePI(TdotH, BdotH, NdotH, bsdfData.roughnessT, bsdfData.roughnessB);
}
else
{
#ifdef USE_BSDF_PRE_LAMBDAV
Vis = V_SmithJointGGX(NdotL, prelightData.NdotV, bsdfData.roughness, prelightData.ggxLambdaV);
#else
Vis = V_SmithJointGGX(NdotL, prelightData.NdotV, bsdfData.roughness);
#endif
D = D_GGXDividePI(NdotH, bsdfData.roughness);
}
specularLighting.rgb = F * Vis * D;
#ifdef DIFFUSE_LAMBERT_BRDF
float diffuseTerm = LambertDividePI();
#else
float diffuseTerm = DisneyDiffuseDividePI(prelightData.NdotV, NdotL, LdotH, bsdfData.perceptualRoughness);
#endif
diffuseLighting.rgb = bsdfData.diffuseColor * diffuseTerm;
}
//-----------------------------------------------------------------------------
// EvaluateBSDF_Punctual
//-----------------------------------------------------------------------------

float3 L = normalize(unL);
float attenuation = GetDistanceAttenuation(unL, lightData.invSqrAttenuationRadius);
attenuation *= GetAngleAttenuation(L, lightData.forward, lightData.angleScale, lightData.angleOffset);
// Reminder: lights are ortiented backward (-Z)
attenuation *= GetAngleAttenuation(L, -lightData.forward, lightData.angleScale, lightData.angleOffset);
float illuminance = saturate(dot(bsdfData.normalWS, L)) * attenuation;
diffuseLighting = float4(0.0, 0.0, 0.0, 1.0);

{
float3 H = normalize(V + L);
float LdotH = saturate(dot(L, H));
float NdotH = saturate(dot(bsdfData.normalWS, H));
float NdotL = saturate(dot(bsdfData.normalWS, L));
float3 F = F_Schlick(bsdfData.fresnel0, LdotH);
BSDF(V, L, positionWS, prelightData, bsdfData, diffuseLighting.rgb, specularLighting.rgb);
diffuseLighting.rgb *= lightData.color * illuminance * lightData.diffuseScale;
specularLighting.rgb *= lightData.color * illuminance * lightData.specularScale;
}
}
float Vis;
float D;
// TODO: this way of handling aniso may not be efficient, or maybe with material classification, need to check perf here
// Maybe always using aniso maybe a win ?
if (bsdfData.materialId == MATERIALID_LIT_ANISO)
{
float TdotL = saturate(dot(bsdfData.tangentWS, L));
float BdotL = saturate(dot(bsdfData.bitangentWS, L));
//-----------------------------------------------------------------------------
// EvaluateBSDF_Area - Reference
//-----------------------------------------------------------------------------
#ifdef USE_BSDF_PRE_LAMBDAV
Vis = V_SmithJointGGXAnisoLambdaV( prelightData.TdotV, prelightData.BdotV, prelightData.NdotV, TdotL, BdotL, NdotL,
bsdfData.roughnessT, bsdfData.roughnessB, prelightData.anisoGGXlambdaV);
#else
Vis = V_SmithJointGGXAniso( prelightData.TdotV, prelightData.BdotV, prelightData.NdotV, TdotL, BdotL, NdotL,
bsdfData.roughnessT, bsdfData.roughnessB);
#endif
void IntegrateGGXAreaRef(float3 V, float3 positionWS, PreLightData prelightData, 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);
float TdotH = saturate(dot(bsdfData.tangentWS, H));
float BdotH = saturate(dot(bsdfData.bitangentWS, H));
D = D_GGXAnisoDividePI(TdotH, BdotH, NdotH, bsdfData.roughnessT, bsdfData.roughnessB);
}
else
diffuseLighting = float4(0.0, 0.0, 0.0, 1.0);
specularLighting = float4(0.0, 0.0, 0.0, 1.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(lightData.positionWS, 1.0));
if (lightData.shapeType == AREASHAPETYPE_SPHERE)
SampleSphere(u, localToWorld, lightData.size.x, lightPdf, P, Ns);
else if (lightData.shapeType == AREASHAPETYPE_HEMISPHERE)
SampleHemisphere(u, localToWorld, lightData.size.x, lightPdf, P, Ns);
else if (lightData.shapeType == AREASHAPETYPE_CYLINDER)
SampleCylinder(u, localToWorld, lightData.size.x, lightData.size.y, lightPdf, P, Ns);
else if (lightData.shapeType == AREASHAPETYPE_RECTANGLE)
SampleRectangle(u, localToWorld, lightData.size.x, lightData.size.y, lightPdf, P, Ns);
else if (lightData.shapeType == AREASHAPETYPE_DISK)
SampleDisk(u, localToWorld, lightData.size.x, lightPdf, P, Ns);
else if (lightData.shapeType == AREASHAPETYPE_LINE)
// SampleLine(u, localToWorld, areaLight.lightRadius0, lightPdf, P, Ns);
; // TODO
// Get distance
float3 unL = P - positionWS;
float sqrDist = dot(unL, unL);
float3 L = normalize(unL);
// We calculate area reference light with the area integral rather than the solid angle one.
float illuminance = saturate(dot(Ns, -L)) * saturate(dot(bsdfData.normalWS, L)) / (sqrDist * lightPdf);
float3 localDiffuseLighting = float3(0.0, 0.0, 0.0);
float3 localSpecularLighting = float3(0.0, 0.0, 0.0);
if (illuminance > 0.0)
#ifdef USE_BSDF_PRE_LAMBDAV
Vis = V_SmithJointGGX(NdotL, prelightData.NdotV, bsdfData.roughness, prelightData.ggxLambdaV);
#else
Vis = V_SmithJointGGX(NdotL, prelightData.NdotV, bsdfData.roughness);
#endif
D = D_GGXDividePI(NdotH, bsdfData.roughness);
BSDF(V, L, positionWS, prelightData, bsdfData, localDiffuseLighting, localSpecularLighting);
localDiffuseLighting *= lightData.color * illuminance * lightData.diffuseScale;
localSpecularLighting *= lightData.color * illuminance * lightData.specularScale;
specularLighting.rgb = F * Vis * D;
#ifdef DIFFUSE_LAMBERT_BRDF
float diffuseTerm = LambertDividePI();
#else
float diffuseTerm = DisneyDiffuseDividePI(prelightData.NdotV, NdotL, LdotH, bsdfData.perceptualRoughness);
#endif
diffuseLighting.rgb = bsdfData.diffuseColor * diffuseTerm;
diffuseLighting.rgb *= lightData.color * illuminance;
specularLighting.rgb *= lightData.color * illuminance;
diffuseLighting.rgb += localDiffuseLighting;
specularLighting.rgb += localSpecularLighting;
diffuseLighting.rgb /= float(sampleCount);
specularLighting.rgb /= float(sampleCount);
// Reference code for image based lighting
// EvaluateBSDF_Area
//-----------------------------------------------------------------------------
void EvaluateBSDF_Area( float3 V, float3 positionWS, PreLightData prelightData, AreaLightData lightData, BSDFData bsdfData,
out float4 diffuseLighting,
out float4 specularLighting)
{
#ifdef LIT_DISPLAY_REFERENCE
IntegrateGGXAreaRef(V, positionWS, prelightData, lightData, bsdfData, diffuseLighting, specularLighting);
#else
// TODO: This could be precomputed
float halfWidth = lightData.size.x * 0.5;
float halfHeight = lightData.size.y * 0.5;
float3 p0 = lightData.positionWS + lightData.right * -halfWidth + lightData.up * halfHeight;
float3 p1 = lightData.positionWS + lightData.right * -halfWidth + lightData.up * -halfHeight;
float3 p2 = lightData.positionWS + lightData.right * halfWidth + lightData.up * -halfHeight;
float3 p3 = lightData.positionWS + lightData.right * halfWidth + lightData.up * halfHeight;
float4x3 matL = float4x3(p0, p1, p2, p3);
float4x3 L = matL - 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, lightData.twoSided) * 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, lightData.twoSided) * bsdfData.diffuseColor;
//#else
// TODO: Disney
//#endif
// Divide all by 2 PI as it is Lambert integration for diffuse
diffuseLighting.rgb *= lightData.color * INV_TWO_PI * lightData.diffuseScale;
specularLighting.rgb *= lightData.color * INV_TWO_PI * lightData.specularScale;
// TODO: current area light code doesn't take into account artist attenuation radius!
#endif
}
//-----------------------------------------------------------------------------
// EvaluateBSDF_Env - Reference
// ----------------------------------------------------------------------------
// Ref: Moving Frostbite to PBR (Appendix A)

out float4 diffuseLighting,
out float4 specularLighting)
{
// Reference Lambert diffuse / GGX Specular
//#define LIT_DISPLAY_REFERENCE
#ifdef LIT_DISPLAY_REFERENCE
specularLighting.rgb = IntegrateSpecularGGXIBLRef(V, lightData, bsdfData, UNITY_PASS_ENV(_EnvTextures));

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);

2
Assets/ScriptableRenderLoop/HDRenderLoop/Shaders/Material/Material.hlsl


#include "Assets/ScriptableRenderLoop/ShaderLibrary/Packing.hlsl"
#include "Assets/ScriptableRenderLoop/ShaderLibrary/BSDF.hlsl"
#include "Assets/ScriptableRenderLoop/ShaderLibrary/CommonLighting.hlsl"
#include "Assets/ScriptableRenderLoop/ShaderLibrary/Sampling.hlsl"
#include "Assets/ScriptableRenderLoop/ShaderLibrary/AreaLighting.hlsl"
#include "Assets/ScriptableRenderLoop/ShaderLibrary/ImageBasedLighting.hlsl"
#include "Assets/ScriptableRenderLoop/ShaderLibrary/Debug.hlsl"
#include "Assets/ScriptableRenderLoop/ShaderLibrary/GeometricTools.hlsl"

18
Assets/ScriptableRenderLoop/ShaderLibrary/Common.hlsl


// Common math definition and fastmath function
// ----------------------------------------------------------------------------
#define PI 3.14159265359f
#define TWO_PI 6.28318530718f
#define FOUR_PI 12.56637061436f
#define INV_PI 0.31830988618f
#define INV_TWO_PI 0.15915494309f
#define INV_FOUR_PI 0.07957747155f
#define HALF_PI 1.57079632679f
#define INV_HALF_PI 0.636619772367f
#define PI 3.14159265359
#define TWO_PI 6.28318530718
#define FOUR_PI 12.56637061436
#define INV_PI 0.31830988618
#define INV_TWO_PI 0.15915494309
#define INV_FOUR_PI 0.07957747155
#define HALF_PI 1.57079632679
#define INV_HALF_PI 0.636619772367
#define FLT_EPSILON 1.192092896e-07f // smallest such that 1.0 + FLT_EPSILON != 1.0
#define MERGE_NAME(X, Y) X##Y

35
Assets/ScriptableRenderLoop/ShaderLibrary/CommonLighting.hlsl


#ifndef UNITY_COMMON_LIGHTING_INCLUDED
#define UNITY_COMMON_LIGHTING_INCLUDED
// Ligthing convention
// Light direction is oriented backward (-Z). i.e in shader code, light direction is - lightData.forward
//-----------------------------------------------------------------------------
// Attenuation functions
//-----------------------------------------------------------------------------

{
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

191
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 : http://www.cs.virginia.edu/~jdl/bib/globillum/mis/shirley96.pdf + PBRT
// Caution: Our light point backward (-Z), these sampling function follow this convention
//-----------------------------------------------------------------------------
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); // 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); // 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 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;
Ns = UniformSampleSphere(u1, u2);
// Transform from unit sphere to world space
P = radius * Ns + localToWorld[3].xyz;
// pdf is inverse of 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 hemisphere 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, (float3x3)(localToWorld));
// pdf is inverse of 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 cylinder surface
float t = (u1 - 0.5) * width;
float theta = 2.0 * PI * u2;
float cosTheta = cos(theta);
float sinTheta = sin(theta);
// Cylinder are align on the right axis
P = float3(t, radius * cosTheta, radius * sinTheta);
Ns = normalize(float3(0.0, cosTheta, sinTheta));
// Transform to world space
P = mul(float4(P, 1.0), localToWorld).xyz;
Ns = mul(Ns, (float3x3)(localToWorld));
// pdf is inverse of area
lightPdf = 1.0 / (TWO_PI * radius * width);
}
void SampleRectangle( float2 u,
float4x4 localToWorld,
float width,
float height,
out float lightPdf,
out float3 P,
out float3 Ns)
{
// Random point at rectangle surface
P = float3((u.x - 0.5) * width, (u.y - 0.5) * height, 0);
Ns = float3(0, 0, -1); // Light point backward (-Z)
// Transform to world space
P = mul(float4(P, 1.0), localToWorld).xyz;
Ns = mul(Ns, (float3x3)(localToWorld));
// pdf is inverse of 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 disk surface
P = UniformSampleDisk(u.x, u.y) * radius;
Ns = float3(0.0, 0.0, -1.0); // Light point backward (-Z)
// Transform to world space
P = mul(float4(P, 1.0), localToWorld).xyz;
Ns = mul(Ns, (float3x3)(localToWorld));
// pdf is inverse of area
lightPdf = 1.0 / (PI * radius * radius);
}
#endif // UNITY_SAMPLING_INCLUDED

173
Assets/ScriptableRenderLoop/ShaderLibrary/AreaLighting.hlsl


#ifndef UNITY_AREA_LIGHTING_INCLUDED
#define UNITY_AREA_LIGHTING_INCLUDED
float IntegrateEdge(float3 v1, float3 v2)
{
float cosTheta = dot(v1, v2);
// TODO: Explain the 0.9999 <= precision is important!
cosTheta = clamp(cosTheta, -0.9999, 0.9999);
// TODO: Experiment with fastAcos
float theta = acos(cosTheta);
float 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, bool twoSided)
{
// 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, twoSided);
}
#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:

9
Assets/ScriptableRenderLoop/fptl/RegularForwardLightingTemplate.hlsl.meta


fileFormatVersion: 2
guid: addc185d48decc34d9bf21cb3a4ea28c
timeCreated: 1476774180
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

9
Assets/ScriptableRenderLoop/fptl/RegularForwardLightingUtils.hlsl.meta


fileFormatVersion: 2
guid: 13dff6ea8f6c55743a3cbb841066cc27
timeCreated: 1476774180
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

9
Assets/ScriptableRenderLoop/fptl/RegularForwardReflectionTemplate.hlsl.meta


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