diff --git a/package/Shaders/Common/Glints/Glints2023.hlsli b/package/Shaders/Common/Glints/Glints2023.hlsli
new file mode 100644
index 000000000..0293cfc09
--- /dev/null
+++ b/package/Shaders/Common/Glints/Glints2023.hlsli
@@ -0,0 +1,523 @@
+Texture2D<float4> _Glint2023NoiseMap : register(t28);
+
+//=======================================================================================
+// TOOLS
+//=======================================================================================
+float erfinv(float x)
+{
+	float w, p;
+	w = -log((1.0 - x) * (1.0 + x));
+	if (w < 5.000000) {
+		w = w - 2.500000;
+		p = 2.81022636e-08;
+		p = 3.43273939e-07 + p * w;
+		p = -3.5233877e-06 + p * w;
+		p = -4.39150654e-06 + p * w;
+		p = 0.00021858087 + p * w;
+		p = -0.00125372503 + p * w;
+		p = -0.00417768164 + p * w;
+		p = 0.246640727 + p * w;
+		p = 1.50140941 + p * w;
+	} else {
+		w = sqrt(w) - 3.000000;
+		p = -0.000200214257;
+		p = 0.000100950558 + p * w;
+		p = 0.00134934322 + p * w;
+		p = -0.00367342844 + p * w;
+		p = 0.00573950773 + p * w;
+		p = -0.0076224613 + p * w;
+		p = 0.00943887047 + p * w;
+		p = 1.00167406 + p * w;
+		p = 2.83297682 + p * w;
+	}
+	return p * x;
+}
+
+float3 sampleNormalDistribution(float3 u, float mu, float sigma)
+{
+	//return mu + sigma * (sqrt(-2.0 * log(u.x))* cos(2.0 * pi * u.y));
+	float x0 = sigma * 1.414213f * erfinv(2.0 * u.x - 1.0) + mu;
+	float x1 = sigma * 1.414213f * erfinv(2.0 * u.y - 1.0) + mu;
+	float x2 = sigma * 1.414213f * erfinv(2.0 * u.z - 1.0) + mu;
+	return float3(x0, x1, x2);
+}
+
+float4 sampleNormalDistribution(float4 u, float mu, float sigma)
+{
+	//return mu + sigma * (sqrt(-2.0 * log(u.x))* cos(2.0 * pi * u.y));
+	float x0 = sigma * 1.414213f * erfinv(2.0 * u.x - 1.0) + mu;
+	float x1 = sigma * 1.414213f * erfinv(2.0 * u.y - 1.0) + mu;
+	float x2 = sigma * 1.414213f * erfinv(2.0 * u.z - 1.0) + mu;
+	float x3 = sigma * 1.414213f * erfinv(2.0 * u.w - 1.0) + mu;
+	return float4(x0, x1, x2, x3);
+}
+
+float3 pcg3dFloat(uint3 v)
+{
+	v = v * 1664525u + 1013904223u;
+
+	v.x += v.y * v.z;
+	v.y += v.z * v.x;
+	v.z += v.x * v.y;
+
+	v ^= v >> 16u;
+
+	v.x += v.y * v.z;
+	v.y += v.z * v.x;
+	v.z += v.x * v.y;
+
+	return v * (1.0 / 4294967296.0);
+}
+
+float HashWithoutSine13(float3 p3)
+{
+	p3 = frac(p3 * .1031);
+	p3 += dot(p3, p3.yzx + 33.33);
+	return frac((p3.x + p3.y) * p3.z);
+}
+
+float2x2 Inverse(float2x2 A)
+{
+	return float2x2(A[1][1], -A[0][1], -A[1][0], A[0][0]) / determinant(A);
+}
+
+void GetGradientEllipse(float2 duvdx, float2 duvdy, out float2 ellipseMajor, out float2 ellipseMinor)
+{
+	float2x2 J = float2x2(duvdx, duvdy);
+	J = Inverse(J);
+	J = mul(J, transpose(J));
+
+	float a = J[0][0];
+	float b = J[0][1];
+	float c = J[1][0];
+	float d = J[1][1];
+
+	float T = a + d;
+	float D = a * d - b * c;
+	float SQ = sqrt(abs(T * T / 3.99999 - D));
+	float L1 = T / 2.0 - SQ;
+	float L2 = T / 2.0 + SQ;
+
+	float2 A0 = float2(L1 - d, c);
+	float2 A1 = float2(L2 - d, c);
+	float r0 = rsqrt(L1);
+	float r1 = rsqrt(L2);
+	ellipseMajor = normalize(A0) * r0;
+	ellipseMinor = normalize(A1) * r1;
+}
+
+float2 RotateUV(float2 uv, float rotation, float2 mid)
+{
+	float2 rel_uv = uv - mid;
+	float2 sincos_rot;
+	sincos(rotation, sincos_rot.y, sincos_rot.x);
+	return float2(
+		sincos_rot.x * rel_uv.x + sincos_rot.y * rel_uv.y + mid.x,
+		sincos_rot.x * rel_uv.y - sincos_rot.y * rel_uv.x + mid.y);
+}
+
+float BilinearLerp(float4 values, float2 valuesLerp)
+{
+	// Values XY = float4(00, 01, 10, 11)
+	float resultX = lerp(values.x, values.z, valuesLerp.x);
+	float resultY = lerp(values.y, values.w, valuesLerp.x);
+	float result = lerp(resultX, resultY, valuesLerp.y);
+	return result;
+}
+
+float4 BilinearLerpParallel4(float4 values00, float4 values01, float4 values10, float4 values11, float4 valuesLerpX, float4 valuesLerpY)
+{
+	// Values XY = float4(00, 01, 10, 11)
+	float4 resultX = lerp(values00, values10, valuesLerpX);
+	float4 resultY = lerp(values01, values11, valuesLerpX);
+	float4 result = lerp(resultX, resultY, valuesLerpY);
+	return result;
+}
+
+float Remap(float s, float a1, float a2, float b1, float b2)
+{
+	return b1 + (s - a1) * (b2 - b1) / (a2 - a1);
+}
+
+float Remap01To(float s, float b1, float b2)
+{
+	return b1 + s * (b2 - b1);
+}
+
+float RemapTo01(float s, float a1, float a2)
+{
+	return (s - a1) / (a2 - a1);
+}
+
+float4 RemapTo01(float4 s, float4 a1, float4 a2)
+{
+	return (s - a1) / (a2 - a1);
+}
+
+float3 GetBarycentricWeights(float2 p, float2 a, float2 b, float2 c)
+{
+	/*float2 v0 = b - a;
+	float2 v1 = c - a;
+	float2 v2 = p - a;
+	float d00 = dot(v0, v0);
+	float d01 = dot(v0, v1);
+	float d11 = dot(v1, v1);
+	float d20 = dot(v2, v0);
+	float d21 = dot(v2, v1);
+	float denom = d00 * d11 - d01 * d01;
+	float v = (d11 * d20 - d01 * d21) / denom;
+	float w = (d00 * d21 - d01 * d20) / denom;
+	float u = 1.0 - v - w;
+	return float3(u, v, w);*/
+
+	float2 v0 = b - a;
+	float2 v1 = c - a;
+	float2 v2 = p - a;
+	float den = v0.x * v1.y - v1.x * v0.y;
+	float rcpDen = rcp(den);
+	float v = (v2.x * v1.y - v1.x * v2.y) * rcpDen;
+	float w = (v0.x * v2.y - v2.x * v0.y) * rcpDen;
+	float u = 1.0f - v - w;
+	return float3(u, v, w);
+}
+
+float4 GetBarycentricWeightsTetrahedron(float3 p, float3 v1, float3 v2, float3 v3, float3 v4)
+{
+	float3 c11 = v1 - v4, c21 = v2 - v4, c31 = v3 - v4, c41 = v4 - p;
+
+	float2 m1 = c31.yz / c31.x;
+	float2 c12 = c11.yz - c11.x * m1, c22 = c21.yz - c21.x * m1, c32 = c41.yz - c41.x * m1;
+
+	float4 uvwk = 0.0.rrrr;
+	float m2 = c22.y / c22.x;
+	uvwk.x = (c32.x * m2 - c32.y) / (c12.y - c12.x * m2);
+	uvwk.y = -(c32.x + c12.x * uvwk.x) / c22.x;
+	uvwk.z = -(c41.x + c21.x * uvwk.y + c11.x * uvwk.x) / c31.x;
+	uvwk.w = 1.0 - uvwk.z - uvwk.y - uvwk.x;
+
+	return uvwk;
+}
+
+void UnpackFloat(float input, out float a, out float b)
+{
+	uint uintInput = asuint(input);
+	a = f16tof32(uintInput >> 16);
+	b = f16tof32(uintInput);
+}
+
+void UnpackFloatParallel4(float4 input, out float4 a, out float4 b)
+{
+	uint4 uintInput = asuint(input);
+	a = f16tof32(uintInput >> 16);
+	b = f16tof32(uintInput);
+}
+
+//=======================================================================================
+// GLINTS TEST NOVEMBER 2022
+//=======================================================================================
+
+struct GlintInput
+{
+	float3 H;
+	float2 uv;
+	float2 duvdx;
+	float2 duvdy;
+
+	float ScreenSpaceScale;
+	float LogMicrofacetDensity;
+	float MicrofacetRoughness;
+	float DensityRandomization;
+};
+
+void CustomRand4Texture(GlintInput params, float2 slope, float2 slopeRandOffset, out float4 outUniform, out float4 outGaussian, out float2 slopeLerp)
+{
+	uint2 size = 512;
+	float2 slope2 = abs(slope) / params.MicrofacetRoughness;
+	slope2 = slope2 + (slopeRandOffset * size);
+	slopeLerp = frac(slope2);
+	uint2 slopeCoord = uint2(floor(slope2)) % size;
+
+	float4 packedRead = _Glint2023NoiseMap[slopeCoord];
+	UnpackFloatParallel4(packedRead, outUniform, outGaussian);
+}
+
+float GenerateAngularBinomialValueForSurfaceCell(float4 randB, float4 randG, float2 slopeLerp, float footprintOneHitProba, float binomialSmoothWidth, float footprintMean, float footprintSTD, float microfacetCount)
+{
+	float4 gating;
+	if (binomialSmoothWidth > 0.0000001)
+		gating = saturate(RemapTo01(randB, footprintOneHitProba + binomialSmoothWidth, footprintOneHitProba - binomialSmoothWidth));
+	else
+		gating = randB < footprintOneHitProba;
+
+	float4 gauss = randG * footprintSTD + footprintMean;
+	gauss = clamp(floor(gauss), 0, microfacetCount);
+	float4 results = gating * (1.0 + gauss);
+	float result = BilinearLerp(results, slopeLerp);
+	return result;
+}
+
+float SampleGlintGridSimplex(GlintInput params, float2 uv, uint gridSeed, float2 slope, float footprintArea, float targetNDF, float gridWeight)
+{
+	// Get surface space glint simplex grid cell
+	const float2x2 gridToSkewedGrid = float2x2(1.0, -0.57735027, 0.0, 1.15470054);
+	float2 skewedCoord = mul(gridToSkewedGrid, uv);
+	int2 baseId = int2(floor(skewedCoord));
+	float3 temp = float3(frac(skewedCoord), 0.0);
+	temp.z = 1.0 - temp.x - temp.y;
+	float s = step(0.0, -temp.z);
+	float s2 = 2.0 * s - 1.0;
+	int2 glint0 = baseId + int2(s, s);
+	int2 glint1 = baseId + int2(s, 1.0 - s);
+	int2 glint2 = baseId + int2(1.0 - s, s);
+	float3 barycentrics = float3(-temp.z * s2, s - temp.y * s2, s - temp.x * s2);
+
+	// Generate per surface cell random numbers
+	float3 rand0 = pcg3dFloat(uint3(glint0 + 2147483648, gridSeed));  // TODO : optimize away manual seeds
+	float3 rand1 = pcg3dFloat(uint3(glint1 + 2147483648, gridSeed));
+	float3 rand2 = pcg3dFloat(uint3(glint2 + 2147483648, gridSeed));
+
+	// Get per surface cell per slope cell random numbers
+	float4 rand0SlopesB, rand1SlopesB, rand2SlopesB, rand0SlopesG, rand1SlopesG, rand2SlopesG;
+	float2 slopeLerp0, slopeLerp1, slopeLerp2;
+	CustomRand4Texture(params, slope, rand0.yz, rand0SlopesB, rand0SlopesG, slopeLerp0);
+	CustomRand4Texture(params, slope, rand1.yz, rand1SlopesB, rand1SlopesG, slopeLerp1);
+	CustomRand4Texture(params, slope, rand2.yz, rand2SlopesB, rand2SlopesG, slopeLerp2);
+
+	// Compute microfacet count with randomization
+	float3 logDensityRand = clamp(sampleNormalDistribution(float3(rand0.x, rand1.x, rand2.x), params.LogMicrofacetDensity.r, params.DensityRandomization), 0.0, 50.0);  // TODO : optimize sampleNormalDist
+	float3 microfacetCount = max(1e-8, footprintArea.rrr * exp(logDensityRand));
+	float3 microfacetCountBlended = microfacetCount * gridWeight;
+
+	// Compute binomial properties
+	float hitProba = params.MicrofacetRoughness * targetNDF;                                           // probability of hitting desired half vector in NDF distribution
+	float3 footprintOneHitProba = (1.0 - pow(abs(1.0 - hitProba.rrr), microfacetCountBlended));        // probability of hitting at least one microfacet in footprint
+	float3 footprintMean = (microfacetCountBlended - 1.0) * hitProba.rrr;                              // Expected value of number of hits in the footprint given already one hit
+	float3 footprintSTD = sqrt((microfacetCountBlended - 1.0) * hitProba.rrr * (1.0 - hitProba.rrr));  // Standard deviation of number of hits in the footprint given already one hit
+	float3 binomialSmoothWidth = 0.1 * clamp(footprintOneHitProba * 10, 0.0, 1.0) * clamp((1.0 - footprintOneHitProba) * 10, 0.0, 1.0);
+
+	// Generate numbers of reflecting microfacets
+	float result0, result1, result2;
+	result0 = GenerateAngularBinomialValueForSurfaceCell(rand0SlopesB, rand0SlopesG, slopeLerp0, footprintOneHitProba.x, binomialSmoothWidth.x, footprintMean.x, footprintSTD.x, microfacetCountBlended.x);
+	result1 = GenerateAngularBinomialValueForSurfaceCell(rand1SlopesB, rand1SlopesG, slopeLerp1, footprintOneHitProba.y, binomialSmoothWidth.y, footprintMean.y, footprintSTD.y, microfacetCountBlended.y);
+	result2 = GenerateAngularBinomialValueForSurfaceCell(rand2SlopesB, rand2SlopesG, slopeLerp2, footprintOneHitProba.z, binomialSmoothWidth.z, footprintMean.z, footprintSTD.z, microfacetCountBlended.z);
+
+	// Interpolate result for glint grid cell
+	float3 results = float3(result0, result1, result2) / microfacetCount.xyz;
+	float result = dot(results, barycentrics);
+	return result;
+}
+
+void GetAnisoCorrectingGridTetrahedron(bool centerSpecialCase, inout float thetaBinLerp, float ratioLerp, float lodLerp, out float3 p0, out float3 p1, out float3 p2, out float3 p3)
+{
+	[branch] if (centerSpecialCase == true)  // SPECIAL CASE (no anisotropy, center of blending pattern, different triangulation)
+	{
+		float3 a = float3(0, 1, 0);
+		float3 b = float3(0, 0, 0);
+		float3 c = float3(1, 1, 0);
+		float3 d = float3(0, 1, 1);
+		float3 e = float3(0, 0, 1);
+		float3 f = float3(1, 1, 1);
+		[branch] if (lodLerp > 1.0 - ratioLerp)  // Upper pyramid
+		{
+			[branch] if (RemapTo01(lodLerp, 1.0 - ratioLerp, 1.0) > thetaBinLerp)  // Left-up tetrahedron (a, e, d, f)
+			{
+				p0 = a;
+				p1 = e;
+				p2 = d;
+				p3 = f;
+			}
+			else  // Right-down tetrahedron (f, e, c, a)
+			{
+				p0 = f;
+				p1 = e;
+				p2 = c;
+				p3 = a;
+			}
+		}
+		else  // Lower tetrahedron (b, a, c, e)
+		{
+			p0 = b;
+			p1 = a;
+			p2 = c;
+			p3 = e;
+		}
+	}
+	else  // NORMAL CASE
+	{
+		float3 a = float3(0, 1, 0);
+		float3 b = float3(0, 0, 0);
+		float3 c = float3(0.5, 1, 0);
+		float3 d = float3(1, 0, 0);
+		float3 e = float3(1, 1, 0);
+		float3 f = float3(0, 1, 1);
+		float3 g = float3(0, 0, 1);
+		float3 h = float3(0.5, 1, 1);
+		float3 i = float3(1, 0, 1);
+		float3 j = float3(1, 1, 1);
+		[branch] if (thetaBinLerp < 0.5 && thetaBinLerp * 2.0 < ratioLerp)  // Prism A
+		{
+			[branch] if (lodLerp > 1.0 - ratioLerp)  // Upper pyramid
+			{
+				[branch] if (RemapTo01(lodLerp, 1.0 - ratioLerp, 1.0) > RemapTo01(thetaBinLerp * 2.0, 0.0, ratioLerp))  // Left-up tetrahedron (a, f, h, g)
+				{
+					p0 = a;
+					p1 = f;
+					p2 = h;
+					p3 = g;
+				}
+				else  // Right-down tetrahedron (c, a, h, g)
+				{
+					p0 = c;
+					p1 = a;
+					p2 = h;
+					p3 = g;
+				}
+			}
+			else  // Lower tetrahedron (b, a, c, g)
+			{
+				p0 = b;
+				p1 = a;
+				p2 = c;
+				p3 = g;
+			}
+		}
+		else if (1.0 - ((thetaBinLerp - 0.5) * 2.0) > ratioLerp)  // Prism B
+		{
+			[branch] if (lodLerp < 1.0 - ratioLerp)  // Lower pyramid
+			{
+				[branch] if (RemapTo01(lodLerp, 0.0, 1.0 - ratioLerp) > RemapTo01(thetaBinLerp, 0.5 - (1.0 - ratioLerp) * 0.5, 0.5 + (1.0 - ratioLerp) * 0.5))  // Left-up tetrahedron (b, g, i, c)
+				{
+					p0 = b;
+					p1 = g;
+					p2 = i;
+					p3 = c;
+				}
+				else  // Right-down tetrahedron (d, b, c, i)
+				{
+					p0 = d;
+					p1 = b;
+					p2 = c;
+					p3 = i;
+				}
+			}
+			else  // Upper tetrahedron (c, g, h, i)
+			{
+				p0 = c;
+				p1 = g;
+				p2 = h;
+				p3 = i;
+			}
+		}
+		else  // Prism C
+		{
+			[branch] if (lodLerp > 1.0 - ratioLerp)  // Upper pyramid
+			{
+				[branch] if (RemapTo01(lodLerp, 1.0 - ratioLerp, 1.0) > RemapTo01((thetaBinLerp - 0.5) * 2.0, 1.0 - ratioLerp, 1.0))  // Left-up tetrahedron (c, j, h, i)
+				{
+					p0 = c;
+					p1 = j;
+					p2 = h;
+					p3 = i;
+				}
+				else  // Right-down tetrahedron (e, i, c, j)
+				{
+					p0 = e;
+					p1 = i;
+					p2 = c;
+					p3 = j;
+				}
+			}
+			else  // Lower tetrahedron (d, e, c, i)
+			{
+				p0 = d;
+				p1 = e;
+				p2 = c;
+				p3 = i;
+			}
+		}
+	}
+
+	return;
+}
+
+float4 SampleGlints2023NDF(GlintInput params, float targetNDF, float maxNDF)
+{
+	// ACCURATE PIXEL FOOTPRINT ELLIPSE
+	float2 ellipseMajor, ellipseMinor;
+	GetGradientEllipse(params.duvdx, params.duvdy, ellipseMajor, ellipseMinor);
+	float ellipseRatio = length(ellipseMajor) / (length(ellipseMinor) + 1e-8);
+
+	// SHARED GLINT NDF VALUES
+	float halfScreenSpaceScaler = params.ScreenSpaceScale * 0.5;
+	float footprintArea = length(ellipseMajor) * halfScreenSpaceScaler * length(ellipseMinor) * halfScreenSpaceScaler * 4.0;
+	float2 slope = params.H.xy;  // Orthogrtaphic slope projected grid
+	float rescaledTargetNDF = targetNDF / maxNDF;
+
+	// MANUAL LOD COMPENSATION
+	float lod = log2(length(ellipseMinor) * halfScreenSpaceScaler);
+	float lod0 = (int)lod;  //lod >= 0.0 ? (int)(lod) : (int)(lod - 1.0);
+	float lod1 = lod0 + 1;
+	float divLod0 = pow(2.0, lod0);
+	float divLod1 = pow(2.0, lod1);
+	float lodLerp = frac(lod);
+	float footprintAreaLOD0 = exp2(2.0 * lod0);
+	float footprintAreaLOD1 = exp2(2.0 * lod1);
+
+	// MANUAL ANISOTROPY RATIO COMPENSATION
+	float ratio0 = max(pow(2.0, (int)log2(ellipseRatio)), 1.0);
+	float ratio1 = ratio0 * 2.0;
+	float ratioLerp = saturate(Remap(ellipseRatio, ratio0, ratio1, 0.0, 1.0));
+
+	// MANUAL ANISOTROPY ROTATION COMPENSATION
+	float2 v1 = float2(0.0, 1.0);
+	float2 v2 = normalize(ellipseMajor);
+	float theta = atan2(v1.x * v2.y - v1.y * v2.x, v1.x * v2.x + v1.y * v2.y);
+	float thetaGrid = 1.57079632679 / max(ratio0, 2.0);
+	float thetaBin = (int)(theta / thetaGrid) * thetaGrid;
+	thetaBin = thetaBin + (thetaGrid / 2.0);
+	float thetaBin0 = theta < thetaBin ? thetaBin - thetaGrid / 2.0 : thetaBin;
+	float thetaBinH = thetaBin0 + thetaGrid / 4.0;
+	float thetaBin1 = thetaBin0 + thetaGrid / 2.0;
+	float thetaBinLerp = Remap(theta, thetaBin0, thetaBin1, 0.0, 1.0);
+	thetaBin0 = thetaBin0 <= 0.0 ? 3.1415926535 + thetaBin0 : thetaBin0;
+
+	// TETRAHEDRONIZATION OF ROTATION + RATIO + LOD GRID
+	bool centerSpecialCase = (ratio0.x == 1.0);
+	float2 divLods = float2(divLod0, divLod1);
+	float2 footprintAreas = float2(footprintAreaLOD0, footprintAreaLOD1);
+	float2 ratios = float2(ratio0, ratio1);
+	float4 thetaBins = float4(thetaBin0, thetaBinH, thetaBin1, 0.0);  // added 0.0 for center singularity case
+	float3 tetraA, tetraB, tetraC, tetraD;
+	GetAnisoCorrectingGridTetrahedron(centerSpecialCase, thetaBinLerp, ratioLerp, lodLerp, tetraA, tetraB, tetraC, tetraD);
+	if (centerSpecialCase == true)  // Account for center singularity in barycentric computation
+		thetaBinLerp = Remap01To(thetaBinLerp, 0.0, ratioLerp);
+	float4 tetraBarycentricWeights = GetBarycentricWeightsTetrahedron(float3(thetaBinLerp, ratioLerp, lodLerp), tetraA, tetraB, tetraC, tetraD);  // Compute barycentric coordinates within chosen tetrahedron
+
+	// PREPARE NEEDED ROTATIONS
+	tetraA.x *= 2;
+	tetraB.x *= 2;
+	tetraC.x *= 2;
+	tetraD.x *= 2;
+	if (centerSpecialCase == true)  // Account for center singularity (if center vertex => no rotation)
+	{
+		tetraA.x = (tetraA.y == 0) ? 3 : tetraA.x;
+		tetraB.x = (tetraB.y == 0) ? 3 : tetraB.x;
+		tetraC.x = (tetraC.y == 0) ? 3 : tetraC.x;
+		tetraD.x = (tetraD.y == 0) ? 3 : tetraD.x;
+	}
+	float2 uvRotA = RotateUV(params.uv, thetaBins[tetraA.x], 0.0.rr);
+	float2 uvRotB = RotateUV(params.uv, thetaBins[tetraB.x], 0.0.rr);
+	float2 uvRotC = RotateUV(params.uv, thetaBins[tetraC.x], 0.0.rr);
+	float2 uvRotD = RotateUV(params.uv, thetaBins[tetraD.x], 0.0.rr);
+
+	// SAMPLE GLINT GRIDS
+	uint gridSeedA = HashWithoutSine13(float3(log2(divLods[tetraA.z]), fmod(thetaBins[tetraA.x], 6.28318530718), ratios[tetraA.y])) * 4294967296.0;
+	uint gridSeedB = HashWithoutSine13(float3(log2(divLods[tetraB.z]), fmod(thetaBins[tetraB.x], 6.28318530718), ratios[tetraB.y])) * 4294967296.0;
+	uint gridSeedC = HashWithoutSine13(float3(log2(divLods[tetraC.z]), fmod(thetaBins[tetraC.x], 6.28318530718), ratios[tetraC.y])) * 4294967296.0;
+	uint gridSeedD = HashWithoutSine13(float3(log2(divLods[tetraD.z]), fmod(thetaBins[tetraD.x], 6.28318530718), ratios[tetraD.y])) * 4294967296.0;
+	float sampleA = SampleGlintGridSimplex(params, uvRotA / divLods[tetraA.z] / float2(1.0, ratios[tetraA.y]), gridSeedA, slope, ratios[tetraA.y] * footprintAreas[tetraA.z], rescaledTargetNDF, tetraBarycentricWeights.x);
+	float sampleB = SampleGlintGridSimplex(params, uvRotB / divLods[tetraB.z] / float2(1.0, ratios[tetraB.y]), gridSeedB, slope, ratios[tetraB.y] * footprintAreas[tetraB.z], rescaledTargetNDF, tetraBarycentricWeights.y);
+	float sampleC = SampleGlintGridSimplex(params, uvRotC / divLods[tetraC.z] / float2(1.0, ratios[tetraC.y]), gridSeedC, slope, ratios[tetraC.y] * footprintAreas[tetraC.z], rescaledTargetNDF, tetraBarycentricWeights.z);
+	float sampleD = SampleGlintGridSimplex(params, uvRotD / divLods[tetraD.z] / float2(1.0, ratios[tetraD.y]), gridSeedD, slope, ratios[tetraD.y] * footprintAreas[tetraD.z], rescaledTargetNDF, tetraBarycentricWeights.w);
+	return min((sampleA + sampleB + sampleC + sampleD) * (1.0 / params.MicrofacetRoughness), 20) * maxNDF;  // somewhat brute force way of prevent glazing angle extremities
+}
\ No newline at end of file
diff --git a/package/Shaders/Common/Glints/noisegen.cs.hlsl b/package/Shaders/Common/Glints/noisegen.cs.hlsl
new file mode 100644
index 000000000..31f9ad940
--- /dev/null
+++ b/package/Shaders/Common/Glints/noisegen.cs.hlsl
@@ -0,0 +1,130 @@
+RWTexture2D<float4> tex_noise : register(u0);
+
+static const float GAUSSIAN_AVG = 0.0;
+static const float GAUSSIAN_STD = 1.0;
+
+uint CoordToFlatId(uint2 coord, uint width)
+{
+	return coord.y * width + coord.x;
+}
+
+float PackFloats(float a, float b)
+{
+	uint a16 = f32tof16(a);
+	uint b16 = f32tof16(b);
+	uint abPacked = (a16 << 16) | b16;
+	return asfloat(abPacked);
+}
+
+uint WangHash(uint seed)
+{
+	seed = (seed ^ 61) ^ (seed >> 16);
+	seed *= 9;
+	seed = seed ^ (seed >> 4);
+	seed *= 0x27d4eb2d;
+	seed = seed ^ (seed >> 15);
+	return seed;
+}
+
+void RandXorshift(inout uint rngState)
+{
+	// Xorshift algorithm from George Marsaglia's paper
+	rngState ^= (rngState << 13);
+	rngState ^= (rngState >> 17);
+	rngState ^= (rngState << 5);
+}
+
+float RandXorshiftFloat(inout uint rngState)
+{
+	RandXorshift(rngState);
+	float res = float(rngState) * (1.0 / 4294967296.0);
+	return res;
+}
+
+float Erf(float x)
+{
+	// Save the sign of x
+	int sign = 1;
+	if (x < 0)
+		sign = -1;
+	x = abs(x);
+
+	// A&S formula 7.1.26
+	float t = 1.0 / (1.0 + 0.3275911 * x);
+	float y = 1.0 - (((((1.061405429 * t + -1.453152027) * t) + 1.421413741) * t + -0.284496736) * t + 0.254829592) * t * exp(-x * x);
+
+	return sign * y;
+}
+
+float ErfInv(float x)
+{
+	float w, p;
+	w = -log((1.0f - x) * (1.0f + x));
+	if (w < 5.000000f) {
+		w = w - 2.500000;
+		p = 2.81022636e-08;
+		p = 3.43273939e-07 + p * w;
+		p = -3.5233877e-06 + p * w;
+		p = -4.39150654e-06 + p * w;
+		p = 0.00021858087 + p * w;
+		p = -0.00125372503 + p * w;
+		p = -0.00417768164 + p * w;
+		p = 0.246640727 + p * w;
+		p = 1.50140941 + p * w;
+	} else {
+		w = sqrt(w) - 3.000000;
+		p = -0.000200214257;
+		p = 0.000100950558 + p * w;
+		p = 0.00134934322 + p * w;
+		p = -0.00367342844 + p * w;
+		p = 0.00573950773 + p * w;
+		p = -0.0076224613 + p * w;
+		p = 0.00943887047 + p * w;
+		p = 1.00167406 + p * w;
+		p = 2.83297682 + p * w;
+	}
+	return p * x;
+}
+
+float CDF(float x, float mu, float sigma)
+{
+	float U = 0.5 * (1 + Erf((x - mu) / (sigma * sqrt(2.0))));
+	return U;
+}
+
+float InvCDF(float U, float mu, float sigma)
+{
+	float x = sigma * sqrt(2.0) * ErfInv(2.0 * U - 1.0) + mu;
+	return x;
+}
+
+[numthreads(32, 32, 1)] void main(const uint2 tid
+								  : SV_DispatchThreadID) {
+	uint2 size;
+	tex_noise.GetDimensions(size.x, size.y);
+	int offset = size.x * size.y * 0x69420;
+
+	// Generate random numbers for this cell and the next ones in X and Y
+	int2 pixelCoord00 = tid.xy;
+	uint rngState00 = WangHash(CoordToFlatId(pixelCoord00 * 123, size.x) + offset);
+	float u00 = RandXorshiftFloat(rngState00);
+	float g00 = InvCDF(RandXorshiftFloat(rngState00), GAUSSIAN_AVG, GAUSSIAN_STD);
+
+	int2 pixelCoord01 = (pixelCoord00 + int2(0, 1)) % size;
+	uint rngState01 = WangHash(CoordToFlatId(pixelCoord01 * 123, size.x) + offset);
+	float u01 = RandXorshiftFloat(rngState01);
+	float g01 = InvCDF(RandXorshiftFloat(rngState01), GAUSSIAN_AVG, GAUSSIAN_STD);
+
+	int2 pixelCoord10 = (pixelCoord00 + int2(1, 0)) % size;
+	uint rngState10 = WangHash(CoordToFlatId(pixelCoord10 * 123, size.x) + offset);
+	float u10 = RandXorshiftFloat(rngState10);
+	float g10 = InvCDF(RandXorshiftFloat(rngState10), GAUSSIAN_AVG, GAUSSIAN_STD);
+
+	int2 pixelCoord11 = (pixelCoord00 + int2(1, 1)) % size;
+	uint rngState11 = WangHash(CoordToFlatId(pixelCoord11 * 123, size.x) + offset);
+	float u11 = RandXorshiftFloat(rngState11);
+	float g11 = InvCDF(RandXorshiftFloat(rngState11), GAUSSIAN_AVG, GAUSSIAN_STD);
+
+	// Pack 8 values into 4
+	tex_noise[tid.xy] = float4(PackFloats(u00, g00), PackFloats(u01, g01), PackFloats(u10, g10), PackFloats(u11, g11));
+}
\ No newline at end of file
diff --git a/package/Shaders/Common/PBR.hlsli b/package/Shaders/Common/PBR.hlsli
index b8c1c4264..f52e8295e 100644
--- a/package/Shaders/Common/PBR.hlsli
+++ b/package/Shaders/Common/PBR.hlsli
@@ -8,7 +8,9 @@
 #define TruePBR_InterlayerParallax (1 << 7)
 #define TruePBR_CoatNormal (1 << 8)
 #define TruePBR_Fuzz (1 << 9)
