I know how you love these shader questions, and I'm sorry about that. The following is a really good whole-scene shader that renders, entirely in the fragment shader, a phenomenal ocean (and its containing scene, including the sky). This may seem like a stupid question, but I would like for it to consider the whole scene but only render the ocean (that is to say, for the god-rays and clouds to affect the ocean as they currently do, but to only paint the ocean on a jpct plane). Is that too hard a question or do you see a simple solution (I've looked and, with my very limited understanding of shaders, haven't found one).
// Clouds: slice based volumetric height-clouds with god-rays, density, sun-radiance/shadow
// and
// Water: simple reflecting sky/sun and cloud shaded height-modulated waves
//
// Created by Frank Hugenroth 03/2013
//
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
//
// noise and raymarching based on concepts and code from shaders by inigo quilez
//
// some variables to change :)
uniform vec3 iResolution; //VIEWPORT RESOLUTION (IN PIXELS)
uniform float iGlobalTime; //SHADER PLAYBACK TIME (IN SECONDS)
//uniform float iChannelTime[4]; //CHANNEL PLAYBACK TIME (IN SECONDS)
//uniform vec3 iChannelResolution[4]; //CHANNEL RESOLUTION (IN PIXELS)
uniform sampler2D iChannel0;
uniform sampler2D iChannel1;
uniform sampler2D iChannel2;
uniform sampler2D iChannel3;
//uniform vec4 iDate;
//#define RENDER_GODRAYS 1 // set this to 1 to enable god-rays
#define RENDER_GODRAYS 0 // disable god-rays
#define RENDER_CLOUDS 1//1
#define RENDER_WATER 1
float waterlevel = 70.0; // height of the water
float wavegain = 1.0; // change to adjust the general water wave level
float large_waveheight = 1.0; // change to adjust the "heavy" waves (set to 0.0 to have a very still ocean :)
float small_waveheight = 1.0; // change to adjust the small waves
vec3 fogcolor = vec3( 0.5, 0.7, 1.1 );
vec3 skybottom = vec3( 0.6, 0.8, 1.2 );
vec3 skytop = vec3(0.05, 0.2, 0.5);
vec3 reflskycolor= vec3(0.025, 0.10, 0.20);
vec3 watercolor = vec3(0.2, 0.25, 0.3);
vec3 light = normalize( vec3( 0.1, 0.25, 0.9 ) );
// random/hash function
float hash( float n ) {
return fract(cos(n)*41415.92653);
}
// 2d noise function
float noise( in vec2 x ) {
vec2 p = floor(x);
vec2 f = smoothstep(0.0, 1.0, fract(x));
float n = p.x + p.y*57.0;
return mix(mix( hash(n+ 0.0), hash(n+ 1.0),f.x),
mix( hash(n+ 57.0), hash(n+ 58.0),f.x),f.y);
}
// 3d noise function
float noise( in vec3 x ) {
vec3 p = floor(x);
vec3 f = smoothstep(0.0, 1.0, fract(x));
float n = p.x + p.y*57.0 + 113.0*p.z;
return mix(mix(mix( hash(n+ 0.0), hash(n+ 1.0),f.x),
mix( hash(n+ 57.0), hash(n+ 58.0),f.x),f.y),
mix(mix( hash(n+113.0), hash(n+114.0),f.x),
mix( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z);
}
mat3 m = mat3( 0.00, 1.60, 1.20, -1.60, 0.72, -0.96, -1.20, -0.96, 1.28 );
// Fractional Brownian motion
float fbm( vec3 p ) {
float f = 0.5000*noise( p ); p = m*p*1.1;
f += 0.2500*noise( p ); p = m*p*1.2;
f += 0.1666*noise( p ); p = m*p;
f += 0.0834*noise( p );
return f;
}
mat2 m2 = mat2(1.6,-1.2,1.2,1.6);
// Fractional Brownian motion
float fbm( vec2 p ) {
float f = 0.5000*noise( p ); p = m2*p;
f += 0.2500*noise( p ); p = m2*p;
f += 0.1666*noise( p ); p = m2*p;
f += 0.0834*noise( p );
return f;
}
// this calculates the water as a height of a given position
float water( vec2 p ) {
float height = waterlevel;
vec2 shift1 = 0.001*vec2( iGlobalTime*160.0*2.0, iGlobalTime*120.0*2.0 );
vec2 shift2 = 0.001*vec2( iGlobalTime*190.0*2.0, -iGlobalTime*130.0*2.0 );
// coarse crossing 'ocean' waves...
float wave = 0.0;
wave += sin(p.x*0.021 + shift2.x)*4.5;
wave += sin(p.x*0.0172+p.y*0.010 + shift2.x*1.121)*4.0;
wave -= sin(p.x*0.00104+p.y*0.005 + shift2.x*0.121)*4.0;
// ...added by some smaller faster waves...
wave += sin(p.x*0.02221+p.y*0.01233+shift2.x*3.437)*5.0;
wave += sin(p.x*0.03112+p.y*0.01122+shift2.x*4.269)*2.5 ;
wave *= large_waveheight;
// ...added by some distored random waves (which makes the water looks like water :)
wave += fbm(p*0.01+shift1)*small_waveheight*8.0;
wave += fbm(p*0.022+shift2)*small_waveheight*6.0;
height += wave;
return height;
}
// cloud intersection raycasting
float trace_fog(in vec3 rStart, in vec3 rDirection ) {
#if RENDER_CLOUDS
// makes the clouds moving...
vec2 shift = vec2( iGlobalTime*80.0, iGlobalTime*60.0 );
float sum = 0.0;
// use only 12 cloud-layers ;)
// this improves performance but results in "god-rays shining through clouds" effect (sometimes)...
for (int q=1000; q<1012; q++) {
float c = (float(q-1000)*50.0+350.0-rStart.y) / rDirection.y;// cloud distance
vec3 cpos = rStart + c*rDirection + vec3(831.0, 321.0+float(q-1000)*.75-shift.x*0.2, 1330.0+shift.y*3.0); // cloud position
float alpha = smoothstep(0.5, 1.0, fbm( cpos*0.0015 )); // cloud density
if (alpha > 0.8)
break;
sum += (1.0-sum)*alpha; // alpha saturation
}
return clamp( 1.0-sum, 0.0, 1.0 );
#else
return 1.0;
#endif
}
// fog and water intersection function.
// 1st: collects fog intensity while traveling
// 2nd: check if hits the water surface and returns the distance
bool trace(in vec3 rStart, in vec3 rDirection, in float sundot, out float fog, out float dist) {
float h = 20.0;
float t = 0.0;
float st = 1.0;
float alpha = 0.1;
float asum = 0.0;
vec3 p = rStart;
for (int j = 1000; j < 1120; j++) {
// some speed-up if all is far away...
if (t > 500.0)
st = 2.0;
else if( t>800.0 )
st = 5.0;
else if( t>1000.0 )
st = 12.0;
p = rStart + t*rDirection; // calc current ray position
#if RENDER_GODRAYS
if (p.y > 50.0 && sundot > 0.001 && t>300.0 && t < 2400.0) {
alpha = sundot * clamp((p.y-waterlevel)/waterlevel, 0.0, 1.0) * 20.0 * st * 0.0012*smoothstep(0.90, 1.0, trace_fog(p,light));
asum += (1.0-asum)*alpha;
if (asum > 0.9)
break;
}
#endif
h = p.y - water(p.xz);
if ( h < 0.1 ) {// hit the water?
dist = t;
fog = asum;
return true;
}
if (p.y > 450.0) // lost in space? quit...
break;
// speed up ray if possible...
if (rDirection.y > 0.0) // look up (sky!) -> make large steps
t += 30.0 * st;
else if (p.y < waterlevel+20.0)
t += max(1.0,2.0*h)*st;
else
t += max(1.0,1.0*h)*st;
}
dist = t;
fog = asum;
if (h<10.0)
return true;
return false;
}
vec3 camera( float time ) {
return vec3( 500.0 * sin(1.5+1.57*time), 0.0, 1200.0*time );
}
void main(void) {
vec2 xy = -1.0 + 2.0*gl_FragCoord.xy / iResolution.xy;
vec2 s = xy*vec2(1.75,1.0);
// get camera position and view direction
float time = (iGlobalTime+13.5)*.05;
vec3 campos = camera( time );
vec3 camtar = camera( time + 0.4 );
campos.y = max(waterlevel+30.0, waterlevel+90.0 + 60.0*sin(time*2.0));
camtar.y = campos.y*0.9;
float roll = 0.14*sin(time*1.2);
vec3 cw = normalize(camtar-campos);
vec3 cp = vec3(sin(roll), cos(roll),0.0);
vec3 cu = normalize(cross(cw,cp));
vec3 cv = normalize(cross(cu,cw));
vec3 rd = normalize( s.x*cu + s.y*cv + 1.6*cw );
float sundot = clamp(dot(rd,light),0.0,1.0);
vec3 col;
float fog=0.0, dist=0.0;
if (!trace(campos,rd,sundot, fog, dist)) {
// render sky
float t = pow(1.0-0.7*rd.y, 15.0);
col = 0.8*(skybottom*t + skytop*(1.0-t));
// sun
col += 0.47*vec3(1.6,1.4,1.0)*pow( sundot, 350.0 );
// sun haze
col += 0.4*vec3(0.8,0.9,1.0)*pow( sundot, 2.0 );
#if RENDER_CLOUDS
// CLOUDS
vec2 shift = vec2( iGlobalTime*80.0, iGlobalTime*60.0 );
vec4 sum = vec4(0,0,0,0);
for (int q=1000; q<1120; q++) { // 120 layers
float c = (float(q-1000)*10.0+350.0-campos.y) / rd.y; // cloud height
vec3 cpos = campos + c*rd + vec3(831.0, 321.0+float(q-1000)*.15-shift.x*0.2, 1330.0+shift.y*3.0); // cloud position
float alpha = smoothstep(0.5, 1.0, fbm( cpos*0.0015 ))*.9; // fractal cloud density
vec3 localcolor = mix(vec3( 1.1, 1.05, 1.0 ), 0.7*vec3( 0.4,0.4,0.3 ), alpha); // density color white->gray
alpha = (1.0-sum.w)*alpha; // alpha/density saturation (the more a cloud layer's density, the more the higher layers will be hidden)
sum += vec4(localcolor*alpha, alpha); // sum up weightened color
if (sum.w>0.98)
break;
}
float alpha = smoothstep(0.7, 1.0, sum.w);
sum.rgb /= sum.w+0.0001;
// This is an important stuff to darken dense-cloud parts when in front (or near)
// of the sun (simulates cloud-self shadow)
sum.rgb -= 0.6*vec3(0.8, 0.75, 0.7)*pow(sundot,13.0)*alpha;
// This brightens up the low-density parts (edges) of the clouds (simulates light scattering in fog)
sum.rgb += 0.2*vec3(1.3, 1.2, 1.0)* pow(sundot,5.0)*(1.0-alpha);
col = mix( col, sum.rgb , sum.w*(1.0-t) );
#endif
// add god-rays
col += vec3(0.5, 0.4, 0.3)*fog;
}
else {
#if RENDER_WATER
// render water
vec3 wpos = campos + dist*rd; // calculate position where ray meets water
// calculate water-mirror
vec2 xdiff = vec2(1.0, 0.0)*wavegain;
vec2 ydiff = vec2(0.0, 1.0)*wavegain;
// get the reflected ray direction
rd = reflect(rd, vec3(water(wpos.xz-xdiff) - water(wpos.xz+xdiff), 1.0, water(wpos.xz-ydiff) - water(wpos.xz+ydiff)));
float sh = smoothstep(0.2, 1.0, trace_fog(wpos+20.0*rd,rd))*.7+.3;
// water reflects more the lower the reflecting angle is...
float refl = 1.0-clamp(dot(rd,vec3(0.0, 1.0, 0.0)),0.0,1.0);
float wsky = refl*sh; // reflecting (sky-color) amount
float wwater = (1.0-refl)*sh; // water-color amount
float sundot = clamp(dot(rd,light),0.0,1.0);
// watercolor
col = wsky*reflskycolor; // reflecting sky-color
col += wwater*watercolor;
// Sun
float wsunrefl = wsky*(0.25*pow( sundot, 10.0 )+0.25*pow( sundot, 1.5)+0.25*pow( sundot, 200.0));
col += vec3(1.5,1.3,1.0)*wsunrefl; // sun reflection
#endif
// global depth-fog
float fo = 1.0-exp(-pow(0.0003*dist, 1.5));
vec3 fco = fogcolor + 0.6*vec3(0.6,0.5,0.4)*pow( sundot, 4.0 );
col = mix( col, fco, fo );
// add god-rays
col += vec3(0.5, 0.4, 0.3)*fog;
}
gl_FragColor=vec4(col,1.0);
}