Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
fb1ad41
v1
davo0411 Apr 27, 2026
8ec7e2b
Update Lighting.hlsl
davo0411 Apr 27, 2026
7f1c49b
perf
davo0411 Apr 27, 2026
a7a1e13
Update ExtendedMaterials.hlsli
davo0411 Apr 27, 2026
9b311b7
style: 🎨 apply pre-commit.ci formatting
pre-commit-ci[bot] Apr 27, 2026
5159df9
more
davo0411 Apr 27, 2026
3a802f7
moree
davo0411 Apr 27, 2026
f940c45
Merge branch 'tv-perf-3' of https://github.com/davo0411/skyrim-commun…
davo0411 Apr 27, 2026
a9d90cd
style: 🎨 apply pre-commit.ci formatting
pre-commit-ci[bot] Apr 27, 2026
7d576a6
chat changes
davo0411 Apr 28, 2026
6b5248a
shadow fixes
davo0411 Apr 28, 2026
bc88402
more changes
davo0411 Apr 28, 2026
2a8cb8f
style: 🎨 apply pre-commit.ci formatting
pre-commit-ci[bot] Apr 28, 2026
5fd29b5
perf: consistent world texel density
doodlum Apr 30, 2026
30237cf
Merge branch 'dev' into tv-perf-3
davo0411 May 4, 2026
a19d072
fixes a bajillion
davo0411 May 4, 2026
0e17ca6
remove distance shenanigans
davo0411 May 4, 2026
b7164bd
fix
davo0411 May 4, 2026
806f5f4
shader refactors
davo0411 May 4, 2026
04297e0
bit better quality
davo0411 May 4, 2026
0c51a7d
fixes
davo0411 May 4, 2026
85ad499
Update ExtendedMaterials.ini
davo0411 May 7, 2026
ad3e62e
x4000 fix
davo0411 May 7, 2026
362f57b
better stepping math
davo0411 May 7, 2026
fff9c50
Merge branch 'dev' into tv-perf-3
davo0411 May 7, 2026
8659663
Merge branch 'dev' into tv-perf-3
davo0411 May 7, 2026
5b0129e
Merge branch 'dev' into tv-perf-3
davo0411 May 11, 2026
c807e3e
fixes minor
davo0411 May 15, 2026
936a3cd
Merge branch 'tv-perf-3' of https://github.com/davo0411/skyrim-commun…
davo0411 May 15, 2026
06d56a1
Merge branch 'dev' into tv-perf-3
davo0411 May 26, 2026
32903c6
Merge branch 'dev' into tv-perf-3
davo0411 May 31, 2026
2c45e9b
fixes
davo0411 Jun 1, 2026
8e33d9e
perf
davo0411 Jun 1, 2026
9266535
Update ExtendedMaterialsParallaxCore.hlsli
davo0411 Jun 1, 2026
c75fc95
Merge branch 'dev' into tv-perf-3
davo0411 Jun 3, 2026
6425495
Merge branch 'dev' into tv-perf-3
davo0411 Jun 4, 2026
0331f04
Update en.json
davo0411 Jun 4, 2026
ba28328
Merge branch 'tv-perf-3' of https://github.com/davo0411/skyrim-commun…
davo0411 Jun 4, 2026
502efab
Merge branch 'dev' into tv-perf-3
davo0411 Jun 6, 2026
e19dcf4
Merge branch 'dev' into tv-perf-3
davo0411 Jun 6, 2026
0faddc7
Merge branch 'dev' into tv-perf-3
davo0411 Jun 8, 2026
cc1615b
fix: cn translation & localisation merge
davo0411 Jun 8, 2026
4b091ab
fix: steps at grazing angles, higher quality
davo0411 Jun 8, 2026
3ca3517
Merge branch 'dev' into tv-perf-3
davo0411 Jun 12, 2026
9aabefb
x4000 fix
davo0411 Jun 12, 2026
fd457cc
Merge branch 'dev' into tv-perf-3
davo0411 Jun 12, 2026
e0a99b1
compile fixes
davo0411 Jun 12, 2026
b87abec
Merge branch 'dev' into tv-perf-3
davo0411 Jun 12, 2026
a77d5a9
nuke mip reduction in samples
davo0411 Jun 12, 2026
c84068e
Merge branch 'dev' into tv-perf-3
davo0411 Jun 15, 2026
8c83990
Merge branch 'dev' into tv-perf-3
davo0411 Jun 17, 2026
ecae673
Merge branch 'dev' into tv-perf-3
davo0411 Jun 17, 2026
3a49f73
Update ru.json
davo0411 Jun 17, 2026
1369e11
Merge branch 'dev' into tv-perf-3
davo0411 Jul 10, 2026
2823197
Merge branch 'dev' into tv-perf-3
davo0411 Jul 13, 2026
1c0fe9d
fixes, comment fixes
davo0411 Jul 13, 2026
1cf8e87
Update TerrainVariation.hlsli
davo0411 Jul 13, 2026
986f6e1
fable run
davo0411 Jul 13, 2026
0c23f4b
x4000 fix
davo0411 Jul 13, 2026
60ecabb
Merge branch 'dev' into tv-perf-3
davo0411 Jul 19, 2026
a57c0d4
chore: remove VR_opt define from parallax.
davo0411 Jul 19, 2026
1ae601a
fix: mip increasing with upscaling render % lowering
davo0411 Jul 19, 2026
257de32
perf: optimise parallax, remove grazing "opts", much better quality
davo0411 Jul 19, 2026
30a467e
fix: terrain bobbing
davo0411 Jul 19, 2026
d2dd8de
chore: doxygen
davo0411 Jul 19, 2026
78ba05d
fix: clean tv file, add dither
davo0411 Jul 19, 2026
b3b39cb
fix: parallax warping fix
davo0411 Jul 19, 2026
81bcd58
perf: opt pass
davo0411 Jul 19, 2026
cddc6fc
fix: x4000 warnings
davo0411 Jul 20, 2026
5c591c2
Merge branch 'dev' into tv-perf-3
davo0411 Jul 20, 2026
d7b46bb
perf: ai comment fixes
davo0411 Jul 26, 2026
2748221
Merge branch 'dev' into tv-perf-3
doodlum Jul 26, 2026
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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
/**
* @file ExtendedMaterialsParallaxCore.hlsli
* @brief Parallax occlusion march and refine (namespace ExtendedMaterials).
*/

