Newer
Older
Import / applications / HighwayDash / ports / Game / Shaders.cpp
//  BlockyFroggy
//  Copyright © 2017 John Ryland.
//  All rights reserved.
#include "Shaders.h"


//! @todomake these resources too that the resource loader loads. (or optionally overridable by loaded ones)
//! @todo overall project file, something which contains list of models, cameras, lights, scenes / levels etc.
//! @todo different loading schemes, streaming, portal based, procedurally generated


const char* s_vertexShader = R"(
//
//  Vertex Shader
//  HighwayDash
//
//  Created by John Ryland on 8/02/2016.
//  Copyright © 2016 John Ryland. All rights reserved.
//
attribute vec4 position;
attribute vec4 delta;
attribute vec4 animParams;
attribute vec4 barycentric;
attribute vec3 normal;
attribute vec4 color; // almost deprecated as will come from textures

varying lowp  vec4  colorVarying;
varying highp vec4  shadowCoordVarying;
varying highp vec4  positionVarying;
varying highp float positionVaryingX;
varying highp vec2  noisePosition;
varying highp float lightVarying;
varying       vec4  barycentricVarying;
varying       vec3  normalVarying;  // doing vertex lighting now as more efficient
// On higher end spec devices, could do per-pixel lighting instead

varying highp vec2  textureCoordVarying;

/*
varying lowp  vec2  textureCoordVarying1;
varying lowp  vec2  textureCoordVarying2;
varying lowp  vec2  textureCoordVarying3;
varying lowp  vec2  textureCoordVarying4;
*/

uniform mat4 modelViewProjectionMatrix;
uniform mat4 shadowMapMVP;
uniform float time;
uniform vec4 translation;

//const vec4 lightPosition = vec4(530.0, -430.0, 650.0, 0.0);
const vec4 lightPosition = vec4(700.0, -330.0, 150.0, 0.0);

void main()
{
  if (debugTriangles) {
    barycentricVarying = barycentric;
  }

  highp mat4 mvp = modelViewProjectionMatrix;
  highp mat3 mvp3 = mat3(mvp[0][0], mvp[0][1], mvp[0][2],
      mvp[1][0], mvp[1][1], mvp[1][2],
      mvp[2][0], mvp[2][1], mvp[2][2]);

  normalVarying = normalize(mvp3 * normal);

  highp float off = animParams.x * animParams.y;
  highp float modulus = mod(position.x + translation.x + delta.x * time - off, 1000.0 + animParams.y);
  highp float x = modulus + off;// - animParams.y;

  positionVarying = position + translation;//[gl_InstanceID];// + vec4(delta.xyz * t, 0.0);
  positionVarying.x = x;
  if (enableDarkenedSides)
  {
    positionVaryingX = x;
  }

  
  noisePosition = vec2(
      ((position.x + translation.x) / 10.0) * 4.,
      mod((position.y + translation.y) / (3.0), 15.0) * 4.
      );


  if (enableShadows) {
    shadowCoordVarying = shadowMapMVP * positionVarying + vec4( 2.5/1024.0, 12.5/1024.0, 0., 0. );
  }

  //vec4 lightPos = modelViewProjectionMatrix * lightPosition;
  vec4 lightPos = lightPosition;
  colorVarying = color;

  // using textureCoordVarying.yx gives a nice effect for background blocks
  highp float texIdx = color.z;
  highp float u = mod(texIdx,8.0);
  highp float v = floor(texIdx/8.0);
  textureCoordVarying = (color.xy + vec2(u, v)) * vec2(256.0/2048.0, 256.0/2048.0);
  
  /*
  if (enableBevels)
  {
    textureCoordVarying1 = textureCoordVarying + vec2( 0.00000,  0.00012 );
    textureCoordVarying2 = textureCoordVarying + vec2( 0.00012,  0.00000 );
    textureCoordVarying3 = textureCoordVarying + vec2( 0.00000, -0.00012 );
    textureCoordVarying4 = textureCoordVarying + vec2(-0.00012,  0.00000 );
  }
   */
  

  positionVarying = modelViewProjectionMatrix * positionVarying;
  gl_Position = positionVarying;
  
  
  lightVarying = dot(normalVarying, normalize(lightPosition.xyz - positionVarying.xyz));
  /*
  if (enableLighting && !enableBevels)
  {
    lightVarying = max(0.2, 0.3* dot(normalVarying, normalize(lightPosition.xyz - positionVarying.xyz)) + 0.68);
  }
   */
  
}
)";