-#define TruePBR_HairMarschner (1 << 9)
+#define TruePBR_HairMarschner (1 << 10)
+#define TruePBR_Glint (1 << 11)
+
 #define TruePBR_LandTile0PBR (1 << 0)
 #define TruePBR_LandTile1PBR (1 << 1)
 #define TruePBR_LandTile2PBR (1 << 2)
@@ -21,9 +23,19 @@
 #define TruePBR_LandTile3HasDisplacement (1 << 9)
 #define TruePBR_LandTile4HasDisplacement (1 << 10)
 #define TruePBR_LandTile5HasDisplacement (1 << 11)
+#define TruePBR_LandTile0HasGlint (1 << 12)
+#define TruePBR_LandTile1HasGlint (1 << 13)
+#define TruePBR_LandTile2HasGlint (1 << 14)
+#define TruePBR_LandTile3HasGlint (1 << 15)
+#define TruePBR_LandTile4HasGlint (1 << 16)
+#define TruePBR_LandTile5HasGlint (1 << 17)
 
 namespace PBR
 {
+#if defined(GLINT)
+#	include "Common/Glints/Glints2023.hlsli"
+#endif
+
 	struct SurfaceProperties
 	{
 		float3 BaseColor;
@@ -39,8 +51,40 @@ namespace PBR
 		float3 CoatF0;
 		float3 FuzzColor;
 		float FuzzWeight;
+		float GlintScreenSpaceScale;
+		float GlintLogMicrofacetDensity;
+		float GlintMicrofacetRoughness;
+		float GlintDensityRandomization;
 	};
 
+	SurfaceProperties InitSurfaceProperties()
+	{
+		SurfaceProperties surfaceProperties;
+
+		surfaceProperties.Roughness = 1;
+		surfaceProperties.Metallic = 0;
+		surfaceProperties.AO = 1;
+		surfaceProperties.F0 = 0.04;
+
+		surfaceProperties.SubsurfaceColor = 0;
+		surfaceProperties.Thickness = 0;
+
+		surfaceProperties.CoatColor = 0;
+		surfaceProperties.CoatStrength = 0;
+		surfaceProperties.CoatRoughness = 0;
+		surfaceProperties.CoatF0 = 0.04;
+
+		surfaceProperties.FuzzColor = 0;
+		surfaceProperties.FuzzWeight = 0;
+
+		surfaceProperties.GlintScreenSpaceScale = 1.5;
+		surfaceProperties.GlintLogMicrofacetDensity = 40.0;
+		surfaceProperties.GlintMicrofacetRoughness = 0.015;
+		surfaceProperties.GlintDensityRandomization = 2.0;
+
+		return surfaceProperties;
+	}
+
 	float3 AdjustDirectionalLightColor(float3 lightColor)
 	{
 		return pbrSettings.DirectionalLightColorMultiplier * sRGB2Lin(lightColor);
@@ -110,6 +154,22 @@ namespace PBR
 		return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);
 	}
 
