DYT/Tool/TritonSDK/Resources/flat-fft.fx
2024-12-25 22:48:51 +08:00

1220 lines
36 KiB
HLSL

//#define HDR
#ifdef PS30
#define DETAIL
#endif
#define DETAIL_OCTAVE 2.0
#define DETAIL_BLEND 0.4
#define SPECULAR_BOOST 500.0
#define PRECISION_GUARD 2.0
#define SPECULAR_DISTANCE_FACTOR 70.0
#define BUBBLES 0.1
uniform float3 L;
uniform float3 lightColor;
uniform float3 ambientColor;
uniform float3 refractColor;
uniform float shininess;
uniform float4x4 modelview;
uniform float4x4 projection;
uniform float depthOffset;
uniform float4x4 invModelviewProj;
uniform float4 plane;
uniform float3x3 basis;
uniform float3x3 invBasis;
uniform float3 cameraPos;
uniform float4x4 gridScale;
uniform float gridSize;
uniform float2 textureSize;
uniform float foamScale;
uniform bool hasEnvMap;
uniform float fogDensity;
uniform float3 fogColor;
uniform float fogDensityBelow;
uniform float noiseAmplitude;
uniform float3x3 cubeMapMatrix;
uniform float invNoiseDistance;
uniform float invDampingDistance;
uniform bool doWakes;
uniform bool hasPlanarReflectionMap;
uniform float3x3 planarReflectionMapMatrix;
uniform float planarReflectionDisplacementScale;
uniform float3 floorPlanePoint;
uniform float3 floorPlaneNormal;
uniform float foamBlend;
uniform float washLength;
uniform float textureLODBias;
uniform float planarReflectionBlend;
uniform float2 zNearFar;
uniform bool hasHeightMap;
uniform float time;
uniform float seaLevel;
uniform float4x4 heightMapMatrix;
uniform bool hasDepthMap;
uniform int numKelvinWakes;
uniform int numPropWashes;
uniform int numCircularWaves;
uniform bool depthOnly;
uniform bool underwater;
uniform float3 doubleRefractionColor;
uniform float doubleRefractionIntensity;
uniform float oneOverGamma;
uniform float sunIntensity;
uniform float reflectivityScale;
uniform float transparency;
#ifdef BREAKING_WAVES
uniform float kexp;
uniform float breakerWavelength;
uniform float breakerWavelengthVariance;
uniform float4 breakerDirection;
uniform float breakerAmplitude;
uniform float breakerPhaseConstant;
uniform float surgeDepth;
uniform float steepnessVariance;
uniform float breakerDepthFalloff;
#endif
#define PI 3.14159265
#define TWOPI (2.0 * 3.14159265)
struct CircularWave {
float amplitude;
float radius;
float k;
float halfWavelength;
float3 position;
};
struct KelvinWake {
float amplitude;
float3 position;
float3 shipPosition;
float foamAmount;
float hullWakeWidth;
float hullWakeLengthReciprocal;
};
#ifdef PROPELLER_WASH
struct PropWash {
float3 deltaPos;
float washWidth;
float beamWidth;
float3 propPosition;
float distFromSource;
float washLength;
float alphaStart;
float alphaEnd;
};
uniform PropWash washes[MAX_PROP_WASHES];
#endif
uniform CircularWave circularWaves[MAX_CIRCULAR_WAVES];
uniform KelvinWake wakes[MAX_KELVIN_WAKES];
#ifdef DX9
TEXTURE displacementTexture; // Kelvin wakes
TEXTURE displacementMap; // FFT waves
TEXTURE slopeFoamMap;
TEXTURE cubeMap;
TEXTURE foamTex;
TEXTURE noiseTex;
TEXTURE washTex;
TEXTURE planarReflectionMap;
TEXTURE heightMap;
TEXTURE breakerTex;
TEXTURE depthMap;
TEXTURE hullWakeTexture;
sampler2D gDisplacementSampler = sampler_state {
Texture = (displacementMap);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
sampler2D gDisplacementTextureSampler = sampler_state {
Texture = (displacementTexture);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
sampler2D gHeightSampler = sampler_state {
Texture = (heightMap);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = CLAMP;
AddressV = CLAMP;
};
sampler2D gDepthSampler = sampler_state {
Texture = (depthMap);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = CLAMP;
AddressV = CLAMP;
};
sampler2D gSlopeFoamSampler = sampler_state {
Texture = (slopeFoamMap);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
samplerCUBE gCubeSampler = sampler_state {
Texture = (cubeMap);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = CLAMP;
AddressV = CLAMP;
AddressW = CLAMP;
};
sampler2D gFoamSampler = sampler_state {
Texture = (foamTex);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
sampler2D gWashSampler = sampler_state {
Texture = (washTex);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = CLAMP;
AddressV = WRAP;
};
sampler2D gBreakerSampler = sampler_state {
Texture = (breakerTex);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = WRAP;
AddressV = CLAMP;
};
sampler2D gNoiseSampler = sampler_state {
Texture = (noiseTex);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
sampler2D gPlanarReflectionSampler = sampler_state {
Texture = (planarReflectionMap);
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
/*sampler2D gHullWakeTextureSampler = sampler_state {
Texture = (hullWakeTexture);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};*/
#else
Texture2D displacementTexture;
Texture2D displacementMap;
Texture2D slopeFoamMap;
TextureCube cubeMap;
Texture2D foamTex;
Texture2D noiseTex;
Texture2D washTex;
Texture2D planarReflectionMap;
Texture2D heightMap;
Texture2D depthMap;
Texture2D breakerTex;
Texture2D hullWakeTexture;
SamplerState gBiLinearSamClamp {
Filter = MIN_MAG_LINEAR_MIP_POINT;
AddressU = CLAMP;
AddressV = CLAMP;
};
SamplerState gTriLinearSamWrap {
Filter = MIN_MAG_MIP_LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
SamplerState gTriLinearSamWash {
Filter = MIN_MAG_MIP_LINEAR;
AddressU = CLAMP;
AddressV = WRAP;
};
SamplerState gTriLinearSamBreaker {
Filter = MIN_MAG_MIP_LINEAR;
AddressU = WRAP;
AddressV = CLAMP;
};
#endif
float getDepthFromHeightmap(in float3 worldPos)
{
float depth = DEFAULT_DEPTH;
float4 texCoords = mul(float4(worldPos, 1.0), heightMapMatrix);
if (texCoords.x > 0 && texCoords.x < 1.0 && texCoords.y > 0 && texCoords.y < 1.0) {
float4 tc4 = float4(texCoords.x, texCoords.y, 0.0f, 0.0f);
#ifdef DX9
float height = tex2Dlod(gHeightSampler, tc4).x;
#else
float height = heightMap.SampleLevel(gBiLinearSamClamp, texCoords, 0).x;
#endif
depth = -(height - seaLevel);
}
return depth;
}
float getDepthFromDepthmap(in float3 worldPos)
{
float4 eyePos = mul(float4(worldPos, 1.0), modelview);
float4 clipPos = mul(eyePos, projection);
float3 ndcPos = clipPos.xyz / clipPos.w;
float2 texPos = (ndcPos.xy + float2(1.0, 1.0)) * float2(0.5, 0.5);
#ifdef DX9
float4 tc4 = float4(texPos.x, texPos.y, 0.0, 0.0);
float terrainZ = tex2Dlod(gDepthSampler, tc4).x;
#else
float terrainZ = depthMap.SampleLevel(gBiLinearSamClamp, texPos, 0).x;
#endif
terrainZ = lerp(zNearFar.x, zNearFar.y, terrainZ);
float4 terrainWorld = mul(float4(ndcPos.x, ndcPos.y, terrainZ, 1.0), invModelviewProj);
terrainWorld /= terrainWorld.w;
return length(terrainWorld.xyz - worldPos);
}
void computeTransparency(in float3 worldPos, inout float4 transparencyDepthBreakers)
{
float depth = 1000.0;
// Compute depth at this position
if (hasHeightMap) {
depth = getDepthFromHeightmap(worldPos);
} else if (hasDepthMap) {
depth = getDepthFromDepthmap(worldPos);
} else {
float3 up = mul(float3(0, 0, 1.0), invBasis);
float3 l = -up;
float3 l0 = worldPos;
float3 n = floorPlaneNormal;
float3 p0 = floorPlanePoint;
float numerator = dot((p0 - l0), n);
float denominator = dot(l, n);
if (abs(denominator) > 0.0001) {
depth = numerator / denominator;
}
}
// Compute fog at this distance underwater
float fogExponent = abs(depth) * fogDensityBelow;
float transparency = clamp(exp(-abs(fogExponent)), 0.0, 1.0);
transparencyDepthBreakers.xy = float2(transparency, depth);
}
float mod(float x, float y)
{
return x - y * floor(x/y);
}
void applyBreakingWaves(inout float3 v, inout float4 transparencyDepthBreakers, out float2 breakerTexCoords)
{
breakerTexCoords = float2(0.0, 0.0);
#ifdef BREAKING_WAVES
float breaker = 0;
float depth = transparencyDepthBreakers.y;
if (hasHeightMap && breakerAmplitude > 0 && depth > 0 && depth < breakerWavelength * 0.5) {
float halfWavelength = breakerWavelength * 0.5;
float scaleFactor = ((depth - halfWavelength) / halfWavelength);
float wavelength = breakerWavelength + scaleFactor * breakerWavelengthVariance;
float halfKexp = kexp * 0.5;
scaleFactor = (depth - halfKexp) / halfKexp;
scaleFactor *= 1.0 + steepnessVariance;
float k = kexp + scaleFactor;
float3 localDir = mul(breakerDirection.xyz, basis);
localDir.z = 0;
localDir = normalize(localDir);
float dotResult = dot(-localDir.xy, v.xy) * TWOPI / wavelength;
float finalz = (dotResult + breakerPhaseConstant * time);
float3 binormal = cross(float3(0.0, 0.0, 1.0), localDir);
breakerTexCoords.x = dot(binormal.xy, v.xy);
breakerTexCoords.x /= foamScale * 5.0;
#define OFFSET (PI * 0.3)
float y = mod(finalz, TWOPI);
float num = PI - y;
float den = PI - OFFSET;
breakerTexCoords.y = num / den;
float sinz = sin(finalz);
finalz = (sinz + 1.0) * 0.5;
finalz = breakerAmplitude * pow(finalz, k);
finalz *= 1.0 - min(depth * breakerDepthFalloff / halfWavelength, 1.0);
finalz = max(0, finalz);
breaker = clamp(sinz, 0.0, 1.0) * 2.0;
breaker *= 1.0 - min((depth * 3.0 * breakerDepthFalloff) / halfWavelength, 1.0);
// Hide the backs of waves if we're transparent
float opacity = 1.0 - transparencyDepthBreakers.x;
finalz = lerp(0.0, finalz, pow(opacity, 6.0));
v.z += finalz;
}
transparencyDepthBreakers.z = breaker;
#endif
}
void applyCircularWaves(inout float3 v, in float fade, out float2 slope, out float foam)
{
int i;
slope = float2(0.0, 0.0);
float disp = 0.0;
for (i = 0; i < numCircularWaves; i++) {
float2 D = (v - circularWaves[i].position).xy;
float dist = length(D);
float r = dist - circularWaves[i].radius;
if (abs(r) < circularWaves[i].halfWavelength) {
float amplitude = circularWaves[i].amplitude;
float theta = circularWaves[i].k * r;
disp += amplitude * cos(theta);
float derivative = amplitude * -sin(theta);
slope += D * (derivative / dist);
}
}
v.z += disp * fade;
foam = length(slope);
slope *= fade;
}
void applyKelvinWakes(inout float3 v, in float fade, inout float2 slope, inout float foam)
{
int i;
#ifdef KELVIN_WAKES
float2 accumSlope = float2(0.0, 0.0);
float disp = 0.0;
float hullWakeFoam = 0;
for (i = 0; i < numKelvinWakes; i++) {
float3 X0 = wakes[i].position - wakes[i].shipPosition;
float3 T = normalize(X0);
float3 N = float3(0,0,1);
float3 B = normalize(cross(N, T));
float3 P = v - wakes[i].shipPosition;
float3 X;
X.x = dot(P.xy, T.xy);
X.y = dot(P.xy, B.xy);
float xLen = length(X0);
float2 tc;
tc.x = X.x / (1.54 * xLen);
tc.y = (X.y) / (1.54 * xLen) + 0.5;
if (tc.x >= 0.01 && tc.x <= 0.99 && tc.y >= 0.01 && tc.y <= 0.99) {
#ifdef DX9
float4 tc4 = float4(tc.x, tc.y, 0.0f, 0.0f);
float4 sample = tex2Dlod(gDisplacementTextureSampler, tc4);
#else
float4 sample = displacementTexture.SampleLevel(gTriLinearSamWrap, tc, 0);
#endif
float displacement = sample.w;
displacement *= wakes[i].amplitude * fade;
float3 thisnormal = normalize(sample.xyz * 2.0 - 1.0);
float invmax = rsqrt( max( dot(T,T), dot(B,B) ) );
float3x3 TBN = float3x3( T * invmax, B * invmax, N );
thisnormal = normalize(mul(thisnormal, TBN));
if (abs(thisnormal.x) < 0.05 && abs(thisnormal.y) < 0.05) {
thisnormal = float3(0, 0, 1);
}
thisnormal.xy *= min(1.0, wakes[i].amplitude);
thisnormal = normalize(thisnormal);
accumSlope += float2(thisnormal.x / thisnormal.z, thisnormal.y / thisnormal.z);
disp += displacement;
#ifdef DX9
#else
if(wakes[i].hullWakeLengthReciprocal > 0.0) {
tc.y = X.x * (wakes[i].hullWakeLengthReciprocal);
tc.x = (X.y) / (wakes[i].hullWakeWidth) + 0.5;
if (tc.x >= 0.01 && tc.x <= 0.99 && tc.y >= 0.01 && tc.y <= 0.99) {
float4 hullWakeSample = hullWakeTexture.SampleLevel(gTriLinearSamWrap, tc, 0);
if(hullWakeSample.z > 0.0) {
float ty = X.x * wakes[i].hullWakeLengthReciprocal;
float t = length(P) * wakes[i].hullWakeLengthReciprocal;
if(ty < 0.1) {
float tScale = 10.0*ty;
hullWakeFoam = hullWakeSample.z * wakes[i].foamAmount * tScale;
} else if(ty > 0.9) {
float tScale = 1.0-(10.0*(ty-0.9));
hullWakeFoam = hullWakeSample.z * wakes[i].foamAmount * tScale;
} else {
hullWakeFoam = hullWakeSample.z * wakes[i].foamAmount;
}
}
}
}
#endif
}
}
v.z += disp * fade;
//foam += min(1.0, length(accumSlope));
foam = min(1.0, foam+hullWakeFoam+length(accumSlope));
slope += accumSlope * fade;
#endif
}
void applyPropWash(in float3 v, out float3 washTexCoords)
{
washTexCoords = float3(0.0, 0.0, 0.0);
#ifdef PROPELLER_WASH
for (int i = 0; i < numPropWashes; i++) {
if (washes[i].distFromSource == 0) continue;
float3 C = washes[i].deltaPos;
float3 A = v - washes[i].propPosition;
float segmentLength = length(C);
// Compute t
float t0 = dot(C, A) / dot(C, C);
// Compute enough overlap to account for curved paths.
float overlap = (washes[i].washWidth / segmentLength) * 0.5;
if (t0 >= -overlap && t0 <= 1.0 + overlap) {
// Compute distance from source
float distFromSource = washes[i].distFromSource - (1.0 - t0) * segmentLength;
distFromSource = max(0.0, distFromSource);
// Compute wash width
float washWidth = (washes[i].washWidth * pow(distFromSource, 1.0 / 4.5)) * 0.5;
// Compute distance to line
float3 B = A - C;
float3 aCrossB = cross(A, B);
float d = length(aCrossB) / segmentLength;
// The direction of A X B indicates if we're 'left' or 'right' of the path
float nd = d / washWidth;
if (nd >= 0.0 && nd <= 1.0) {
float t = t0;
float3 up = float3(0,0,1.0);
if(dot(up, aCrossB) >= 0) {
t = (1.0-nd*0.5)-0.5;
} else {
t = 0.5+(nd*0.5);
}
nd = t;
washTexCoords.x = nd;
// The t0 parameter from our initial distance test to the line segment makes
// for a handy t texture coordinate
//scale texture by 4 to reduce tiling
washTexCoords.y = (washes[i].washLength - distFromSource) / (4.0 * washes[i].beamWidth);
float blend = lerp(washes[i].alphaEnd, washes[i].alphaStart, t0);
blend = clamp(blend, 0.0, 1.0);
if(nd <= 0.05 || nd >= 0.95) {
blend = 0;
} else if(nd <= 0.1) {
blend *= (nd-.05)*20.0;
} else if (nd >= .9) {
blend *= (.95-nd)*20.0;
}
washTexCoords.z = blend;
}
}
}
#endif
}
float3 applyPropWashFrag(in float3 v)
{
float3 washTexCoords = float3(0.0, 0.0, 0.0);
float3 Cw = float3(0.0, 0.0, 0.0);
#ifdef PROPELLER_WASH
float numHits = 0.0;
for (int i = 0; i < numPropWashes; i++) {
if (washes[i].distFromSource == 0) continue;
float3 C = washes[i].deltaPos;
float3 A = v - washes[i].propPosition;
float segmentLength = length(C);
// Compute t
float t0 = dot(C, A) / dot(C, C);
// Compute enough overlap to account for curved paths.
float overlap = (washes[i].washWidth / segmentLength) * 0.5;
if (t0 >= -overlap && t0 <= 1.0 + overlap) {
// Compute distance from source
float distFromSource = washes[i].distFromSource - (1.0 - t0) * segmentLength;
distFromSource = max(0.0, distFromSource);
// Compute wash width
float washWidth = (washes[i].washWidth * pow(distFromSource, 1.0 / 4.5)) * 0.5;
// Compute distance to line
float3 B = A - C;
float3 aCrossB = cross(A, B);
float d = length(aCrossB) / segmentLength;
// The direction of A X B indicates if we're 'left' or 'right' of the path
float nd = d / washWidth;
if (nd >= 0.0 && nd <= 1.0) {
float t = nd;
float3 up = float3(0, 0, 1.0);
if (dot(up, aCrossB) >= 0) {
t = (1.0 - nd * 0.5) - 0.5;
} else {
t = 0.5 + (nd * 0.5);
}
nd = t;
washTexCoords.x = nd;
// The t0 parameter from our initial distance test to the line segment makes
// for a handy t texture coordinate
washTexCoords.y = (washes[i].washLength - distFromSource) / washes[i].beamWidth;
float blend = lerp(washes[i].alphaEnd, washes[i].alphaStart, t0);
//float distFromCenter = d / washWidth;
blend *= max(0.0, 1.0 - nd * nd);
//if (washes[i].number == 0) blend *= 1.0 - clamp(t0 * t0, 0.0, 1.0);
//blend *= smoothstep(0, 0.1, nd);
washTexCoords.z = clamp(blend, 0.0, 1.0);
#ifdef DX9
Cw += tex2D(gWashSampler, washTexCoords.xy).xyz * ambientColor * washTexCoords.z;
#else
Cw += washTex.Sample(gTriLinearSamWash, washTexCoords.xy).xyz * ambientColor * washTexCoords.z;
#endif
numHits += 1.0;
}
}
}
#endif
return numHits > 0.0 ? Cw / numHits : Cw;
}
void displace(inout float3 v, inout float2 texCoords, inout float2 foamTexCoords, inout float2 noiseTexCoords,
out float2 slope, out float foam, out float3 washTexCoords, inout float4 transparencyDepthBreakers,
out float2 breakerTexCoords)
{
// Transform so z is up
float3 localV = mul(v, basis);
texCoords = localV.xy / textureSize;
float4 tc4 = float4(texCoords.x, texCoords.y, 0.0, 0.0);
#ifdef DX9
float3 displacement = tex2Dlod(gDisplacementSampler, tc4).xyz;
#else
float3 displacement = displacementMap.SampleLevel(gTriLinearSamWrap, texCoords, 0).xyz;
#endif
float opacity = 1.0 - transparencyDepthBreakers.x;
displacement.z = lerp(0.0, displacement.z, pow(opacity, 6.0));
float fade = 1.0 - smoothstep(0.0, 1.0, length(v - cameraPos) * invDampingDistance);
#ifdef BREAKING_WAVES
// Fade out waves in the surge zone
float depthFade = 1.0;
if (surgeDepth > 0) {
depthFade = min(surgeDepth, transparencyDepthBreakers.y) / surgeDepth;
depthFade = clamp(depthFade, 0.0, 1.0);
}
fade *= depthFade;
transparencyDepthBreakers.w = depthFade;
#endif
localV.xy += displacement.xy * fade;
if (doWakes) {
slope = float2(0,0);
applyCircularWaves(localV, fade, slope, foam);
applyKelvinWakes(localV, fade, slope, foam);
#ifndef PER_FRAGMENT_PROP_WASH
applyPropWash(localV, washTexCoords);
#endif
} else {
washTexCoords = float3(0.0, 0.0, 0.0);
slope = float2( 0.f, 0.f );
foam = 0.f;
}
#ifdef PER_FRAGMENT_PROP_WASH
washTexCoords = localV;
#endif
localV.z += displacement.z * fade;
applyBreakingWaves(localV, transparencyDepthBreakers, breakerTexCoords);
foamTexCoords = localV.xy / foamScale;
noiseTexCoords = texCoords * 0.03f;
v = mul(localV, invBasis);
}
bool projectToSea(in float4 v, out float4 vWorld)
{
// Get the line this screen position projects to
float4 p0 = v;
float4 p1 = v;
p0.z = zNearFar.x;
p1.z = zNearFar.y;
// Transform into world coords
p0 = mul(p0, invModelviewProj);
p1 = mul(p1, invModelviewProj);
// Intersect with the sea level
float3 up = mul(float3(0, 0, 1), invBasis);
float4 p = plane;
float altitude = dot(cameraPos, up) - seaLevel;
float offset = 0;
if (altitude < PRECISION_GUARD && altitude >= 0) {
p.w += PRECISION_GUARD;
offset = PRECISION_GUARD;
} else if (altitude > -PRECISION_GUARD && altitude < 0) {
p.w -= PRECISION_GUARD;
offset = -PRECISION_GUARD;
}
float4 dp = p1 - p0;
float t = -dot(p0, p) / dot( dp, p);
if (t > 0.0 && t < 1.0) {
vWorld = p0 + dp * t;
vWorld /= vWorld.w;
vWorld.xyz += up * offset;
return true;
} else {
vWorld = float4(0.0, 0.0, 0.0, 0.0);
return false;
}
}
#ifdef DX9
void VS( float4 position : POSITION,
out float4 oPosition : POSITION0,
out float2 texCoords : TEXCOORD0,
out float2 foamTexCoords : TEXCOORD1,
out float2 noiseTexCoords : TEXCOORD2,
out float4 V : TEXCOORD3,
out float4 transparencyDepthBreakers : TEXCOORD4,
out float4 wakeNormalsAndFoam : TEXCOORD5,
out float fogFactor : TEXCOORD6,
out float3 washTexCoords : TEXCOORD7,
out float2 breakerTexCoords : TEXCOORD8
)
#else
void VS( float4 position : POSITION,
out float4 oPosition : SV_POSITION,
out float2 texCoords : TEXCOORD0,
out float2 foamTexCoords : TEXCOORD1,
out float2 noiseTexCoords : TEXCOORD2,
out float4 V : TEXCOORD3,
out float4 transparencyDepthBreakers : TEXCOORD4,
out float4 wakeNormalsAndFoam : TEXCOORD5,
out float fogFactor : FOG,
out float3 washTexCoords : TEXCOORD6,
out float2 breakerTexCoords : TEXCOORD7
)
#endif
{
float4 worldPos = float4(0.0, 0.0, 0.0, 0.0);
float4 gridPos = mul(position, gridScale);
wakeNormalsAndFoam = float4( 0.0, 0.0, 1.0, 0.0 );
transparencyDepthBreakers = float4(0.0, 0.0, 0.0, 0.0);
breakerTexCoords = float2(0.0, 0.0);
if (projectToSea(gridPos, worldPos)) {
// Displace
float2 slope;
float foam;
computeTransparency(worldPos.xyz, transparencyDepthBreakers);
displace(worldPos.xyz, texCoords, foamTexCoords, noiseTexCoords, slope, foam, washTexCoords, transparencyDepthBreakers, breakerTexCoords);
float3 sx = float3(1.0, 0.0, slope.x);
float3 sy = float3(0.0, 1.0, slope.y);
wakeNormalsAndFoam.xyz = normalize(cross(sx, sy));
wakeNormalsAndFoam.w = min(1.0, foam);
float3 V3 = (worldPos.xyz - cameraPos);
// Project it back again, apply depth offset.
float4 v = mul(worldPos, modelview);
v.w -= depthOffset;
oPosition = mul(v, projection);
V = float4(V3.x, V3.y, V3.z, oPosition.z / oPosition.w);
} else {
V = float4(0.0, 0.0, 0.0, 0.0);
washTexCoords = float3(0.0, 0.0, 0.0);
foamTexCoords = float2(0.0, 0.0);
noiseTexCoords = float2(0.0, 0.0);
texCoords = float2(0.0, 0.0);
oPosition = float4(gridPos.x, gridPos.y, 2.0, 1.0);
}
float fogExponent = length(V.xyz) * fogDensity;
fogFactor = saturate(exp(-(fogExponent * fogExponent)));
}
#ifdef DX9
float4 PS(float2 texCoords : TEXCOORD0,
float2 foamTexCoords : TEXCOORD1,
float2 noiseTexCoords : TEXCOORD2,
float4 V : TEXCOORD3,
float4 transparencyDepthBreakers : TEXCOORD4,
float4 wakeNormalsAndFoam : TEXCOORD5,
float fogFactor : TEXCOORD6,
float3 washTexCoords : TEXCOORD7,
float2 breakerTexCoords : TEXCOORD8 ) : COLOR {
#else
float4 PS(float4 posH : SV_POSITION,
float2 texCoords : TEXCOORD0,
float2 foamTexCoords : TEXCOORD1,
float2 noiseTexCoords : TEXCOORD2,
float4 V : TEXCOORD3,
float4 transparencyDepthBreakers : TEXCOORD4,
float4 wakeNormalsAndFoam : TEXCOORD5,
float fogFactor : FOG,
float3 washTexCoords : TEXCOORD6,
float2 breakerTexCoords : TEXCOORD7 ) : SV_TARGET {
#endif
#ifdef PS30
if (hasHeightMap && transparencyDepthBreakers.y < 0) {
discard;
}
#endif
#ifdef PS30
if (depthOnly) {
return float4(V.w, V.w, V.w, 1.0);
}
#endif
const float IOR = 1.33333;
float tileFade = exp(-length(V.xyz) * invNoiseDistance);
float horizDist = length((mul(V.xyz, basis)).xy);
#ifdef PS30
float horizDistNorm = horizDist * invNoiseDistance;
float tileFadeHoriz = exp(-horizDistNorm);
#endif
float3 vNorm = normalize(V.xyz);
#ifdef DX9
float4 tc = float4(texCoords.x, texCoords.y, 0.0, textureLODBias);
#ifdef HIGHALT
float4 slopesAndFoamHigh = tex2Dbias(gSlopeFoamSampler, tc).xyzw;
float4 slopesAndFoamMed = tex2Dbias(gSlopeFoamSampler, float4((tc.xy + 0.1) * 0.25, tc.z, tc.w)).xyzw;
float4 slopesAndFoamLow = tex2Dbias(gSlopeFoamSampler, float4((tc.xy + 0.7) * 0.125, tc.z, tc.w)).xyzw;
float altitude = abs(mul(V.xyz, basis).z);
float invBlendDist = 10.0 * invNoiseDistance;
float amp = 1.0 - min(1.0, altitude * invBlendDist);
float4 slopesAndFoam = slopesAndFoamHigh * amp;
float totalAmp = amp;
amp = min(0.5, altitude * invBlendDist);
slopesAndFoam += slopesAndFoamMed * amp;
totalAmp += amp;
amp = min(0.5, altitude * invBlendDist * 0.5);
slopesAndFoam += slopesAndFoamLow * amp;
totalAmp += amp;
slopesAndFoam /= totalAmp;
#else
float4 slopesAndFoam = tex2Dbias(gSlopeFoamSampler, tc).xyzw;
#endif
float fresnelScale = length(slopesAndFoam.xyz);
#ifdef DETAIL
for (int n = 1; n <= NUM_OCTAVES; n++) {
float4 dtc = tc;
dtc.xy = tc.xy * DETAIL_OCTAVE * n;
slopesAndFoam.xyz += tex2Dbias(gSlopeFoamSampler, dtc).xyz * DETAIL_BLEND;
}
#endif
float3 unscaledNoise = (tex2D(gNoiseSampler, noiseTexCoords).xyz - float3(0.5, 0.5, 0.5));
#else
#ifdef HIGHALT
float4 slopesAndFoamHigh = slopeFoamMap.SampleBias(gTriLinearSamWrap, texCoords, textureLODBias).xyzw;
float4 slopesAndFoamMed = slopeFoamMap.SampleBias(gTriLinearSamWrap, (texCoords + 0.1) * 0.25, textureLODBias).xyzw;
float4 slopesAndFoamLow = slopeFoamMap.SampleBias(gTriLinearSamWrap, (texCoords + 0.7) * 0.125, textureLODBias).xyzw;
float altitude = abs(mul(V.xyz, basis).z);
float invBlendDist = 10.0 * invNoiseDistance;
float amp = 1.0 - min(1.0, altitude * invBlendDist);
float4 slopesAndFoam = slopesAndFoamHigh * amp;
float totalAmp = amp;
amp = min(0.5, altitude * invBlendDist);
slopesAndFoam += slopesAndFoamMed * amp;
totalAmp += amp;
amp = min(0.5, altitude * invBlendDist * 0.5);
slopesAndFoam += slopesAndFoamLow * amp;
totalAmp += amp;
slopesAndFoam /= totalAmp;
#else
float4 slopesAndFoam = slopeFoamMap.SampleBias(gTriLinearSamWrap, texCoords, textureLODBias).xyzw;
#endif
float fresnelScale = length(slopesAndFoam.xyz);
#ifdef DETAIL
for (int n = 1; n <= NUM_OCTAVES; n++) {
float2 dtc = texCoords * DETAIL_OCTAVE * n;
slopesAndFoam.xyz += slopeFoamMap.SampleBias(gTriLinearSamWrap, dtc, textureLODBias).xyz * DETAIL_BLEND;
}
#endif
float3 unscaledNoise = (noiseTex.Sample(gTriLinearSamWrap, noiseTexCoords).xyz - float3(0.5, 0.5, 0.5));
#endif
float3 normalNoise = unscaledNoise * noiseAmplitude;
normalNoise.z = abs(normalNoise.z);
slopesAndFoam.xyz = normalize(slopesAndFoam.xyz);
float3 wakeNormal = wakeNormalsAndFoam.xyz;
#ifdef BREAKING_WAVES
float breakerFade = transparencyDepthBreakers.w;
float3 realNormal = lerp(float3(0.0, 0.0, 1.0), slopesAndFoam.xyz, breakerFade);
#else
float3 realNormal = slopesAndFoam.xyz;
#endif
realNormal = normalize(realNormal + wakeNormal);
#ifdef SPARKLE
float bias = horizDistNorm * horizDistNorm * -64.0;
bias = lerp(bias, -5.0, clamp(altitude * invBlendDist, 0.0, 1.0));
#ifdef DX9
float4 stc = float4(texCoords.x, texCoords.y, 0.0, bias);
float3 specularSlopes = tex2Dbias(gSlopeFoamSampler, tc).xyz;
#else
float3 specularSlopes = slopeFoamMap.SampleBias(gTriLinearSamWrap, texCoords.xy, bias).xyz;
#endif
float3 specNormal = mul(normalize(specularSlopes.xyz), invBasis);
#endif
#ifdef PS30
float3 fadedNormal = lerp(float3(0, 0, 1), realNormal, tileFadeHoriz);
float3 nNorm = mul(normalize(fadedNormal + (normalNoise * (1.0 - tileFade))), invBasis);
#else
float3 nNorm = mul(normalize(realNormal + (normalNoise * (1.0 - tileFade))), invBasis);
#endif
float3 P = reflect(vNorm, nNorm);
#ifndef FAST_FRESNEL
// We don't need no stinkin Fresnel approximation, do it for real
float3 S = refract(P, nNorm, 1.0 / IOR);
float cos_theta1 = (dot(P, nNorm));
float cos_theta2 = (dot(S, -nNorm));
float Fp = (cos_theta1 - (IOR * cos_theta2)) /
(cos_theta1 + (IOR * cos_theta2));
float Fs = (cos_theta2 - (IOR * cos_theta1)) /
(cos_theta2 + (IOR * cos_theta1));
Fp = Fp * Fp;
Fs = Fs * Fs;
float reflectivity = clamp((Fs + Fp) * 0.5, 0.0, 1.0);
#else
//float reflectivity = clamp( 0.02+0.97*pow((1.0-dot(reflection, nNorm)),5.0), 0.0, 1.0 );
//float reflectivity = clamp(pow((1.0f-dot(P, nNorm)),5.0f), 0.0f, 1.0f );
float r=(1.2-1.0)/(1.2+1.0);
float reflectivity = max(0.0,min(1.0,r+(1.0-r)*pow((1.0-dot(nNorm,P)) * fresnelScale, 4.0)));
#endif
// No reflections on foam.
reflectivity = lerp(reflectivity, 0.0, clamp(wakeNormalsAndFoam.w, 0.0, 1.0));
reflectivity *= reflectivityScale;
#ifdef PS30
P = mul(P, basis);
P.z = max(0.0, P.z);
P = mul(normalize(P), invBasis);
#endif
#ifdef DX9
float3 envColor = hasEnvMap ? texCUBElod(gCubeSampler, float4(mul(P, cubeMapMatrix), 0)) : ambientColor;
float3 foamColor = tex2D(gFoamSampler, foamTexCoords).xyz;
#ifdef PS30
foamColor += tex2D(gFoamSampler, foamTexCoords * 1.7).xyz;
foamColor += tex2D(gFoamSampler, foamTexCoords * 0.3).xyz;
#endif
#else
float3 envColor = hasEnvMap ? cubeMap.SampleLevel(gTriLinearSamWrap, mul(P, cubeMapMatrix), 0).xyz : ambientColor;
float3 foamColor = foamTex.Sample(gTriLinearSamWrap, foamTexCoords).xyz;
foamColor += foamTex.Sample(gTriLinearSamWrap, foamTexCoords * 1.7).xyz;
foamColor += foamTex.Sample(gTriLinearSamWrap, foamTexCoords * 0.3).xyz;
#endif
#ifdef PS30
if( hasPlanarReflectionMap ) {
float3 up = mul( float3( 0., 0., 1. ), invBasis );
// perturb view vector by normal xy coords multiplied by displacement scale
// when we do it in world oriented space this perturbation is equal to:
// ( nNorm - dot( nNorm, up ) * up ) == invBasis * vec3( ( basis * nNorm ).xy, 0 )
float3 vNormPerturbed = vNorm + ( nNorm - dot( nNorm, up ) * up ) * planarReflectionDisplacementScale;
float3 tc = mul( vNormPerturbed, planarReflectionMapMatrix );
#ifdef DX9
float4 planarColor = tex2Dproj( gPlanarReflectionSampler, float4( tc.xy, 0., tc.z ) );
#else
float4 planarColor = planarReflectionMap.Sample(gBiLinearSamClamp, tc.xy / tc.z );
#endif
envColor = lerp( envColor.rgb, planarColor.rgb, planarColor.a * planarReflectionBlend);
}
#endif
#ifdef HDR
float3 Clight = ambientColor + lightColor * max(0, dot(L, nNorm));
#else
float3 Clight = min(ambientColor + lightColor * max(0, dot(L, nNorm)), float3(1.0, 1.0, 1.0));
#endif
float3 Cskylight = lerp(refractColor * Clight, envColor, reflectivity);
float3 Cfoam = foamColor * ambientColor;
float3 refractedL = L;
#ifdef PS30
if (underwater) {
refractedL = refract(normalize(L), invBasis[2], 1.0 / 1.333);
nNorm = -nNorm;
}
#endif
#ifdef SPARKLE
float3 R = reflect(refractedL, specNormal);
#else
float3 R = reflect(refractedL, nNorm);
#endif
float spec = max(0.0f, dot(vNorm, R));
float3 Csunlight = lightColor * min(1.0, pow(spec, shininess + horizDist * SPECULAR_DISTANCE_FACTOR) * sunIntensity * SPECULAR_BOOST * reflectivity);
float3 Ci = Cskylight + Csunlight;
// Fade out foam with distance to hide tiling
#ifdef BREAKING_WAVES
float foamAmount = (clamp(slopesAndFoam.w, 0.0, 1.0) * breakerFade + wakeNormalsAndFoam.w) * tileFadeHoriz * foamBlend;
foamAmount = foamAmount * foamAmount;
Ci = Ci + (Cfoam * foamAmount);
#ifdef DX9
float3 breakerCol = tex2D(gBreakerSampler, breakerTexCoords).xyz;
#else
float3 breakerCol = breakerTex.Sample(gTriLinearSamBreaker, breakerTexCoords).xyz;
#endif
float breakerNoise = max(0.0, (1.0 - abs(unscaledNoise.x * 10.0)));
Ci += breakerCol * transparencyDepthBreakers.z * tileFadeHoriz * breakerNoise;
#else
float foamAmount = (clamp(slopesAndFoam.w, 0.0, 1.0) + wakeNormalsAndFoam.w) * tileFadeHoriz * foamBlend;
foamAmount = foamAmount * foamAmount;
Ci = Ci + (Cfoam * foamAmount);
#endif
#ifdef PROPELLER_WASH
#ifdef PER_FRAGMENT_PROP_WASH
float3 Cw = applyPropWashFrag(washTexCoords);
#else // PER_FRAGMENT_PROP_WASH
#ifdef DX9
#ifdef PS30
float3 Cw = tex2D(gWashSampler, washTexCoords.xy).xyz * ambientColor * washTexCoords.z;
#endif // PS30
#else // DX9
float3 Cw = washTex.Sample(gTriLinearSamWash, washTexCoords.xy).xyz * ambientColor * washTexCoords.z;
#endif // DX9
#endif // PER_FRAGMENT_PROP_WASH
Ci = Ci + Cw;
#endif // PROPELLER_WASH
#ifdef PS30
float doubleRefraction = max(0, dot(-vNorm, nNorm)) * (1.0 - dot(-vNorm, invBasis[2]));
doubleRefraction += slopesAndFoam.w * BUBBLES;
doubleRefraction *= tileFade;
Ci += doubleRefractionColor * Clight * doubleRefraction * doubleRefractionIntensity;
#endif
#ifdef PS30
float transparencyLocal = clamp(transparencyDepthBreakers.x, 0.0, 1.0);
float alpha = hasHeightMap ? 1.0 - transparencyLocal : lerp(1.0 - transparencyLocal, 1.0, reflectivity);
float4 waterColor = float4(Ci, alpha);
float4 fogColor4 = float4(fogColor, hasHeightMap ? alpha : 1.0);
float4 finalColor = lerp(fogColor4, waterColor, clamp(fogFactor, 0.0, 1.0));
finalColor.xyz = pow(finalColor.xyz, float3(oneOverGamma, oneOverGamma, oneOverGamma));
#else
float4 finalColor = float4(Ci, 1.0);
#endif
finalColor.w *= transparency;
#ifndef HDR
finalColor = clamp(finalColor, 0.0, 1.0);
#endif
return finalColor;
}
#ifdef DX11
technique11 ColorTech {
pass P0
{
SetVertexShader( CompileShader( vs_5_0, VS() ) );
SetGeometryShader( NULL );
SetPixelShader( CompileShader( ps_5_0, PS() ) );
}
}
#endif
#ifdef DX10
technique10 ColorTech {
pass P0
{
SetVertexShader( CompileShader( vs_4_0, VS() ) );
SetGeometryShader( NULL );
SetPixelShader( CompileShader( ps_4_0, PS() ) );
}
}
#endif
#ifdef DX10LEVEL9
technique10 ColorTech {
pass P0
{
SetVertexShader( CompileShader( vs_4_0_level_9_1, VS() ) );
SetGeometryShader( NULL );
SetPixelShader( CompileShader( ps_4_0_level_9_1, PS() ) );
}
}
#endif
#ifdef DX9
technique {
pass P0
{
SetVertexShader( CompileShader( vs_3_0, VS() ) );
#ifdef PS30
SetPixelShader( CompileShader( ps_3_0, PS() ) );
#else
SetPixelShader( CompileShader( ps_2_0, PS() ) );
#endif
}
}
#endif