const char* s_fragmentShader = R"(
//
//  Fragment Shader
//  HighwayDash
//
//  Created by John Ryland on 8/02/2016.
//  Copyright © 2016 John Ryland. All rights reserved.
//
varying lowp  vec4  colorVarying;
varying highp float lightVarying;
varying highp vec4  shadowCoordVarying;
varying highp vec4  positionVarying;
varying highp float positionVaryingX;
varying highp vec2  noisePosition;
varying       vec4  barycentricVarying;
varying       vec3  normalVarying;

varying highp vec2  textureCoordVarying;

/*
varying lowp  vec2  textureCoordVarying1;
varying lowp  vec2  textureCoordVarying2;
varying lowp  vec2  textureCoordVarying3;
varying lowp  vec2  textureCoordVarying4;
*/

uniform sampler2D   shadowMap;
uniform sampler2D   texture;
uniform float       shadowStrength;
uniform bool        mixNoise;
uniform float       time;

//const vec4 lightPosition = vec4(0.0, -330.0, 650.0, 0.0);
const vec4 lightPosition = vec4(700.0, -330.0, 150.0, 0.0);


// From noise tutorial here:
//   https://www.raywenderlich.com/70208/opengl-es-pixel-shaders-tutorial
float randomNoise(vec2 p)
{
  /*
  float n = p.x + p.y * 57.0;
  n = pow((n*8192.0), n);
  return 1.0 - mod( (n * (n * n * 15731.0 + 789221.0) + 1376312589.0), 2147483648.0) / 1073741824.0;
  */
  //return fract(6791.*sin(47.*p.x+p.y*9973.));
  return fract(6791.*sin(47.*p.x+p.y*73.));
}

float smoothNoise(vec2 p)
{
  vec2 nn = vec2(p.x, p.y+1.);
  vec2 ee = vec2(p.x+1., p.y);
  vec2 ss = vec2(p.x, p.y-1.);
  vec2 ww = vec2(p.x-1., p.y);
  vec2 cc = vec2(p.x, p.y);

  float sum = 0.;
  sum += randomNoise(nn)/8.;
  sum += randomNoise(ee)/8.;
  sum += randomNoise(ss)/8.;
  sum += randomNoise(ww)/8.;
  sum += randomNoise(cc)/2.;

  return sum;
}

float interpolatedNoise(vec2 p)
{
  vec2 s = smoothstep(0., 1., fract(p));

  float q11 = smoothNoise(vec2(floor(p.x), floor(p.y)));
  float q12 = smoothNoise(vec2(floor(p.x), ceil(p.y)));
  float q21 = smoothNoise(vec2(ceil(p.x), floor(p.y)));
  float q22 = smoothNoise(vec2(ceil(p.x), ceil(p.y)));

  float r1 = mix(q11, q21, s.x);
  float r2 = mix(q12, q22, s.x);

  return mix(r1, r2, s.y);
}

