forked from FAForever/fa
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwater2.fx
652 lines (544 loc) · 17.3 KB
/
water2.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
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
#define FIDELITY_LOW 0x00
#define FIDELITY_MEDIUM 0x01
#define FIDELITY_HIGH 0x02
float3 ViewPosition;
float WaterElevation;
float Time;
texture SkyMap;
texture NormalMap0;
texture NormalMap1;
texture NormalMap2;
texture NormalMap3;
texture RefractionMap;
texture ReflectionMap;
texture FresnelLookup;
float4 ViewportScaleOffset;
float4 TerrainScale;
texture WaterRamp;
float waveCrestThreshold = 1;
float3 waveCrestColor = float3( 1, 1, 1);
float4x4 WorldToView;
float4x4 Projection;
// red: originally wave normal strength, now used for shadow
// green: water depth (autogenerated by the engine)
// blue: shoreline (autogenerated by the engine)
// alpha: foam reduction
texture UtilityTextureC;
samplerCUBE SkySampler = sampler_state
{
Texture = <SkyMap>;
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
AddressW = WRAP;
};
sampler NormalSampler0 = sampler_state
{
Texture = <NormalMap0>;
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
sampler NormalSampler1 = sampler_state
{
Texture = <NormalMap1>;
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
sampler NormalSampler2 = sampler_state
{
Texture = <NormalMap2>;
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
sampler NormalSampler3 = sampler_state
{
Texture = <NormalMap3>;
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
sampler FresnelSampler = sampler_state
{
Texture = <FresnelLookup>;
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = CLAMP;
AddressV = CLAMP;
};
sampler RefractionSampler = sampler_state
{
Texture = <RefractionMap>;
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = CLAMP;
AddressV = CLAMP;
};
sampler ReflectionSampler = sampler_state
{
Texture = <ReflectionMap>;
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = CLAMP;
AddressV = CLAMP;
};
sampler2D UtilitySamplerC = sampler_state
{
Texture = (UtilityTextureC);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = CLAMP;
AddressV = CLAMP;
};
sampler2D MaskSampler = sampler_state
{
Texture = (UtilityTextureC);
MipFilter = POINT;
MinFilter = POINT;
MagFilter = POINT;
AddressU = CLAMP;
AddressV = CLAMP;
};
// surface water color
float3 waterColor = float3(0,0.7,1.5);
float3 waterColorLowFi = float3(0.7647,0.8784,0.9647);
// these actually get overridden by the map
float2 waterLerp = 0.3;
float refractionScale = 0.015;
// fresnel parameters
// both are unused by the shaders
float fresnelBias = 0.1;
float fresnelPower = 1.5;
// these actually get overridden by the map
float unitreflectionAmount = 0.5;
float skyreflectionAmount = 1.5;
// 4 repeat rates for the 4 wave texture layers
float4 normalRepeatRate = float4(0.0009, 0.009, 0.05, 0.5);
// 4 vectors of normal movements
float2 normal1Movement = float2(0.5, -0.95);
float2 normal2Movement = float2(0.05, -0.095);
float2 normal3Movement = float2(0.01, 0.03);
float2 normal4Movement = float2(0.0005, 0.0009);
// sun parameters
float SunShininess = 50;
float3 SunDirection = normalize(float3( 0.1 , -0.967, 0.253));
float3 SunColor = normalize(float3( 1.2, 0.7, 0.5 ));
// unused by the shaders
float sunReflectionAmount = 5;
// unused by the shaders
float SunGlow;
struct LOWFIDELITY_VERTEX
{
float4 position : POSITION0;
float2 texcoord0 : TEXCOORD0;
float3 viewDirection : TEXCOORD1;
float2 normal0 : TEXCOORD2;
float2 normal1 : TEXCOORD3;
float2 normal2 : TEXCOORD4;
float2 normal3 : TEXCOORD5;
};
LOWFIDELITY_VERTEX LowFidelityVS(float4 position : POSITION,float2 texcoord0 : TEXCOORD0 )
{
LOWFIDELITY_VERTEX vertex = (LOWFIDELITY_VERTEX)0;
position.y = WaterElevation;
vertex.position = mul(float4(position.xyz,1),mul(WorldToView,Projection));
vertex.texcoord0 = texcoord0;
vertex.viewDirection = position.xyz - ViewPosition;
vertex.normal0 = ( position.xz + ( normal1Movement * Time )) * normalRepeatRate.x;
vertex.normal1 = ( position.xz + ( normal2Movement * Time )) * normalRepeatRate.y;
vertex.normal2 = ( position.xz + ( normal3Movement * Time )) * normalRepeatRate.z;
vertex.normal3 = ( position.xz + ( normal4Movement * Time )) * normalRepeatRate.w;
return vertex;
}
float4 LowFidelityPS0( LOWFIDELITY_VERTEX vertex) : COLOR
{
float4 water = tex2D(UtilitySamplerC,vertex.texcoord0);
float alpha = clamp(water.g,0.1,1.0);
return float4(0.1, 0.5, 2.0, alpha);
}
float4 LowFidelityPS1( LOWFIDELITY_VERTEX vertex) : COLOR
{
float4 water = tex2D(UtilitySamplerC,vertex.texcoord0);
float alpha = clamp(water.g,0,0.2);
float w0 = tex2D(NormalSampler0,vertex.normal0).a;
float w1 = tex2D(NormalSampler1,vertex.normal1).a;
float w2 = tex2D(NormalSampler2,vertex.normal2).a;
float w3 = tex2D(NormalSampler3,vertex.normal3).a;
float waveCrest = saturate(( w0 + w1 + w2 + w3 ) - waveCrestThreshold);
return float4(waveCrestColor,waveCrest);
}
technique Water_LowFidelity
<
string abstractTechnique = "TWater";
int fidelity = FIDELITY_LOW;
>
{
pass P0
{
AlphaBlendEnable = true;
ColorWriteEnable = 0x07;
SrcBlend = SrcAlpha;
DestBlend = InvSrcAlpha;
ZEnable = true;
ZFunc = lessequal;
ZWriteEnable = false;
VertexShader = compile vs_1_1 LowFidelityVS();
PixelShader = compile ps_2_0 LowFidelityPS0();
}
pass P1
{
AlphaBlendEnable = true;
ColorWriteEnable = 0x07;
SrcBlend = SrcAlpha;
DestBlend = InvSrcAlpha;
ZEnable = true;
ZFunc = lessequal;
ZWriteEnable = false;
VertexShader = compile vs_1_1 LowFidelityVS();
PixelShader = compile ps_2_0 LowFidelityPS1();
}
}
///
///
///
struct VS_OUTPUT
{
float4 mPos : POSITION;
float2 mTexUV : TEXCOORD0;
float2 mLayer0 : TEXCOORD1;
float2 mLayer1 : TEXCOORD2;
float2 mLayer2 : TEXCOORD3;
float2 mLayer3 : TEXCOORD4;
float3 mViewVec : TEXCOORD5;
float4 mScreenPos : TEXCOORD6;
//float3 mLightVector : TEXCOORD6;
};
VS_OUTPUT WaterVS(
#ifdef DIRECT3D10
// stricter shader linkages require that this match the input layout
float3 inPos : POSITION,
#else
float4 inPos : POSITION,
#endif
float2 inTexUV : TEXCOORD0 )
{
inPos.y = WaterElevation;
VS_OUTPUT result;
float4x4 wvp = mul( WorldToView, Projection );
result.mPos = mul( float4(inPos.xyz,1), wvp );
// output the map coordinate so we can sample the water texture
result.mTexUV = inTexUV;
// output the screen position so we can sample the reflection / mask
result.mScreenPos = result.mPos;
// calculate the texture coordinates for all 3 layers of water
result.mLayer0 = (inPos.xz + (normal1Movement * Time)) * normalRepeatRate.x;
result.mLayer1 = (inPos.xz + (normal2Movement * Time)) * normalRepeatRate.y;
result.mLayer2 = (inPos.xz + (normal3Movement * Time)) * normalRepeatRate.z;
result.mLayer3 = (inPos.xz + (normal4Movement * Time)) * normalRepeatRate.w;
// calculate the view vector
result.mViewVec = ViewPosition - inPos.xyz;
return result;
}
const float PI = 3.14159265359;
float FresnelSchlick(float dot, float F0)
{
return F0 + (1.0 - F0) * pow(1.0 - dot, 5.0);
}
float NormalDistribution(float3 n, float3 h, float roughness)
{
float a2 = roughness*roughness;
float nDotH = max(dot(n, h), 0.0);
float nDotH2 = nDotH*nDotH;
float num = a2;
float denom = nDotH2 * (a2 - 1.0) + 1.0;
denom = PI * denom * denom;
return num / denom;
}
float GeometrySchlick(float nDotV, float roughness)
{
float r = (roughness + 1.0);
float k = (r*r) / 8.0;
float num = nDotV;
float denom = nDotV * (1.0 - k) + k;
return num / denom;
}
float GeometrySmith(float3 n, float nDotV, float3 l, float roughness)
{
float nDotL = max(dot(n, l), 0.0);
float gs2 = GeometrySchlick(nDotV, roughness);
float gs1 = GeometrySchlick(nDotL, roughness);
return gs1 * gs2;
}
float3 calculateSunReflection(float3 R, float3 v, float3 n)
{
float3 color;
// Legacy fallback for the old behaviour, so we don't change all maps accidentally.
// This check works because the default sun position is under the horizon.
if (SunDirection.y < 0.0) {
float3 sunReflection = pow(saturate(dot(-R, SunDirection)), SunShininess) * SunColor;
color = sunReflection * FresnelSchlick(max(dot(n, v), 0.0), 0.06);
} else {
float roughness = 1.0 / SunShininess;
float facingSpecular = 0.02;
float3 l = SunDirection;
float3 h = normalize(v + l);
float nDotL = max(dot(n, l), 0.0);
float nDotV = abs(dot(n, v)) + 0.001;
float3 F = FresnelSchlick(max(dot(h, v), 0.0), facingSpecular).xxx;
float NDF = NormalDistribution(n, h, roughness);
float G = GeometrySmith(n, nDotV, l, roughness);
float3 numerator = PI * NDF * G * F;
// add 0.0001 to avoid division by zero
float denominator = 4.0 * nDotV * nDotL + 0.0001;
float3 reflected = numerator / denominator;
color = reflected * SunColor * nDotL;
}
return color;
}
float4 HighFidelityPS( VS_OUTPUT inV,
uniform bool alphaTestEnable,
uniform int alphaFunc,
uniform int alphaRef ) : COLOR
{
// calculate the depth of water at this pixel
float4 waterTexture = tex2D( UtilitySamplerC, inV.mTexUV );
float waterDepth = waterTexture.g;
float3 viewVector = normalize(inV.mViewVec);
// get perspective correct coordinate for sampling from the other textures
// the screenPos is then in 0..1 range with the origin at the top left of the screen
float OneOverW = 1.0 / inV.mScreenPos.w;
inV.mScreenPos.xyz *= OneOverW;
float2 screenPos = inV.mScreenPos.xy * ViewportScaleOffset.xy;
screenPos += ViewportScaleOffset.zw;
// get the unaltered sea floor
float4 backGroundPixels = tex2D(RefractionSampler, screenPos);
// because the alpha value holds the unit parts above water and uses a small value
// for the land cutout, we multiply by a large number and then saturate
float mask = saturate(backGroundPixels.a * 255);
// calculate the normal we will be using for the water surface
float4 W0 = tex2D( NormalSampler0, inV.mLayer0 );
float4 W1 = tex2D( NormalSampler1, inV.mLayer1 );
float4 W2 = tex2D( NormalSampler2, inV.mLayer2 );
float4 W3 = tex2D( NormalSampler3, inV.mLayer3 );
float4 sum = W0 + W1 + W2 + W3;
float waveCrest = saturate( sum.a - waveCrestThreshold );
// scale, bias and normalize
float3 N = 2.0 * sum.xyz - 4.0;
N = normalize(N.xzy);
float3 R = reflect(-viewVector, N);
// get the correct coordinate for sampling refraction and reflection
float2 refractionPos = screenPos;
refractionPos -= sqrt(waterDepth) * refractionScale * N.xz * OneOverW;
// keep in mind the alpha channel holds the unit parts above water
// specifically the alpha channel of that unit's shader
float4 refractedPixels = tex2D(RefractionSampler, refractionPos);
// we need to exclude the unit refraction above the water line. This creats small areas with
// no refraction, but the water color in the next step will make this mostly unnoticeable
refractedPixels.rgb = lerp(refractedPixels, backGroundPixels, saturate(refractedPixels.a * 255)).rgb;
// we want to lerp in the water color based on depth, but clamped
float factor = clamp(waterDepth, waterLerp.x, waterLerp.y);
refractedPixels.rgb = lerp(refractedPixels.rgb, waterColor, factor);
// We can't compute wich part of the unit we would hit with our reflection vector,
// so we have to resort to an approximation using the refractionPos
float4 unitReflections = tex2D(ReflectionSampler, refractionPos);
float4 skyReflection = texCUBE(SkySampler, R);
// The alpha channel acts as a mask for unit parts above the water and probably
// uses unitReflectionAmount as the positive value of the mask
float3 reflections = lerp(skyReflection.rgb, unitReflections.rgb, saturate(unitReflections.a));
// Schlick approximation for fresnel
float NDotV = saturate(dot(viewVector, N));
float fresnel = FresnelSchlick(NDotV, 0.08);
// the default value of 1.5 is way to high, but we want to preserve manually set values in existing maps
if (skyreflectionAmount == 1.5)
skyreflectionAmount = 1.0;
float3 water = lerp(refractedPixels, reflections, saturate(fresnel * skyreflectionAmount));
// add in the sun reflection
float3 sunReflection = calculateSunReflection(R, viewVector, N);
// the sun shouldn't be visible where a unit reflection is
sunReflection *= (1 - saturate(unitReflections.a * 4));
// we can control this value to have terrain cast a shadow on the water surface
sunReflection *= waterTexture.r;
water += sunReflection;
// Lerp in the wave crests
water = lerp(water, waveCrestColor, (1 - waterTexture.a) * (1 - waterTexture.b) * waveCrest);
// return the pixels masked out by the water mask
float4 returnPixels;
returnPixels.rgb = water;
returnPixels.a = 1 - mask;
return returnPixels;
}
float4 MediumFidelityPS0( LOWFIDELITY_VERTEX vertex) : COLOR
{
float4 water = tex2D(UtilitySamplerC,vertex.texcoord0);
float alpha = clamp(water.g, 0, 0.3);
return float4(waterColorLowFi, alpha);
}
float4 MediumFidelityPS1( LOWFIDELITY_VERTEX vertex) : COLOR
{
float4 water = tex2D(UtilitySamplerC,vertex.texcoord0);
float alpha = clamp(water.g,0,0.2);
float w0 = tex2D(NormalSampler0,vertex.normal0).a;
float w1 = tex2D(NormalSampler1,vertex.normal1).a;
float w2 = tex2D(NormalSampler2,vertex.normal2).a;
float w3 = tex2D(NormalSampler3,vertex.normal3).a;
float waveCrest = saturate(( w0 + w1 + w2 + w3 ) - waveCrestThreshold);
return float4(waveCrestColor, waveCrest);
}
technique Water_MedFidelity
<
string abstractTechnique = "TWater";
int fidelity = FIDELITY_MEDIUM;
>
{
pass P0
{
AlphaBlendEnable = true;
ColorWriteEnable = 0x07;
SrcBlend = SrcAlpha;
DestBlend = InvSrcAlpha;
ZEnable = true;
ZFunc = lessequal;
ZWriteEnable = false;
VertexShader = compile vs_1_1 LowFidelityVS();
PixelShader = compile ps_2_0 MediumFidelityPS0();
}
pass P1
{
AlphaBlendEnable = true;
ColorWriteEnable = 0x07;
SrcBlend = SrcAlpha;
DestBlend = InvSrcAlpha;
ZEnable = true;
ZFunc = lessequal;
ZWriteEnable = false;
VertexShader = compile vs_1_1 LowFidelityVS();
PixelShader = compile ps_2_0 MediumFidelityPS1();
}
}
technique Water_HighFidelity
<
string abstractTechnique = "TWater";
int fidelity = FIDELITY_HIGH;
>
{
pass P0
{
AlphaState( AlphaBlend_Disable_Write_RGB )
DepthState( Depth_Enable_LessEqual_Write_None )
RasterizerState( Rasterizer_Cull_CW )
#ifndef DIRECT3D10
AlphaTestEnable = true;
AlphaRef = 0x00;
AlphaFunc = NotEqual;
#endif
VertexShader = compile vs_1_1 WaterVS();
PixelShader = compile ps_2_a HighFidelityPS( true, d3d_NotEqual, 0 );
}
}
struct MASK_OUTPUT
{
float4 mPos : POSITION0;
float2 mTexUV0 : TEXCOORD0;
};
MASK_OUTPUT WaterAlphaMaskVS(
#ifdef DIRECT3D10
// stricter shader linkages require that this match the input layout
float3 inPos : POSITION,
#else
float4 inPos : POSITION0,
#endif
float2 inTexUV0 : TEXCOORD0 )
{
MASK_OUTPUT result = (MASK_OUTPUT) 0;
float4x4 wvp = mul( WorldToView, Projection );
result.mPos = mul( float4(inPos.xyz,1), wvp );
// water properties coords
result.mTexUV0 = inTexUV0;
return result;
}
float4 WaterLayAlphaMaskPS( MASK_OUTPUT inV,
uniform bool alphaTestEnable,
uniform int alphaFunc,
uniform int alphaRef ) : COLOR
{
float4 output = tex2D( MaskSampler, inV.mTexUV0 );
#ifdef DIRECT3D10
if( alphaTestEnable )
AlphaTestD3D10( output, alphaFunc, alphaRef );
#endif
return float4(0,0,0,output.b);
}
technique TWaterLayAlphaMask
{
pass P0
{
AlphaState( AlphaBlend_Disable_Write_A )
DepthState( Depth_Enable_Less_Write_None )
RasterizerState( Rasterizer_Cull_CW )
#ifndef DIRECT3D10
AlphaTestEnable = true;
AlphaFunc = Equal;
AlphaRef = 0;
#endif
VertexShader = compile vs_1_1 WaterAlphaMaskVS();
PixelShader = compile ps_2_0 WaterLayAlphaMaskPS( true, d3d_Equal, 0 );
}
}
/// Shoreline
///
///
struct SHORELINE_VERTEX
{
float4 position : POSITION0;
};
SHORELINE_VERTEX ShorelineVS( float2 position : POSITION0)
{
SHORELINE_VERTEX vertex = (SHORELINE_VERTEX)0;
vertex.position = float4( position.x, WaterElevation, position.y, 1);
vertex.position = mul( vertex.position, mul( WorldToView, Projection));
return vertex;
}
float4 ShorelinePS( SHORELINE_VERTEX vertex,
uniform bool alphaTestEnable,
uniform int alphaFunc,
uniform int alphaRef ) : COLOR0
{
float4 output = float4(0,0,0,0);
#ifdef DIRECT3D10
if( alphaTestEnable )
AlphaTestD3D10( output.a, alphaFunc, alphaRef );
#endif
return output;
}
technique TShoreline
{
pass P0
{
AlphaState( AlphaBlend_Disable_Write_A )
DepthState( Depth_Enable_Less_Write_None )
RasterizerState( Rasterizer_Cull_CCW )
#ifndef DIRECT3D10
AlphaTestEnable = true;
AlphaFunc = Equal;
AlphaRef = 0;
#endif
VertexShader = compile vs_1_1 ShorelineVS();
PixelShader = compile ps_2_0 ShorelinePS( true, d3d_Equal, 0 );
}
}