2026-04-16 오브젝트 그림자

This commit is contained in:
skrwns304@gmail.com
2026-04-16 04:58:10 +09:00
parent 0fe8b18872
commit 42646a636f
303 changed files with 54374 additions and 20 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c7318e34b1fd83b4b9c1d76f71e029ed
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 490fe8da71f8576488b30a4a922fc442
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,98 @@
using System;
using UnityEngine;
using UnityEngine.Rendering;
namespace BadDog.Rendering.AreaLight.LTC
{
/// <summary>
/// "Charlie" Sheen Implementation
/// Source from Sony Pictures Imageworks by Estevez and Kulla, "Production Friendly Microfacet Sheen BRDF" (http://blog.selfshadow.com/publications/s2017-shading-course/imageworks/s2017_pbs_imageworks_sheen.pdf)
/// Details: https://knarkowicz.wordpress.com/2018/01/04/cloth-shading/
/// </summary>
public struct BRDF_Charlie : IBRDF
{
public double Eval(ref Vector3 _tsView, ref Vector3 _tsLight, float _alpha, out double _pdf)
{
if (_tsView.z <= 0)
{
_pdf = 0;
return 0;
}
_alpha = Mathf.Max(0.002f, _alpha);
Vector3 H = Vector3.Normalize(_tsView + _tsLight);
double NdotL = _tsLight.z;
double NdotV = _tsView.z;
double NdotH = H.z;
// D
double D = CharlieD(_alpha, NdotH);
// Ashikmin masking/shadowing
// double G = V_Ashikhmin( NdotV, NdotL );
double G = V_Charlie(NdotV, NdotL, _alpha);
// fr = F(H) * G(V, L) * D(H)
// Note that the usual 1 / (4 * (N.L) * (N.V)) part of the Cook-Torrance micro-facet model is actually contained in the G visibility term in our case (as reported by Ashkmin in "Distribution-based BRDFs" eq. 2)
double res = D * G * NdotL; // We also include the (N.L) term here
// We're using uniform distribution
_pdf = 0.5 / Math.PI;
return res;
}
// Paper recommend plain uniform sampling of upper hemisphere instead of importance sampling for Charlie
public void GetSamplingDirection(ref Vector3 _tsView, float _alpha, float _U1, float _U2, ref Vector3 _direction)
{
float phi = 2.0f * Mathf.PI * _U1;
float cosTheta = 1.0f - _U2;
float sinTheta = Mathf.Sqrt(1 - cosTheta * cosTheta);
_direction = new Vector3(sinTheta * Mathf.Cos(phi), sinTheta * Mathf.Sin(phi), cosTheta);
}
double CharlieD(float _roughness, double _NdotH)
{
double invR = 1.0 / _roughness;
double cos2h = _NdotH * _NdotH;
double sin2h = 1.0f - cos2h;
double res = (2.0 + invR) * Math.Pow(sin2h, invR * 0.5) / (2.0 * Math.PI);
return res;
}
double V_Ashikhmin(double _NdotV, double _NdotL)
{
return 1.0 / (4.0 * (_NdotL + _NdotV - _NdotL * _NdotV));
}
// Note: This version doesn't include the softening of the paper: Production Friendly Microfacet Sheen BRDF
double V_Charlie(double _NdotV, double _NdotL, double _roughness)
{
double lambdaV = _NdotV < 0.5 ? Math.Exp(CharlieL(_NdotV, _roughness)) : Math.Exp(2.0 * CharlieL(0.5, _roughness) - CharlieL(1.0 - _NdotV, _roughness));
double lambdaL = _NdotL < 0.5 ? Math.Exp(CharlieL(_NdotL, _roughness)) : Math.Exp(2.0 * CharlieL(0.5, _roughness) - CharlieL(1.0 - _NdotL, _roughness));
return 1.0 / ((1.0 + lambdaV + lambdaL) * (4.0 * _NdotV * _NdotL));
}
double CharlieL(double x, double _roughness)
{
float r = Mathf.Clamp01((float)_roughness);
r = 1.0f - r * r;
float a = Mathf.Lerp(25.3245f, 21.5473f, r);
float b = Mathf.Lerp(3.32435f, 3.82987f, r);
float c = Mathf.Lerp(0.16801f, 0.19823f, r);
float d = Mathf.Lerp(-1.27393f, -1.97760f, r);
float e = Mathf.Lerp(-4.85967f, -4.32054f, r);
double res = a / (1.0 + b * Math.Pow(Math.Max(0, x), c)) + d * x + e;
return res;
}
public BadDog.Rendering.AreaLight.LTCLightingModel GetLightingModel()
{
return BadDog.Rendering.AreaLight.LTCLightingModel.Charlie;
}
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: e3273a65ebf1c474397e1eb12a73cf98
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/BRDF/BRDF_Charlie.cs
uploadId: 884030

View File

@@ -0,0 +1,60 @@
using System;
using UnityEngine;
using UnityEngine.Rendering;
namespace BadDog.Rendering.AreaLight.LTC
{
/// <summary>
/// Cook-Torrance implementation of the BRDF interface
/// </summary>
public struct BRDF_CookTorrance : IBRDF
{
public double Eval(ref Vector3 _tsView, ref Vector3 _tsLight, float _alpha, out double _pdf)
{
if (_tsView.z <= 0)
{
_pdf = 0;
return 0;
}
_alpha = Mathf.Max(0.002f, _alpha);
Vector3 H = (_tsView + _tsLight).normalized;
double NdotL = Math.Max(1e-8, _tsLight.z);
double NdotV = Math.Max(1e-8, _tsView.z);
double NdotH = H.z;
double LdotH = Math.Max(1e-8, Vector3.Dot(_tsLight, H));
// D
double cosb2 = NdotH * NdotH;
double m2 = _alpha * _alpha;
double D = Math.Exp((cosb2 - 1.0) / (cosb2 * m2)) // exp( -tan(a)² / m² )
/ Math.Max(1e-12, Math.PI * m2 * cosb2 * cosb2); // / (PI * m² * cos(a)^4)
// masking/shadowing
double G = Math.Min(1, 2.0 * NdotH * Math.Min(NdotV, NdotL) / LdotH);
// fr = F(H) * G(V, L) * D(H) / (4 * (N.L) * (N.V))
double res = D * G / (4.0 * NdotV); // Full specular mico-facet model is F * D * G / (4 * NdotL * NdotV) but since we're fitting with the NdotL included, it gets nicely canceled out!
// pdf = D(H) * (N.H) / (4 * (L.H))
_pdf = Math.Abs(D * NdotH / (4.0 * LdotH));
return res;
}
public void GetSamplingDirection(ref Vector3 _tsView, float _alpha, float _U1, float _U2, ref Vector3 _direction)
{
float phi = 2.0f * Mathf.PI * _U1;
float cosTheta = 1.0f / Mathf.Sqrt(1 - _alpha * _alpha * Mathf.Log(Mathf.Max(1e-6f, _U2)));
float sinTheta = Mathf.Sqrt(1 - cosTheta * cosTheta);
Vector3 H = new Vector3(sinTheta * Mathf.Cos(phi), sinTheta * Mathf.Sin(phi), cosTheta);
_direction = 2.0f * Vector3.Dot(H, _tsView) * H - _tsView; // Mirror view direction
}
public BadDog.Rendering.AreaLight.LTCLightingModel GetLightingModel()
{
return BadDog.Rendering.AreaLight.LTCLightingModel.CookTorrance;
}
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 432ee4d0aec53c943894a42d0bf30444
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/BRDF/BRDF_CookTorrance.cs
uploadId: 884030

View File

@@ -0,0 +1,83 @@
using System;
using UnityEngine;
using UnityEngine.Rendering;
namespace BadDog.Rendering.AreaLight.LTC
{
/// <summary>
/// Disney Diffuse Implementation
/// Source from 2012 Burley, B. "Physically-Based Shading at Disney" Section 5.3
/// (https://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf)
/// </summary>
public struct BRDF_Disney : IBRDF
{
public double Eval(ref Vector3 _tsView, ref Vector3 _tsLight, float _alpha, out double _pdf)
{
if (_tsView.z <= 0)
{
_pdf = 0;
return 0;
}
_alpha = Mathf.Max(0.002f, _alpha);
double NdotL = Math.Max(0, _tsLight.z);
double NdotV = Math.Max(0, _tsView.z);
double LdotV = Math.Max(0, Vector3.Dot(_tsLight, _tsView));
double perceptualRoughness = Math.Sqrt(_alpha);
// (2 * LdotH * LdotH) = 1 + LdotV
// real fd90 = 0.5 + 2 * LdotH * LdotH * perceptualRoughness;
double fd90 = 0.5 + (perceptualRoughness + perceptualRoughness * LdotV);
// Two schlick fresnel term
double lightScatter = F_Schlick(1.0, fd90, NdotL);
double viewScatter = F_Schlick(1.0, fd90, NdotV);
// Normalize the BRDF for polar view angles of up to (Pi/4).
// We use the worst case of (roughness = albedo = 1), and, for each view angle,
// integrate (brdf * cos(theta_light)) over all light directions.
// The resulting value is for (theta_view = 0), which is actually a little bit larger
// than the value of the integral for (theta_view = Pi/4).
// Hopefully, the compiler folds the constant together with (1/Pi).
double res = lightScatter * viewScatter / Math.PI;
res /= 1.03571;
// Remember we must include the N.L term!
res *= NdotL;
// Cosine-weighted hemisphere sampling
_pdf = NdotL / Math.PI;
return res;
}
public void GetSamplingDirection(ref Vector3 _tsView, float _alpha, float _U1, float _U2, ref Vector3 _direction)
{
// Performs uniform sampling of the unit disk.
// Ref: PBRT v3, p. 777.
float r = Mathf.Sqrt(_U1);
float phi = 2.0f * Mathf.PI * _U2;
// Performs cosine-weighted sampling of the hemisphere.
// Ref: PBRT v3, p. 780.
_direction.x = r * Mathf.Cos(phi);
_direction.y = r * Mathf.Sin(phi);
_direction.z = Mathf.Sqrt(1 - _U1); // Project the point from the disk onto the hemisphere.
}
double F_Schlick(double _F0, double _F90, double _cosTheta)
{
double x = 1.0 - _cosTheta;
double x2 = x * x;
double x5 = x * x2 * x2;
return (_F90 - _F0) * x5 + _F0; // sub mul mul mul sub mad
}
public BadDog.Rendering.AreaLight.LTCLightingModel GetLightingModel()
{
return BadDog.Rendering.AreaLight.LTCLightingModel.DisneyDiffuse;
}
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: ff4aa0c2af19abb42bade44e76256186
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/BRDF/BRDF_Disney.cs
uploadId: 884030

View File

@@ -0,0 +1,74 @@
using System;
using UnityEngine;
using UnityEngine.Rendering;
namespace BadDog.Rendering.AreaLight.LTC
{
/// <summary>
/// GGX implementation of the BRDF interface
/// </summary>
public class BRDF_GGX : IBRDF
{
public double Eval(ref Vector3 _tsView, ref Vector3 _tsLight, float _alpha, out double _pdf)
{
if (_tsView.z <= 0)
{
_pdf = 0;
return 0;
}
// masking
double lambdaV = Lambda(_tsView.z, _alpha);
// shadowing
double G2 = 0;
if (_tsLight.z > 0.0f)
{
double lambdaL = Lambda(_tsLight.z, _alpha);
G2 = 1.0 / (1.0 + lambdaV + lambdaL);
}
// D
Vector3 H = _tsView + _tsLight;
float lengthH = H.magnitude;
if (lengthH > 1e-8f)
H = H / lengthH;
else
H = new Vector3(0, 0, 1);
double slopex = H.x / H.z;
double slopey = H.y / H.z;
double D = 1.0 / (1.0 + (slopex * slopex + slopey * slopey) / _alpha / _alpha);
D = D * D;
D = D / (Math.PI * _alpha * _alpha * H.z * H.z * H.z * H.z);
// Full specular mico-facet model is F * D * G / (4 * NdotL * NdotV) but since we're fitting with the NdotL included, it gets nicely canceled out!
double res = D * G2 / 4.0 / _tsView.z;
// pdf = D(H) * (N.H) / (4 * (L.H))
_pdf = Math.Abs(D * H.z / 4.0 / Vector3.Dot(_tsView, H));
return res;
}
public void GetSamplingDirection(ref Vector3 _tsView, float _alpha, float _U1, float _U2, ref Vector3 _direction)
{
float phi = 2.0f * Mathf.PI * _U1;
float r = _alpha * Mathf.Sqrt(_U2 / (1.0f - _U2));
Vector3 H = new Vector3(r * Mathf.Cos(phi), r * Mathf.Sin(phi), 1.0f).normalized;
_direction = -_tsView + 2.0f * H * Vector3.Dot(H, _tsView);
}
double Lambda(float _cosTheta, float _alpha)
{
double a = 1.0f / _alpha / Math.Tan(Math.Acos(_cosTheta));
double lambda = _cosTheta < 1.0 ? 0.5 * (-1.0 + Math.Sqrt(1.0 + 1.0 / (a * a))) : 0.0;
return lambda;
}
public BadDog.Rendering.AreaLight.LTCLightingModel GetLightingModel()
{
return BadDog.Rendering.AreaLight.LTCLightingModel.GGX;
}
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: e67e2cd414ae81a4f8f5a76bce9f6b35
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/BRDF/BRDF_GGX.cs
uploadId: 884030

View File

@@ -0,0 +1,38 @@
//////////////////////////////////////////////////////////////////////////
// BRDF Interface
//////////////////////////////////////////////////////////////////////////
//
using System;
using UnityEngine;
using UnityEngine.Rendering;
namespace BadDog.Rendering.AreaLight.LTC
{
/// <summary>
/// BRDF Interface that you must implement in order to generate a new table
/// </summary>
public interface IBRDF
{
/// <summary>
/// Evaluation of the ***cosine-weighted*** BRDF
/// </summary>
/// <param name="_tsView">The vector pointing toward the camera</param>
/// <param name="_tsLight">The vector pointing toward the light</param>
/// <param name="_alpha">Surface roughness</param>
/// <param name="_pdf">The Probability Density Function of sampling the light in that direction</param>
/// <returns></returns>
double Eval(ref Vector3 _tsView, ref Vector3 _tsLight, float _alpha, out double _pdf);
/// <summary>
/// Gets an importance-sampled light direction given a view vector and the surface roughness
/// </summary>
/// <param name="_tsView">The vector pointing toward the camera</param>
/// <param name="_alpha">>Surface roughness</param>
/// <param name="_U1">A random value in [0,1]</param>
/// <param name="_U2">A 2nd random value in [0,1]</param>
/// <param name="_direction">The generated direction</param>
void GetSamplingDirection(ref Vector3 _tsView, float _alpha, float _U1, float _U2, ref Vector3 _direction);
BadDog.Rendering.AreaLight.LTCLightingModel GetLightingModel();
};
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 8e6541bb3b842494ab1ec4bf036a37e9
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/BRDF/BRDF_Interface.cs
uploadId: 884030

View File

@@ -0,0 +1,112 @@
using System;
using UnityEngine;
using UnityEngine.Rendering;
namespace BadDog.Rendering.AreaLight.LTC
{
/// <summary>
/// Disney Diffuse Implementation
/// Source from 2012 Burley, B. "Physically-Based Shading at Disney" Section 5.3
/// (https://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf)
/// </summary>
public struct BRDF_KajiyaKaySpecular : IBRDF
{
public double Eval(ref Vector3 _tsView, ref Vector3 _tsLight, float _alpha, out double _pdf)
{
if (_tsView.z <= 0)
{
_pdf = 0;
return 0;
}
_alpha = Mathf.Max(0.002f, _alpha);
double perceptualRoughness = Math.Sqrt(_alpha);
Vector3 T = Vector3.right;
Vector3 N = Vector3.forward;
double NdotV = Math.Max(0, _tsView.z);
double NdotL = Math.Max(0, _tsLight.z);
double LdotV = Math.Max(0, Vector3.Dot(_tsLight, _tsView));
Vector3 H = Vector3.Normalize(_tsLight + _tsView);
double LdotH = Math.Max(0, Vector3.Dot(_tsLight, H));
Vector3 t1 = ShiftTangent(T, N, 0.0f);
Vector3 t2 = ShiftTangent(T, N, 0.0f);
double specularExponent = RoughnessToBlinnPhongSpecularExponent(_alpha);
// Balancing energy between lobes, as well as between diffuse and specular is left to artists.
double hairSpec1 = D_KajiyaKay(t1, H, specularExponent);
double hairSpec2 = D_KajiyaKay(t2, H, specularExponent);
double fd90 = 0.5 + (perceptualRoughness + perceptualRoughness * LdotV);
double F = F_Schlick(1.0, fd90, LdotH);
// G = NdotL * NdotV.
double res = 0.25 * F * (hairSpec1 + hairSpec2) * NdotL * Math.Min(Math.Max(NdotV * double.MaxValue, 0.0), 1.0);
// Uniform Sampling
_pdf = 0.5 / Math.PI;
return res;
}
// Uniform Sampling
public void GetSamplingDirection(ref Vector3 _tsView, float _alpha, float _U1, float _U2, ref Vector3 _direction)
{
float phi = 2.0f * Mathf.PI * _U1;
float cosTheta = 1.0f - _U2;
float sinTheta = Mathf.Sqrt(1 - cosTheta * cosTheta);
_direction = new Vector3(sinTheta * Mathf.Cos(phi), sinTheta * Mathf.Sin(phi), cosTheta);
}
double RoughnessToBlinnPhongSpecularExponent(double roughness)
{
// 1e-4 and 3e3 are the lowest/highest values that do not cause numerical instabilities with the fitting tool
return Math.Min(Math.Max(2.0 / (roughness * roughness) - 2.0, 1e-4), 3e3);
}
double F_Schlick(double _F0, double _F90, double _cosTheta)
{
double x = 1.0f - _cosTheta;
double x2 = x * x;
double x5 = x * x2 * x2;
return (_F90 - _F0) * x5 + _F0; // sub mul mul mul sub mad
}
//http://web.engr.oregonstate.edu/~mjb/cs519/Projects/Papers/HairRendering.pdf
Vector3 ShiftTangent(Vector3 T, Vector3 N, float shift)
{
return Vector3.Normalize(T + N * shift);
}
double PositivePow(double value, double power)
{
return Math.Pow(Math.Max(Math.Abs(value), 1.192092896e-07), power);
}
double D_KajiyaKay(Vector3 T, Vector3 H, double specularExponent)
{
float TdotH = Vector3.Dot(T, H);
float sinTHSq = Mathf.Clamp(1.0f - TdotH * TdotH, 0.0f, 1.0f);
float dirAttn = Mathf.Clamp(TdotH + 1.0f, 0.0f, 1.0f); // Evgenii: this seems like a hack? Do we really need this?
// Note: Kajiya-Kay is not energy conserving.
// We attempt at least some energy conservation by approximately normalizing Blinn-Phong NDF.
// We use the formulation with the NdotL.
// See http://www.thetenthplanet.de/archives/255.
double n = specularExponent;
double norm = (n + 2) / (2.0 * Math.PI);
return dirAttn * norm * PositivePow(sinTHSq, 0.5 * n);
}
public BadDog.Rendering.AreaLight.LTCLightingModel GetLightingModel()
{
return BadDog.Rendering.AreaLight.LTCLightingModel.KajiyaKaySpecular;
}
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4288b6ce37f7e2f40b421a4053ff7b73
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/BRDF/BRDF_KajiyaKaySpecular.cs
uploadId: 884030

View File

@@ -0,0 +1,27 @@
using System;
using UnityEngine;
using UnityEngine.Rendering;
namespace BadDog.Rendering.AreaLight.LTC
{
public struct BRDF_Marschner : IBRDF
{
public double Eval(ref Vector3 _tsView, ref Vector3 _tsLight, float _alpha, out double _pdf)
{
// Uniform sampled over a sphere.
_pdf = 1f / (4f * Math.PI);
return 0f;
}
public void GetSamplingDirection(ref Vector3 _tsView, float _alpha, float _U1, float _U2, ref Vector3 _direction)
{
_direction = Vector3.up;
}
public BadDog.Rendering.AreaLight.LTCLightingModel GetLightingModel()
{
return BadDog.Rendering.AreaLight.LTCLightingModel.Marschner;
}
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4986784e6e6a4a048aa882f5bcd44d55
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/BRDF/BRDF_Marschner.cs
uploadId: 884030

View File

@@ -0,0 +1,80 @@
using System;
using UnityEngine;
using UnityEngine.Rendering;
namespace BadDog.Rendering.AreaLight.LTC
{
/// <summary>
/// Oren-Nayar Implementation
/// Source from 1994 Oren, M. Nayar, S. K. "Generalization of Lambert's Reflectance Model"
/// </summary>
public struct BRDF_OrenNayar : IBRDF
{
public double Eval(ref Vector3 _tsView, ref Vector3 _tsLight, float _alpha, out double _pdf)
{
if (_tsView.z <= 0)
{
_pdf = 0;
return 0;
}
float sigma = Mathf.Max(0.002f, 0.5f * Mathf.PI * _alpha); // Standard deviation is a [0,PI/2] angle
double NdotL = Math.Max(0, _tsLight.z);
double NdotV = Math.Max(0, _tsView.z);
double gamma = (_tsView.x * _tsLight.x + _tsView.y * _tsLight.y)
/ Math.Max(1e-20, Math.Sqrt(1.0 - NdotV * NdotV) * Math.Sqrt(1.0 - NdotL * NdotL));
double rough_sq = sigma * sigma;
double A = 1.0 - 0.5 * (rough_sq / (rough_sq + 0.57)); // You can replace 0.33 by 0.57 to simulate the missing inter-reflection term, as specified in footnote of page 22 of the 1992 paper
double B = 0.45 * (rough_sq / (rough_sq + 0.09));
// Original formulation
// float angle_vn = acos( NdotV );
// float angle_ln = acos( NdotL );
// float alpha = max( angle_vn, angle_ln );
// float beta = min( angle_vn, angle_ln );
// float C = sin(alpha) * tan(beta);
// Optimized formulation (without tangents, arccos or sines)
double cos_alpha = NdotV < NdotL ? NdotV : NdotL;
double cos_beta = NdotV < NdotL ? NdotL : NdotV;
double sin_alpha = Math.Sqrt(1.0 - cos_alpha * cos_alpha);
double sin_beta = Math.Sqrt(1.0 - cos_beta * cos_beta);
double C = sin_alpha * sin_beta / Math.Max(1e-20, cos_beta);
double res = A + B * Math.Max(0.0, gamma) * C;
res /= Math.PI;
// Remember we must include the N.L term!
res *= NdotL;
// Cosine-weighted hemisphere sampling
_pdf = NdotL / Math.PI;
return res;
}
// Here we use a simple cosine-weighted hemisphere sampling
public void GetSamplingDirection(ref Vector3 _tsView, float _alpha, float _U1, float _U2, ref Vector3 _direction)
{
// Performs uniform sampling of the unit disk.
// Ref: PBRT v3, p. 777.
float r = Mathf.Sqrt(_U1);
float phi = 2.0f * Mathf.PI * _U2;
// Performs cosine-weighted sampling of the hemisphere.
// Ref: PBRT v3, p. 780.
_direction.x = r * Mathf.Cos(phi);
_direction.y = r * Mathf.Sin(phi);
_direction.z = Mathf.Sqrt(1 - _U1); // Project the point from the disk onto the hemisphere.
}
public BadDog.Rendering.AreaLight.LTCLightingModel GetLightingModel()
{
return BadDog.Rendering.AreaLight.LTCLightingModel.OrenNayar;
}
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: cdf80bc060c4e8d4a8665c2b4a01044b
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/BRDF/BRDF_OrenNayar.cs
uploadId: 884030

View File

@@ -0,0 +1,65 @@
using System;
using UnityEngine;
using UnityEngine.Rendering;
namespace BadDog.Rendering.AreaLight.LTC
{
/// <summary>
/// Ward implementation of the BRDF interface
// Formulas come from -> Walter, B. 2005 "Notes on the Ward BRDF" (https://pdfs.semanticscholar.org/330e/59117d7da6c794750730a15f9a178391b9fe.pdf)
// The BRDF though, is the one most proeminently used by the AxF materials and is based on the Geisler-Moroder variation of Ward (http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.169.9908&rep=rep1&type=pdf)
/// </summary>
public struct BRDF_Ward : IBRDF
{
public double Eval(ref Vector3 _tsView, ref Vector3 _tsLight, float _alpha, out double _pdf)
{
if (_tsView.z <= 0)
{
_pdf = 0;
return 0;
}
_alpha = Mathf.Max(0.002f, _alpha);
Vector3 H = (_tsView + _tsLight).normalized;
double NdotL = Math.Max(1e-8, _tsLight.z);
double NdotH = Math.Max(1e-8, H.z);
double LdotH = Math.Max(1e-8, Vector3.Dot(_tsLight, H));
// D (basically a Beckmann distribution + an additional divider for albedo bounding)
double m2 = _alpha * _alpha;
double cosb2 = NdotH * NdotH;
double D = Math.Exp(-(1 - cosb2) / (m2 * cosb2)) // exp( -tan(a)² / m² )
/ (Math.PI * m2 * cosb2 * cosb2); // / (PI * m² * cos(a)^4)
D /= 4.0 * LdotH * LdotH; // Moroder
// fr = F(H) * D(H)
double res = D;
// Remember we must include the N.L term!
res *= NdotL;
// From Walter, eq. 24 we know that pdf(H) = D(H) * (N.H)
_pdf = Math.Abs(D * NdotH);
return res;
}
public void GetSamplingDirection(ref Vector3 _tsView, float _alpha, float _U1, float _U2, ref Vector3 _direction)
{
// Ward NDF sampling (eqs. 6 & 7 from above paper)
float tanTheta = _alpha * Mathf.Sqrt(-Mathf.Log(Mathf.Max(1e-6f, _U1)));
float phi = _U2 * 2.0f * Mathf.PI;
float cosTheta = 1.0f / Mathf.Sqrt(1 + tanTheta * tanTheta);
float sinTheta = Mathf.Sqrt(1 - cosTheta * cosTheta);
Vector3 H = new Vector3(sinTheta * Mathf.Cos(phi), sinTheta * Mathf.Sin(phi), cosTheta);
_direction = 2.0f * Vector3.Dot(H, _tsView) * H - _tsView; // Mirror view direction
}
public BadDog.Rendering.AreaLight.LTCLightingModel GetLightingModel()
{
return BadDog.Rendering.AreaLight.LTCLightingModel.Ward;
}
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 70fe8e92f23e84f49aabe7648f8e96f3
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/BRDF/BRDF_Ward.cs
uploadId: 884030

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: af77b71311e87fa4cab06868233eb576
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 96d291f160288ea468dde951087fa54e
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/Generated/LtcData.BRDF_Charlie.cs
uploadId: 884030

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: c435ba0443ef98a4e9e433deafc998ee
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/Generated/LtcData.BRDF_CookTorrance.cs
uploadId: 884030

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: eb7356e92da7a6f45945e956f9059704
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/Generated/LtcData.BRDF_Disney.cs
uploadId: 884030

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 5c69fea42cc6cc8408effc5faaa54d9a
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/Generated/LtcData.BRDF_GGX.cs
uploadId: 884030

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 88b3449d431767348a2539b2d5060742
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/Generated/LtcData.BRDF_KajiyaKaySpecular.cs
uploadId: 884030

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 5b69d82badd3dc24bb73acfdf1aa56a1
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/Generated/LtcData.BRDF_Marschner.cs
uploadId: 884030

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 0119dd4c2903cc34f9ed4e249d71c6f7
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/Generated/LtcData.BRDF_OrenNayar.cs
uploadId: 884030

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 217af18bde2ac0645bd0f017a4e3cfe7
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/Generated/LtcData.BRDF_Ward.cs
uploadId: 884030

View File

@@ -0,0 +1,135 @@
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using BadDog.Rendering.AreaLight.LTC;
namespace BadDog.Rendering.AreaLight
{
[GenerateHLSL]
public enum LTCLightingModel
{
// Lit, Stack-Lit and Fabric/Silk
GGX,
DisneyDiffuse,
// Fabric/CottonWool shader
Charlie,
// FabricLambert, (Isotropic)
// Hair
KajiyaKaySpecular,
// KajiyaKayDiffuse, (Isotropic)
Marschner, // TODO
// Other
CookTorrance,
Ward,
OrenNayar,
Count
}
public partial class LTCAreaLight
{
public static IBRDF GetBRDFInterface(LTCLightingModel model)
{
switch (model)
{
case LTCLightingModel.GGX:
return new BRDF_GGX();
case LTCLightingModel.DisneyDiffuse:
return new BRDF_Disney();
case LTCLightingModel.Charlie:
return new BRDF_Charlie();
case LTCLightingModel.KajiyaKaySpecular:
return new BRDF_KajiyaKaySpecular();
case LTCLightingModel.Marschner:
return new BRDF_Marschner();
case LTCLightingModel.CookTorrance:
return new BRDF_CookTorrance();
case LTCLightingModel.Ward:
return new BRDF_Ward();
case LTCLightingModel.OrenNayar:
return new BRDF_OrenNayar();
}
return new BRDF_GGX();
}
static LTCAreaLight s_Instance;
public static LTCAreaLight instance
{
get
{
if (s_Instance == null)
s_Instance = new LTCAreaLight();
return s_Instance;
}
}
int m_refCounting;
// For area lighting - We pack all texture inside a texture array to reduce the number of resource required
Texture2DArray m_LtcData; // 0: m_LtcGGXMatrix - RGBA, 1: m_LtcDisneyDiffuseMatrix - RGBA
public const int k_LtcLUTResolution = 64;
LTCAreaLight()
{
m_refCounting = 0;
}
public void Build()
{
Debug.Assert(m_refCounting >= 0);
if (m_refCounting == 0)
{
m_LtcData = new Texture2DArray(k_LtcLUTResolution, k_LtcLUTResolution, (int)LTCLightingModel.Count, GraphicsFormat.R16G16B16A16_SFloat, TextureCreationFlags.None)
{
hideFlags = HideFlags.HideAndDontSave,
wrapMode = TextureWrapMode.Clamp,
filterMode = FilterMode.Bilinear,
name = CoreUtils.GetTextureAutoName(k_LtcLUTResolution, k_LtcLUTResolution, GraphicsFormat.R16G16B16A16_SFloat, depth: (int)LTCLightingModel.Count, dim: TextureDimension.Tex2DArray, name: "LTC_LUT")
};
m_LtcData.SetPixelData(s_LtcMatrixData_BRDF_GGX, 0, (int)LTCLightingModel.GGX);
m_LtcData.SetPixelData(s_LtcMatrixData_BRDF_Disney, 0, (int)LTCLightingModel.DisneyDiffuse);
m_LtcData.SetPixelData(s_LtcMatrixData_BRDF_Charlie, 0, (int)LTCLightingModel.Charlie);
m_LtcData.SetPixelData(s_LtcMatrixData_BRDF_KajiyaKaySpecular, 0, (int)LTCLightingModel.KajiyaKaySpecular);
// TODO: Generate the Marschner LCT Table
m_LtcData.SetPixelData(s_LtcMatrixData_BRDF_CookTorrance, 0, (int)LTCLightingModel.CookTorrance);
m_LtcData.SetPixelData(s_LtcMatrixData_BRDF_Ward, 0, (int)LTCLightingModel.Ward);
m_LtcData.SetPixelData(s_LtcMatrixData_BRDF_OrenNayar, 0, (int)LTCLightingModel.OrenNayar);
m_LtcData.Apply();
}
m_refCounting++;
}
public void Cleanup()
{
m_refCounting--;
if (m_refCounting == 0)
{
CoreUtils.Destroy(m_LtcData);
}
Debug.Assert(m_refCounting >= 0);
}
public void Bind(CommandBuffer cmd)
{
cmd.SetGlobalTexture("_LtcData", m_LtcData);
}
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: aea83b49b088a6047859da87f9277680
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/LTCAreaLight/LTCAreaLight.cs
uploadId: 884030

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a19f6e467644af244b2d0559127ea6cf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,168 @@
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
namespace BadDog.Rendering.AreaLight
{
public class PreIntegratedFGD
{
[GenerateHLSL]
public enum FGDTexture
{
Resolution = 64
}
static PreIntegratedFGD s_Instance;
public static PreIntegratedFGD instance
{
get
{
if (s_Instance == null)
{
s_Instance = new PreIntegratedFGD();
}
return s_Instance;
}
}
public enum FGDIndex
{
FGD_GGXAndDisneyDiffuse = 0,
FGD_CharlieAndFabricLambert = 1,
FGD_Marschner = 2,
Count = 3
}
bool[] m_isInit = new bool[(int)FGDIndex.Count];
int[] m_refCounting = new int[(int)FGDIndex.Count];
Material[] m_PreIntegratedFGDMaterial = new Material[(int)FGDIndex.Count];
RenderTexture[] m_PreIntegratedFGD = new RenderTexture[(int)FGDIndex.Count];
Shader[] m_CustomShaders = new Shader[(int)FGDIndex.Count];
PreIntegratedFGD()
{
for (int i = 0; i < (int)FGDIndex.Count; ++i)
{
m_isInit[i] = false;
m_refCounting[i] = 0;
m_CustomShaders[i] = null;
}
}
/// <summary>
/// Set custom shader for a specific FGD index. If null, will fallback to Shader.Find()
/// </summary>
public void SetShader(FGDIndex index, Shader shader)
{
if (index >= FGDIndex.Count)
{
Debug.LogError($"Invalid FGD index: {index}");
return;
}
m_CustomShaders[(int)index] = shader;
}
private Shader GetShaderForIndex(FGDIndex index)
{
// Use custom shader if set, otherwise fallback to Shader.Find()
Shader customShader = m_CustomShaders[(int)index];
if (customShader != null)
{
return customShader;
}
switch (index)
{
case FGDIndex.FGD_GGXAndDisneyDiffuse: return Shader.Find("Hidden/BadDog/URP/PreIntegratedFGD_GGXDisneyDiffuse");
case FGDIndex.FGD_CharlieAndFabricLambert: return Shader.Find("Hidden/BadDog/URP/PreIntegratedFGD_CharlieFabricLambert");
case FGDIndex.FGD_Marschner: return Shader.Find("Hidden/BadDog/URP/PreIntegratedFGD_Marschner");
default: Debug.LogError($"Unable to get shader for index: {index}."); break;
}
return null;
}
public void Build(FGDIndex index)
{
Debug.Assert(index != FGDIndex.Count);
Debug.Assert(m_refCounting[(int)index] >= 0);
if (m_refCounting[(int)index] == 0)
{
Shader pixelShader = GetShaderForIndex(index);
int res = (int)FGDTexture.Resolution;
m_PreIntegratedFGDMaterial[(int)index] = CoreUtils.CreateEngineMaterial(pixelShader);
m_PreIntegratedFGD[(int)index] = new RenderTexture(res, res, 0, GraphicsFormat.A2B10G10R10_UNormPack32)
{
hideFlags = HideFlags.HideAndDontSave,
filterMode = FilterMode.Bilinear,
wrapMode = TextureWrapMode.Clamp,
name = CoreUtils.GetRenderTargetAutoName(res, res, 1, GraphicsFormat.A2B10G10R10_UNormPack32, $"preIntegrated{index}")
};
m_PreIntegratedFGD[(int)index].Create();
m_isInit[(int)index] = false;
}
m_refCounting[(int)index]++;
}
public void RenderInit(FGDIndex index, CommandBuffer cmd)
{
if (m_isInit[(int)index] && m_PreIntegratedFGD[(int)index].IsCreated())
{
return;
}
// If we are in wireframe mode, the drawfullscreen will not work as expected, but we don't need the LUT anyway
// So create the texture to avoid errors, it will be initialized by the first render without wireframe
if (GL.wireframe)
{
m_PreIntegratedFGD[(int)index].Create();
return;
}
CoreUtils.DrawFullScreen(cmd, m_PreIntegratedFGDMaterial[(int)index], new RenderTargetIdentifier(m_PreIntegratedFGD[(int)index]));
m_isInit[(int)index] = true;
}
public void Cleanup(FGDIndex index)
{
m_refCounting[(int)index]--;
if (m_refCounting[(int)index] == 0)
{
CoreUtils.Destroy(m_PreIntegratedFGDMaterial[(int)index]);
CoreUtils.Destroy(m_PreIntegratedFGD[(int)index]);
m_isInit[(int)index] = false;
}
Debug.Assert(m_refCounting[(int)index] >= 0);
}
public void Bind(CommandBuffer cmd, FGDIndex index)
{
switch (index)
{
case FGDIndex.FGD_GGXAndDisneyDiffuse:
cmd.SetGlobalTexture(PreIntegratedFGDShaderIDs._PreIntegratedFGD_GGXDisneyDiffuse, m_PreIntegratedFGD[(int)index]);
break;
case FGDIndex.FGD_CharlieAndFabricLambert:
cmd.SetGlobalTexture(PreIntegratedFGDShaderIDs._PreIntegratedFGD_CharlieAndFabric, m_PreIntegratedFGD[(int)index]);
break;
case FGDIndex.FGD_Marschner:
cmd.SetGlobalTexture(PreIntegratedFGDShaderIDs._PreIntegratedFGD_Marschner, m_PreIntegratedFGD[(int)index]);
break;
default:
break;
}
}
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 595ce7af30b78214f9930f6a78f1b917
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/PreIntegratedFGD/PreIntegratedFGD.cs
uploadId: 884030

View File

@@ -0,0 +1,16 @@
using UnityEngine;
namespace BadDog.Rendering.AreaLight
{
/// <summary>
/// Shader property IDs for Pre-Integrated FGD (Fresnel, Geometric, Diffuse)
/// </summary>
public static class PreIntegratedFGDShaderIDs
{
// Pre-integrated FGD texture IDs
public static readonly int _PreIntegratedFGD_GGXDisneyDiffuse = Shader.PropertyToID("_PreIntegratedFGD_GGXDisneyDiffuse");
public static readonly int _PreIntegratedFGD_CharlieAndFabric = Shader.PropertyToID("_PreIntegratedFGD_CharlieAndFabric");
public static readonly int _PreIntegratedFGD_Marschner = Shader.PropertyToID("_PreIntegratedFGD_Marschner");
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 71b06b7ce73f870438526fc0d026ba8c
AssetOrigin:
serializedVersion: 1
productId: 346790
packageName: Realtime Area Light for URP
packageVersion: 1.3.0
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Material/PreIntegratedFGD/PreIntegratedFGDShaderIDs.cs
uploadId: 884030