Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
static const float DEG2RAD = 0.01745329251;
static const float RAD2DEG = 57.2957795131;

Texture2D<float4> _Glint2023NoiseMap : register(t28);

//=======================================================================================
Expand Down Expand Up @@ -226,10 +223,25 @@ void UnpackFloatParallel4(float4 input, out float4 a, out float4 b)
//=======================================================================================
// GLINTS TEST NOVEMBER 2022
//=======================================================================================
void CustomRand4Texture(float2 slope, float2 slopeRandOffset, out float4 outUniform, out float4 outGaussian, out float2 slopeLerp)

struct GlintInput
{
int2 size = snowSettings.Glint2023NoiseMapSize.rr;
float2 slope2 = abs(slope) / snowSettings.MicrofacetRoughness;
bool enabled;
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)
{
int2 size = 512;
float2 slope2 = abs(slope) / params.MicrofacetRoughness;
slope2 = slope2 + (slopeRandOffset * size);
slopeLerp = frac(slope2);
int2 slopeCoord = int2(floor(slope2)) % size;
Expand All @@ -253,7 +265,7 @@ float GenerateAngularBinomialValueForSurfaceCell(float4 randB, float4 randG, flo
return result;
}

float SampleGlintGridSimplex(float2 uv, uint gridSeed, float2 slope, float footprintArea, float targetNDF, float gridWeight)
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);
Expand All @@ -276,17 +288,17 @@ float SampleGlintGridSimplex(float2 uv, uint gridSeed, float2 slope, float footp
// Get per surface cell per slope cell random numbers
float4 rand0SlopesB, rand1SlopesB, rand2SlopesB, rand0SlopesG, rand1SlopesG, rand2SlopesG;
float2 slopeLerp0, slopeLerp1, slopeLerp2;
CustomRand4Texture(slope, rand0.yz, rand0SlopesB, rand0SlopesG, slopeLerp0);
CustomRand4Texture(slope, rand1.yz, rand1SlopesB, rand1SlopesG, slopeLerp1);
CustomRand4Texture(slope, rand2.yz, rand2SlopesB, rand2SlopesG, 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), snowSettings.LogMicrofacetDensity.r, snowSettings.DensityRandomization), 0.0, 50.0); // TODO : optimize sampleNormalDist
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(0.0.rrr, footprintArea.rrr * exp(logDensityRand));
float3 microfacetCountBlended = microfacetCount * gridWeight;

