Shader Problem

Started by AGP, January 22, 2014, 04:58:53 PM

Previous topic - Next topic

AGP

Do we have the ability to read from the depth buffer?

EgonOlsen

No, not really. You can grab the depth buffer from the GPU, but as all reads from the graphics card's context, it's much too slow to do it a real time application.

AGP

Is there no way to improve that that doesn't change it significantly (to get a ray-marched water shader to work with Objec3Ds)?

EgonOlsen

No. GPUs are optimized for accessing their local memory. It's already much slower to transfer data to them and it's really slow to read from them.

AGP

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);
}

EgonOlsen

I don't see how...this kind of shader actually abuses the shader concept to use it as a drawing canvas IMHO. If you apply it to a plane, you would end up with an image of an ocean projected onto a plane. Just like a perspective painting that has fallen over...

AGP

I did apply it to a plane. The result is very strange: depending on where I am over the plane I can be looking either at the sky or at the ocean. But the ocean sure looks great.

EgonOlsen

As said, i don't see a way to use this on a plane unless you really understand what it does and can adjust it so that it works on real 3d geometry.

AGP

Quote
Here's a question: how expensive is it to get all the ocean vertices under the boat every frame (outside of the shader)? I'm thinking about just rotating the boat by the largest slope within the area of the boat (and doing this, obviously, for every frame)...
...
Quote
I guess one would have a physics simulation of the ocean and the ships that controls the actual entities and that runs on the cpu and some shaders that add to the visuals. The tricky thing is to sync them.

There's a technique that involves swapping between two buffers called transform feedback. I'm playing with it right now. If I should have any success, I will post the solution here.