#ifndef EXTENDED_MATERIALS_PARALLAX_CORE_HLSLI
#define EXTENDED_MATERIALS_PARALLAX_CORE_HLSLI

/**
* @brief Computes parallax-occlusion displaced UVs.
* @details Transforms the view into tangent space, marches the height slab in
* steps of four, then refines the hit with binary search and secant.
* Landscape uses a softened view-Z denominator (with FlattenAmount) to
* limit silhouette swim, sizes the step count from UV span in texels,
* and samples height on a coarse march mip for march / contact / secant.
* @param pixelOffset Hit depth along the height slab in [0,1].
* @param weights [LANDSCAPE] Layer weights after optional height blending.
* @return Displaced texture coordinates.
*/
#if defined(LANDSCAPE)
float2 GetParallaxCoords(PS_INPUT input, float2 coords, float mipLevels[6], float maxTexDim, float3 viewDir, float3x3 tbn, float noise, DisplacementParams params[6],
StochasticOffsets sharedOffset,
out float pixelOffset,
out float weights[6])
#else
float2 GetParallaxCoords(float2 coords, float mipLevel, float3 viewDir, float3x3 tbn, float noise, Texture2D<float4> tex, SamplerState texSampler, uint channel, DisplacementParams params, out float pixelOffset)
#endif
{
pixelOffset = 0.0;
float3 viewDirTS = normalize(mul(tbn, viewDir));
float ndotv = saturate(viewDirTS.z);

#if defined(LANDSCAPE)
/** Softened view-Z with FlattenAmount; abs + floor limit silhouette stretch. */
float parallaxZ = max(abs(viewDirTS.z) * 0.7 + 0.3 + params[0].FlattenAmount, 0.0625);
float2 parallaxDir = viewDirTS.xy / parallaxZ;
#else
/** Same soft denom as landscape; abs avoids negative-TS-Z blowups on curved meshes. */
float parallaxZ = max(abs(viewDirTS.z) * 0.7 + 0.3 + params.FlattenAmount, 0.0625);
float2 parallaxDir = viewDirTS.xy / parallaxZ;
#endif

#if defined(LANDSCAPE)
float viewDist = length(input.WorldPosition.xyz);
float blendFactor = SharedData::extendedMaterialSettings.EnableHeightBlending ? 1.0 : 0.0;
float4 w1 = input.LandBlendWeights1;
float2 w2 = input.LandBlendWeights2.xy;
const float marchHeightBlendFactor = 0.0;

weights[0] = w1.x;
weights[1] = w1.y;
weights[2] = w1.z;
weights[3] = w1.w;
weights[4] = w2.x;
weights[5] = w2.y;

# if defined(TRUE_PBR)
float scale = TerrainMaxWeightedHeightScaleW(w1, w2, params);
float terrainHeightNormMul = rcp(max(scale, 1e-4));
float maxHeight = 0.1 * scale;
# else
float scale = 1;
float terrainHeightNormMul = 1.0;
float maxHeight = 0.1 * scale;
# endif
#else
float scale = params.HeightScale;
float maxHeight = 0.1 * scale;
#endif
float minHeight = maxHeight * 0.5;
float2 resultCoords = coords;

#if defined(LANDSCAPE) && defined(TRUE_PBR)
[branch] if (scale <= 0.001) {
pixelOffset = 0.0;
if (SharedData::extendedMaterialSettings.EnableHeightBlending) {
float unusedHeight = GetTerrainHeight(noise, input, coords, mipLevels, params, blendFactor, w1, w2, sharedOffset, weights);
}
} else
#elif !defined(LANDSCAPE)
[branch] if (scale <= 0.001) {
pixelOffset = 0.0;
} else
#endif
{
const uint minSteps = 4;
#if defined(LANDSCAPE)
const uint maxStepsCap = 32;
#else
const uint maxStepsCap = 32;
const float baseMaxSteps = 8;
#endif

/** Squared grazing factor; near head-on stays cheap. */
float grazing = (1.0 - ndotv);
grazing *= grazing;

#if defined(LANDSCAPE)
float marchMip = ComputeParallaxMarchMip(mipLevels[0], viewDist);
float marchMipLevels[6];
float parallaxLODMip = mipLevels[0];
[unroll] for (uint mi = 0; mi < 6; mi++)
marchMipLevels[mi] = marchMip;
#else
float parallaxLODMip = mipLevel;
float marchMipLevel = ComputeParallaxMarchMip(mipLevel, 0.0);
#endif
float distStepScale = lerp(0.25, 1.0, saturate((3.0 - parallaxLODMip) * (1.0 / 3.0)));

#if defined(LANDSCAPE)
/**
* Step count from UV travel in texels (and a grazing angle floor),
* so grazing rays do not skip height features between samples.
*/
float uvMarchSpan = dot(abs(parallaxDir), maxHeight + minHeight);
float texelsPerStep = lerp(3.5, 1.75, saturate(grazing));
uint uvSteps = (uint)(uvMarchSpan * maxTexDim * rcp(texelsPerStep) * distStepScale + 0.5);
uint angleSteps = (uint)(lerp((float)minSteps, (float)maxStepsCap, grazing) * distStepScale + 0.5);
uint numSteps = max(minSteps, max(uvSteps, angleSteps));
numSteps = min(numSteps, maxStepsCap);
numSteps = (numSteps + 2) & ~3;
#else
float grazingStepBoost = lerp(1.0, 1.65, grazing);
float angleStepMul = clamp(0.5 * rcp(max(ndotv, 0.0625)), 0.5, 2.5);
uint numSteps = max(minSteps, (uint)(scale * baseMaxSteps * angleStepMul * distStepScale * grazingStepBoost));
numSteps = min(numSteps, maxStepsCap);
numSteps = (numSteps + 2) & ~3;
#endif

uint contactIters = grazing > 0.2 ? 4u : 2u;
uint secantIters = grazing > 0.25 ? 2u : 1u;

float stepSize = rcp((float)numSteps);

float2 offsetPerStep = parallaxDir * maxHeight * stepSize;
#if defined(LANDSCAPE)
/** Full-step ray-start dither breaks residual step bands on terrain. */
float rayDither = saturate(noise);
float2 prevOffset = parallaxDir * minHeight + coords.xy - offsetPerStep * rayDither;
float prevBound = 1.0 - rayDither * stepSize;
#else
float2 prevOffset = parallaxDir * minHeight + coords.xy;
float prevBound = 1.0;
#endif
float prevHeight = 1.0;

float2 pt1 = 0;
float2 pt2 = 0;
bool intersectionFound = false;

[loop] while (numSteps > 0)
{
float4 currentOffset[2];
currentOffset[0] = prevOffset.xyxy - float4(1, 1, 2, 2) * offsetPerStep.xyxy;
currentOffset[1] = prevOffset.xyxy - float4(3, 3, 4, 4) * offsetPerStep.xyxy;
float4 currentBound = prevBound.xxxx - float4(1, 2, 3, 4) * stepSize;

float4 currHeight;
#if defined(LANDSCAPE)
currHeight = GetTerrainHeightQuadRayMarch(noise, input, currentOffset[0].xy, currentOffset[0].zw, currentOffset[1].xy, currentOffset[1].zw, marchMipLevels, params, marchHeightBlendFactor, w1, w2, sharedOffset, weights) * terrainHeightNormMul + 0.5;
#else
currHeight.x = tex.SampleLevel(texSampler, currentOffset[0].xy, marchMipLevel)[channel];
currHeight.y = tex.SampleLevel(texSampler, currentOffset[0].zw, marchMipLevel)[channel];
currHeight.z = tex.SampleLevel(texSampler, currentOffset[1].xy, marchMipLevel)[channel];
currHeight.w = tex.SampleLevel(texSampler, currentOffset[1].zw, marchMipLevel)[channel];

currHeight = AdjustDisplacementNormalized(currHeight, params);
#endif

bool4 testResult = currHeight >= currentBound;
[branch] if (any(testResult))
{
intersectionFound = true;
float2 outOffset;
[branch] if (testResult.x)
{
outOffset = prevOffset;
pt1 = float2(currentBound.x, currHeight.x);
pt2 = float2(prevBound, prevHeight);
}
else if (testResult.y)
{
outOffset = currentOffset[0].xy;
pt1 = float2(currentBound.y, currHeight.y);
pt2 = float2(currentBound.x, currHeight.x);
}
else if (testResult.z)
{
outOffset = currentOffset[0].zw;
pt1 = float2(currentBound.z, currHeight.z);
pt2 = float2(currentBound.y, currHeight.y);
}
else
{
outOffset = currentOffset[1].xy;
pt1 = float2(currentBound.w, currHeight.w);
pt2 = float2(currentBound.z, currHeight.z);
}
prevOffset = outOffset;
break;
}

prevOffset = currentOffset[1].zw;
prevBound = currentBound.w;
prevHeight = currHeight.w;
numSteps -= 4;
}

float parallaxAmount = 0.0;
[branch] if (intersectionFound)
{
float tNear = pt1.x;
float hNear = pt1.y;
float fNear = hNear - tNear;
float tFar = pt2.x;
float hFar = pt2.y;
float fFar = hFar - tFar;

/** Binary search on f(t) = h(t) - t before secant. */
[loop] for (uint c = 0; c < contactIters; c++)
{
float tMid = 0.5 * (tNear + tFar);
float2 midCoords = coords.xy + parallaxDir * (((1.0 - tMid) * -maxHeight) + minHeight);
float hMid = 0.0;
#if defined(LANDSCAPE)
hMid = GetTerrainHeight(noise, input, midCoords, marchMipLevels, params, marchHeightBlendFactor, w1, w2, sharedOffset, weights) * terrainHeightNormMul + 0.5;
#else
hMid = tex.SampleLevel(texSampler, midCoords, marchMipLevel)[channel];
hMid = AdjustDisplacementNormalized(hMid, params);
#endif
float fMid = hMid - tMid;
[branch] if (fMid >= 0.0)
{
tNear = tMid;
hNear = hMid;
fNear = fMid;
}
else
{
tFar = tMid;
hFar = hMid;
fFar = fMid;
}
}

/** Secant iterations on f(t) = h(t) - t. */
[loop] for (uint i = 0; i < secantIters; i++)
{
float denominator = fNear - fFar;
float r = abs(denominator) > EPSILON_DIVISION ? saturate(fNear / denominator) : 0.5;
float tSecant = lerp(tNear, tFar, r);
float2 secantCoords = coords.xy + parallaxDir * (((1.0 - tSecant) * -maxHeight) + minHeight);

float hSecant = 0.0;
#if defined(LANDSCAPE)
hSecant = GetTerrainHeight(noise, input, secantCoords, marchMipLevels, params, marchHeightBlendFactor, w1, w2, sharedOffset, weights) * terrainHeightNormMul + 0.5;
#else
hSecant = tex.SampleLevel(texSampler, secantCoords, marchMipLevel)[channel];
hSecant = AdjustDisplacementNormalized(hSecant, params);
#endif

float fSecant = hSecant - tSecant;
[branch] if (fSecant >= 0.0)
{
tNear = tSecant;
hNear = hSecant;
fNear = fSecant;
}
else
{
tFar = tSecant;
hFar = hSecant;
fFar = fSecant;
}
}

float denominator = fNear - fFar;
float r = abs(denominator) > EPSILON_DIVISION ? saturate(fNear / denominator) : 0.5;
parallaxAmount = lerp(tNear, tFar, r);
}

float offset = (1.0 - parallaxAmount) * -maxHeight + minHeight;
pixelOffset = saturate(parallaxAmount);
float2 finalCoords = parallaxDir * offset + coords.xy;
#if defined(LANDSCAPE)
if (SharedData::extendedMaterialSettings.EnableHeightBlending) {
float unusedHeight = GetTerrainHeight(noise, input, finalCoords, mipLevels, params, blendFactor, w1, w2, sharedOffset, weights);
}
#endif
resultCoords = finalCoords;
}
return resultCoords;
}