+#if defined(GLINT)
+	float3 GetSpecularDirectLightMultiplierMicrofacetWithGlint(float roughness, float3 specularColor, float NdotL, float NdotV, float NdotH, float VdotH, GlintInput glintInput, out float3 F)
+	{
+		float D = GetNormalDistributionFunctionGGX(roughness, NdotH);
+		[branch] if (glintInput.LogMicrofacetDensity > 1.1)
+		{
+			float D_max = GetNormalDistributionFunctionGGX(roughness, 1);
+			D = SampleGlints2023NDF(glintInput, D, D_max);
+		}
+		float G = GetVisibilityFunctionSmithJointApprox(roughness, NdotV, NdotL);
+		F = GetFresnelFactorSchlick(specularColor, VdotH);
+
+		return D * G * F;
+	}
+#endif
+
 	float3 GetSpecularDirectLightMultiplierMicrofacet(float roughness, float3 specularColor, float NdotL, float NdotV, float NdotH, float VdotH, out float3 F)
 	{
 		float D = GetNormalDistributionFunctionGGX(roughness, NdotH);
@@ -312,7 +372,8 @@ namespace PBR
 		return color;
 	}
 
-	void GetDirectLightInput(out float3 diffuse, out float3 coatDiffuse, out float3 transmission, out float3 specular, float3 N, float3 coatN, float3 V, float3 coatV, float3 L, float3 coatL, float3 lightColor, float3 coatLightColor, SurfaceProperties surfaceProperties)
+	void GetDirectLightInput(out float3 diffuse, out float3 coatDiffuse, out float3 transmission, out float3 specular, float3 N, float3 coatN, float3 V, float3 coatV, float3 L, float3 coatL, float3 lightColor, float3 coatLightColor, SurfaceProperties surfaceProperties,
+		float3x3 tbnTr, float2 uv)
 	{
 		diffuse = 0;
 		coatDiffuse = 0;
@@ -343,8 +404,24 @@ namespace PBR
 		{
 			diffuse += lightColor * satNdotL;
 
+#if defined(GLINT)
+			GlintInput glintInput;
+			glintInput.H = mul(tbnTr, H);
+			glintInput.uv = uv;
+			glintInput.duvdx = ddx(uv);
+			glintInput.duvdy = ddy(uv);
+			glintInput.ScreenSpaceScale = max(1, surfaceProperties.GlintScreenSpaceScale);
+			glintInput.LogMicrofacetDensity = clamp(40.f - surfaceProperties.GlintLogMicrofacetDensity, 1, 40);
+			glintInput.MicrofacetRoughness = clamp(surfaceProperties.GlintMicrofacetRoughness, 0.005, 0.3);
+			glintInput.DensityRandomization = clamp(surfaceProperties.GlintDensityRandomization, 0, 5);
+#endif
+
 			float3 F;
+#if defined(GLINT)
+			specular += PI * GetSpecularDirectLightMultiplierMicrofacetWithGlint(surfaceProperties.Roughness, surfaceProperties.F0, satNdotL, satNdotV, satNdotH, satVdotH, glintInput, F) * lightColor * satNdotL;
+#else
 			specular += PI * GetSpecularDirectLightMultiplierMicrofacet(surfaceProperties.Roughness, surfaceProperties.F0, satNdotL, satNdotV, satNdotH, satVdotH, F) * lightColor * satNdotL;
+#endif
 
 			float2 specularBRDF = 0;
 			[branch] if (pbrSettings.UseMultipleScattering)
diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl
index bcf63d860..09e1d4cd8 100644
--- a/package/Shaders/Lighting.hlsl
+++ b/package/Shaders/Lighting.hlsl
@@ -599,13 +599,22 @@ cbuffer PerTechnique : register(b0)
 cbuffer PerMaterial : register(b1)
 {
 	float4 LODTexParams : packoffset(c0);  // TerrainTexOffset in xy, LodBlendingEnabled in z
+#	if !(defined(LANDSCAPE) && defined(TRUE_PBR))
 	float4 TintColor : packoffset(c1);
 	float4 EnvmapData : packoffset(c2);  // fEnvmapScale in x, 1 or 0 in y depending of if has envmask
 	float4 ParallaxOccData : packoffset(c3);
 	float4 SpecularColor : packoffset(c4);  // Shininess in w, color in xyz
 	float4 SparkleParams : packoffset(c5);
 	float4 MultiLayerParallaxData : packoffset(c6);  // Layer thickness in x, refraction scale in y, uv scale in zw
-	float4 LightingEffectParams : packoffset(c7);    // fSubSurfaceLightRolloff in x, fRimLightPower in y
+#	else
+	float4 LandscapeTexture1GlintParameters : packoffset(c1);
+	float4 LandscapeTexture2GlintParameters : packoffset(c2);
+	float4 LandscapeTexture3GlintParameters : packoffset(c3);
+	float4 LandscapeTexture4GlintParameters : packoffset(c4);
+	float4 LandscapeTexture5GlintParameters : packoffset(c5);
+	float4 LandscapeTexture6GlintParameters : packoffset(c6);
+#	endif
+	float4 LightingEffectParams : packoffset(c7);  // fSubSurfaceLightRolloff in x, fRimLightPower in y
 	float4 IBLParams : packoffset(c8);
 
 #	if !defined(TRUE_PBR)
@@ -696,10 +705,12 @@ float GetRimLightMultiplier(float3 L, float3 V, float3 N)
 	return exp2(LightingEffectParams.y * log2(1 - NdotV)) * saturate(dot(V, -L));
 }
 
+#	if !defined(TRUE_PBR)
 float ProcessSparkleColor(float color)
 {
 	return exp2(SparkleParams.y * log2(min(1, abs(color))));
 }
+#	endif
 
 float3 GetLightSpecularInput(PS_INPUT input, float3 L, float3 V, float3 N, float3 lightColor, float shininess, float2 uv)
 {
@@ -1015,11 +1026,13 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 
 #	endif  // defined (SKINNED) || !defined (MODELSPACENORMALS)
 
-#	if defined(LANDSCAPE) && !defined(TRUE_PBR)
+#	if !defined(TRUE_PBR)
+#		if defined(LANDSCAPE)
 	float shininess = dot(input.LandBlendWeights1, LandscapeTexture1to4IsSpecPower) + input.LandBlendWeights2.x * LandscapeTexture5to6IsSpecPower.x + input.LandBlendWeights2.y * LandscapeTexture5to6IsSpecPower.y;
-#	else
+#		else
 	float shininess = SpecularColor.w;
-#	endif  // defined (LANDSCAPE)
+#		endif  // defined (LANDSCAPE)
+#	endif
 
 	float3 viewPosition = mul(CameraView[eyeIndex], float4(input.WorldPosition.xyz, 1)).xyz;
 	float2 screenUV = ViewToUV(viewPosition, true, eyeIndex);
@@ -1239,6 +1252,8 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 
 	float4 rawRMAOS = 0;
 
+	float4 glintParameters = 0;
+
 #	if defined(LANDSCAPE)
 	if (input.LandBlendWeights1.x > 0.0) {
 #	endif  // LANDSCAPE
@@ -1288,6 +1303,9 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 		[branch] if ((PBRFlags & TruePBR_LandTile0PBR) != 0)
 		{
 			rawRMAOS = input.LandBlendWeights1.x * TexRMAOSSampler.Sample(SampRMAOSSampler, diffuseUv) * float4(PBRParams1.x, 1, 1, PBRParams1.z);
+			if ((PBRFlags & TruePBR_LandTile0HasGlint) != 0) {
+				glintParameters += input.LandBlendWeights1.x * LandscapeTexture1GlintParameters;
+			}
 		}
 		else
 		{
@@ -1295,6 +1313,9 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 		}
 #		else
 		rawRMAOS = TexRMAOSSampler.Sample(SampRMAOSSampler, diffuseUv) * float4(PBRParams1.x, 1, 1, PBRParams1.z);
+		if ((PBRFlags & TruePBR_Glint) != 0) {
+			glintParameters = MultiLayerParallaxData;
+		}
 #		endif
 #	endif
 
@@ -1340,10 +1361,13 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 		[branch] if ((PBRFlags & TruePBR_LandTile1PBR) != 0)
 		{
 			rawRMAOS += input.LandBlendWeights1.y * TexLandRMAOS2Sampler.Sample(SampLandRMAOS2Sampler, uv) * float4(LandscapeTexture2PBRParams.x, 1, 1, LandscapeTexture2PBRParams.z);
+			if ((PBRFlags & TruePBR_LandTile1HasGlint) != 0) {
+				glintParameters += input.LandBlendWeights1.y * LandscapeTexture2GlintParameters;
+			}
 		}
 		else
 		{
-			rawRMAOS += input.LandBlendWeights1.y * float4(1 - landNormal2.w, 0, 1, 1);
+			rawRMAOS += input.LandBlendWeights1.y * float4(1 - landNormal2.w, 0, 1, 0.04);
 		}
 #		endif
 	}
@@ -1364,10 +1388,13 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 		[branch] if ((PBRFlags & TruePBR_LandTile2PBR) != 0)
 		{
 			rawRMAOS += input.LandBlendWeights1.z * TexLandRMAOS3Sampler.Sample(SampLandRMAOS3Sampler, uv) * float4(LandscapeTexture3PBRParams.x, 1, 1, LandscapeTexture3PBRParams.z);
+			if ((PBRFlags & TruePBR_LandTile2HasGlint) != 0) {
+				glintParameters += input.LandBlendWeights1.z * LandscapeTexture3GlintParameters;
+			}
 		}
 		else
 		{
-			rawRMAOS += input.LandBlendWeights1.z * float4(1 - landNormal3.w, 0, 1, 1);
+			rawRMAOS += input.LandBlendWeights1.z * float4(1 - landNormal3.w, 0, 1, 0.04);
 		}
 #		endif
 	}
@@ -1388,10 +1415,13 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 		[branch] if ((PBRFlags & TruePBR_LandTile3PBR) != 0)
 		{
 			rawRMAOS += input.LandBlendWeights1.w * TexLandRMAOS4Sampler.Sample(SampLandRMAOS4Sampler, uv) * float4(LandscapeTexture4PBRParams.x, 1, 1, LandscapeTexture4PBRParams.z);
+			if ((PBRFlags & TruePBR_LandTile3HasGlint) != 0) {
+				glintParameters += input.LandBlendWeights1.w * LandscapeTexture4GlintParameters;
+			}
 		}
 		else
 		{
-			rawRMAOS += input.LandBlendWeights1.w * float4(1 - landNormal4.w, 0, 1, 1);
+			rawRMAOS += input.LandBlendWeights1.w * float4(1 - landNormal4.w, 0, 1, 0.04);
 		}
 #		endif
 	}
@@ -1412,10 +1442,13 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 		[branch] if ((PBRFlags & TruePBR_LandTile4PBR) != 0)
 		{
 			rawRMAOS += input.LandBlendWeights2.x * TexLandRMAOS5Sampler.Sample(SampLandRMAOS5Sampler, uv) * float4(LandscapeTexture5PBRParams.x, 1, 1, LandscapeTexture5PBRParams.z);
+			if ((PBRFlags & TruePBR_LandTile4HasGlint) != 0) {
+				glintParameters += input.LandBlendWeights2.x * LandscapeTexture5GlintParameters;
+			}
 		}
 		else
 		{
-			rawRMAOS += input.LandBlendWeights2.x * float4(1 - landNormal5.w, 0, 1, 1);
+			rawRMAOS += input.LandBlendWeights2.x * float4(1 - landNormal5.w, 0, 1, 0.04);
 		}
 #		endif
 	}
