forked from QianMo/GPU-Gems-Book-Source-Code
-
Notifications
You must be signed in to change notification settings - Fork 0
/
WaterRip.fx
467 lines (401 loc) · 11.7 KB
/
WaterRip.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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
textureCUBE tEnvMap;
texture tBumpMap;
float4x4 cWorld2NDC;
float4 cWaterTint;
float4 cFrequency;
float4 cPhase;
float4 cAmplitude;
float4 cDirX;
float4 cDirY;
float4 cSpecAtten; // uvScale is w component
float4 cCameraPos; // world space
float4 cEnvAdjust;
float4 cEnvTint;
float4x4 cLocal2World;
float4 cLengths;
float4 cDepthOffset; // water level is w component
float4 cDepthScale;
float4 cFogParams;
float4 cDirXK;
float4 cDirYK;
float4 cDirXW;
float4 cDirYW;
float4 cKW;
float4 cDirXSqKW;
float4 cDirXDirYKW;
float4 cDirYSqKW;
sampler BumpSamp = sampler_state
{
Texture = <tBumpMap>;
MinFilter = LINEAR;
MagFilter = LINEAR;
MipFilter = LINEAR;
};
sampler EnvSamp = sampler_state
{
Texture = <tEnvMap>;
MinFilter = LINEAR;
MagFilter = LINEAR;
MipFilter = LINEAR;
};
#pragma PACK_MATRIX (ROW_MAJOR)
struct VertIn
{
float4 Position : POSITION;
float4 Color : COLOR0;
};
struct VertOut
{
float4 Position : POSITION;
float4 modColor : COLOR0;
float4 addColor : COLOR1;
float Fog : FOG;
float4 TexCoord0 : TEXCOORD0; // Ripple texture coords
float4 BTN_X : TEXCOORD1; // Binormal.x, Tangent.x, Normal.x
float4 BTN_Y : TEXCOORD2; // Bin.y, Tan.y, Norm.y
float4 BTN_Z : TEXCOORD3; // Bin.z, Tan.z, Norm.z
};
void CalcSinCos(const in float4 wPos,
const in float4 dirX,
const in float4 dirY,
const in float4 amplitude,
const in float4 frequency,
const in float4 phase,
const in float4 lengths,
const in float ooEdgeLength,
const in float scale,
out float4 sines, out float4 cosines)
{
// Dot x and y with direction vectors
float4 dists = dirX * wPos.xxxx;
dists = dirY * wPos.yyyy + dists;
// Scale in our frequency and add in our phase
dists = dists * frequency;
dists = dists + phase;
const float kPi = 3.14159265f;
const float kTwoPi = 2.f * kPi;
const float kOOTwoPi = 1.f / kTwoPi;
// Mod into range [-Pi..Pi]
dists = dists + kPi;
dists = dists * kOOTwoPi;
dists = frac(dists);
dists = dists * kTwoPi;
dists = dists - kPi;
float4 dists2 = dists * dists;
float4 dists3 = dists2 * dists;
float4 dists4 = dists2 * dists2;
float4 dists5 = dists3 * dists2;
float4 dists6 = dists3 * dists3;
float4 dists7 = dists4 * dists3;
const float4 kSinConsts = float4(1.f, -1.f/6.f, 1.f/120.f, -1.f/5040.f);
const float4 kCosConsts = float4(1.f, -1.f/2.f, 1.f/24.f, -1.f/720.f);
sines = dists + dists3 * kSinConsts.yyyy + dists5 * kSinConsts.zzzz + dists7 * kSinConsts.wwww;
cosines = kCosConsts.xxxx + dists2 * kCosConsts.yyyy + dists4 * kCosConsts.zzzz + dists6 * kCosConsts.wwww;
float4 filteredAmp = lengths * ooEdgeLength;
filteredAmp = max(filteredAmp, 0.f);
filteredAmp = min(filteredAmp, 1.f);
filteredAmp = filteredAmp * scale;
filteredAmp = filteredAmp * amplitude;
sines = sines * filteredAmp;
cosines = cosines * filteredAmp * scale;
}
float3 FinitizeEyeRay(const in float3 cam2Vtx, const in float4 envAdjust)
{
// Compute our finitized eyeray.
// Our "finitized" eyeray is:
// camPos + D * t - envCenter = D * t - (envCenter - camPos)
// with
// D = (pos - camPos) / |pos - camPos| // normalized usual eyeray
// and
// t = D dot F + sqrt( (D dot F)^2 - G )
// with
// F = (envCenter - camPos) => envAdjust.xyz
// G = F^2 - R^2 => nevAdjust.w
// where R is the sphere radius.
//
// This all derives from the positive root of equation
// (camPos + (pos - camPos) * t - envCenter)^2 = R^2,
// In other words, where on a sphere of radius R centered about envCenter
// does the ray from the real camera position through this point hit.
//
// Note that F and G are both constants (one 3-point, one scalar).
float dDotF = dot(cam2Vtx, float3(envAdjust.xyz));
float t = dDotF + sqrt(dDotF * dDotF - envAdjust.w);
return cam2Vtx * t - float3(envAdjust.xyz);
}
void CalcScreenPosAndFog(const in float4x4 world2NDC, const in float4 fogParams, const in float4 wPos, out float4 scrPos, out float fog)
{
// Calc screen position and fog from screen W
// Fog is basic linear from start distance to end distance.
float4 sPos = mul(world2NDC, wPos);
fog = (sPos.w + fogParams.x) * fogParams.y;
scrPos = sPos;
}
void CalcFinalColors(const in float3 norm,
const in float3 cam2Vtx,
const in float opacMin,
const in float opacScale,
const in float colorFilter,
const in float opacFilter,
const in float4 tint,
out float4 modColor,
out float4 addColor)
{
// Calculate colors
// Final color will be
// rgb = Color1.rgb + Color0.rgb * envMap.rgb
// alpha = Color0.a
// Color 0
// Vertex based Fresnel-esque effect.
// Input vertex color.b limits how much we attenuate based on angle.
// So we map
// (dot(norm,cam2Vtx)==0) => 1 for grazing angle
// and (dot(norm,cam2Vtx)==1 => 1-In.Color.b for perpendicular view.
float atten = 1.0 + dot(norm, cam2Vtx) * opacMin;
// Filter the color based on depth
modColor.rgb = colorFilter * atten;
// Boost the alpha so the reflections fade out faster than the tint
// and apply the input attenuation factors.
modColor.a = (atten + 1.0) * 0.5 * opacFilter * opacScale * tint.a;
// Color 1 is just a constant.
addColor = tint;
}
void CalcEyeRayAndBumpAttenuation(const in float4 wPos,
const in float4 cameraPos,
const in float4 specAtten,
out float3 cam2Vtx,
out float pertAtten)
{
// Get normalized vec from camera to vertex, saving original distance.
cam2Vtx = float3(wPos.xyz) - float3(cameraPos.xyz);
pertAtten = length(cam2Vtx);
cam2Vtx = cam2Vtx / pertAtten;
// Calculate our normal perturbation attenuation. This attenuation will be
// applied to the horizontal components of the normal read from the computed
// ripple bump map, mostly to fight aliasing. This doesn't attenuate the
// color computed from the normal map, it attenuates the "bumps".
pertAtten = pertAtten + specAtten.x;
pertAtten = pertAtten * specAtten.y;
pertAtten = min(pertAtten, 1.f);
pertAtten = max(pertAtten, 0.f);
pertAtten = pertAtten * pertAtten; // Square it to account for perspective.
pertAtten = pertAtten * specAtten.z;
}
// Depth filter channels control:
// dFilter.x => overall opacity
// dFilter.y => reflection strength
// dFilter.z => wave height
float3 CalcDepthFilter(const in float4 depthOffset, const in float4 depthScale, const in float4 wPos, const in float waterLevel)
{
float3 dFilter = float3(depthOffset.xyz) - wPos.zzz;
dFilter = dFilter * float3(depthScale.xyz);
dFilter = max(dFilter, 0.f);
dFilter = min(dFilter, 1.f);
return dFilter;
}
// See Pos in CalcTangentBasis comments.
float4 CalcFinalPosition(in float4 wPos,
const in float4 sines,
const in float4 cosines,
const in float depthOffset,
const in float4 dirXK,
const in float4 dirYK)
{
// Height
// Sum to a scalar
float h = dot(sines, 1.f) + depthOffset;
// Clamp to never go beneath input height
wPos.z = max(wPos.z, h);
wPos.x = wPos.x + dot(cosines, dirXK);
wPos.y = wPos.y + dot(cosines, dirYK);
return wPos;
}
// Normal, binormal, tangent
// Okay, here we go:
// W == sum(k w Dir.x^2 A sin())
// V == sum(k w Dir.x Dir.y A sin())
// U == sum(k w Dir.y^2 A sin())
//
// T == sum(A sin())
//
// S == sum(k Dir.x A cos())
// R == sum(k Dir.y A cos())
//
// Q == sum(k w A cos())
//
// M == sum(A cos())
//
// P == sum(w Dir.x A cos())
// N == sum(w Dir.y A cos())
//
// Then:
// Pos = (in.x + S, in.y + R, waterheight + T)
//
// Bin = (1 - W, -V, P)
// Tan = (-V, 1 - U, N)
// Nor = (-P, -N, 1 - Q)
//
// Remember we want the transpose of Binormal, Tangent, and Normal
void CalcTangentBasis(const in float4 sines,
const in float4 cosines,
const in float4 dirXSqKW,
const in float4 dirXDirYKW,
const in float4 dirYSqKW,
const in float4 dirXW,
const in float4 dirYW,
const in float4 KW,
const in float pertAtten,
const in float3 eyeRay,
out float4 BTN_X,
out float4 BTN_Y,
out float4 BTN_Z,
out float3 norm)
{
// Note that we're swapping Y and Z and negating Z (rotation about X)
// to match the D3D convention of Y being up in cubemaps.
BTN_X.x = 1.f + dot(sines, -dirXSqKW);
BTN_X.y = dot(sines, -dirXDirYKW);
BTN_X.z = dot(cosines, -dirXW);
BTN_X.xy = BTN_X.xy * pertAtten;
norm.x = BTN_X.z;
BTN_Z.x = dot(sines, -dirXDirYKW);
BTN_Z.y = 1.f + dot(sines, -dirYSqKW);
BTN_Z.z = dot(cosines, -dirYW);
BTN_Z.xy = BTN_Z.xy * pertAtten;
norm.y = BTN_Z.z;
BTN_Y.x = -dot(cosines, dirXW);
BTN_Y.y = -dot(cosines, dirYW);
BTN_Y.z = -(1.f + dot(sines, -KW));
BTN_Y.xy = BTN_Y.xy * pertAtten;
norm.z = -BTN_Y.z;
BTN_X.w = eyeRay.x;
BTN_Y.w = -eyeRay.z;
BTN_Z.w = eyeRay.y;
}
VertOut vs_main(VertIn In,
uniform float4x4 kWorld2NDC,
uniform float4 kWaterTint,
uniform float4 kFrequency,
uniform float4 kPhase,
uniform float4 kAmplitude,
uniform float4 kDirX,
uniform float4 kDirY,
uniform float4 kSpecAtten, // uvScale is w component
uniform float4 kCameraPos, // world space
uniform float4 kEnvAdjust,
uniform float4 kEnvTint,
uniform float4x4 kLocal2World,
uniform float4 kLengths,
uniform float4 kDepthOffset, // water level is w component
uniform float4 kDepthScale,
uniform float4 kFogParams,
uniform float4 kDirXK,
uniform float4 kDirYK,
uniform float4 kDirXW,
uniform float4 kDirYW,
uniform float4 kKW,
uniform float4 kDirXSqKW,
uniform float4 kDirXDirYKW,
uniform float4 kDirYSqKW
)
{
VertOut vOut;
// Evaluate world space base position. All subsequent calculations in world space.
float4 wPos = mul(kLocal2World, In.Position);
// Calculate ripple UV from position
vOut.TexCoord0.xy = wPos.xy * kSpecAtten.ww;
vOut.TexCoord0.z = 0.f;
vOut.TexCoord0.w = 1.f;
// Get our depth based filters.
float3 dFilter = CalcDepthFilter(kDepthOffset, kDepthScale, wPos, kDepthOffset.w);
// Build our 4 waves
float4 sines;
float4 cosines;
CalcSinCos(wPos,
kDirX, kDirY,
kAmplitude, kFrequency, kPhase,
kLengths, In.Color.a, dFilter.z,
sines, cosines);
wPos = CalcFinalPosition(wPos, sines, cosines, kDepthOffset.w, kDirXK, kDirYK);
// We have our final position. We'll be needing normalized vector from camera
// to vertex several times, so we go ahead and grab it.
float3 cam2Vtx;
float pertAtten;
CalcEyeRayAndBumpAttenuation(wPos, kCameraPos, kSpecAtten, cam2Vtx, pertAtten);
// Compute our finitized eyeray.
float3 eyeRay = FinitizeEyeRay(cam2Vtx, kEnvAdjust);
float3 norm;
CalcTangentBasis(sines, cosines,
kDirXSqKW,
kDirXDirYKW,
kDirYSqKW,
kDirXW,
kDirYW,
kKW,
pertAtten,
eyeRay,
vOut.BTN_X,
vOut.BTN_Y,
vOut.BTN_Z,
norm);
// Calc screen position and fog
CalcScreenPosAndFog(kWorld2NDC, kFogParams, wPos, vOut.Position, vOut.Fog.x);
CalcFinalColors(norm,
cam2Vtx,
In.Color.b,
In.Color.r,
dFilter.y,
dFilter.x,
kWaterTint,
vOut.modColor,
vOut.addColor);
return vOut;
}
technique T0
{
pass P0
{
VertexShader = compile vs_1_1 vs_main(cWorld2NDC,
cWaterTint,
cFrequency,
cPhase,
cAmplitude,
cDirX,
cDirY,
cSpecAtten,
cCameraPos,
cEnvAdjust,
cEnvTint,
cLocal2World,
cLengths,
cDepthOffset,
cDepthScale,
cFogParams,
cDirXK,
cDirYK,
cDirXW,
cDirYW,
cKW,
cDirXSqKW,
cDirXDirYKW,
cDirYSqKW);
PixelShader =
asm
{
ps_1_1
tex t0
texm3x3pad t1, t0_bx2
texm3x3pad t2, t0_bx2
texm3x3vspec t3, t0_bx2
mad r0.rgb, t3, v0, v1;
+mov r0.a, v0;
};
Sampler[0] = (BumpSamp);
Sampler[1] = (BumpSamp);
Sampler[2] = (BumpSamp);
Sampler[3] = (EnvSamp);
CullMode = NONE;
SrcBlend = SrcAlpha;
DestBlend = InvSrcAlpha;
}
}