1401 lines
42 KiB
HLSL
1401 lines
42 KiB
HLSL
#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 float3 north;
|
|
uniform float3 east;
|
|
uniform float3 northPole;
|
|
uniform float3 radii;
|
|
uniform float3 oneOverRadii;
|
|
uniform bool hasEnvMap;
|
|
uniform float fogDensity;
|
|
uniform float fogDensityBelow;
|
|
uniform float3 fogColor;
|
|
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 planarHeight;
|
|
uniform float textureLODBias;
|
|
uniform float planarAdjust;
|
|
uniform float3 referenceLocation;
|
|
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;
|
|
};
|
|
|
|
#ifdef DX9
|
|
struct KelvinWake {
|
|
float amplitude;
|
|
float3 position;
|
|
float3 shipPosition;
|
|
};
|
|
#else
|
|
struct KelvinWake {
|
|
float amplitude;
|
|
float3 position;
|
|
float3 shipPosition;
|
|
float foamAmount;
|
|
float hullWakeWidth;
|
|
float hullWakeLengthReciprocal;
|
|
};
|
|
#endif
|
|
|
|
#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 displacementMap;
|
|
TEXTURE displacementTexture;
|
|
TEXTURE slopeFoamMap;
|
|
TEXTURE cubeMap;
|
|
TEXTURE foamTex;
|
|
TEXTURE noiseTex;
|
|
TEXTURE washTex;
|
|
TEXTURE planarReflectionMap;
|
|
TEXTURE heightMap;
|
|
TEXTURE depthMap;
|
|
TEXTURE breakerTex;
|
|
|
|
sampler2D gDisplacementSampler = sampler_state {
|
|
Texture = (displacementMap);
|
|
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;
|
|
};
|
|
|
|
#else
|
|
|
|
Texture2D displacementMap;
|
|
Texture2D displacementTexture;
|
|
Texture2D slopeFoamMap;
|
|
TextureCube cubeMap;
|
|
Texture2D foamTex;
|
|
Texture2D noiseTex;
|
|
Texture2D washTex;
|
|
Texture2D planarReflectionMap;
|
|
Texture2D heightMap;
|
|
Texture2D depthMap;
|
|
Texture2D breakerTex;
|
|
Texture2D hullWakeTexture;
|
|
|
|
SamplerState gTriLinearSamWrap {
|
|
Filter = MIN_MAG_MIP_LINEAR;
|
|
AddressU = WRAP;
|
|
AddressV = WRAP;
|
|
};
|
|
|
|
SamplerState gTriLinearSamWash {
|
|
Filter = MIN_MAG_MIP_LINEAR;
|
|
AddressU = CLAMP;
|
|
AddressV = WRAP;
|
|
};
|
|
|
|
SamplerState gBiLinearSamClamp {
|
|
Filter = MIN_MAG_LINEAR_MIP_POINT;
|
|
AddressU = CLAMP;
|
|
AddressV = CLAMP;
|
|
};
|
|
|
|
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.0, 0.0);
|
|
|
|
#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 localPos)
|
|
{
|
|
float4 eyePos = mul(float4(localPos, 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) - length(localPos);
|
|
}
|
|
|
|
void computeTransparency(in float3 worldPos, in float3 localPos, in float3 up, inout float4 transparencyDepthBreakers)
|
|
{
|
|
float depth = 1000.0;
|
|
// Compute depth at this position
|
|
if (hasHeightMap) {
|
|
depth = getDepthFromHeightmap(worldPos);
|
|
} else if (hasDepthMap) {
|
|
depth = getDepthFromDepthmap(localPos);
|
|
} else {
|
|
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 float3 localPos, float fade, out float2 slope, out float foam)
|
|
{
|
|
int i;
|
|
|
|
float3 slope3 = float3(0.0, 0.0, 0.0);
|
|
float disp = 0.0;
|
|
|
|
for (i = 0; i < numCircularWaves; i++) {
|
|
|
|
float3 D = (localPos - circularWaves[i].position);
|
|
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);
|
|
slope3 += D * (derivative / dist);
|
|
}
|
|
}
|
|
|
|
v.z += disp * fade;
|
|
|
|
slope = mul(slope3, basis).xy;
|
|
|
|
foam = length(slope.xy);
|
|
}
|
|
|
|
void applyKelvinWakes(inout float3 v, in float3 localPos, float fade, in float3 up, inout float2 slope, inout float foam)
|
|
{
|
|
int i;
|
|
#ifdef KELVIN_WAKES
|
|
float2 accumSlope = float2(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 = up;
|
|
float3 B = normalize(cross(N, T));
|
|
|
|
float3 P = localPos - wakes[i].shipPosition;
|
|
float3 X;
|
|
X.x = dot(P, T);
|
|
X.y = dot(P, B);
|
|
// X.z = dot(P, N);
|
|
|
|
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.0, 0.0);
|
|
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));
|
|
|
|
// Convert to z-up
|
|
thisnormal = mul(thisnormal, basis);
|
|
|
|
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) {
|
|
#ifdef DX9
|
|
float4 tc4 = float4(tc.x, tc.y, 0.0f, 0.0f);
|
|
float4 hullWakeSample = tex2Dlod(hullWakeTexture, tc4);
|
|
#else
|
|
float4 hullWakeSample = hullWakeTexture.SampleLevel(gTriLinearSamWrap, tc, 0);
|
|
#endif
|
|
|
|
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, in float3 localPos, in float3 up, 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 = localPos - washes[i].propPosition;
|
|
float3 B = localPos - (washes[i].propPosition + C);
|
|
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 aCrossB = cross(A, B);
|
|
float d = length(aCrossB) / length(C);
|
|
|
|
// 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;
|
|
|
|
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 localPos, in float3 up)
|
|
{
|
|
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 = localPos - washes[i].propPosition;
|
|
float3 B = localPos - (washes[i].propPosition + C);
|
|
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 aCrossB = cross(A, B);
|
|
float d = length(aCrossB) / length(C);
|
|
|
|
// 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 - distFromCenter * distFromCenter);
|
|
//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.0f;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return numHits > 0.0 ? Cw / numHits : Cw;
|
|
}
|
|
|
|
#ifdef DX9
|
|
// Intersect a ray of origin P0 and direction v against a unit sphere centered at the origin
|
|
bool raySphereIntersect(in float3 p0, in float3 v, out float3 intersection)
|
|
{
|
|
float twop0v = 2.0 * dot(p0, v);
|
|
float p02 = dot(p0, p0);
|
|
float v2 = dot(v, v);
|
|
|
|
float disc = twop0v * twop0v - (4.0 * v2)*(p02 - 1);
|
|
if (disc > 0) {
|
|
float discSqrt = sqrt(disc);
|
|
float den = 2.0 * v2;
|
|
float t = (-twop0v - discSqrt) / den;
|
|
if (t < 0) {
|
|
t = (-twop0v + discSqrt) / den;
|
|
}
|
|
intersection = p0 + t * v;
|
|
return true;
|
|
} else {
|
|
intersection = float3(0.0, 0.0, 0.0);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Intersect a ray against an ellipsoid centered at the origin
|
|
bool rayEllipsoidIntersect(in float3 R0, in float3 Rd, out float3 intersection)
|
|
{
|
|
// Distort the ray so it aims toward a unit sphere, do a sphere intersection
|
|
// and scale it back to the ellpsoid's space.
|
|
|
|
float3 scaledR0 = R0 * oneOverRadii;
|
|
float3 scaledRd = Rd * oneOverRadii;
|
|
|
|
float3 sphereIntersection;
|
|
if (raySphereIntersect(scaledR0, scaledRd, sphereIntersection)) {
|
|
intersection = sphereIntersection * radii;
|
|
return true;
|
|
} else {
|
|
intersection = float3(0.0, 0.0, 0.0);
|
|
return false;
|
|
}
|
|
}
|
|
#else
|
|
// Intersect a ray of origin P0 and direction v against a unit sphere centered at the origin
|
|
bool raySphereIntersect(in double3 p0, in double3 v, out double3 intersection)
|
|
{
|
|
double twop0v = 2.0 * dot(p0, v);
|
|
double p02 = dot(p0, p0);
|
|
double v2 = dot(v, v);
|
|
|
|
double disc = twop0v * twop0v - (4.0 * v2)*(p02 - 1);
|
|
if (disc > 0) {
|
|
double discSqrt = sqrt(disc);
|
|
double den = 2.0 * v2;
|
|
double t = (-twop0v - discSqrt) / den;
|
|
if (t < 0) {
|
|
t = (-twop0v + discSqrt) / den;
|
|
}
|
|
intersection = p0 + t * v;
|
|
return true;
|
|
} else {
|
|
intersection = double3(0.0, 0.0, 0.0);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Intersect a ray against an ellipsoid centered at the origin
|
|
bool rayEllipsoidIntersect(in double3 R0, in double3 Rd, out double3 intersection)
|
|
{
|
|
// Distort the ray so it aims toward a unit sphere, do a sphere intersection
|
|
// and scale it back to the ellpsoid's space.
|
|
|
|
double3 scaledR0 = R0 * double3(oneOverRadii);
|
|
double3 scaledRd = Rd * double3(oneOverRadii);
|
|
|
|
double3 sphereIntersection;
|
|
if (raySphereIntersect(scaledR0, scaledRd, sphereIntersection)) {
|
|
intersection = double3(sphereIntersection) * double3(radii);
|
|
return true;
|
|
} else {
|
|
intersection = double3(0.0, 0.0, 0.0);
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Alternate, faster method - but it can't handle viewpoints inside the ellipsoid.
|
|
// If you don't need underwater views, this may be better for you.
|
|
bool rayEllipsoidIntersectFast(in float3 R0, in float3 Rd, out float3 intersection)
|
|
{
|
|
float3 q = R0 * oneOverRadii;
|
|
float3 bUnit = normalize(Rd * oneOverRadii);
|
|
float wMagnitudeSquared = dot(q, q) - 1.0;
|
|
|
|
float t = -dot(bUnit, q);
|
|
float tSquared = t * t;
|
|
|
|
if ((t >= 0.0) && (tSquared >= wMagnitudeSquared)) {
|
|
float temp = t - sqrt(tSquared - wMagnitudeSquared);
|
|
float3 r = (q + temp * bUnit);
|
|
intersection = r * radii;
|
|
|
|
return true;
|
|
} else {
|
|
intersection = float3(0.0, 0.0, 0.0);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool rayPlaneIntersect(in float4 p0, in float4 p1, out float4 intersection)
|
|
{
|
|
float offset = 0;
|
|
float4 p = plane;
|
|
if (plane.w < PRECISION_GUARD && plane.w >= 0) {
|
|
p.w = PRECISION_GUARD;
|
|
offset = PRECISION_GUARD - plane.w;
|
|
} else if (plane.w < 0 && plane.w >= -PRECISION_GUARD) {
|
|
p.w = -PRECISION_GUARD;
|
|
offset = -PRECISION_GUARD - plane.w;
|
|
}
|
|
|
|
// Intersect with the sea level
|
|
float4 dp = p1 - p0;
|
|
float t = -dot(p0, p) / dot( dp, p);
|
|
if (t > 0.0 && t < 1.0) {
|
|
intersection = dp * t + p0;
|
|
intersection /= intersection.w;
|
|
float3 up = normalize(cameraPos);
|
|
intersection.xyz += up * offset;
|
|
return true;
|
|
} else {
|
|
intersection = float4(0.0, 0.0, 0.0, 0.0);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool projectToSea(in float4 v, out float4 worldPos, out float4 localPos)
|
|
{
|
|
// Get the line this screen position projects to
|
|
float4 p0 = v;
|
|
p0.z = zNearFar.x;
|
|
float4 p1 = v;
|
|
p1.z = zNearFar.y;
|
|
|
|
// Transform into world coords
|
|
p0 = mul(p0, invModelviewProj);
|
|
p1 = mul(p1, invModelviewProj);
|
|
|
|
if (plane.w < planarHeight) {
|
|
float4 intersect;
|
|
// Intersect with the sea level
|
|
if (rayPlaneIntersect(p0, p1, localPos)) {
|
|
worldPos = localPos + float4(cameraPos, 1.0);
|
|
// Account for error from plane approximation
|
|
float dist = length(localPos.xyz + (plane.xyz * plane.w));
|
|
float2 v = float2(radii.x, dist);
|
|
float error = planarAdjust + (length(v) - radii.x);
|
|
float3 errorv = plane.xyz * error;
|
|
worldPos.xyz -= errorv;
|
|
localPos.xyz -= errorv;
|
|
return true;
|
|
} else {
|
|
localPos = float4(0.0, 0.0, 0.0, 0.0);
|
|
worldPos = float4(0.0, 0.0, 0.0, 0.0);
|
|
return false;
|
|
}
|
|
} else {
|
|
#ifdef DX9
|
|
float3 intersect = float3(0.0, 0.0, 0.0);
|
|
float3 p03 = p0.xyz / p0.w;
|
|
float3 p13 = p1.xyz / p1.w;
|
|
if (rayEllipsoidIntersect(p03 + cameraPos, p13 - p03, intersect)) {
|
|
localPos = float4(intersect - cameraPos, 1.0);
|
|
worldPos = float4(intersect, 1.0);
|
|
return true;
|
|
} else {
|
|
localPos = float4(0.0, 0.0, 0.0, 0.0);
|
|
worldPos = float4(0.0, 0.0, 0.0, 0.0);
|
|
return false;
|
|
}
|
|
#else
|
|
double3 intersect = double3(0.0, 0.0, 0.0);
|
|
double3 p03 = p0.xyz / p0.w;
|
|
double3 p13 = p1.xyz / p1.w;
|
|
if (rayEllipsoidIntersect(p03 + double3(cameraPos), p13 - p03, intersect)) {
|
|
localPos = float4(intersect - double3(cameraPos), 1.0);
|
|
worldPos = float4(intersect, 1.0);
|
|
return true;
|
|
} else {
|
|
localPos = float4(0.0, 0.0, 0.0, 0.0);
|
|
worldPos = float4(0.0, 0.0, 0.0, 0.0);
|
|
return false;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
float3 computeArcLengths(in float3 localPos, in float3 northDir, in float3 eastDir)
|
|
{
|
|
float3 pt = referenceLocation + localPos;
|
|
return float3(dot(pt, eastDir), dot(pt, northDir), 0);
|
|
}
|
|
|
|
#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 float3 up : TEXCOORD4,
|
|
out float4 transparencyDepthBreakers : TEXCOORD5,
|
|
out float4 wakeNormalAndFoam : TEXCOORD6,
|
|
out float4 fogFactor : COLOR0,
|
|
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 float3 up : TEXCOORD4,
|
|
out float4 transparencyDepthBreakers : TEXCOORD5,
|
|
out float4 wakeNormalAndFoam : TEXCOORD6,
|
|
out float fogFactor : FOG,
|
|
out float3 washTexCoords : TEXCOORD7,
|
|
out float2 breakerTexCoords : TEXCOORD8
|
|
)
|
|
#endif
|
|
{
|
|
// To avoid precision issues, the translation component of the modelview matrix
|
|
// is zeroed out, and the camera position passed in via cameraPos
|
|
|
|
float4 worldPos = float4(0.0, 0.0, 0.0, 0.0);
|
|
|
|
float4 gridPos = mul(float4(position.x, position.y, 0.0, 1.0), gridScale);
|
|
|
|
bool above = true;
|
|
|
|
wakeNormalAndFoam = 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);
|
|
|
|
float4 localPos = float4(0.0, 0.0, 0.0, 0.0);
|
|
if (projectToSea(gridPos, worldPos, localPos)) {
|
|
// Here, worldPos is relative to the center of the Earth, since
|
|
// projectToSea added the camera position back in after transforming
|
|
|
|
up = normalize(worldPos.xyz);
|
|
|
|
computeTransparency(worldPos.xyz, localPos.xyz, up, transparencyDepthBreakers);
|
|
|
|
// Transform position on the ellipsoid into a planar reference,
|
|
// x east, y north, z up
|
|
float3 planar = computeArcLengths(localPos.xyz, north, east);
|
|
|
|
float fade = 1.0 - smoothstep(0.0, 1.0, length(localPos.xyz) * 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
|
|
// Compute displacement
|
|
texCoords = planar.xy / textureSize;
|
|
float4 tc4 = float4(texCoords.x, texCoords.y, 0, 0);
|
|
#ifdef DX9
|
|
float3 displacement = tex2Dlod(gDisplacementSampler, tc4).xyz;
|
|
#else
|
|
float3 displacement = displacementMap.SampleLevel(gTriLinearSamWrap, tc4.xy, 0).xyz;
|
|
#endif
|
|
// Hide the backs of waves if we're transparent
|
|
float opacity = 1.0 - transparencyDepthBreakers.x;
|
|
displacement.z = lerp(0.0, displacement.z, pow(opacity, 6.0));
|
|
|
|
displacement *= fade;
|
|
|
|
foamTexCoords = (planar.xy + displacement.xy) / foamScale;
|
|
noiseTexCoords = texCoords * 0.03;
|
|
|
|
localPos.xyz += displacement.x * east + displacement.y * north;
|
|
|
|
if (doWakes) {
|
|
float3 wakeNormal;
|
|
float wakeFoam;
|
|
float2 slope;
|
|
applyCircularWaves(planar, localPos.xyz, fade, slope, wakeFoam);
|
|
applyKelvinWakes(planar, localPos.xyz, fade, up, slope, wakeFoam);
|
|
|
|
#ifndef PER_FRAGMENT_PROP_WASH
|
|
applyPropWash(planar, localPos.xyz, up, washTexCoords);
|
|
#endif
|
|
float3 sx = float3(1.0, 0.0, slope.x * fade);
|
|
float3 sy = float3(0.0, 1.0, slope.y * fade);
|
|
wakeNormal = normalize(cross(sx, sy));
|
|
|
|
wakeNormalAndFoam.xyz = wakeNormal;
|
|
wakeNormalAndFoam.w = min(1.0, wakeFoam);
|
|
} else {
|
|
washTexCoords = float3(0.0, 0.0, 0.0);
|
|
}
|
|
|
|
#ifdef PER_FRAGMENT_PROP_WASH
|
|
washTexCoords = localPos.xyz;
|
|
#endif
|
|
|
|
localPos.xyz += displacement.z * up;
|
|
|
|
applyBreakingWaves(planar, transparencyDepthBreakers, breakerTexCoords);
|
|
|
|
// Transform back into geocentric coords
|
|
|
|
// Make relative to the camera, add in displacement
|
|
localPos.xyz += planar.z * up;
|
|
|
|
// Project it back again, apply depth offset.
|
|
float4 v = mul(localPos, modelview);
|
|
v.w -= depthOffset;
|
|
oPosition = mul(v, projection);
|
|
|
|
V.xyz = localPos.xyz;
|
|
V.w = oPosition.z / oPosition.w;
|
|
} else {
|
|
// No intersection, move the vert out of clip space
|
|
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);
|
|
up = float3(0.0, 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(float posH : POSITION0,
|
|
float2 texCoords : TEXCOORD0,
|
|
float2 foamTexCoords : TEXCOORD1,
|
|
float2 noiseTexCoords : TEXCOORD2,
|
|
float4 V : TEXCOORD3,
|
|
float3 up : TEXCOORD4,
|
|
float4 transparencyDepthBreakers : TEXCOORD5,
|
|
float4 wakeNormalAndFoam : TEXCOORD6,
|
|
float fogFactor : COLOR0,
|
|
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,
|
|
float3 up : TEXCOORD4,
|
|
float4 transparencyDepthBreakers : TEXCOORD5,
|
|
float4 wakeNormalAndFoam : TEXCOORD6,
|
|
float fogFactor : FOG,
|
|
float3 washTexCoords : TEXCOORD7,
|
|
float2 breakerTexCoords : TEXCOORD8 ) : 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);
|
|
|
|
float3 localEast = normalize(cross(northPole, up));
|
|
float3 localNorth = cross(up, localEast);
|
|
|
|
#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.5f, 0.5f, 0.5f));
|
|
#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.5f, 0.5f, 0.5f));
|
|
#endif
|
|
float3 normalNoise = unscaledNoise * noiseAmplitude;
|
|
normalNoise.z = abs(normalNoise.z);
|
|
slopesAndFoam.xyz = normalize(slopesAndFoam.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
|
|
|
|
#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, bias).xyz;
|
|
#endif
|
|
float3 specNormal = normalize(specularSlopes);
|
|
specNormal = normalize(specNormal.x * localEast + specNormal.y * localNorth + specNormal.z * up);
|
|
#endif
|
|
|
|
#ifdef PS30
|
|
float3 fadedNormal = lerp(float3(0.0, 0.0, 1.0), realNormal, tileFadeHoriz);
|
|
float3 N = fadedNormal + normalNoise + (wakeNormalAndFoam.xyz - float3(0.0, 0.0, 1.0));
|
|
#else
|
|
float3 N = realNormal + normalNoise + (wakeNormalAndFoam.xyz - float3(0.0, 0.0, 1.0));
|
|
#endif
|
|
|
|
float3 nNorm = normalize(N.x * localEast + N.y * localNorth + N.z * up);
|
|
|
|
float3 reflection = reflect(vNorm, nNorm);
|
|
|
|
#ifndef FAST_FRESNEL
|
|
// We don't need no stinkin Fresnel approximation, do it for real
|
|
|
|
float3 refraction = refract(vNorm, nNorm, 1.0 / IOR);
|
|
|
|
float cos_theta1 = (dot(vNorm, nNorm));
|
|
float cos_theta2 = (dot(refraction, 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(reflection, 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,reflection)) * fresnelScale, 4.0)));
|
|
#endif
|
|
|
|
// No reflections on foam.
|
|
reflectivity = lerp(reflectivity, 0.0, clamp(wakeNormalAndFoam.w, 0.0, 1.0));
|
|
|
|
reflectivity *= reflectivityScale;
|
|
|
|
#ifdef DX9
|
|
float3 envColor = hasEnvMap ? texCUBE(gCubeSampler, mul(reflection, cubeMapMatrix)).xyz : ambientColor;
|
|
#ifdef PS30
|
|
float3 foamColor = tex2D(gFoamSampler, foamTexCoords).xyz;
|
|
foamColor += tex2D(gFoamSampler, foamTexCoords * 1.7).xyz;
|
|
foamColor += tex2D(gFoamSampler, foamTexCoords * 0.3).xyz;
|
|
#endif
|
|
#else
|
|
float3 envColor = hasEnvMap ? cubeMap.Sample(gTriLinearSamWrap, mul(reflection, cubeMapMatrix)).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 ) {
|
|
// 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
|
|
|
|
#ifndef HDR
|
|
float3 Clight = min(ambientColor + lightColor * max(0, dot(L, nNorm)), float3(1.0, 1.0, 1.0));
|
|
#else
|
|
float3 Clight = ambientColor + lightColor * max(0, dot(L, nNorm));
|
|
#endif
|
|
|
|
float3 Cskylight = lerp(refractColor * Clight, envColor, reflectivity);
|
|
|
|
float3 refractedL = L;
|
|
|
|
#ifdef PS30
|
|
if (underwater) {
|
|
refractedL = refract(normalize(L), up, 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;
|
|
|
|
#ifdef PROPELLER_WASH
|
|
#ifdef PER_FRAGMENT_PROP_WASH
|
|
float3 Cw = applyPropWashFrag(washTexCoords, up);
|
|
#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, up));
|
|
doubleRefraction += slopesAndFoam.w * BUBBLES;
|
|
doubleRefraction *= tileFade;
|
|
Ci += doubleRefractionColor * Clight * doubleRefraction * doubleRefractionIntensity;
|
|
#endif
|
|
|
|
#ifdef PS30
|
|
// Fade out foam with distance to hide tiling, do alpha
|
|
float3 Cfoam = foamColor * ambientColor;
|
|
#ifdef BREAKING_WAVES
|
|
float foamAmount = (clamp(slopesAndFoam.w, 0.0, 1.0) * breakerFade + wakeNormalAndFoam.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) + wakeNormalAndFoam.w) * tileFadeHoriz * foamBlend;
|
|
foamAmount = foamAmount * foamAmount;
|
|
Ci = Ci + (Cfoam * foamAmount);
|
|
#endif
|
|
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);
|
|
#else
|
|
float4 waterColor = float4(Ci, 1.0);
|
|
float4 fogColor4 = float4(fogColor.xyz, 1.0);
|
|
#endif
|
|
|
|
float4 finalColor = lerp(fogColor4, waterColor, clamp(fogFactor, 0.0, 1.0));
|
|
|
|
finalColor.xyz = pow(finalColor.xyz, float3(oneOverGamma, oneOverGamma, oneOverGamma));
|
|
|
|
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
|
|
|