@@ -1436,10 +1469,13 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 		[branch] if ((PBRFlags & TruePBR_LandTile5PBR) != 0)
 		{
 			rawRMAOS += input.LandBlendWeights2.y * TexLandRMAOS6Sampler.Sample(SampLandRMAOS6Sampler, uv) * float4(LandscapeTexture6PBRParams.x, 1, 1, LandscapeTexture6PBRParams.z);
+			if ((PBRFlags & TruePBR_LandTile5HasGlint) != 0) {
+				glintParameters += input.LandBlendWeights2.y * LandscapeTexture6GlintParameters;
+			}
 		}
 		else
 		{
-			rawRMAOS += input.LandBlendWeights2.y * float4(1 - landNormal6.w, 0, 1, 1);
+			rawRMAOS += input.LandBlendWeights2.y * float4(1 - landNormal6.w, 0, 1, 0.04);
 		}
 #		endif
 	}
@@ -1584,23 +1620,17 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 	float3 screenSpaceNormal = normalize(WorldToView(worldSpaceNormal, false, eyeIndex));
 
 #	if defined(TRUE_PBR)
-	PBR::SurfaceProperties pbrSurfaceProperties;
+	PBR::SurfaceProperties pbrSurfaceProperties = PBR::InitSurfaceProperties();
 
 	pbrSurfaceProperties.Roughness = saturate(rawRMAOS.x);
 	pbrSurfaceProperties.Metallic = saturate(rawRMAOS.y);
 	pbrSurfaceProperties.AO = rawRMAOS.z;
 	pbrSurfaceProperties.F0 = lerp(saturate(rawRMAOS.w), baseColor.xyz, pbrSurfaceProperties.Metallic);
 