# if !defined(LANDSCAPE)
/**
* @brief Soft self-shadow from a height map along light direction L.
* @see https://advances.realtimerendering.com/s2006/Tatarchuk-POM.pdf
*/
float GetParallaxSoftShadowMultiplier(float2 coords, float mipLevel, float3 L, float sh0, Texture2D<float4> tex, SamplerState texSampler, uint channel, float quality, float noise, DisplacementParams params)
{
[branch] if (quality > 0.0)
{
uint tapCount = ParallaxShadowTapCount(quality);
float shadowStrength = ShadowIntensity * (4.0 / tapCount);
float2 rayDir = L.xy * 0.1 * params.HeightScale;
float4 multipliers = rcp((float4(1, 2, 3, 4) + noise));
float4 sh = sh0.xxxx;
sh.x = AdjustDisplacementNormalized(tex.SampleLevel(texSampler, coords + rayDir * multipliers.x, mipLevel)[channel], params);
if (quality > 0.25)
sh.y = AdjustDisplacementNormalized(tex.SampleLevel(texSampler, coords + rayDir * multipliers.y, mipLevel)[channel], params);
if (quality > 0.5)
sh.z = AdjustDisplacementNormalized(tex.SampleLevel(texSampler, coords + rayDir * multipliers.z, mipLevel)[channel], params);
if (quality > 0.75)
sh.w = AdjustDisplacementNormalized(tex.SampleLevel(texSampler, coords + rayDir * multipliers.w, mipLevel)[channel], params);
return 1.0 - saturate(dot(max(0, sh - sh0), shadowStrength));
}
return 1.0;
}

# endif

#endif // EXTENDED_MATERIALS_PARALLAX_CORE_HLSLI
Loading
Loading