void main()
{
  // Distance
  float dist = 0.0;

  if (enableShadowAttenuation || enableDistanceFog)
  {
    dist = gl_FragCoord.z / gl_FragCoord.w;
  }

  // Gamma
//  float visibility = 1.8;
  float visibility = 1.3;
  float inShadow = 0.0;

  // Calc if in shadow
  if (enableShadows)
  {
    vec2 shCo = shadowCoordVarying.xy;
    /*
       float shadowZ = texture2D(shadowMap, shCo).z * 0.30;
       shadowZ = shadowZ + texture2D(shadowMap, shCo + vec2( 0.000, 0.001)).z * 0.10;
       shadowZ = shadowZ + texture2D(shadowMap, shCo + vec2( 0.000,-0.001)).z * 0.10;
       shadowZ = shadowZ + texture2D(shadowMap, shCo + vec2( 0.001, 0.000)).z * 0.10;
       shadowZ = shadowZ + texture2D(shadowMap, shCo + vec2(-0.001, 0.000)).z * 0.10;
       shadowZ = shadowZ + texture2D(shadowMap, shCo + vec2( 0.001, 0.001)).z * 0.075;
       shadowZ = shadowZ + texture2D(shadowMap, shCo + vec2(-0.001, 0.001)).z * 0.075;
       shadowZ = shadowZ + texture2D(shadowMap, shCo + vec2( 0.001,-0.001)).z * 0.075;
       shadowZ = shadowZ + texture2D(shadowMap, shCo + vec2(-0.001,-0.001)).z * 0.075;
     */
    float shadowZ1 = texture2D(shadowMap, shCo).z;
    
    /*
    bool softShadows = false;
    if (softShadows)
    {
      inShadow = 5.0;//20.0 / clamp((shadowCoordVaryingZ - shadowZ1) * 2000.0, 1.0, 20.0);
      float shadowZ = shadowZ1;
      shadowZ = shadowZ + texture2D(shadowMap, shCo + inShadow * vec2( 0.001, 0.001)).z;
      shadowZ = shadowZ + texture2D(shadowMap, shCo + inShadow * vec2( 0.001,-0.001)).z;
      shadowZ = shadowZ + texture2D(shadowMap, shCo + inShadow * vec2(-0.001, 0.001)).z;
      shadowZ = shadowZ + texture2D(shadowMap, shCo + inShadow * vec2(-0.001,-0.001)).z;
      shadowZ = shadowZ / 5.0;
      
      float radius = clamp((shadowCoordVarying.z - shadowZ) * 15000.0, 1.0, 2.0);
      inShadow = clamp((shadowCoordVarying.z - shadowZ1) * 1000.0, 0.0, 0.2) * 0.5;
      
      inShadow = inShadow + clamp((shadowCoordVarying.z - texture2D(shadowMap, shCo + radius * vec2( 0.0012, 0.0015)).z) * 1000.0, 0.0, 0.2) * 0.125;
      inShadow = inShadow + clamp((shadowCoordVarying.z - texture2D(shadowMap, shCo + radius * vec2(-0.0014, 0.0019)).z) * 1000.0, 0.0, 0.2) * 0.125;
      inShadow = inShadow + clamp((shadowCoordVarying.z - texture2D(shadowMap, shCo + radius * vec2( 0.0013,-0.0016)).z) * 1000.0, 0.0, 0.2) * 0.125;
      inShadow = inShadow + clamp((shadowCoordVarying.z - texture2D(shadowMap, shCo + radius * vec2(-0.0017,-0.0011)).z) * 1000.0, 0.0, 0.2) * 0.125;
    } else
    */
    {
      inShadow = clamp((shadowCoordVarying.z - shadowZ1) * 1000.0, 0.0, 0.2);
      //inShadow = 20.0 / clamp((shadowCoordVarying.z - shadowZ1) * 2000.0, 1.0, 20.0);
    }

    //shadowZ += 0.1;

    // Reduce shadow effect in distance
    if (enableShadowAttenuation)
    {
      float shadowDist = clamp(dist - 3.0, 0.0, 1.0);
      inShadow = inShadow * (1.0 - shadowDist);
    }

    // Apply shadow
    //visibility = visibility * (1.0 - inShadow * shadowStrength);
    visibility = visibility * (1.5 - inShadow);
  }

  if (enableDarkenedSides)
  {
    // Darken sides (out of playing bounds)
    float dx = 0.0;
    dx = max(0.0, 520.0 - positionVaryingX);
    dx = max(dx, positionVaryingX - 680.0);
    visibility = visibility * ((180.0-dx)/150.0);
  }

  vec4 col;
  if (enableTextures)
  {
    lowp vec4 tcol = texture2D(texture, textureCoordVarying);

    if (mixNoise)
    {
      //tcol = (0.30 + 0.75*fract(sin(noisePosition.y*0.7 + noisePosition.x*0.05 + time*1.2) + sin(noisePosition.x + noisePosition.y*0.02 + time*1.))) * tcol;
      //tcol = smoothNoise(noisePosition + vec2(time/10.0, time/10.0)) * tcol;
      tcol = interpolatedNoise(noisePosition + vec2(time/10.0, time/10.0)) * tcol;
    }
    
    /*
    if (enableLighting && !enableBevels)
    {
      // If has provided color and texture color is blank, use the provided color
      col = mix(colorVarying, tcol, tcol.w); // mix with even amounts
      if (enableRampShading)
      {
        // Assumes ramp loaded in to texture slot 2
        //col = col * texture2D(texture,  vec2(0.25 + min(light, 1.0)*0.125, max(0.0,min(visibility/2.0,1.0))*0.125));//0.05));
        col = col * texture2D(texture,  vec2(0.25 + min(lightVarying, 1.0)*0.125, mod(textureCoordVarying.x+textureCoordVarying.y, 0.125) ));
      }
      else
      {
        col = col * vec4(lightVarying);
      }
    }
    else
     */
    if (enableLighting)
    {
      lowp vec3 n3 = normalVarying;//vec3(0.0);
      lowp vec3 nr = n3;
    
      //vec3 lightDirection = lightPosition.xyz - positionVarying.xyz;
      float n_dot_l = lightVarying;
      
      
      if (false && enableBevels)
      {
        lowp vec2 n1 = vec2(0.0);
        lowp vec2 n2 = vec2(0.0);
        
        lowp vec2 textureCoordVarying1 = textureCoordVarying + vec2( 0.00000,  0.00012 );
        lowp vec2 textureCoordVarying2 = textureCoordVarying + vec2( 0.00012,  0.00000 );
        lowp vec2 textureCoordVarying3 = textureCoordVarying + vec2( 0.00000, -0.00012 );
        lowp vec2 textureCoordVarying4 = textureCoordVarying + vec2(-0.00012,  0.00000 );

        n1.x =  (1.0 - texture2D(texture, textureCoordVarying1).a);
        n1.y =  (1.0 - texture2D(texture, textureCoordVarying2).a);
        n2.x =  (1.0 - texture2D(texture, textureCoordVarying3).a);
        n2.y =  (1.0 - texture2D(texture, textureCoordVarying4).a);
        n1 = n1 - n2;

        
        nr.x = n3.x - n3.z * n1.x + n3.y * n1.x;
        nr.y = n3.y + n3.x * n1.x - n3.z * n1.y;
        nr.z = n3.z + n3.x * n1.y + n3.y * n1.y;
        nr = normalize(nr);

        vec3 lightDirection = lightPosition.xyz - positionVarying.xyz;
        n_dot_l = max(n_dot_l, dot(nr, normalize(lightDirection)));
      }
      

      // If has provided color and texture color is blank, use the provided color
      col = mix(colorVarying, tcol, tcol.w); // mix with even amounts
      //highp vec4 col = vec4(tcol.xyz + colorVarying.xyz, tcol.a * colorVarying.a); // blend using alpha
    
    
      if (enableRampShading)
      {
        // Assumes ramp loaded in to texture slot 2
        float rampIndex = min(max(0.3, 0.3 * n_dot_l + 0.7), 1.0);
        //col = col * texture2D(texture,  vec2(0.25 + rampIndex*0.125, max(0.0,min(visibility/2.0,1.0))*0.125));//0.05));
        col = (1.0 - inShadow) * col * texture2D(texture,  vec2(0.25 + rampIndex*0.125, 0.5*0.125));//0.05));
        //col = col * texture2D(texture,  vec2(0.25 + min(light, 1.0)*0.125, mod(textureCoordVarying.x+textureCoordVarying.y, 0.125) ));
      }
      else
      {
        float light = max(0.5, (0.8 - inShadow) * n_dot_l + 0.3);
        col = col * vec4(light);
        //col = mix(col, lightCol, 0.05);
      }
    }
    else
    {
      col = mix(colorVarying, tcol, tcol.w);
    }
    
/*
    // cube texturing idea - kind of could make something like minecraft blocks with this approach
    vec2 tileMapPos = vec2(3.0 * 0.125 + floor(tcol.r*(255.0/16.0))/16.0 * 0.125, fract(tcol.r*(255.0/16.0)) * 0.125);
    tileMapPos = tileMapPos + vec2(fract(textureCoordVarying.x*2047.5)/16.0 * 0.125, fract(textureCoordVarying.y*2047.5)/16.0 * 0.125);
    col = mix(col, texture2D(texture, tileMapPos), 0.3);
*/
  }
  else
  {
    //col = interpolatedNoise(noisePosition) * colorVarying; // ignore texture colors
    col = colorVarying; // ignore texture colors
  }

  col = col * visibility;

  // Distance fog
  if (enableDistanceFog)
  {
    col = mix(vec4(0.3, 0.3, 0.3, 1.0) * visibility, col, clamp((5.0 - dist)/(5.0 - 3.0), 0.0, 1.0 ));
  }

  gl_FragColor = col;

  
  // Debugging showing the triangle edges
  if (debugTriangles) {
    const float threshold = 0.125;
    if (barycentricVarying.x < threshold || barycentricVarying.y < threshold || barycentricVarying.z < threshold)
    {
      gl_FragColor = vec4(0.8, 0.8, 0.9, 1.0) * col;
    }
  }

}
)";