-	pbrSurfaceProperties.SubsurfaceColor = 0;
-	pbrSurfaceProperties.Thickness = 0;
-
-	pbrSurfaceProperties.CoatColor = 0;
-	pbrSurfaceProperties.CoatStrength = 0;
-	pbrSurfaceProperties.CoatRoughness = 0;
-	pbrSurfaceProperties.CoatF0 = 0.04;
-
-	pbrSurfaceProperties.FuzzColor = 0;
-	pbrSurfaceProperties.FuzzWeight = 0;
+	pbrSurfaceProperties.GlintScreenSpaceScale = glintParameters.x;
+	pbrSurfaceProperties.GlintLogMicrofacetDensity = glintParameters.y;
+	pbrSurfaceProperties.GlintMicrofacetRoughness = glintParameters.z;
+	pbrSurfaceProperties.GlintDensityRandomization = glintParameters.w;
 
 	baseColor.xyz *= 1 - pbrSurfaceProperties.Metallic;
 
@@ -1926,7 +1956,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 		float3 coatFinalLightColor = coatShadowDirLightColorMultiplier * pbrDirLightColor;
 
 		float3 dirDiffuseColor, coatDirDiffuseColor, dirTransmissionColor, dirSpecularColor;
-		PBR::GetDirectLightInput(dirDiffuseColor, coatDirDiffuseColor, dirTransmissionColor, dirSpecularColor, modelNormal.xyz, coatModelNormal, refractedViewDirection, viewDirection, refractedDirLightDirection, DirLightDirection, mainLayerFinalLightColor, coatFinalLightColor, pbrSurfaceProperties);
+		PBR::GetDirectLightInput(dirDiffuseColor, coatDirDiffuseColor, dirTransmissionColor, dirSpecularColor, modelNormal.xyz, coatModelNormal, refractedViewDirection, viewDirection, refractedDirLightDirection, DirLightDirection, mainLayerFinalLightColor, coatFinalLightColor, pbrSurfaceProperties, tbnTr, uvOriginal);
 		lightsDiffuseColor += dirDiffuseColor;
 		coatLightsDiffuseColor += coatDirDiffuseColor;
 		transmissionColor += dirTransmissionColor;
@@ -2008,7 +2038,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 				refractedLightDirection = -refract(-normalizedLightDirection, coatModelNormal, eta);
 			}
 			float3 pbrLightColor = PBR::AdjustPointLightColor(lightColor);
-			PBR::GetDirectLightInput(pointDiffuseColor, coatPointDiffuseColor, pointTransmissionColor, pointSpecularColor, modelNormal.xyz, coatModelNormal, refractedViewDirection, viewDirection, refractedLightDirection, normalizedLightDirection, pbrLightColor, pbrLightColor, pbrSurfaceProperties);
+			PBR::GetDirectLightInput(pointDiffuseColor, coatPointDiffuseColor, pointTransmissionColor, pointSpecularColor, modelNormal.xyz, coatModelNormal, refractedViewDirection, viewDirection, refractedLightDirection, normalizedLightDirection, pbrLightColor, pbrLightColor, pbrSurfaceProperties, tbnTr, uvOriginal);
 			lightsDiffuseColor += pointDiffuseColor;
 			coatLightsDiffuseColor += coatPointDiffuseColor;
 			transmissionColor += pointTransmissionColor;
@@ -2147,7 +2177,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 			}
 
 			float3 pointDiffuseColor, coatPointDiffuseColor, pointTransmissionColor, pointSpecularColor;
-			PBR::GetDirectLightInput(pointDiffuseColor, coatPointDiffuseColor, pointTransmissionColor, pointSpecularColor, worldSpaceNormal.xyz, coatWorldNormal, refractedViewDirectionWS, worldSpaceViewDirection, refractedLightDirection, normalizedLightDirection, mainLayerFinalLightColor, coatFinalLightColor, pbrSurfaceProperties);
+			PBR::GetDirectLightInput(pointDiffuseColor, coatPointDiffuseColor, pointTransmissionColor, pointSpecularColor, worldSpaceNormal.xyz, coatWorldNormal, refractedViewDirectionWS, worldSpaceViewDirection, refractedLightDirection, normalizedLightDirection, mainLayerFinalLightColor, coatFinalLightColor, pbrSurfaceProperties, tbnTr, uvOriginal);
 			lightsDiffuseColor += pointDiffuseColor;
 			coatLightsDiffuseColor += coatPointDiffuseColor;
 			transmissionColor += pointTransmissionColor;
diff --git a/package/Shaders/RunGrass.hlsl b/package/Shaders/RunGrass.hlsl
index 9d7f2c150..24a1627f1 100644
--- a/package/Shaders/RunGrass.hlsl
+++ b/package/Shaders/RunGrass.hlsl
@@ -456,6 +456,8 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 
 	normal = normalize(lerp(normal, normalize(input.SphereNormal.xyz), input.SphereNormal.w));
 
+	float3x3 tbn = 0;
+
 #			if !defined(TRUE_PBR)
 	if (complex)
 #			endif  // !TRUE_PBR
@@ -463,7 +465,8 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 		float3 normalColor = GrassLighting::TransformNormal(specColor.xyz);
 		// world-space -> tangent-space -> world-space.
 		// This is because we don't have pre-computed tangents.
-		normal = normalize(mul(normalColor, GrassLighting::CalculateTBN(normal, -input.WorldPosition.xyz, input.TexCoord.xy)));
+		tbn = GrassLighting::CalculateTBN(normal, -input.WorldPosition.xyz, input.TexCoord.xy);
+		normal = normalize(mul(normalColor, tbn));
 	}
 
 #			if !defined(TRUE_PBR)
@@ -474,24 +477,13 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 #			if defined(TRUE_PBR)
 	float4 rawRMAOS = TexRMAOSSampler.Sample(SampRMAOSSampler, input.TexCoord.xy) * float4(PBRParams1.x, 1, 1, PBRParams1.y);
 
-	PBR::SurfaceProperties pbrSurfaceProperties;
+	PBR::SurfaceProperties pbrSurfaceProperties = PBR::InitSurfaceProperties();
 
 	pbrSurfaceProperties.Roughness = saturate(rawRMAOS.x);
 	pbrSurfaceProperties.Metallic = saturate(rawRMAOS.y);
 	pbrSurfaceProperties.AO = rawRMAOS.z;
 	pbrSurfaceProperties.F0 = lerp(saturate(rawRMAOS.w), baseColor.xyz, pbrSurfaceProperties.Metallic);
 
-	pbrSurfaceProperties.SubsurfaceColor = 0;
-	pbrSurfaceProperties.Thickness = 0;
-
-	pbrSurfaceProperties.CoatColor = 0;
-	pbrSurfaceProperties.CoatStrength = 0;
-	pbrSurfaceProperties.CoatRoughness = 0;
-	pbrSurfaceProperties.CoatF0 = 0.04;
-
-	pbrSurfaceProperties.FuzzColor = 0;
-	pbrSurfaceProperties.FuzzWeight = 0;
-
 	baseColor.xyz *= 1 - pbrSurfaceProperties.Metallic;
 
 	pbrSurfaceProperties.BaseColor = baseColor.xyz;
@@ -554,7 +546,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 		float3 pbrDirLightColor = PBR::AdjustDirectionalLightColor(DirLightColorShared.xyz) * dirLightColorMultiplier * dirShadow;
 
 		float3 dirDiffuseColor, coatDirDiffuseColor, dirTransmissionColor, dirSpecularColor;
-		PBR::GetDirectLightInput(dirDiffuseColor, coatDirDiffuseColor, dirTransmissionColor, dirSpecularColor, normal, normal, viewDirection, viewDirection, DirLightDirection, DirLightDirection, pbrDirLightColor, pbrDirLightColor, pbrSurfaceProperties);
+		PBR::GetDirectLightInput(dirDiffuseColor, coatDirDiffuseColor, dirTransmissionColor, dirSpecularColor, normal, normal, viewDirection, viewDirection, DirLightDirection, DirLightDirection, pbrDirLightColor, pbrDirLightColor, pbrSurfaceProperties, tbn, input.TexCoord.xy);
 		lightsDiffuseColor += dirDiffuseColor;
 		transmissionColor += dirTransmissionColor;
 		specularColorPBR += dirSpecularColor;
