229 lines
6.8 KiB
Forth
Raw Normal View History

/*
Copyright (c) 2013 yvt
2016-11-07 00:38:09 +09:00
This file is part of OpenSpades.
2016-11-07 00:38:09 +09:00
OpenSpades is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
2016-11-07 00:38:09 +09:00
OpenSpades is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
2016-11-07 00:38:09 +09:00
You should have received a copy of the GNU General Public License
along with OpenSpades. If not, see <http://www.gnu.org/licenses/>.
2016-11-07 00:38:09 +09:00
*/
//varying vec2 detailCoord;
varying vec3 fogDensity;
varying vec3 screenPosition;
varying vec3 viewPosition;
varying vec3 worldPosition;
2016-11-07 00:38:09 +09:00
varying vec2 worldPositionOriginal;
uniform sampler2D screenTexture;
uniform sampler2D depthTexture;
2016-11-18 15:40:10 +02:00
uniform sampler2D mainTexture;
uniform sampler2D waveTexture1;
uniform sampler2D waveTexture2;
uniform sampler2D waveTexture3;
2013-09-04 22:11:39 +09:00
uniform sampler2D mirrorTexture;
2013-11-11 00:23:48 +09:00
uniform mat4 viewMatrix;
uniform vec3 fogColor;
uniform vec3 skyColor;
uniform vec2 zNearFar;
uniform vec4 fovTan;
uniform vec4 waterPlane;
uniform vec3 viewOrigin;
uniform vec2 displaceScale;
vec3 EvaluateSunLight();
vec3 EvaluateAmbientLight(float detailAmbientOcclusion);
2016-11-07 00:38:09 +09:00
float GGXDistribution(float m, float dotHalf);
float decodeDepth(float w, float near, float far){
return far * near / mix(far, near, w);
}
float depthAt(vec2 pt){
float w = texture2D(depthTexture, pt).x;
return decodeDepth(w, zNearFar.x, zNearFar.y);
}
void main() {
vec3 worldPositionFromOrigin = worldPosition - viewOrigin;
2016-11-07 00:38:09 +09:00
vec4 waveCoord = worldPositionOriginal.xyxy * vec4(vec2(0.08),
vec2(0.15704))
+ vec4(0., 0., 0.754, 0.1315);
2016-11-07 00:38:09 +09:00
vec2 waveCoord2 = worldPositionOriginal.xy * 0.02344 + vec2(.154, .7315);
// evaluate waveform (normal vector)
vec3 wave = texture2D(waveTexture1, waveCoord.xy).xyz;
2013-09-04 23:09:57 +09:00
wave = mix(vec3(-0.0025), vec3(0.0025), wave);
2016-11-07 00:38:09 +09:00
wave.xy *= 0.08 * 0.4;
// detail
vec2 wave2 = texture2D(waveTexture2, waveCoord.zw).xy;
2013-09-04 23:09:57 +09:00
wave2 = mix(vec2(-0.0025), vec2(0.0025), wave2);
2016-11-07 00:38:09 +09:00
wave2.xy *= 0.15704 * 0.2;
wave.xy += wave2;
2016-11-07 00:38:09 +09:00
// rough
wave2 = texture2D(waveTexture3, waveCoord2.xy).xy;
2013-09-04 23:09:57 +09:00
wave2 = mix(vec2(-0.0025), vec2(0.0025), wave2);
wave2.xy *= 0.02344 * 2.5;
wave.xy += wave2;
2016-11-07 00:38:09 +09:00
wave.z = (1. / 128.) / (4.); // (negated normal vector!)
wave.xyz = normalize(wave.xyz);
2016-11-07 00:38:09 +09:00
vec2 origScrPos = screenPosition.xy / screenPosition.z;
vec2 scrPos = origScrPos;
2016-11-07 00:38:09 +09:00
2013-11-11 00:23:48 +09:00
float scale = 1. / viewPosition.z;
vec2 disp = wave.xy * 0.1;
2013-09-04 23:42:14 +09:00
scrPos += disp * scale * displaceScale * 4.;
2016-11-07 00:38:09 +09:00
// check envelope length.
// if the displaced location points the out of the water,
// reset to the original pos.
float depth = depthAt(scrPos);
2016-11-07 00:38:09 +09:00
// convert to view coord
vec3 sampledViewCoord = vec3(mix(fovTan.zw, fovTan.xy, scrPos), 1.) * -depth;
float planeDistance = dot(vec4(sampledViewCoord, 1.), waterPlane);
if(planeDistance < 0.){
// reset!
// original pos must be in the water.
scrPos = origScrPos;
depth = depthAt(scrPos);
if(depth + viewPosition.z < 0.){
// if the pixel is obscured by a object,
// this fragment of water is not visible
//discard; done by early-Z test
}
}else{
2014-03-19 21:28:27 +09:00
depth = planeDistance / abs(waterPlane.z /* == dot(waterPlane, vec4(0.,0.,1.,0.)) */);
depth -= viewPosition.z;
}
2016-11-07 00:38:09 +09:00
float envelope = clamp((depth + viewPosition.z), 0., 1.);
envelope = 1. - (1. - envelope) * (1. - envelope);
2016-11-07 00:38:09 +09:00
// water color
// TODO: correct integral
vec2 waterCoord = worldPosition.xy;
vec2 integralCoord = floor(waterCoord) + .5;
vec2 blurDir = (worldPositionFromOrigin.xy);
blurDir /= max(length(blurDir), 1.);
vec2 blurDirSign = mix(vec2(-1.), vec2(1.), step(0., blurDir));
vec2 startPos = (waterCoord - integralCoord) * blurDirSign;
vec2 diffPos = blurDir * envelope * blurDirSign * .5 /*limit blur*/;
vec2 subCoord = 1. - clamp((vec2(0.5) - startPos) / diffPos,
0., 1.);
vec2 sampCoord = integralCoord + subCoord * blurDirSign;
2016-11-18 15:40:10 +02:00
vec3 waterColor = texture2D(mainTexture, sampCoord / 512.).xyz;
2013-11-11 00:23:48 +09:00
waterColor *= EvaluateSunLight() + EvaluateAmbientLight(1.);
2016-11-07 00:38:09 +09:00
// underwater object color
gl_FragColor = texture2D(screenTexture, scrPos);
2014-04-08 03:36:07 +09:00
#if !LINEAR_FRAMEBUFFER
gl_FragColor.xyz *= gl_FragColor.xyz; // screen color to linear
2014-04-08 03:36:07 +09:00
#endif
2016-11-07 00:38:09 +09:00
// apply fog color to water color now.
// note that fog is already applied to underwater object.
waterColor = mix(waterColor, fogColor, fogDensity);
2016-11-07 00:38:09 +09:00
// blend water color with the underwater object's color.
gl_FragColor.xyz = mix(gl_FragColor.xyz, waterColor, envelope);
2016-11-07 00:38:09 +09:00
// attenuation factor for addition blendings below
vec3 att = 1. - fogDensity;
2016-11-07 00:38:09 +09:00
/* ------- Reflection -------- */
2016-11-07 00:38:09 +09:00
2013-11-11 00:23:48 +09:00
vec3 ongoing = normalize(worldPositionFromOrigin);
2016-11-07 00:38:09 +09:00
2013-11-11 00:23:48 +09:00
// bluring for far surface
float lodBias = 1.0 / ongoing.z;
float dispScaleByLod = min(1., ongoing.z * 0.5);
lodBias = log2(lodBias);
lodBias = clamp(lodBias, 0., 2.);
2016-11-07 00:38:09 +09:00
2013-09-04 22:11:39 +09:00
// compute reflection color
vec2 scrPos2 = origScrPos;
disp.y = -abs(disp.y * 3.);
scrPos2 -= disp * scale * displaceScale * 15.;
2016-11-07 00:38:09 +09:00
vec3 refl = texture2D(mirrorTexture, scrPos2, lodBias).xyz;
2014-04-08 03:36:07 +09:00
#if !LINEAR_FRAMEBUFFER
2013-09-04 22:11:39 +09:00
refl *= refl; // linearize
2014-04-08 03:36:07 +09:00
#endif
2016-11-07 00:38:09 +09:00
// reflectivity
vec3 sunlight = EvaluateSunLight();
float reflective = dot(ongoing, wave.xyz);
reflective = clamp(1. - reflective, 0., 1.);
2016-11-07 00:38:09 +09:00
float orig_reflective = reflective;
reflective *= reflective;
reflective *= reflective;
reflective = mix(reflective, orig_reflective * .6,
clamp(lodBias * .13 - .13, 0., 1.));
//reflective += .03;
2016-11-07 00:38:09 +09:00
// reflection
#if USE_VOLUMETRIC_FOG
// it's actually impossible for water reflection to cope with volumetric fog.
// fade the water reflection so that we don't see sharp boundary of water
refl *= att;
#endif
gl_FragColor.xyz = mix(gl_FragColor.xyz,
refl,
reflective);
2016-11-07 00:38:09 +09:00
/* ------- Specular Reflection -------- */
2016-11-07 00:38:09 +09:00
// specular reflection
if(dot(sunlight, vec3(1.)) > 0.0001){
2016-11-07 00:38:09 +09:00
// can't use CockTorrance here -- CockTorrance's fresenel term
// is hard-coded for higher roughness values
vec3 halfVec = vec3(0., 1., 1.) + ongoing;
halfVec = dot(halfVec, halfVec) < .00000000001 ? vec3(1., 0., 0.) : normalize(halfVec);
float halfVecDot = max(dot(halfVec, wave), 0.00001);
float m = 0.002 + 0.0003 / (abs(ongoing.z) + 0.0006); // roughness
float spec = GGXDistribution(m, halfVecDot);
// fresnel
spec *= reflective;
2016-11-07 00:38:09 +09:00
// geometric shadowing (Kelemen)
float dot1 = dot(vec3(0., 1., 1.), wave);
float dot2 = dot(ongoing, wave);
float visibility = dot1 * dot2 / (halfVecDot * halfVecDot);
spec *= max(0., visibility);
// limit brightness (flickering specular reflection might cause seizure to some people)
spec = min(spec, 50.);
gl_FragColor.xyz += sunlight * spec * att;
}
2016-11-07 00:38:09 +09:00
2014-04-08 03:36:07 +09:00
#if !LINEAR_FRAMEBUFFER
gl_FragColor.xyz = sqrt(gl_FragColor.xyz);
2014-04-08 03:36:07 +09:00
#endif
2016-11-07 00:38:09 +09:00
gl_FragColor.w = 1.;
}