Skip to content
Open
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
42 changes: 35 additions & 7 deletions CodeWalker.Shaders/BasicPS.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,43 @@

float4 main(VS_OUTPUT input) : SV_TARGET
{
// Calculate parallax offset if height mapping is enabled
float2 parallaxTexOffset = float2(0, 0);
float parallaxSelfShadow = 1.0;
if (EnableHeightMap && RenderMode == 0)
{
float3 viewDir = -normalize(input.CamRelPos); // Negate to get direction FROM surface TO camera
float3 norm0 = normalize(input.Normal);
float3 tang0 = normalize(input.Tangent.xyz);
float3 bitang0 = normalize(input.Bitangent.xyz);
parallaxTexOffset = ParallaxOffset(
Heightmap, TextureSS, input.Texcoord0,
viewDir, norm0, tang0, bitang0,
heightScale, heightBias);

// Parallax self-shadow, transform light dir to tangent space and trace
float3 tanLightDir;
tanLightDir.x = dot(tang0, GlobalLights.LightDir.xyz);
tanLightDir.y = dot(bitang0, GlobalLights.LightDir.xyz);
tanLightDir.z = dot(norm0, GlobalLights.LightDir.xyz);
float shadowAmount = TraceSelfShadow(Heightmap, TextureSS,
input.Texcoord0 + parallaxTexOffset,
tanLightDir, 1.0, heightScale);
parallaxSelfShadow = 1.0 - shadowAmount * PARALLAX_SELF_SHADOW_AMOUNT;
}

// Apply parallax offset to base texture coordinates
float2 texc0 = input.Texcoord0 + parallaxTexOffset;

float4 c = float4(0.5, 0.5, 0.5, 1);
if (RenderMode == 0) c = float4(1, 1, 1, 1);
if (EnableTexture > 0)
{
float2 texc = input.Texcoord0;
float2 texc = texc0;
if (RenderMode >= 5)
{
if (RenderSamplerCoord == 2) texc = input.Texcoord1;
else if (RenderSamplerCoord == 3) texc = input.Texcoord2;
if (RenderSamplerCoord == 2) texc = input.Texcoord1 + parallaxTexOffset;
else if (RenderSamplerCoord == 3) texc = input.Texcoord2 + parallaxTexOffset;
}

c = Colourmap.Sample(TextureSS, texc);
Expand Down Expand Up @@ -80,8 +108,8 @@ float4 main(VS_OUTPUT input) : SV_TARGET
if (RenderMode == 0)
{

float4 nv = Bumpmap.Sample(TextureSS, input.Texcoord0); //sample r1.xyzw, v2.xyxx, t3.xyzw, s3 (BumpSampler)
float4 sv = Specmap.Sample(TextureSS, input.Texcoord0); //sample r2.xyzw, v2.xyxx, t4.xyzw, s4 (SpecSampler)
float4 nv = Bumpmap.Sample(TextureSS, texc0); //sample r1.xyzw, v2.xyxx, t3.xyzw, s3 (BumpSampler)
float4 sv = Specmap.Sample(TextureSS, texc0); //sample r2.xyzw, v2.xyxx, t4.xyzw, s4 (SpecSampler)


float2 nmv = nv.xy;
Expand All @@ -92,7 +120,7 @@ float4 main(VS_OUTPUT input) : SV_TARGET
if (EnableDetailMap)
{
//detail normalmapp
r0.xy = input.Texcoord0 * detailSettings.zw; //mul r0.xy, v2.xyxx, detailSettings.zwzz
r0.xy = texc0 * detailSettings.zw; //mul r0.xy, v2.xyxx, detailSettings.zwzz
r0.zw = r0.xy * 3.17; //mul r0.zw, r0.xxxy, l(0.000000, 0.000000, 3.170000, 3.170000)
r0.xy = Detailmap.Sample(TextureSS, r0.xy).xy - 0.5; //sample r1.xyzw, r0.xyxx, t2.xyzw, s2 (DetailSampler) //mad r0.xy, r1.xyxx, l(2.000000, 2.000000, 0.000000, 0.000000), l(-1.000000, -1.000000, 0.000000, 0.000000)
r0.zw = Detailmap.Sample(TextureSS, r0.zw).xy - 0.5; //sample r1.xyzw, r0.zwzz, t2.xyzw, s2 (DetailSampler) //mad r0.zw, r1.xxxy, l(0.000000, 0.000000, 2.000000, 2.000000), l(0.000000, 0.000000, -1.000000, -1.000000) //r0.zw = r0.zw*0.5; //mul r0.zw, r0.zzzw, l(0.000000, 0.000000, 0.500000, 0.500000)
Expand Down Expand Up @@ -161,7 +189,7 @@ float4 main(VS_OUTPUT input) : SV_TARGET

float4 fc = c;

c.rgb = FullLighting(c.rgb, spec, norm, input.Colour0, GlobalLights, EnableShadows, input.Shadows.x, input.LightShadow);
c.rgb = FullLighting(c.rgb, spec, norm, input.Colour0, GlobalLights, EnableShadows, input.Shadows.x, input.LightShadow, parallaxSelfShadow);


if (IsEmissive==1)
Expand Down
5 changes: 5 additions & 0 deletions CodeWalker.Shaders/BasicPS.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Texture2D<float4> Specmap : register(t3);
Texture2D<float4> Detailmap : register(t4);
Texture2D<float4> Colourmap2 : register(t5);
Texture2D<float4> TintPalette : register(t6);
Texture2D<float4> Heightmap : register(t7);
SamplerState TextureSS : register(s0);


Expand Down Expand Up @@ -39,6 +40,10 @@ cbuffer PSGeomVars : register(b2)
float wetnessMultiplier;
uint SpecOnly;
float4 TextureAlphaMask;
uint EnableHeightMap;
float heightScale;
float heightBias;
float Pad0;
}


Expand Down
27 changes: 21 additions & 6 deletions CodeWalker.Shaders/BasicPS_Deferred.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,32 @@

PS_OUTPUT main(VS_OUTPUT input)
{
// Calculate parallax offset if height mapping is enabled
float2 parallaxTexOffset = float2(0, 0);
if (EnableHeightMap && RenderMode == 0)
{
float3 viewDir = -normalize(input.CamRelPos); // Negate to get direction FROM surface TO camera
parallaxTexOffset = ParallaxOffset(
Heightmap, TextureSS, input.Texcoord0,
viewDir, normalize(input.Normal),
normalize(input.Tangent.xyz), normalize(input.Bitangent.xyz),
heightScale, heightBias);
}

// Apply parallax offset to base texture coordinates
float2 texc0 = input.Texcoord0 + parallaxTexOffset;

float4 c = float4(0.5, 0.5, 0.5, 1);
if (RenderMode == 0) c = float4(1, 1, 1, 1);
if (EnableTexture > 0)
{
float2 texc = input.Texcoord0;
float2 texc = texc0;
if (RenderMode >= 5)
{
if (RenderSamplerCoord == 2)
texc = input.Texcoord1;
texc = input.Texcoord1 + parallaxTexOffset;
else if (RenderSamplerCoord == 3)
texc = input.Texcoord2;
texc = input.Texcoord2 + parallaxTexOffset;
}

c = Colourmap.Sample(TextureSS, texc);
Expand Down Expand Up @@ -85,8 +100,8 @@ PS_OUTPUT main(VS_OUTPUT input)
if (RenderMode == 0)
{

float4 nv = Bumpmap.Sample(TextureSS, input.Texcoord0);
float4 sv = Specmap.Sample(TextureSS, input.Texcoord0);
float4 nv = Bumpmap.Sample(TextureSS, texc0);
float4 sv = Specmap.Sample(TextureSS, texc0);


float2 nmv = nv.xy;
Expand All @@ -97,7 +112,7 @@ PS_OUTPUT main(VS_OUTPUT input)
if (EnableDetailMap)
{
//detail normalmapp
r0.xy = input.Texcoord0 * detailSettings.zw;
r0.xy = texc0 * detailSettings.zw;
r0.zw = r0.xy * 3.17;
r0.xy = Detailmap.Sample(TextureSS, r0.xy).xy - 0.5;
r0.zw = Detailmap.Sample(TextureSS, r0.zw).xy - 0.5;
Expand Down
157 changes: 157 additions & 0 deletions CodeWalker.Shaders/Common.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,163 @@ float3 NormalMap(float2 nmv, float bumpinezz, float3 norm, float3 tang, float3 b
}


// POM constants
#define POM_MIN_STEPS 3
#define POM_MAX_STEPS 16
#define POM_VDOTN_BLEND_FACTOR 0.25f
#define POM_HEIGHT_SCALE 0.1f // Global parallax strength multiplier (1.0 = full, 0.5 = half)

// Distance-based POM fade constants (reduces noise at steep angles/distance)
#define POM_DISTANCE_START 5.0f // Distance where fade begins
#define POM_DISTANCE_END 50.0f // Distance where POM is fully disabled

// Binary search refinement for more precise intersection (reduces ring artifacts at close range)
#define POM_BINARY_SEARCH_STEPS 5 // Number of binary search iterations after linear search
#define POM_CLOSE_DISTANCE 2.0f // Distance threshold for close-range step boost
#define POM_CLOSE_STEP_MULTIPLIER 2.0f // Step multiplier when very close to surface

// Distance fade lookup table (5 control points for smooth non-linear falloff)
// Based on GTA V pomWeights table
#define NUM_POM_CTRL_POINTS 5
static const float pomWeights[NUM_POM_CTRL_POINTS] = {
1.0f, // Full quality at close range
0.9f,
0.5f, // 50% at mid distance
0.1f,
0.0f // Disabled at far distance
};

// Compute smooth distance-based fade for POM steps
float ComputePOMDistanceFade(float distanceBlend)
{
if (distanceBlend >= 1.0f)
return 0.0f;

// Find the nearest control points and interpolate
int startPoint = clamp(int(distanceBlend * (NUM_POM_CTRL_POINTS - 1)), 0, NUM_POM_CTRL_POINTS - 2);
int endPoint = startPoint + 1;

float t = distanceBlend * (NUM_POM_CTRL_POINTS - 1) - float(startPoint);
return lerp(pomWeights[startPoint], pomWeights[endPoint], t);
}

// Performs relief mapping by tracing through the height field
float TraceHeight(Texture2D<float4> heightMapSampler, SamplerState samplerState, float2 texCoords, float2 direction, float2 bias, int maxNumberOfSteps)
{
if (maxNumberOfSteps == 0)
{
return 0.0f;
}

float heightStep = 1.0f / float(maxNumberOfSteps);
float2 offsetPerStep = direction * heightStep;

float currentBound = 1.0f;
float previousBound = currentBound;

float2 texCoordOffset = bias;

// Use derivatives for proper mip selection to avoid aliasing
float2 ddx0 = ddx(texCoords.xy);
float2 ddy0 = ddy(texCoords.xy);

float currentHeight = heightMapSampler.SampleGrad(samplerState, texCoords.xy, ddx0, ddy0).r + 1e-6f;
float previousHeight = currentHeight;

[unroll(POM_MAX_STEPS)]
for (int s = 0; s < maxNumberOfSteps; ++s)
{
if (currentHeight < currentBound)
{
previousBound = currentBound;
previousHeight = currentHeight;

currentBound -= heightStep;
texCoordOffset += offsetPerStep;
currentHeight = heightMapSampler.SampleGrad(samplerState, texCoords + texCoordOffset, ddx0, ddy0).r;
}
else
{
break;
}
}

// Interpolate between the two points to find a more precise height
float currentDelta = currentBound - currentHeight;
float previousDelta = previousBound - previousHeight;
float denominator = previousDelta - currentDelta;

float finalHeight = currentHeight;

if (denominator > 0)
{
finalHeight = ((currentBound * previousDelta) - (previousBound * currentDelta)) / denominator;
}

return clamp(finalHeight, 0.0, 1.0f);
}

// Parallax self-shadow: traces through heightmap in light direction to find occlusion
// Returns shadow factor (0 = fully lit, 1 = fully in shadow)
float TraceSelfShadow(Texture2D<float4> heightMapSampler, SamplerState samplerState, float2 texCoords, float3 tanLightDir, float edgeWeight, float hScale)
{
float2 inXY = (tanLightDir.xy * hScale * edgeWeight) / max(tanLightDir.z, 0.01f);

// Sample base height at current (displaced) position
float sh0 = heightMapSampler.SampleLevel(samplerState, texCoords, 0).r;

// Trace 7 samples along light direction with increasing weight for closer occlusion
float shA = (heightMapSampler.SampleLevel(samplerState, texCoords + inXY * 0.88, 0).r - sh0 - 0.88) * 1;
float sh9 = (heightMapSampler.SampleLevel(samplerState, texCoords + inXY * 0.77, 0).r - sh0 - 0.77) * 2;
float sh8 = (heightMapSampler.SampleLevel(samplerState, texCoords + inXY * 0.66, 0).r - sh0 - 0.66) * 4;
float sh7 = (heightMapSampler.SampleLevel(samplerState, texCoords + inXY * 0.55, 0).r - sh0 - 0.55) * 6;
float sh6 = (heightMapSampler.SampleLevel(samplerState, texCoords + inXY * 0.44, 0).r - sh0 - 0.44) * 8;
float sh5 = (heightMapSampler.SampleLevel(samplerState, texCoords + inXY * 0.33, 0).r - sh0 - 0.33) * 10;
float sh4 = (heightMapSampler.SampleLevel(samplerState, texCoords + inXY * 0.22, 0).r - sh0 - 0.22) * 12;

float finalHeight = max(max(max(max(max(max(shA, sh9), sh8), sh7), sh6), sh5), sh4);
return saturate(finalHeight);
}

#define PARALLAX_SELF_SHADOW_AMOUNT 0.95f

// Calculate parallax texture coordinate offset
float2 ParallaxOffset(Texture2D<float4> heightMapSampler, SamplerState samplerState, float2 texCoords,
float3 viewDir, float3 normal, float3 tangent, float3 bitangent,
float inHeightScale, float inHeightBias)
{
// Transform view direction to tangent space
float3 tanEyePos;
tanEyePos.x = dot(tangent.xyz, viewDir.xyz);
tanEyePos.y = dot(bitangent.xyz, viewDir.xyz);
tanEyePos.z = dot(normal.xyz, viewDir.xyz);
tanEyePos = normalize(tanEyePos);

// Clamp Z to avoid division issues at grazing angles
float zLimit = 0.1f;
float clampedZ = max(zLimit, tanEyePos.z);

// Calculate view-dependent step count for quality/performance balance
float VdotN = abs(dot(normalize(viewDir.xyz), normalize(normal.xyz)));
float numberOfSteps = lerp(POM_MAX_STEPS, POM_MIN_STEPS, VdotN);

// Apply global scale based on view angle for smooth falloff
float globalScale = saturate(numberOfSteps - 1.0f) * saturate(VdotN / POM_VDOTN_BLEND_FACTOR);

// Calculate max parallax offset and bias offset
float2 maxParallaxOffset = (-tanEyePos.xy / clampedZ) * inHeightScale * globalScale;
float2 heightBiasOffset = (tanEyePos.xy / clampedZ) * inHeightBias * globalScale;

// Trace through height field
float height = TraceHeight(heightMapSampler, samplerState, texCoords, maxParallaxOffset, heightBiasOffset, (int)numberOfSteps);

// Calculate final texture coordinate offset
float2 texCoordOffset = heightBiasOffset + (maxParallaxOffset * (1.0f - height));

return texCoordOffset;
}




float3 BasicLighting(float4 lightcolour, float4 ambcolour, float pclit)
Expand Down
5 changes: 4 additions & 1 deletion CodeWalker.Shaders/Shadowmap.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ float ShadowAmount(float4 shadowcoord, float shadowdepth)//, inout float4 colour



float3 FullLighting(float3 diff, float3 spec, float3 norm, float4 vc0, uniform ShaderGlobalLightParams globalLights, uint enableShadows, float shadowdepth, float4 shadowcoord)
float3 FullLighting(float3 diff, float3 spec, float3 norm, float4 vc0, uniform ShaderGlobalLightParams globalLights, uint enableShadows, float shadowdepth, float4 shadowcoord, float selfShadow = 1.0)
{
float lf = saturate(dot(norm, globalLights.LightDir.xyz));

Expand All @@ -241,6 +241,9 @@ float3 FullLighting(float3 diff, float3 spec, float3 norm, float4 vc0, uniform S
}
}

// Apply parallax self-shadow to cascade shadow
shadowlit *= selfShadow;

lf *= shadowlit;

float3 speclit = spec*shadowlit;
Expand Down
Loading