const char* s_simpleVertexShader = R"(
attribute vec4 position;
attribute vec4 delta;
attribute vec4 animParams;
attribute vec4 color;
varying lowp vec4 colorVarying;
uniform mat4 modelViewProjectionMatrix;
uniform float time;
void main()
{
  vec4 pos = position;
  float off = animParams.x * animParams.y;
  float modulus = mod(position.x + delta.x * time - off, 1000.0 + animParams.y);
  pos.x = modulus + off;
  colorVarying = color;
  gl_Position = modelViewProjectionMatrix * pos;
}
)";

const char* s_simpleFragmentShader = R"(
varying lowp vec4 colorVarying;
void main()
{
  gl_FragColor = colorVarying;
}
)";




const char* s_quadVertexShader = R"(
const lowp vec2 madd = vec2(0.5,0.5);
attribute vec2 vertexIn;
varying   vec2 textureCoord;
uniform lowp float screenW;
uniform lowp float screenH;
uniform lowp float t;
void main()
{
  textureCoord = vertexIn.xy * madd + madd;
  textureCoord.x = textureCoord.x * screenW;
  textureCoord.y = textureCoord.y * screenH;
  gl_Position = vec4(vertexIn.xy, 0.0, 1.0);
}
)";


const char* s_quadFragmentShader = R"(
uniform sampler2D texture;
varying lowp vec2 textureCoord;
void main()
{
  gl_FragColor = texture2D(texture, textureCoord);
}
)";


