Skip to content

Commit 07e3590

Browse files
committed
add parallax support
1 parent 485d56b commit 07e3590

29 files changed

+685
-47
lines changed

CodeWalker.Shaders/BasicPS.hlsl

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,30 @@
33

44
float4 main(VS_OUTPUT input) : SV_TARGET
55
{
6+
// Calculate parallax offset if height mapping is enabled
7+
float2 parallaxTexOffset = float2(0, 0);
8+
if (EnableHeightMap && RenderMode == 0)
9+
{
10+
float3 viewDir = -normalize(input.CamRelPos); // Negate to get direction FROM surface TO camera
11+
parallaxTexOffset = ParallaxOffset(
12+
Heightmap, TextureSS, input.Texcoord0,
13+
viewDir, normalize(input.Normal),
14+
normalize(input.Tangent.xyz), normalize(input.Bitangent.xyz),
15+
heightScale, heightBias);
16+
}
17+
18+
// Apply parallax offset to base texture coordinates
19+
float2 texc0 = input.Texcoord0 + parallaxTexOffset;
20+
621
float4 c = float4(0.5, 0.5, 0.5, 1);
722
if (RenderMode == 0) c = float4(1, 1, 1, 1);
823
if (EnableTexture > 0)
924
{
10-
float2 texc = input.Texcoord0;
25+
float2 texc = texc0;
1126
if (RenderMode >= 5)
1227
{
13-
if (RenderSamplerCoord == 2) texc = input.Texcoord1;
14-
else if (RenderSamplerCoord == 3) texc = input.Texcoord2;
28+
if (RenderSamplerCoord == 2) texc = input.Texcoord1 + parallaxTexOffset;
29+
else if (RenderSamplerCoord == 3) texc = input.Texcoord2 + parallaxTexOffset;
1530
}
1631

1732
c = Colourmap.Sample(TextureSS, texc);
@@ -80,8 +95,8 @@ float4 main(VS_OUTPUT input) : SV_TARGET
8095
if (RenderMode == 0)
8196
{
8297

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

86101

87102
float2 nmv = nv.xy;
@@ -92,7 +107,7 @@ float4 main(VS_OUTPUT input) : SV_TARGET
92107
if (EnableDetailMap)
93108
{
94109
//detail normalmapp
95-
r0.xy = input.Texcoord0 * detailSettings.zw; //mul r0.xy, v2.xyxx, detailSettings.zwzz
110+
r0.xy = texc0 * detailSettings.zw; //mul r0.xy, v2.xyxx, detailSettings.zwzz
96111
r0.zw = r0.xy * 3.17; //mul r0.zw, r0.xxxy, l(0.000000, 0.000000, 3.170000, 3.170000)
97112
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)
98113
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)

CodeWalker.Shaders/BasicPS.hlsli

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Texture2D<float4> Specmap : register(t3);
66
Texture2D<float4> Detailmap : register(t4);
77
Texture2D<float4> Colourmap2 : register(t5);
88
Texture2D<float4> TintPalette : register(t6);
9+
Texture2D<float4> Heightmap : register(t7);
910
SamplerState TextureSS : register(s0);
1011

1112

@@ -39,6 +40,10 @@ cbuffer PSGeomVars : register(b2)
3940
float wetnessMultiplier;
4041
uint SpecOnly;
4142
float4 TextureAlphaMask;
43+
uint EnableHeightMap;
44+
float heightScale;
45+
float heightBias;
46+
float Pad0;
4247
}
4348

4449

CodeWalker.Shaders/BasicPS_Deferred.hlsl

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,32 @@
33

44
PS_OUTPUT main(VS_OUTPUT input)
55
{
6+
// Calculate parallax offset if height mapping is enabled
7+
float2 parallaxTexOffset = float2(0, 0);
8+
if (EnableHeightMap && RenderMode == 0)
9+
{
10+
float3 viewDir = -normalize(input.CamRelPos); // Negate to get direction FROM surface TO camera
11+
parallaxTexOffset = ParallaxOffset(
12+
Heightmap, TextureSS, input.Texcoord0,
13+
viewDir, normalize(input.Normal),
14+
normalize(input.Tangent.xyz), normalize(input.Bitangent.xyz),
15+
heightScale, heightBias);
16+
}
17+
18+
// Apply parallax offset to base texture coordinates
19+
float2 texc0 = input.Texcoord0 + parallaxTexOffset;
20+
621
float4 c = float4(0.5, 0.5, 0.5, 1);
722
if (RenderMode == 0) c = float4(1, 1, 1, 1);
823
if (EnableTexture > 0)
924
{
10-
float2 texc = input.Texcoord0;
25+
float2 texc = texc0;
1126
if (RenderMode >= 5)
1227
{
1328
if (RenderSamplerCoord == 2)
14-
texc = input.Texcoord1;
29+
texc = input.Texcoord1 + parallaxTexOffset;
1530
else if (RenderSamplerCoord == 3)
16-
texc = input.Texcoord2;
31+
texc = input.Texcoord2 + parallaxTexOffset;
1732
}
1833

1934
c = Colourmap.Sample(TextureSS, texc);
@@ -85,8 +100,8 @@ PS_OUTPUT main(VS_OUTPUT input)
85100
if (RenderMode == 0)
86101
{
87102

88-
float4 nv = Bumpmap.Sample(TextureSS, input.Texcoord0);
89-
float4 sv = Specmap.Sample(TextureSS, input.Texcoord0);
103+
float4 nv = Bumpmap.Sample(TextureSS, texc0);
104+
float4 sv = Specmap.Sample(TextureSS, texc0);
90105

91106

92107
float2 nmv = nv.xy;
@@ -97,7 +112,7 @@ PS_OUTPUT main(VS_OUTPUT input)
97112
if (EnableDetailMap)
98113
{
99114
//detail normalmapp
100-
r0.xy = input.Texcoord0 * detailSettings.zw;
115+
r0.xy = texc0 * detailSettings.zw;
101116
r0.zw = r0.xy * 3.17;
102117
r0.xy = Detailmap.Sample(TextureSS, r0.xy).xy - 0.5;
103118
r0.zw = Detailmap.Sample(TextureSS, r0.zw).xy - 0.5;

CodeWalker.Shaders/Common.hlsli

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,139 @@ float3 NormalMap(float2 nmv, float bumpinezz, float3 norm, float3 tang, float3 b
119119
}
120120

121121

122+
// POM constants
123+
#define POM_MIN_STEPS 3
124+
#define POM_MAX_STEPS 16
125+
#define POM_VDOTN_BLEND_FACTOR 0.25f
126+
#define POM_HEIGHT_SCALE 0.1f // Global parallax strength multiplier (1.0 = full, 0.5 = half)
127+
128+
// Distance-based POM fade constants (reduces noise at steep angles/distance)
129+
#define POM_DISTANCE_START 5.0f // Distance where fade begins
130+
#define POM_DISTANCE_END 50.0f // Distance where POM is fully disabled
131+
132+
// Binary search refinement for more precise intersection (reduces ring artifacts at close range)
133+
#define POM_BINARY_SEARCH_STEPS 5 // Number of binary search iterations after linear search
134+
#define POM_CLOSE_DISTANCE 2.0f // Distance threshold for close-range step boost
135+
#define POM_CLOSE_STEP_MULTIPLIER 2.0f // Step multiplier when very close to surface
136+
137+
// Distance fade lookup table (5 control points for smooth non-linear falloff)
138+
// Based on GTA V pomWeights table
139+
#define NUM_POM_CTRL_POINTS 5
140+
static const float pomWeights[NUM_POM_CTRL_POINTS] = {
141+
1.0f, // Full quality at close range
142+
0.9f,
143+
0.5f, // 50% at mid distance
144+
0.1f,
145+
0.0f // Disabled at far distance
146+
};
147+
148+
// Compute smooth distance-based fade for POM steps
149+
float ComputePOMDistanceFade(float distanceBlend)
150+
{
151+
if (distanceBlend >= 1.0f)
152+
return 0.0f;
153+
154+
// Find the nearest control points and interpolate
155+
int startPoint = clamp(int(distanceBlend * (NUM_POM_CTRL_POINTS - 1)), 0, NUM_POM_CTRL_POINTS - 2);
156+
int endPoint = startPoint + 1;
157+
158+
float t = distanceBlend * (NUM_POM_CTRL_POINTS - 1) - float(startPoint);
159+
return lerp(pomWeights[startPoint], pomWeights[endPoint], t);
160+
}
161+
162+
// Performs relief mapping by tracing through the height field
163+
float TraceHeight(Texture2D<float4> heightMapSampler, SamplerState samplerState, float2 texCoords, float2 direction, float2 bias, int maxNumberOfSteps)
164+
{
165+
if (maxNumberOfSteps == 0)
166+
{
167+
return 0.0f;
168+
}
169+
170+
float heightStep = 1.0f / float(maxNumberOfSteps);
171+
float2 offsetPerStep = direction * heightStep;
172+
173+
float currentBound = 1.0f;
174+
float previousBound = currentBound;
175+
176+
float2 texCoordOffset = bias;
177+
178+
// Use derivatives for proper mip selection to avoid aliasing
179+
float2 ddx0 = ddx(texCoords.xy);
180+
float2 ddy0 = ddy(texCoords.xy);
181+
182+
float currentHeight = heightMapSampler.SampleGrad(samplerState, texCoords.xy, ddx0, ddy0).r + 1e-6f;
183+
float previousHeight = currentHeight;
184+
185+
[unroll(POM_MAX_STEPS)]
186+
for (int s = 0; s < maxNumberOfSteps; ++s)
187+
{
188+
if (currentHeight < currentBound)
189+
{
190+
previousBound = currentBound;
191+
previousHeight = currentHeight;
192+
193+
currentBound -= heightStep;
194+
texCoordOffset += offsetPerStep;
195+
currentHeight = heightMapSampler.SampleGrad(samplerState, texCoords + texCoordOffset, ddx0, ddy0).r;
196+
}
197+
else
198+
{
199+
break;
200+
}
201+
}
202+
203+
// Interpolate between the two points to find a more precise height
204+
float currentDelta = currentBound - currentHeight;
205+
float previousDelta = previousBound - previousHeight;
206+
float denominator = previousDelta - currentDelta;
207+
208+
float finalHeight = currentHeight;
209+
210+
if (denominator > 0)
211+
{
212+
finalHeight = ((currentBound * previousDelta) - (previousBound * currentDelta)) / denominator;
213+
}
214+
215+
return clamp(finalHeight, 0.0, 1.0f);
216+
}
217+
218+
// Calculate parallax texture coordinate offset
219+
float2 ParallaxOffset(Texture2D<float4> heightMapSampler, SamplerState samplerState, float2 texCoords,
220+
float3 viewDir, float3 normal, float3 tangent, float3 bitangent,
221+
float inHeightScale, float inHeightBias)
222+
{
223+
// Transform view direction to tangent space
224+
float3 tanEyePos;
225+
tanEyePos.x = dot(tangent.xyz, viewDir.xyz);
226+
tanEyePos.y = dot(bitangent.xyz, viewDir.xyz);
227+
tanEyePos.z = dot(normal.xyz, viewDir.xyz);
228+
tanEyePos = normalize(tanEyePos);
229+
230+
// Clamp Z to avoid division issues at grazing angles
231+
float zLimit = 0.1f;
232+
float clampedZ = max(zLimit, tanEyePos.z);
233+
234+
// Calculate view-dependent step count for quality/performance balance
235+
float VdotN = abs(dot(normalize(viewDir.xyz), normalize(normal.xyz)));
236+
float numberOfSteps = lerp(POM_MAX_STEPS, POM_MIN_STEPS, VdotN);
237+
238+
// Apply global scale based on view angle for smooth falloff
239+
float globalScale = saturate(numberOfSteps - 1.0f) * saturate(VdotN / POM_VDOTN_BLEND_FACTOR);
240+
241+
// Calculate max parallax offset and bias offset
242+
float2 maxParallaxOffset = (-tanEyePos.xy / clampedZ) * inHeightScale * globalScale;
243+
float2 heightBiasOffset = (tanEyePos.xy / clampedZ) * inHeightBias * globalScale;
244+
245+
// Trace through height field
246+
float height = TraceHeight(heightMapSampler, samplerState, texCoords, maxParallaxOffset, heightBiasOffset, (int)numberOfSteps);
247+
248+
// Calculate final texture coordinate offset
249+
float2 texCoordOffset = heightBiasOffset + (maxParallaxOffset * (1.0f - height));
250+
251+
return texCoordOffset;
252+
}
253+
254+
122255

123256

124257
float3 BasicLighting(float4 lightcolour, float4 ambcolour, float pclit)

0 commit comments

Comments
 (0)