// Compute binomial properties
float hitProba = snowSettings.MicrofacetRoughness * targetNDF; // probability of hitting desired half vector in NDF distribution
float hitProba = params.MicrofacetRoughness * targetNDF; // probability of hitting desired half vector in NDF distribution
float3 footprintOneHitProba = (1.0 - pow(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
Expand Down Expand Up @@ -437,17 +449,20 @@ void GetAnisoCorrectingGridTetrahedron(bool centerSpecialCase, inout float theta
return;
}

float4 SampleGlints2023NDF(float3 localHalfVector, float targetNDF, float maxNDF, float2 uv, float2 duvdx, float2 duvdy)
float4 SampleGlints2023NDF(GlintInput params, float targetNDF, float maxNDF)
{
static const float DEG2RAD = 0.01745329251;
static const float RAD2DEG = 57.2957795131;

// ACCURATE PIXEL FOOTPRINT ELLIPSE
float2 ellipseMajor, ellipseMinor;
GetGradientEllipse(duvdx, duvdy, ellipseMajor, ellipseMinor);
GetGradientEllipse(params.duvdx, params.duvdy, ellipseMajor, ellipseMinor);
float ellipseRatio = length(ellipseMajor) / length(ellipseMinor);

// SHARED GLINT NDF VALUES
float halfScreenSpaceScaler = snowSettings.ScreenSpaceScale * 0.5;
float halfScreenSpaceScaler = params.ScreenSpaceScale * 0.5;
float footprintArea = length(ellipseMajor) * halfScreenSpaceScaler * length(ellipseMinor) * halfScreenSpaceScaler * 4.0;
float2 slope = localHalfVector.xy; // Orthogrtaphic slope projected grid
float2 slope = params.H.xy; // Orthogrtaphic slope projected grid
float rescaledTargetNDF = targetNDF / maxNDF;

// MANUAL LOD COMPENSATION
Expand Down Expand Up @@ -502,19 +517,19 @@ float4 SampleGlints2023NDF(float3 localHalfVector, float targetNDF, float maxNDF
tetraC.x = (tetraC.y == 0) ? 3 : tetraC.x;
tetraD.x = (tetraD.y == 0) ? 3 : tetraD.x;
}
float2 uvRotA = RotateUV(uv, thetaBins[tetraA.x] * DEG2RAD, 0.0.rr);
float2 uvRotB = RotateUV(uv, thetaBins[tetraB.x] * DEG2RAD, 0.0.rr);
float2 uvRotC = RotateUV(uv, thetaBins[tetraC.x] * DEG2RAD, 0.0.rr);
float2 uvRotD = RotateUV(uv, thetaBins[tetraD.x] * DEG2RAD, 0.0.rr);
float2 uvRotA = RotateUV(params.uv, thetaBins[tetraA.x] * DEG2RAD, 0.0.rr);
float2 uvRotB = RotateUV(params.uv, thetaBins[tetraB.x] * DEG2RAD, 0.0.rr);
float2 uvRotC = RotateUV(params.uv, thetaBins[tetraC.x] * DEG2RAD, 0.0.rr);
float2 uvRotD = RotateUV(params.uv, thetaBins[tetraD.x] * DEG2RAD, 0.0.rr);

// SAMPLE GLINT GRIDS
uint gridSeedA = HashWithoutSine13(float3(log2(divLods[tetraA.z]), thetaBins[tetraA.x] % 360, ratios[tetraA.y])) * 4294967296.0;
uint gridSeedB = HashWithoutSine13(float3(log2(divLods[tetraB.z]), thetaBins[tetraB.x] % 360, ratios[tetraB.y])) * 4294967296.0;
uint gridSeedC = HashWithoutSine13(float3(log2(divLods[tetraC.z]), thetaBins[tetraC.x] % 360, ratios[tetraC.y])) * 4294967296.0;
uint gridSeedD = HashWithoutSine13(float3(log2(divLods[tetraD.z]), thetaBins[tetraD.x] % 360, ratios[tetraD.y])) * 4294967296.0;
float sampleA = SampleGlintGridSimplex(uvRotA / divLods[tetraA.z] / float2(1.0, ratios[tetraA.y]), gridSeedA, slope, ratios[tetraA.y] * footprintAreas[tetraA.z], rescaledTargetNDF, tetraBarycentricWeights.x);
float sampleB = SampleGlintGridSimplex(uvRotB / divLods[tetraB.z] / float2(1.0, ratios[tetraB.y]), gridSeedB, slope, ratios[tetraB.y] * footprintAreas[tetraB.z], rescaledTargetNDF, tetraBarycentricWeights.y);
float sampleC = SampleGlintGridSimplex(uvRotC / divLods[tetraC.z] / float2(1.0, ratios[tetraC.y]), gridSeedC, slope, ratios[tetraC.y] * footprintAreas[tetraC.z], rescaledTargetNDF, tetraBarycentricWeights.z);
float sampleD = SampleGlintGridSimplex(uvRotD / divLods[tetraD.z] / float2(1.0, ratios[tetraD.y]), gridSeedD, slope, ratios[tetraD.y] * footprintAreas[tetraD.z], rescaledTargetNDF, tetraBarycentricWeights.w);
return (sampleA + sampleB + sampleC + sampleD) * (1.0 / snowSettings.MicrofacetRoughness) * maxNDF;
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 (sampleA + sampleB + sampleC + sampleD) * (1.0 / params.MicrofacetRoughness) * maxNDF;
}
48 changes: 32 additions & 16 deletions package/Shaders/Common/PBR.hlsli
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "SnowSparkles/Glints2023.hlsli"
#include "Common/Glints/Glints2023.hlsli"

#define TruePBR_HasEmissive (1 << 0)
#define TruePBR_HasDisplacement (1 << 1)
Expand Down Expand Up @@ -76,10 +76,10 @@ namespace PBR
surfaceProperties.FuzzColor = 0;
surfaceProperties.FuzzWeight = 0;

surfaceProperties.GlintScreenSpaceScale = 0;
surfaceProperties.GlintLogMicrofacetDensity = 0;
surfaceProperties.GlintMicrofacetRoughness = 0;
surfaceProperties.GlintDensityRandomization = 0;
surfaceProperties.GlintScreenSpaceScale = 1.5;
surfaceProperties.GlintLogMicrofacetDensity = 18.0;
surfaceProperties.GlintMicrofacetRoughness = 0.015;
surfaceProperties.GlintDensityRandomization = 2.0;

return surfaceProperties;
}
Expand Down Expand Up @@ -153,17 +153,13 @@ namespace PBR
return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);
}