const char* s_shadowVertexShader = R"(
attribute vec4 position;
attribute vec4 delta;
attribute vec4 animParams;
attribute vec4 barycentric;
attribute vec3 normal;
attribute vec4 color; // almost deprecated as will come from textures

uniform highp mat4 modelViewProjectionMatrix;
uniform float time;
uniform vec4 translation;
void main()
{
  float off = animParams.x * animParams.y;
  float modulus = mod(position.x + translation.x + delta.x * time - off, 1000.0 + animParams.y);
  float x = modulus + off;
  highp vec4 pos = position + translation;
  pos.x = x;
  gl_Position = modelViewProjectionMatrix * pos;
}
)";

const char* s_shadowFragmentShader = "void main(){}";


const char* s_hudVertexShader = R"(
attribute vec2 position;
attribute vec3 textureCoords;
attribute vec4 color;
attribute vec4 barycentric;
attribute vec3 normal;

varying lowp vec2 textureCoord;
varying lowp vec4 col2;
uniform vec2 invScreen;
uniform float t;
void main()
{
  vec2 tmp;
  float m = 0.0;
  float f = 0.0;

  // Perhaps could move this out and on to CPU and make textureCoords 4 values, u,v,xIdx,yIdx
  m = mod(textureCoords.z,8.0);
  f = floor(textureCoords.z/8.0);

  tmp = vec2(m*256.0, f*256.0);
  textureCoord = (textureCoords.xy+tmp) * vec2(1.0/2048.0, 1.0/2048.0);

  col2 = color;
  gl_Position = vec4(position * invScreen + vec2(-1.0,1.0), 0.0, 1.0);
}
)";


const char* s_hudFragmentShader = R"(
uniform sampler2D texture;
varying lowp vec2 textureCoord;
varying lowp vec4 col2;
void main()
{
  lowp vec4 tcol = texture2D(texture, textureCoord);
  gl_FragColor = vec4(tcol.xyz + col2.xyz, tcol.a * col2.a);
  // gl_FragColor = vec4(col2.xyz, tcol.a);
}
)";