-
Notifications
You must be signed in to change notification settings - Fork 0
/
unreal_pom.fx
182 lines (135 loc) · 5 KB
/
unreal_pom.fx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/**
\file pom.fx
Parallax occlusion mapping, based on D3D9 example code. Unreal only gives us view-space information (not world space), but this works fine as we can just use 0,0,0 as the eye position.
*/
int g_nMinSamples = 10;
int g_nMaxSamples = 75;
int g_nLODThreshold;
/**
Geometry shader code to calculate tangent space matrix
http://wiki.gamedev.net/index.php/D3DBook:(Lighting)_Per-Pixel_Lighting#Moving_From_Per-Vertex_To_Per-Pixel
*/
float3x3 computeTangentSpaceMatrix(in float3 pos[3], in float2 tc[3])
{
float3 A = pos[1] - pos[0];
float3 B = pos[2] - pos[0];
float2 P = tc[1] - tc[0];
float2 Q = tc[2] - tc[0];
float fraction = 1.0f / ( P.x * Q.y - Q.x * P.y );
float3 normal = normalize( cross( A, B ) );
float3 tangent = float3
(
(Q.y * A.x - P.y * B.x) * fraction,
(Q.y * A.y - P.y * B.y) * fraction,
(Q.y * A.z - P.y * B.z) * fraction
);
float3 bitangent = float3
(
(P.x * B.x - Q.x * A.x) * fraction,
(P.x * B.y - Q.x * A.y) * fraction,
(P.x * B.z - Q.x * A.z) * fraction
);
// Some simple aliases
float NdotT = dot( normal, tangent );
float NdotB = dot( normal, bitangent );
float TdotB = dot( tangent, bitangent );
// Apply Gram-Schmidt orthogonalization
tangent = tangent - NdotT * normal;
//bitangent = bitangent - NdotB * normal - TdotB * tangent;
//KENTIE: using cross product instead of Gram-Schmidt for binormal prevents seams/glitches
bool rightHanded = (dot(cross(tangent, bitangent), normal) >= 0);
bitangent = cross(normal,tangent);
if(!rightHanded)
bitangent*=-1;
//END KENTIE
// Pack the vectors into the matrix output
float3x3 tsMatrix;
tsMatrix[0] = normalize(tangent);
tsMatrix[1] = normalize(bitangent);
tsMatrix[2] = normalize(normal);
return tsMatrix;
}
/**
Ray direction calculation, normally in vertex shader but now in geometry shader as the required information is only available there
*/
float2 calcPOMVector(float3 vViewTS,float g_fHeightMapScale)
{
// Compute the ray direction for intersecting the height field profile with
// current view ray. See the above paper for derivation of this computation.
float2 vParallaxOffsetTS;
// Compute initial parallax displacement direction:
float2 vParallaxDirection = normalize( vViewTS.xy );
// The length of this vector determines the furthest amount of displacement:
float fLength = length( vViewTS );
float fParallaxLength = sqrt( fLength * fLength - vViewTS.z * vViewTS.z ) /vViewTS.z;
// Compute the actual reverse parallax displacement vector:
vParallaxOffsetTS = vParallaxDirection * fParallaxLength;
// Need to scale the amount of displacement to account for different height ranges
// in height maps. This is controlled by an artist-editable parameter:
vParallaxOffsetTS *= g_fHeightMapScale;
return vParallaxOffsetTS;
}
/**
Simplified POM shader
*/
float2 POM(float4 pos, float3 viewTS, float3 normal, float2 texCoord, float2 vParallaxOffsetTS, Texture2D tex)
{
// Normalize the interpolated vectors:
float3 vViewTS = normalize( viewTS );
float3 vViewWS = normalize( -pos.xyz );
float3 vNormalWS = normalize( normal );
float4 cResultColor = float4( 0, 0, 0, 1 );
float2 texSample = texCoord;
int nNumSteps = (int) lerp( g_nMaxSamples, g_nMinSamples, dot( vViewWS, vNormalWS ) );
float fCurrHeight = 0.0;
float fStepSize = 1.0 / (float) nNumSteps;
float fPrevHeight = 1.0;
float fNextHeight = 0.0;
int nStepIndex = 0;
bool bCondition = true;
float2 vTexOffsetPerStep = fStepSize * vParallaxOffsetTS;
float2 vTexCurrentOffset = texCoord;
float fCurrentBound = 1.0;
float fParallaxAmount = 0.0;
float2 pt1 = 0;
float2 pt2 = 0;
float2 texOffset2 = 0;
while ( nStepIndex < nNumSteps )
{
vTexCurrentOffset -= vTexOffsetPerStep;
// Sample height map which in this case is stored in the alpha channel of the normal map:
fCurrHeight = tex.SampleLevel(samLinear,vTexCurrentOffset, 0).r;
fCurrentBound -= fStepSize;
if ( fCurrHeight > fCurrentBound )
{
pt1 = float2( fCurrentBound, fCurrHeight );
pt2 = float2( fCurrentBound + fStepSize, fPrevHeight );
texOffset2 = vTexCurrentOffset - vTexOffsetPerStep;
nStepIndex = nNumSteps + 1;
fPrevHeight = fCurrHeight;
}
else
{
nStepIndex++;
fPrevHeight = fCurrHeight;
}
}
float fDelta2 = pt2.x - pt2.y;
float fDelta1 = pt1.x - pt1.y;
float fDenominator = fDelta2 - fDelta1;
// SM 3.0 requires a check for divide by zero, since that operation will generate
// an 'Inf' number instead of 0, as previous models (conveniently) did:
if ( fDenominator == 0.0f )
{
fParallaxAmount = 0.0f;
}
else
{
fParallaxAmount = (pt1.x * fDelta2 - pt2.x * fDelta1 ) / fDenominator;
}
float2 vParallaxOffset = vParallaxOffsetTS * (1 - fParallaxAmount );
// The computed texture offset for the displaced point on the pseudo-extruded surface:
float2 texSampleBase = texCoord - vParallaxOffset;
texSample = texSampleBase;
return texSample;
}