@@ -610,7 +602,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
 				{
 					float3 pointDiffuseColor, coatDirDiffuseColor, pointTransmissionColor, pointSpecularColor;
 					float3 pbrLightColor = PBR::AdjustPointLightColor(lightColor * intensityMultiplier);
-					PBR::GetDirectLightInput(pointDiffuseColor, coatDirDiffuseColor, pointTransmissionColor, pointSpecularColor, normal, normal, viewDirection, viewDirection, normalizedLightDirection, normalizedLightDirection, pbrLightColor, pbrLightColor, pbrSurfaceProperties);
+					PBR::GetDirectLightInput(pointDiffuseColor, coatDirDiffuseColor, pointTransmissionColor, pointSpecularColor, normal, normal, viewDirection, viewDirection, normalizedLightDirection, normalizedLightDirection, pbrLightColor, pbrLightColor, pbrSurfaceProperties, tbn, input.TexCoord.xy);
 					lightsDiffuseColor += pointDiffuseColor;
 					transmissionColor += pointTransmissionColor;
 					specularColorPBR += pointSpecularColor;
diff --git a/src/Deferred.cpp b/src/Deferred.cpp
index d3ec22d3e..3f0f1c706 100644
--- a/src/Deferred.cpp
+++ b/src/Deferred.cpp
@@ -2,6 +2,7 @@
 
 #include "ShaderCache.h"
 #include "State.h"
+#include "TruePBR.h"
 #include "Util.h"
 
 #include "Features/DynamicCubemaps.h"
@@ -271,6 +272,7 @@ void Deferred::PrepassPasses()
 
 	stateUpdateFlags.set(RE::BSGraphics::ShaderFlags::DIRTY_RENDERTARGET);  // Run OMSetRenderTargets again
 
+	TruePBR::GetSingleton()->PrePass();
 	for (auto* feature : Feature::GetFeatureList()) {
 		if (feature->loaded) {
 			feature->Prepass();
diff --git a/src/ShaderCache.cpp b/src/ShaderCache.cpp
index 0fcbce51d..7741e25fa 100644
--- a/src/ShaderCache.cpp
+++ b/src/ShaderCache.cpp
@@ -65,6 +65,9 @@ namespace SIE
 			}
 			if ((descriptor & static_cast<uint32_t>(ShaderCache::LightingShaderFlags::TruePbr)) != 0) {
 				defines[lastIndex++] = { "TRUE_PBR", nullptr };
+				if ((descriptor & static_cast<uint32_t>(ShaderCache::LightingShaderFlags::AnisoLighting)) != 0) {
+					defines[lastIndex++] = { "GLINT", nullptr };
+				}
 			}
 
 			for (auto* feature : Feature::GetFeatureList()) {
@@ -760,6 +763,12 @@ namespace SIE
 					{ "LandscapeTexture5PBRParams", 49 },
 					{ "LandscapeTexture6PBRParams", 50 },
 					{ "PBRParams2", 51 },
+					{ "LandscapeTexture1GlintParameters", 52 },
+					{ "LandscapeTexture2GlintParameters", 53 },
+					{ "LandscapeTexture3GlintParameters", 54 },
+					{ "LandscapeTexture4GlintParameters", 55 },
+					{ "LandscapeTexture5GlintParameters", 56 },
+					{ "LandscapeTexture6GlintParameters", 57 },
 				});
 			} else {
 				lightingPS.insert({
@@ -790,6 +799,12 @@ namespace SIE
 					{ "LandscapeTexture5PBRParams", 41 },
 					{ "LandscapeTexture6PBRParams", 42 },
 					{ "PBRParams2", 43 },
+					{ "LandscapeTexture1GlintParameters", 44 },
+					{ "LandscapeTexture2GlintParameters", 45 },
+					{ "LandscapeTexture3GlintParameters", 46 },
+					{ "LandscapeTexture4GlintParameters", 47 },
+					{ "LandscapeTexture5GlintParameters", 48 },
+					{ "LandscapeTexture6GlintParameters", 49 },
 				});
 			}
 
diff --git a/src/ShaderCache.h b/src/ShaderCache.h
index 868677c9e..6c02e3994 100644
--- a/src/ShaderCache.h
+++ b/src/ShaderCache.h
@@ -246,7 +246,7 @@ namespace SIE
 			ShadowDir = 1 << 13,
 			DefShadow = 1 << 14,
 			ProjectedUV = 1 << 15,
-			AnisoLighting = 1 << 16,
+			AnisoLighting = 1 << 16,  // Reused for glint with PBR
 			AmbientSpecular = 1 << 17,
 			WorldMap = 1 << 18,
 			BaseObjectIsSnow = 1 << 19,
diff --git a/src/TruePBR.cpp b/src/TruePBR.cpp
index 7a9b7598a..9f0f07e74 100644
--- a/src/TruePBR.cpp
+++ b/src/TruePBR.cpp
@@ -8,42 +8,10 @@
 #include "Hooks.h"
 #include "ShaderCache.h"
 #include "State.h"
+#include "Util.h"
 
 namespace PNState
 {
-	template <typename ResultType>
-	bool Read(const json& config, const std::string_view& key, ResultType& result)
-	{
-		if (!config.is_object()) {
-			return false;
-		}
-
-		auto it = config.find(key);
-		if (it == config.end()) {
-			return false;
-		}
-
-		const json& section = it.value();
-
-		if constexpr (std::is_same_v<ResultType, std::array<float, 3>> || std::is_same_v<ResultType, RE::NiColor>) {
-			if (section.is_array() && section.size() == 3 &&
-				section[0].is_number_float() && section[1].is_number_float() &&
-				section[2].is_number_float()) {
-				result[0] = section[0];
-				result[1] = section[1];
-				result[2] = section[2];
-				return true;
-			}
-		}
-		if constexpr (std::is_same_v<ResultType, float>) {
-			if (section.is_number_float()) {
-				result = section;
-				return true;
-			}
-		}
-		return false;
-	}
-
 	void ReadPBRRecordConfigs(const std::string& rootPath, std::function<void(const std::string&, const json&)> recordReader)
 	{
 		if (std::filesystem::exists(rootPath)) {
@@ -98,6 +66,27 @@ namespace PNState
 	}
 }
 
+namespace nlohmann
+{
+	void to_json(json& section, const RE::NiColor& result)
+	{
+		section = { result[0],
+			result[1],
+			result[2] };
+	}
+
+	void from_json(const json& section, RE::NiColor& result)
+	{
+		if (section.is_array() && section.size() == 3 &&
+			section[0].is_number_float() && section[1].is_number_float() &&
+			section[2].is_number_float()) {
+			result[0] = section[0];
+			result[1] = section[1];
+			result[2] = section[2];
+		}
+	}
+}
+
 void TruePBR::DrawSettings()
 {
 	if (ImGui::TreeNodeEx("PBR", ImGuiTreeNodeFlags_DefaultOpen)) {
@@ -192,6 +181,85 @@ void TruePBR::SaveSettings(json& o_json)
 	o_json["Ambient Light Color Multiplier"] = globalPBRAmbientLightColorMultiplier;
 }
 
+void TruePBR::PrePass()
+{
+	auto context = State::GetSingleton()->context;
+	if (!glintsNoiseTexture)
+		SetupGlintsTexture();
+	ID3D11ShaderResourceView* srv = glintsNoiseTexture->srv.get();
+	context->PSSetShaderResources(28, 1, &srv);
+}
+
+void TruePBR::SetupGlintsTexture()
+{
+	constexpr uint noiseTexSize = 512;
+
+	D3D11_TEXTURE2D_DESC tex_desc{
+		.Width = noiseTexSize,
+		.Height = noiseTexSize,
+		.MipLevels = 1,
+		.ArraySize = 1,
+		.Format = DXGI_FORMAT_R32G32B32A32_FLOAT,
+		.SampleDesc = { .Count = 1, .Quality = 0 },
+		.Usage = D3D11_USAGE_DEFAULT,
+		.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS,
+		.CPUAccessFlags = 0,
+		.MiscFlags = 0
+	};
+	D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = {
+		.Format = tex_desc.Format,
+		.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D,
+		.Texture2D = {
+			.MostDetailedMip = 0,
+			.MipLevels = 1 }
+	};
+	D3D11_UNORDERED_ACCESS_VIEW_DESC uav_desc = {
+		.Format = tex_desc.Format,
+		.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D,
+		.Texture2D = { .MipSlice = 0 }
+	};
+
+	glintsNoiseTexture = eastl::make_unique<Texture2D>(tex_desc);
+	glintsNoiseTexture->CreateSRV(srv_desc);
+	glintsNoiseTexture->CreateUAV(uav_desc);
+
+	// Compile
+	auto noiseGenProgram = reinterpret_cast<ID3D11ComputeShader*>(Util::CompileShader(L"Data\\Shaders\\Common\\Glints\\noisegen.cs.hlsl", {}, "cs_5_0"));
+	if (!noiseGenProgram) {
+		logger::error("Failed to compile glints noise generation shader!");
+		return;
+	}
+
+	// Generate the noise
+	{
+		auto context = State::GetSingleton()->context;
+
+		struct OldState
+		{
+			ID3D11ComputeShader* shader;
+			ID3D11UnorderedAccessView* uav[1];
+			ID3D11ClassInstance* instance;
+			UINT numInstances;
+		};
+
+		OldState newer{}, old{};
+		context->CSGetShader(&old.shader, &old.instance, &old.numInstances);
+		context->CSGetUnorderedAccessViews(0, ARRAYSIZE(old.uav), old.uav);
+
+		{
+			newer.uav[0] = glintsNoiseTexture->uav.get();
+			context->CSSetShader(noiseGenProgram, nullptr, 0);
+			context->CSSetUnorderedAccessViews(0, ARRAYSIZE(newer.uav), newer.uav, nullptr);
+			context->Dispatch((noiseTexSize + 31) >> 5, (noiseTexSize + 31) >> 5, 1);
+		}
+
+		context->CSSetShader(old.shader, &old.instance, old.numInstances);
+		context->CSSetUnorderedAccessViews(0, ARRAYSIZE(old.uav), old.uav, nullptr);
+	}
+
+	noiseGenProgram->Release();
+}
+
 void TruePBR::SetupFrame()
 {
 	float newDirectionalLightScale = 1.f;
@@ -234,22 +302,11 @@ void TruePBR::SetupTextureSetData()
 	pbrTextureSets.clear();
 
 	PNState::ReadPBRRecordConfigs("Data\\PBRTextureSets", [this](const std::string& editorId, const json& config) {
-		PBRTextureSetData textureSetData;
-
-		PNState::Read(config, "roughnessScale", textureSetData.roughnessScale);
-		PNState::Read(config, "displacementScale", textureSetData.displacementScale);
-		PNState::Read(config, "specularLevel", textureSetData.specularLevel);
-		PNState::Read(config, "subsurfaceColor", textureSetData.subsurfaceColor);
-		PNState::Read(config, "subsurfaceOpacity", textureSetData.subsurfaceOpacity);
-		PNState::Read(config, "coatColor", textureSetData.coatColor);
-		PNState::Read(config, "coatStrength", textureSetData.coatStrength);
-		PNState::Read(config, "coatRoughness", textureSetData.coatRoughness);
-		PNState::Read(config, "coatSpecularLevel", textureSetData.coatSpecularLevel);
-		PNState::Read(config, "innerLayerDisplacementOffset", textureSetData.innerLayerDisplacementOffset);
-		PNState::Read(config, "fuzzColor", textureSetData.fuzzColor);
-		PNState::Read(config, "fuzzWeight", textureSetData.fuzzWeight);
-
-		pbrTextureSets.insert_or_assign(editorId, textureSetData);
+		try {
+			pbrTextureSets.insert_or_assign(editorId, config);
+		} catch (const std::exception& e) {
+			logger::error("Failed to deserialize config for {}: {}.", editorId, e.what());
+		}
 	});
 }
 
@@ -278,13 +335,11 @@ void TruePBR::SetupMaterialObjectData()
 	pbrMaterialObjects.clear();
 
 	PNState::ReadPBRRecordConfigs("Data\\PBRMaterialObjects", [this](const std::string& editorId, const json& config) {
-		PBRMaterialObjectData materialObjectData;
-
-		PNState::Read(config, "baseColorScale", materialObjectData.baseColorScale);
-		PNState::Read(config, "roughness", materialObjectData.roughness);
-		PNState::Read(config, "specularLevel", materialObjectData.specularLevel);
-
-		pbrMaterialObjects.insert_or_assign(editorId, materialObjectData);
+		try {
+			pbrMaterialObjects.insert_or_assign(editorId, config);
+		} catch (const std::exception& e) {
+			logger::error("Failed to deserialize config for {}: {}.", editorId, e.what());
+		}
 	});
 }
 
@@ -313,12 +368,11 @@ void TruePBR::SetupLightingTemplateData()
 	pbrLightingTemplates.clear();
 
 	PNState::ReadPBRRecordConfigs("Data\\PBRLightingTemplates", [this](const std::string& editorId, const json& config) {
-		PBRLightingTemplateData lightingTemplateData;
-
-		PNState::Read(config, "directionalLightColorScale", lightingTemplateData.directionalLightColorScale);
-		PNState::Read(config, "directionalAmbientLightColorScale", lightingTemplateData.directionalAmbientLightColorScale);
-
-		pbrLightingTemplates.insert_or_assign(editorId, lightingTemplateData);
+		try {
+			pbrLightingTemplates.insert_or_assign(editorId, config);
+		} catch (const std::exception& e) {
+			logger::error("Failed to deserialize config for {}: {}.", editorId, e.what());
+		}
 	});
 }
 