float3 GetSpecularDirectLightMultiplierMicrofacet(float roughness, float3 specularColor, float3 H, float NdotL, float NdotV, float NdotH, float VdotH, float2 uv, out float3 F)
float3 GetSpecularDirectLightMultiplierMicrofacet(float roughness, float3 specularColor, float NdotL, float NdotV, float NdotH, float VdotH, GlintInput glintInput, out float3 F)
{
float D = GetNormalDistributionFunctionGGX(roughness, NdotH);
#if defined(LANDSCAPE)
[branch] if (PBRFlags & TruePBR_LandGlint)
#else
[branch] if (PBRFlags & TruePBR_Glint)
#endif
[branch] if (glintInput.enabled)
{
float D_max = GetNormalDistributionFunctionGGX(roughness, 1);
D = SampleGlints2023NDF(H, D, D_max, uv, ddx(uv), ddy(uv));
D = SampleGlints2023NDF(glintInput, D, D_max);
}
float G = GetVisibilityFunctionSmithJointApprox(roughness, NdotV, NdotL);
F = GetFresnelFactorSchlick(specularColor, VdotH);
Expand Down Expand Up @@ -373,7 +369,6 @@ namespace PBR
specular = 0;

float3 H = normalize(V + L);
float3 localH = mul(H, tbn);

float NdotL = dot(N, L);
float NdotV = dot(N, V);
Expand All @@ -397,8 +392,26 @@ namespace PBR
{
diffuse += lightColor * satNdotL;

GlintInput glintInput;
#if defined(LANDSCAPE)
glintInput.enabled = PBRFlags & TruePBR_LandGlint;
#else
glintInput.enabled = PBRFlags & TruePBR_Glint;
#endif
[branch] if (glintInput.enabled)
{
glintInput.H = mul(H, tbn);
glintInput.uv = uv;
glintInput.duvdx = ddx(uv);
glintInput.duvdy = ddy(uv);
glintInput.ScreenSpaceScale = surfaceProperties.GlintScreenSpaceScale;
glintInput.LogMicrofacetDensity = surfaceProperties.GlintLogMicrofacetDensity;
glintInput.MicrofacetRoughness = surfaceProperties.GlintMicrofacetRoughness;
glintInput.DensityRandomization = surfaceProperties.GlintDensityRandomization;
}

float3 F;
specular += PI * GetSpecularDirectLightMultiplierMicrofacet(surfaceProperties.Roughness, surfaceProperties.F0, H, satNdotL, satNdotV, satNdotH, satVdotH, uv, F) * lightColor * satNdotL;
specular += PI * GetSpecularDirectLightMultiplierMicrofacet(surfaceProperties.Roughness, surfaceProperties.F0, satNdotL, satNdotV, satNdotH, satVdotH, glintInput, F) * lightColor * satNdotL;

float2 specularBRDF = 0;
[branch] if (pbrSettings.UseMultipleScattering)
Expand Down Expand Up @@ -444,7 +457,8 @@ namespace PBR
}

float3 coatF;
float3 coatSpecular = PI * GetSpecularDirectLightMultiplierMicrofacet(surfaceProperties.CoatRoughness, surfaceProperties.CoatF0, H, coatNdotL, coatNdotV, coatNdotH, coatVdotH, uv, coatF) * coatLightColor * coatNdotL;
glintInput.H = mul(coatH, tbn);
float3 coatSpecular = PI * GetSpecularDirectLightMultiplierMicrofacet(surfaceProperties.CoatRoughness, surfaceProperties.CoatF0, coatNdotL, coatNdotV, coatNdotH, coatVdotH, glintInput, coatF) * coatLightColor * coatNdotL;

float3 layerAttenuation = 1 - coatF * surfaceProperties.CoatStrength;
diffuse *= layerAttenuation;
Expand All @@ -468,8 +482,10 @@ namespace PBR
float NdotH = saturate(dot(N, H));
float VdotH = saturate(dot(V, H));

GlintInput glintInput;
glintInput.enabled = false;
float3 wetnessF;
float3 wetnessSpecular = PI * GetSpecularDirectLightMultiplierMicrofacet(roughness, wetnessF0, H, NdotL, NdotV, NdotH, VdotH, 0, wetnessF) * lightColor * NdotL;
float3 wetnessSpecular = PI * GetSpecularDirectLightMultiplierMicrofacet(roughness, wetnessF0, NdotL, NdotV, NdotH, VdotH, glintInput, wetnessF) * lightColor * NdotL;

return wetnessSpecular * wetnessStrength;
}
Expand Down
2 changes: 2 additions & 0 deletions src/Deferred.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "ShaderCache.h"
#include "State.h"
#include "TruePBR.h"
#include "Util.h"

