//#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