@@ -343,12 +397,11 @@ bool TruePBR::IsPBRLightingTemplate(const RE::TESForm* lightingTemplate)
 void TruePBR::SavePBRLightingTemplateData(const std::string& editorId)
 {
 	const auto& pbrLightingTemplateData = pbrLightingTemplates[editorId];
-
-	json config;
-	config["directionalLightColorScale"] = pbrLightingTemplateData.directionalLightColorScale;
-	config["directionalAmbientLightColorScale"] = pbrLightingTemplateData.directionalAmbientLightColorScale;
-
-	PNState::SavePBRRecordConfig("Data\\PBRLightingTemplates\\", editorId, config);
+	try {
+		PNState::SavePBRRecordConfig("Data\\PBRLightingTemplates\\", editorId, pbrLightingTemplateData);
+	} catch (const std::exception& e) {
+		logger::error("Failed to serialize config for {}: {}.", editorId, e.what());
+	}
 }
 
 void TruePBR::SetupWeatherData()
@@ -358,12 +411,11 @@ void TruePBR::SetupWeatherData()
 	pbrWeathers.clear();
 
 	PNState::ReadPBRRecordConfigs("Data\\PBRWeathers", [this](const std::string& editorId, const json& config) {
-		PBRWeatherData weatherData;
-
-		PNState::Read(config, "directionalLightColorScale", weatherData.directionalLightColorScale);
-		PNState::Read(config, "directionalAmbientLightColorScale", weatherData.directionalAmbientLightColorScale);
-
-		pbrWeathers.insert_or_assign(editorId, weatherData);
+		try {
+			pbrWeathers.insert_or_assign(editorId, config);
+		} catch (const std::exception& e) {
+			logger::error("Failed to deserialize config for {}: {}.", editorId, e.what());
+		}
 	});
 }
 
@@ -388,12 +440,11 @@ bool TruePBR::IsPBRWeather(const RE::TESForm* weather)
 void TruePBR::SavePBRWeatherData(const std::string& editorId)
 {
 	const auto& pbrWeatherData = pbrWeathers[editorId];
-
-	json config;
-	config["directionalLightColorScale"] = pbrWeatherData.directionalLightColorScale;
-	config["directionalAmbientLightColorScale"] = pbrWeatherData.directionalAmbientLightColorScale;
-
-	PNState::SavePBRRecordConfig("Data\\PBRWeathers\\", editorId, config);
+	try {
+		PNState::SavePBRRecordConfig("Data\\PBRWeathers\\", editorId, pbrWeatherData);
+	} catch (const std::exception& e) {
+		logger::error("Failed to serialize config for {}: {}.", editorId, e.what());
+	}
 }
 
 namespace Permutations
@@ -648,10 +699,13 @@ struct BSLightingShaderProperty_LoadBinary
 				}
 				if (property->flags.any(kSoftLighting)) {
 					pbrMaterial->pbrFlags.set(PBRFlags::Fuzz);
+				} else if (property->flags.any(kFitSlope)) {
+					pbrMaterial->pbrFlags.set(PBRFlags::Glint);
+					pbrMaterial->glintParameters.enabled = true;
 				}
 			}
 			property->flags.set(kVertexLighting);
-			property->flags.reset(kMenuScreen, kSpecular, kGlowMap, kEnvMap, kMultiLayerParallax, kSoftLighting, kRimLighting, kBackLighting, kAnisotropicLighting, kEffectLighting);
+			property->flags.reset(kMenuScreen, kSpecular, kGlowMap, kEnvMap, kMultiLayerParallax, kSoftLighting, kRimLighting, kBackLighting, kAnisotropicLighting, kEffectLighting, kFitSlope);
 		}
 	}
 	static inline REL::Relocation<decltype(thunk)> func;
@@ -682,6 +736,17 @@ struct BSLightingShaderProperty_GetRenderPasses
 				lightingFlags &= ~0b111000u;
 				if (isPbr) {
 					lightingFlags |= static_cast<uint32_t>(SIE::ShaderCache::LightingShaderFlags::TruePbr);
+					if (property->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kMultiTextureLandscape)) {
+						auto* material = static_cast<BSLightingShaderMaterialPBRLandscape*>(property->material);
+						if (material->HasGlint()) {
+							lightingFlags |= static_cast<uint32_t>(SIE::ShaderCache::LightingShaderFlags::AnisoLighting);
+						}
+					} else {
+						auto* material = static_cast<BSLightingShaderMaterialPBR*>(property->material);
+						if (material->pbrFlags.any(PBRFlags::Glint)) {
+							lightingFlags |= static_cast<uint32_t>(SIE::ShaderCache::LightingShaderFlags::AnisoLighting);
+						}
+					}
 				}
 				lightingTechnique = (static_cast<uint32_t>(lightingType) << 24) | lightingFlags;
 				currentPass->passEnum = lightingTechnique + LightingTechniqueStart;
@@ -754,6 +819,9 @@ struct BSLightingShader_SetupMaterial
 							if (pbrMaterial->landscapeDisplacementTextures[textureIndex] != nullptr && pbrMaterial->landscapeDisplacementTextures[textureIndex] != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack) {
 								flags |= (1 << (BSLightingShaderMaterialPBRLandscape::NumTiles + textureIndex));
 							}
+							if (pbrMaterial->glintParameters[textureIndex].enabled) {
+								flags |= (1 << (2 * BSLightingShaderMaterialPBRLandscape::NumTiles + textureIndex));
+							}
 						}
 					}
 					shadowState->SetPSConstant(flags, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 36);
@@ -761,6 +829,7 @@ struct BSLightingShader_SetupMaterial
 
 				{
 					constexpr size_t PBRParamsStartIndex = 37;
+					constexpr size_t GlintParametersStartIndex = 44;
 
 					for (uint32_t textureIndex = 0; textureIndex < BSLightingShaderMaterialPBRLandscape::NumTiles; ++textureIndex) {
 						std::array<float, 3> PBRParams;
@@ -768,6 +837,13 @@ struct BSLightingShader_SetupMaterial
 						PBRParams[1] = pbrMaterial->displacementScales[textureIndex];
 						PBRParams[2] = pbrMaterial->specularLevels[textureIndex];
 						shadowState->SetPSConstant(PBRParams, RE::BSGraphics::ConstantGroupLevel::PerMaterial, PBRParamsStartIndex + textureIndex);
+
+						std::array<float, 4> glintParameters;
+						glintParameters[0] = pbrMaterial->glintParameters[textureIndex].screenSpaceScale;
+						glintParameters[1] = 40.f - pbrMaterial->glintParameters[textureIndex].logMicrofacetDensity;
+						glintParameters[2] = pbrMaterial->glintParameters[textureIndex].microfacetRoughness;
+						glintParameters[3] = pbrMaterial->glintParameters[textureIndex].densityRandomization;
+						shadowState->SetPSConstant(glintParameters, RE::BSGraphics::ConstantGroupLevel::PerMaterial, GlintParametersStartIndex + textureIndex);
 					}
 				}
 
@@ -843,6 +919,15 @@ struct BSLightingShader_SetupMaterial
 						PBRParams3[2] = pbrMaterial->GetFuzzColor().blue;
 						PBRParams3[3] = pbrMaterial->GetFuzzWeight();
 						shadowState->SetPSConstant(PBRParams3, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 27);
+					} else if (pbrMaterial->pbrFlags.any(PBRFlags::Glint)) {
+						shaderFlags.set(PBRShaderFlags::Glint);
+
+						std::array<float, 4> GlintParameters;
+						GlintParameters[0] = pbrMaterial->GetGlintParameters().screenSpaceScale;
+						GlintParameters[0] = 40.f - pbrMaterial->GetGlintParameters().logMicrofacetDensity;
+						GlintParameters[0] = pbrMaterial->GetGlintParameters().microfacetRoughness;
+						GlintParameters[0] = pbrMaterial->GetGlintParameters().densityRandomization;
+						shadowState->SetPSConstant(GlintParameters, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 27);
 					}
 				}
 
@@ -952,10 +1037,11 @@ struct BSLightingShader_SetupGeometry
 {
 	static void thunk(RE::BSLightingShader* shader, RE::BSRenderPass* pass, uint32_t renderFlags)
 	{
-		const uint32_t originalExtraFlags = shader->currentRawTechnique & 0b111000u;
+		const auto originalTechnique = shader->currentRawTechnique;
 
 		if ((shader->currentRawTechnique & static_cast<uint32_t>(SIE::ShaderCache::LightingShaderFlags::TruePbr)) != 0) {
 			shader->currentRawTechnique |= static_cast<uint32_t>(SIE::ShaderCache::LightingShaderFlags::AmbientSpecular);
+			shader->currentRawTechnique ^= static_cast<uint32_t>(SIE::ShaderCache::LightingShaderFlags::AnisoLighting);
 		}
 
 		shader->currentRawTechnique &= ~0b111000u;
@@ -963,12 +1049,7 @@ struct BSLightingShader_SetupGeometry
 
 		func(shader, pass, renderFlags);
 
-		shader->currentRawTechnique &= ~0b111000u;
-		shader->currentRawTechnique |= originalExtraFlags;
-
-		if ((shader->currentRawTechnique & static_cast<uint32_t>(SIE::ShaderCache::LightingShaderFlags::TruePbr)) != 0) {
-			shader->currentRawTechnique &= ~static_cast<uint32_t>(SIE::ShaderCache::LightingShaderFlags::AmbientSpecular);
-		}
+		shader->currentRawTechnique = originalTechnique;
 	}
 	static inline REL::Relocation<decltype(thunk)> func;
 };
@@ -1009,6 +1090,8 @@ void SetupLandscapeTexture(BSLightingShaderMaterialPBRLandscape& material, RE::T
 		material.displacementScales[textureIndex] = textureSetData->displacementScale;
 		material.roughnessScales[textureIndex] = textureSetData->roughnessScale;
 		material.specularLevels[textureIndex] = textureSetData->specularLevel;
+
+		material.glintParameters[textureIndex] = textureSetData->glintParameters;
 	}
 	material.isPbr[textureIndex] = isPbr;
 
diff --git a/src/TruePBR.h b/src/TruePBR.h
index 930bf82ba..94cd419ba 100644
--- a/src/TruePBR.h
+++ b/src/TruePBR.h
@@ -1,5 +1,25 @@
 #pragma once
 