#include "Features/DynamicCubemaps.h"
Expand Down Expand Up @@ -265,6 +266,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();
Expand Down
80 changes: 80 additions & 0 deletions src/TruePBR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "Hooks.h"
#include "ShaderCache.h"
#include "State.h"
#include "Util.h"

namespace PNState
{
Expand Down Expand Up @@ -202,6 +203,15 @@ void TruePBR::SaveSettings(json& o_json)
o_json["Ambient Light Color Multiplier"] = globalPBRAmbientLightColorMultiplier;
}

void TruePBR::PrePass()
{
auto context = State::GetSingleton()->context;
if (context && glintsNoiseTexture) {
ID3D11ShaderResourceView* srv = glintsNoiseTexture->srv.get();
context->PSSetShaderResources(28, 1, &srv);
}
}

void TruePBR::SetupFrame()
{
float newDirectionalLightScale = 1.f;
Expand Down Expand Up @@ -237,6 +247,76 @@ void TruePBR::SetupFrame()
settings.ambientLightColorMultiplier = globalPBRAmbientLightColorMultiplier * weatherPBRDirectionalAmbientLightColorMultiplier;
}

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::SetupTextureSetData()
{
logger::info("[TruePBR] loading PBR texture set configs");
Expand Down
17 changes: 12 additions & 5 deletions src/TruePBR.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#pragma once

#include "Buffer.h"

struct GlintParameters
{
bool enabled = false;
float screenSpaceScale = 1.f;
float logMicrofacetDensity = 1.f;
float microfacetRoughness = 1.f;
float densityRandomization = 1.f;
float screenSpaceScale = 1.5f;
float logMicrofacetDensity = 18.f;
float microfacetRoughness = .015f;
float densityRandomization = 2.f;
};

struct TruePBR
Expand All @@ -24,11 +26,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;
Expand All @@ -37,7 +43,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;
Expand All @@ -46,6 +52,7 @@ struct TruePBR
uint32_t useMultiBounceAO = true;
uint32_t pad[3];
} settings{};
static_assert(sizeof(Settings) % 16 == 0);

struct PBRTextureSetData
{
Expand Down