+#include "Buffer.h"
+
+struct GlintParameters
+{
+	bool enabled = false;
+	float screenSpaceScale = 1.5f;
+	float logMicrofacetDensity = 40.f;
+	float microfacetRoughness = .015f;
+	float densityRandomization = 2.f;
+
+	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GlintParameters, enabled, screenSpaceScale, logMicrofacetDensity,
+		microfacetRoughness, densityRandomization);
+};
+
+namespace nlohmann
+{
+	void to_json(json&, const RE::NiColor&);
+	void from_json(const json&, RE::NiColor&);
+}
+
 struct TruePBR
 {
 public:
@@ -15,11 +35,15 @@ struct TruePBR
 	void SetupResources();
 	void LoadSettings(json& o_json);
 	void SaveSettings(json& o_json);
+	void PrePass();
 	void PostPostLoad();
 
 	void SetShaderResouces();
 	void GenerateShaderPermutations(RE::BSShader* shader);
 
+	void SetupGlintsTexture();
+	eastl::unique_ptr<Texture2D> glintsNoiseTexture = nullptr;
+
 	std::unordered_map<uint32_t, std::string> editorIDs;
 
 	float globalPBRDirectLightColorMultiplier = 1.f;
@@ -28,7 +52,7 @@ struct TruePBR
 	float weatherPBRDirectionalLightColorMultiplier = 1.f;
 	float weatherPBRDirectionalAmbientLightColorMultiplier = 1.f;
 
-	struct alignas(16) Settings
+	struct Settings
 	{
 		float directionalLightColorMultiplier = 1.f;
 		float pointLightColorMultiplier = 1.f;
@@ -37,6 +61,7 @@ struct TruePBR
 		uint32_t useMultiBounceAO = true;
 		uint32_t pad[3];
 	} settings{};
+	static_assert(sizeof(Settings) % 16 == 0);
 
 	struct PBRTextureSetData
 	{
@@ -55,6 +80,12 @@ struct TruePBR
 
 		RE::NiColor fuzzColor;
 		float fuzzWeight = 0.f;
+
+		GlintParameters glintParameters;
+
+		NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(PBRTextureSetData, roughnessScale, displacementScale, specularLevel,
+			subsurfaceColor, subsurfaceOpacity, coatColor, coatStrength, coatRoughness, coatSpecularLevel,
+			innerLayerDisplacementOffset, fuzzColor, fuzzWeight, glintParameters);
 	};
 
 	void SetupFrame();
@@ -70,6 +101,8 @@ struct TruePBR
 		std::array<float, 3> baseColorScale = { 1.f, 1.f, 1.f };
 		float roughness = 1.f;
 		float specularLevel = 1.f;
+
+		NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(PBRMaterialObjectData, baseColorScale, roughness, specularLevel);
 	};
 
 	void SetupMaterialObjectData();
@@ -82,6 +115,8 @@ struct TruePBR
 	{
 		float directionalLightColorScale = 1.f;
 		float directionalAmbientLightColorScale = 1.f;
+
+		NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(PBRLightingTemplateData, directionalLightColorScale, directionalAmbientLightColorScale);
 	};
 
 	void SetupLightingTemplateData();
@@ -95,6 +130,8 @@ struct TruePBR
 	{
 		float directionalLightColorScale = 1.f;
 		float directionalAmbientLightColorScale = 1.f;
+
+		NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(PBRWeatherData, directionalLightColorScale, directionalAmbientLightColorScale);
 	};
 
 	void SetupWeatherData();
diff --git a/src/TruePBR/BSLightingShaderMaterialPBR.cpp b/src/TruePBR/BSLightingShaderMaterialPBR.cpp
index 4bdcafc95..2a6c7561e 100644
--- a/src/TruePBR/BSLightingShaderMaterialPBR.cpp
+++ b/src/TruePBR/BSLightingShaderMaterialPBR.cpp
@@ -27,6 +27,7 @@ void BSLightingShaderMaterialPBR::CopyMembers(RE::BSShaderMaterial* that)
 	coatSpecularLevel = pbrThat->coatSpecularLevel;
 	fuzzColor = pbrThat->fuzzColor;
 	fuzzWeight = pbrThat->fuzzWeight;
+	glintParameters = pbrThat->glintParameters;
 	projectedMaterialBaseColorScale = pbrThat->projectedMaterialBaseColorScale;
 	projectedMaterialRoughness = pbrThat->projectedMaterialRoughness;
 	projectedMaterialSpecularLevel = pbrThat->projectedMaterialSpecularLevel;
@@ -46,7 +47,11 @@ std::uint32_t BSLightingShaderMaterialPBR::ComputeCRC32(uint32_t srcHash)
 		float coatRoughness = 0.f;
 		float coatSpecularLevel = 0.f;
 		std::array<float, 3> fuzzColor = { 0.f, 0.f, 0.f };
-		float fuzzWeight = 0.;
+		float fuzzWeight = 0.f;
+		float screenSpaceScale = 0.f;
+		float logMicrofacetDensity = 0.f;
+		float microfacetRoughness = 0.f;
+		float densityRandomization = 0.f;
 		std::array<float, 3> projectedMaterialBaseColorScale = { 0.f, 0.f, 0.f };
 		float projectedMaterialRoughness = 0.f;
 		float projectedMaterialSpecularLevel = 0.f;
@@ -65,6 +70,10 @@ std::uint32_t BSLightingShaderMaterialPBR::ComputeCRC32(uint32_t srcHash)
 	hashes.fuzzColor[1] = fuzzColor[1] * 100.f;
 	hashes.fuzzColor[2] = fuzzColor[2] * 100.f;
 	hashes.fuzzWeight = fuzzWeight * 100.f;
+	hashes.screenSpaceScale = glintParameters.screenSpaceScale * 100.f;
+	hashes.logMicrofacetDensity = glintParameters.logMicrofacetDensity * 100.f;
+	hashes.microfacetRoughness = glintParameters.microfacetRoughness * 100.f;
+	hashes.densityRandomization = glintParameters.densityRandomization * 100.f;
 	hashes.projectedMaterialBaseColorScale[0] = projectedMaterialBaseColorScale[0] * 100.f;
 	hashes.projectedMaterialBaseColorScale[1] = projectedMaterialBaseColorScale[1] * 100.f;
 	hashes.projectedMaterialBaseColorScale[2] = projectedMaterialBaseColorScale[2] * 100.f;
@@ -134,6 +143,10 @@ void BSLightingShaderMaterialPBR::OnLoadTextureSet(std::uint64_t arg1, RE::BSTex
 							fuzzWeight = textureSetData->fuzzWeight;
 						}
 					}
+
+					if (pbrFlags.any(PBRFlags::Glint)) {
+						glintParameters = textureSetData->glintParameters;
+					}
 				}
 			}
 		}
@@ -226,6 +239,11 @@ void BSLightingShaderMaterialPBR::LoadBinary(RE::NiStream& stream)
 			parameters[2] };
 		fuzzWeight = parameters[3];
 
+		glintParameters.screenSpaceScale = parameters[0];
+		glintParameters.logMicrofacetDensity = parameters[1];
+		glintParameters.microfacetRoughness = parameters[2];
+		glintParameters.densityRandomization = parameters[3];
+
 		if (stream.header.version > 0x4A) {
 			float dummy;
 			stream.iStr->read(&dummy, 1);
@@ -301,4 +319,9 @@ const RE::NiColor& BSLightingShaderMaterialPBR::GetFuzzColor() const
 float BSLightingShaderMaterialPBR::GetFuzzWeight() const
 {
 	return fuzzWeight;
+}
+
+const GlintParameters& BSLightingShaderMaterialPBR::GetGlintParameters() const
+{
+	return glintParameters;
 }
\ No newline at end of file
diff --git a/src/TruePBR/BSLightingShaderMaterialPBR.h b/src/TruePBR/BSLightingShaderMaterialPBR.h
index bacc25a34..065e6e633 100644
--- a/src/TruePBR/BSLightingShaderMaterialPBR.h
+++ b/src/TruePBR/BSLightingShaderMaterialPBR.h
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "TruePBR.h"
+
 enum class PBRFlags : uint32_t
 {
 	Subsurface = 1 << 0,
@@ -9,6 +11,7 @@ enum class PBRFlags : uint32_t
 	CoatNormal = 1 << 4,
 	Fuzz = 1 << 5,
 	HairMarschner = 1 << 6,
+	Glint = 1 << 7,
 };
 
 enum class PBRShaderFlags : uint32_t
@@ -24,6 +27,7 @@ enum class PBRShaderFlags : uint32_t
 	CoatNormal = 1 << 8,
 	Fuzz = 1 << 9,
 	HairMarschner = 1 << 10,
+	Glint = 1 << 11,
 };
 
 class BSLightingShaderMaterialPBR : public RE::BSLightingShaderMaterialBase
@@ -72,6 +76,8 @@ class BSLightingShaderMaterialPBR : public RE::BSLightingShaderMaterialBase
 	const RE::NiColor& GetFuzzColor() const;
 	float GetFuzzWeight() const;
 
+	const GlintParameters& GetGlintParameters() const;
+
 	// members
 	RE::BSShaderMaterial::Feature loadedWithFeature = RE::BSShaderMaterial::Feature::kDefault;
 
@@ -87,6 +93,8 @@ class BSLightingShaderMaterialPBR : public RE::BSLightingShaderMaterialBase
 	float projectedMaterialRoughness = 1.f;
 	float projectedMaterialSpecularLevel = 0.04f;
 
+	GlintParameters glintParameters;
+
 	// Roughness in r, metallic in g, AO in b, nonmetal reflectance in a
 	RE::NiPointer<RE::NiSourceTexture> rmaosTexture;
 
diff --git a/src/TruePBR/BSLightingShaderMaterialPBRLandscape.cpp b/src/TruePBR/BSLightingShaderMaterialPBRLandscape.cpp
index 7826c748a..42b79a9df 100644
--- a/src/TruePBR/BSLightingShaderMaterialPBRLandscape.cpp
+++ b/src/TruePBR/BSLightingShaderMaterialPBRLandscape.cpp
@@ -45,6 +45,7 @@ void BSLightingShaderMaterialPBRLandscape::CopyMembers(RE::BSShaderMaterial* tha
 	pbrThat->terrainTexOffsetX = terrainTexOffsetX;
 	pbrThat->terrainTexOffsetY = terrainTexOffsetY;
 	pbrThat->terrainTexFade = terrainTexFade;
+	pbrThat->glintParameters = glintParameters;
 }
 
 RE::BSShaderMaterial::Feature BSLightingShaderMaterialPBRLandscape::GetFeature() const
@@ -129,4 +130,14 @@ uint32_t BSLightingShaderMaterialPBRLandscape::GetTextures(RE::NiSourceTexture**
 	}
 
 	return textureIndex;
+}
+
+bool BSLightingShaderMaterialPBRLandscape::HasGlint() const
+{
+	for (uint32_t textureIndex = 0; textureIndex < numLandscapeTextures; ++textureIndex) {
+		if (glintParameters[textureIndex].enabled) {
+			return true;
+		}
+	}
+	return false;
 }
\ No newline at end of file
diff --git a/src/TruePBR/BSLightingShaderMaterialPBRLandscape.h b/src/TruePBR/BSLightingShaderMaterialPBRLandscape.h
index 45c07ac40..98b82fafd 100644
--- a/src/TruePBR/BSLightingShaderMaterialPBRLandscape.h
+++ b/src/TruePBR/BSLightingShaderMaterialPBRLandscape.h
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "TruePBR.h"
+
 class BSLightingShaderMaterialPBRLandscape : public RE::BSLightingShaderMaterialBase
 {
 public:
@@ -25,6 +27,8 @@ class BSLightingShaderMaterialPBRLandscape : public RE::BSLightingShaderMaterial
 
 	static BSLightingShaderMaterialPBRLandscape* Make();
 
+	bool HasGlint() const;
+
 	// members
 	std::uint32_t numLandscapeTextures = 0;
 
@@ -48,6 +52,8 @@ class BSLightingShaderMaterialPBRLandscape : public RE::BSLightingShaderMaterial
 	std::array<RE::NiPointer<RE::NiSourceTexture>, NumTiles> landscapeRMAOSTextures;
 	std::array<float, NumTiles> displacementScales;
 	std::array<float, NumTiles> specularLevels;
+
+	std::array<GlintParameters, NumTiles> glintParameters;
 };
 static_assert(offsetof(BSLightingShaderMaterialPBRLandscape, terrainOverlayTexture) == offsetof(RE::BSLightingShaderMaterialLandscape, terrainOverlayTexture));
 static_assert(offsetof(BSLightingShaderMaterialPBRLandscape, terrainNoiseTexture) == offsetof(RE::BSLightingShaderMaterialLandscape, terrainNoiseTexture));