Fresnel effect in PShader not working right?

I’m currently trying to follow the famous Nvidia guide to water simulation, and I’m making some okay progress. My waves are oddly flat but that’s not really the issue. I got to the point where I wanted to implemented a rudimentary Fresnel effect, just changing the color of the waves to a flat value based on angle, but I’m having some really odd results with it.

You can find some gif captures of my issues on Imgur here. The color I’m shifting to with the fresnel effect is the brighter blue, if that wasn’t obvious. You can tell from the first gif that the fresnel effect is already off with its angles from straight on. In the second gif, when I pull the camera further over, the fresnel effect is highly active nowhere close to the camera, and seemingly from an incorrect angle far away.

I calculated the Fresnel factor the same as anyone else does. My normals for my waves seemed to work fine previously based on it their interactions with a point light I’ve been using and moving around the scene, so I don’t believe it’s a problem with normals.

Could it be an issue with the camera position? I’ve been using the eye values for the camera as its position, as I thought that’s pretty much what they represent. Am I incorrect in doing so, and it’s messing up the angle calculation between the normals of the waves and the camera? Or does it have something to do with fragment/vertex positions (which I truthfully do not understand much)?

If it’s of any importance, I cloned and modified the default shaders that are native to processing in order to do this because I was having a lot of issues when I was starting this.

The code is getting quite large but here’s a code dump if it’s of any use to help diagnose the issue:

Sketch


/****************
/ SCENE OBJECTS
/ ***************/
PShape mesh;
PShader waterShader;

/****************
/ INPUT/CONTROL
/ ***************/
enum ControlType { //Inputs change pos. of light or camera
  Light,
  Camera
};

ControlType currentControl; //Current object to control
float[] currentControlVector = new float[3]; //Current vector for movement of object
float[] currentRotateVector = new float[3]; //Current vector for rotation of camera

float moveSpeed = 6f;

/****************
/ WAVE PARAMETERS
/ ***************/
final int NUMWAVES = 8; //This must match the shader!

float[] waveLength = new float[NUMWAVES];
float[] speed = new float[NUMWAVES];
float[] amplitude = new float[NUMWAVES];
float[][] direction = new float[2][NUMWAVES];
float[] steepness = new float[NUMWAVES];

/****************
/ LIGHT PROPERTIES
/ ***************/
float[] lightPos = {0f,-400f , 0};
PVector lightPosVec = new PVector(lightPos[0], lightPos[1], lightPos[2]);

/****************
/ CAMERA PROPERTIES
/ ***************/
float[] camPos;
PVector camPosVec;
PVector camCenter;

float zRotation = 0f;



void setup() {
  size(1600, 900, P3D);
  noStroke();
  
  frameRate(60);
  
  //Set control variables
  camPos = new float[]{width/2f, height/2f - 150, (height/2.0) / tan(PI*30.0 / 180.0)};
  //camPos = new float[]{0, 0, 0};
  camPosVec = new PVector(camPos[0], camPos[1], camPos[2]);
  camCenter = new PVector(width/2, height/2 , 0);
  currentControl = ControlType.Camera;
  
  //Begin creating the mesh for the water
  mesh = createShape();
  
  mesh.beginShape(QUADS);
  mesh.fill(20, 50, 120); //Set direction for normal for light to bounce
 
  
  //Set vertex points for it
  int xRange = 1000;
  int yRange = 1000;
  int spacing = 2;
  for(int x = -xRange; x < xRange; x+= spacing)
  {
    for(int y = -yRange; y < yRange; y +=spacing)
    {
      /*mesh.vertex(x, y);
      mesh.vertex(x+2,y);
      mesh.vertex(x+2,  y+2);
      mesh.vertex(x, y+2);*/
      mesh.normal(float(x), float(x), float(y)); //Set color as position
      mesh.vertex(x, 0,y);
      mesh.normal(float(x), float(y), float(y+spacing)); //Set color as position
      mesh.vertex(x, 0, y+spacing);
       mesh.normal(float(x+spacing), float(y), float(y+spacing)); //Set color as position
      mesh.vertex(x+spacing, 0, y+spacing);
      mesh.normal(float(x+spacing), float(x), float(y)); //Set color as position
      mesh.vertex(x+spacing,0,y);   
    }
  }  
  mesh.endShape();

  //Generate wave parameters for the shader
  GenerateWaveParams();
  
  //Setup the shader
  waterShader = loadShader("waterFrag.glsl", "waterVert.glsl");
  waterShader.set("cameraPosition",camPosVec);
  
  SetWaveParams();
}

void draw() {
  background(255);
  camera(camPos[0], camPos[1], camPos[2],camCenter.x, camCenter.y ,camCenter.z, 0, 1, 0);
  camPosVec.set(camPos[0], camPos[1], camPos[2]);
  waterShader.set("cameraPosition",camPosVec);
  
  push();
    translate(width/2, height/2+100);
    
    //Set normal lights and move the point light as needed
    ambientLight(64, 64, 64);
    pointLight(200, 200, 200, lightPos[0],lightPos[1], lightPos[2]);
    lightPosVec.set(lightPos[0],lightPos[1], lightPos[2]);
    
    //Draw normal sphere to show point light location
    push();
      translate(lightPos[0],lightPos[1], lightPos[2]);
      resetShader();
      fill(128);
      sphere(15);
    pop();
    
    waterShader.set("time", millis()/1000.0);
    
    //println("Light: " + lightPos[0] + " " + lightPos[1] + " " + lightPos[2]);
    push();
      rotateZ(PI);
      shader(waterShader);
      shape(mesh);
    pop();
  
    push();
      translate(0,-10);
      resetShader();
      sphere(30);
    pop();
  
  pop();
  
  MovementHandle();
}

void GenerateWaveParams()
{
  //randomSeed(60);
  
  for(int i = 0; i < NUMWAVES; i++)
  {
    waveLength[i] = random(500)+400;
    speed[i] =random(40f)+10f;
    amplitude[i] = random(8f)+5f;
    direction[0][i] = RandomNegOneOrOne()*(0.7f+random(0.5f));
    direction[1][i] = RandomNegOneOrOne()*(0.7f+random(0.5f));
    steepness[i] =random(0.2f)+0.6f;
    
    /*waveLength[i] = random(60f)+60f;
    speed[i] = random(8)+3f;
    amplitude[i] = random(2)+0.5f;
    direction[0][i] = random(2f)- 0.5f;
    direction[1][i] = random(2f)- 0.5f;
    steepness[i] = random(1f)+0.1f;*/
    /*waveLength[i] = 100f;
    speed[i] = 8f;
    amplitude[i] = 4f;
    direction[0][i] = 0.8f;
    direction[1][i] = 0.5f;*/
    
    
    System.out.println("WAVE " + i + " STATS: ");
    System.out.println(
        waveLength[i]+ " " +
         speed[i]+ " " + 
          amplitude[i]+ " " +
     direction[0][i]+ " " +
     direction[1][i]+ " " +
     steepness[i]+ " ");
   
  }
  
}

void SetWaveParams()
{
  waterShader.set("waveLength", waveLength);
  waterShader.set("speed", speed);
  waterShader.set("amplitude", amplitude);
  waterShader.set("xDirection", direction[0]);
  waterShader.set("yDirection", direction[1]);
  waterShader.set("steepness", steepness);
}

void MovementHandle()
{
  if(keyPressed)
  {
    if(currentControl == ControlType.Light)
    {
      MoveLight();
    }
    else if(currentControl == ControlType.Camera)
    {
      MoveCamera();
    }
  }
}

void MoveLight()
{
  lightPos[0] += currentControlVector[0];
  lightPos[1] += currentControlVector[1];
  lightPos[2] += currentControlVector[2];
}

void MoveCamera()
{

  camPos[0] += currentControlVector[0];
  camCenter.x += currentRotateVector[0];
  camPos[1] += currentControlVector[1];
  camCenter.y += currentRotateVector[1];
  camPos[2] += currentControlVector[2];
  //camCenter.z += currentRotateVector[2];
}


void keyPressed()
{
  switch (key)
  {
    case CODED:
      if (keyCode == UP) {
          currentControlVector[1] += -moveSpeed;
          currentRotateVector[1] += -moveSpeed;
        } else if (keyCode == DOWN) {
           currentControlVector[1] += moveSpeed;
           currentRotateVector[1] += moveSpeed;
        }
        else if (keyCode == LEFT) {
          
           currentRotateVector[0] -= moveSpeed;
        }
        else if (keyCode == RIGHT) {
           currentRotateVector[0] += moveSpeed;
        }
        
        break;
    case 'a':
      currentControlVector[0] += -moveSpeed;
      currentRotateVector[0] += -moveSpeed;
      break;
    case 'd':
      currentControlVector[0] += moveSpeed;
      currentRotateVector[0] += moveSpeed;
      break;
    case 'w':
      currentControlVector[2] += -moveSpeed;
      currentRotateVector[2] += -moveSpeed;
      break;
    case 's':
      currentControlVector[2] += moveSpeed;
      currentRotateVector[2] += moveSpeed;
      break;
    case 'l':
      currentControl = ControlType.Light;
      break;
    case 'c':
      currentControl = ControlType.Camera;
      break;
    
  };
   
}

void keyReleased()
{
  switch (key)
  {
    case CODED:
      if (keyCode == UP) {
          currentControlVector[1] += moveSpeed;
          currentRotateVector[1] += moveSpeed;
        } else if (keyCode == DOWN) {
           currentControlVector[1] += -moveSpeed;
           currentRotateVector[1] += -moveSpeed;
        }
        else if (keyCode == LEFT) {
          
           currentRotateVector[0] += moveSpeed;
        }
        else if (keyCode == RIGHT) {
           currentRotateVector[0] -= moveSpeed;
        }
        
        break;
    case 'a':
      currentControlVector[0] += moveSpeed;
      currentRotateVector[0] += moveSpeed;
      break;
    case 'd':
      currentControlVector[0] += -moveSpeed;
      currentRotateVector[0] += -moveSpeed;
      break;
    case 'w':
      currentControlVector[2] += moveSpeed;
      currentRotateVector[2] += moveSpeed;
      break;
    case 's':
      currentControlVector[2] += -moveSpeed;
      currentRotateVector[2] += -moveSpeed;
      break;
    case 'l':
      currentControl = ControlType.Light;
      break;
    case 'c':
      currentControl = ControlType.Camera;
      break;
    case 'r':
      GenerateWaveParams();
      SetWaveParams();
    
  };
   
}

int RandomNegOneOrOne()
{
  return random(1) > 0.5 ? 1 : -1;
}

Vertex Shader


#define PROCESSING_LIGHT_SHADER
#define MAXWAVES 8

const float pi = 3.14159265358;


uniform mat4 transform;
uniform mat4 modelview;
uniform mat3 normalMatrix;

uniform float time; //Time since shader started
uniform vec3 cameraPosition; //Position of camera for Fresnel

attribute vec4 position; //Position the vertex from Processing
attribute vec4 color; // THIS IS USED TO HOLD X/Z FOR GERSTNER
attribute vec3 normal; //Normal of the vertex from Processing
attribute vec4 ambient;
attribute vec4 specular;
attribute vec4 emissive;
attribute float shininess;

varying vec4 vertColor; //Color passed on to fragment shader
varying vec4 backVertColor; //Color passed on to fragment shader
varying float fresnelFactor;
varying vec3 camVec;
varying vec3 ecVertex;
varying vec3 ecNormal;


uniform float waveLength[MAXWAVES]; //Length of wave
uniform float speed[MAXWAVES]; //Cycle speed of wave
uniform float amplitude[MAXWAVES]; //Wave cycle height
uniform float xDirection[MAXWAVES];
uniform float yDirection[MAXWAVES]; //Flow vector of wave
uniform float steepness[MAXWAVES]; //Q factor of wave (steepness)

uniform int lightCount;
uniform vec4 lightPosition[8];
uniform vec3 lightNormal[8];
uniform vec3 lightAmbient[8];
uniform vec3 lightDiffuse[8];
uniform vec3 lightSpecular[8];      
uniform vec3 lightFalloff[8];
uniform vec2 lightSpot[8];

varying vec3 FragPos;
varying vec3 Vec;
varying vec3 lightDir;

//Some constants that the processing shader used
const float zero_float = 0.0;
const float one_float = 1.0;
const vec3 zero_vec3 = vec3(0);


float falloffFactor(vec3 lightPos, vec3 vertPos, vec3 coeff) {
  vec3 lpv = lightPos - vertPos;
  vec3 dist = vec3(one_float);
  dist.z = dot(lpv, lpv);
  dist.y = sqrt(dist.z);
  return one_float / dot(dist, coeff);
}

float spotFactor(vec3 lightPos, vec3 vertPos, vec3 lightNorm, float minCos, float spotExp) {
  vec3 lpv = normalize(lightPos - vertPos);
  vec3 nln = -one_float * lightNorm;
  float spotCos = dot(nln, lpv);
  return spotCos <= minCos ? zero_float : pow(spotCos, spotExp);
}

float lambertFactor(vec3 lightDir, vec3 vecNormal) {
  return max(zero_float, dot(lightDir, vecNormal));
}

float blinnPhongFactor(vec3 lightDir, vec3 vertPos, vec3 vecNormal, float shine) {
  vec3 np = normalize(vertPos);
  vec3 ldp = normalize(lightDir - np);
  return pow(max(zero_float, dot(ldp, vecNormal)), shine);
}

float GerstnerNormX(int waveNumber, float x, float y)
{
     vec2 direction = (vec2(xDirection[waveNumber], yDirection[waveNumber]));
     float frequency = 2.0*pi/waveLength[waveNumber];
     float waveVector = frequency*dot(direction,vec2(x,y));
     float phase = time * speed[waveNumber]*frequency;
     return direction.x*frequency*amplitude[waveNumber]*cos(waveVector+phase);
}

float GerstnerNormZ(int waveNumber, float x, float y)
{
     vec2 direction = (vec2(xDirection[waveNumber], yDirection[waveNumber]));
     float frequency = 2.0*pi/waveLength[waveNumber];
     float waveVector = frequency*dot(direction,vec2(x,y));
     float phase = time * speed[waveNumber]*frequency;
     return direction.y*frequency*amplitude[waveNumber]*cos(waveVector+phase);
}

float GerstnerNormY(int waveNumber, float x, float y)
{
     vec2 direction = (vec2(xDirection[waveNumber], yDirection[waveNumber]));
     float frequency = 2.0*pi/waveLength[waveNumber];
     float waveVector = frequency*dot(direction,vec2(x,y));
     float phase = time * speed[waveNumber]*frequency;
     float steep = steepness[waveNumber]/(frequency*amplitude[waveNumber]*MAXWAVES);
     return steep*frequency*amplitude[waveNumber]*sin(waveVector+phase);
}

vec3 GerstnerNormal(float x, float z)
{
     float finalX = 0.0;
     float finalY = 0.0;
     float finalZ = 0.0;
     
     for(int i = 0; i < MAXWAVES; i++)
     {
          finalX += GerstnerNormX(i, x, z);
          finalY += GerstnerNormY(i, x, z);
          finalZ += GerstnerNormZ(i, x, z);
     }
     
     return vec3(-finalX, (1.0-finalY),-finalZ);
}

float GerstnerX(int waveNumber, float x, float z)
{
     vec2 direction = (vec2(xDirection[waveNumber], yDirection[waveNumber]));
     float frequency = 2.0*pi/waveLength[waveNumber];
     float waveVector = frequency*dot(direction,vec2(x,z));
     float phase = time * speed[waveNumber]*frequency;
     float steep = steepness[waveNumber]/(frequency*amplitude[waveNumber]*MAXWAVES);
     return steep*amplitude[waveNumber]*direction.x*cos(waveVector + phase);
}

float GerstnerZ(int waveNumber, float x, float z)
{
     vec2 direction = (vec2(xDirection[waveNumber], yDirection[waveNumber]));
     float frequency = 2.0*pi/waveLength[waveNumber];
     float waveVector = frequency*dot(direction,vec2(x,z));
     float phase = time * speed[waveNumber]*frequency;
     float steep = steepness[waveNumber]/(frequency*amplitude[waveNumber]*MAXWAVES);
     return steep*amplitude[waveNumber]*direction.y*cos(waveVector + phase);
}

float GerstnerY(int waveNumber, float x, float z)
{
     vec2 direction = (vec2(xDirection[waveNumber], yDirection[waveNumber]));
     float frequency = 2.0*pi/waveLength[waveNumber];
     float waveVector = frequency*dot(direction,vec2(x,z));
     float phase = time * speed[waveNumber]*frequency;
     return amplitude[waveNumber]*sin(waveVector + phase);
}

//Gerstner Wave Function
vec3 GerstnerWave(float x, float z)
{
     float finalX = 0.0;
     float finalY = 0.0;
     float finalZ = 0.0;
     
     for(int i = 0; i < MAXWAVES; i++)
     {
          finalX += GerstnerX(i, x, z);
          finalY += GerstnerY(i, x, z);
          finalZ += GerstnerZ(i, x, z);
     }
     
     return vec3(x + finalX, finalY, z + finalZ);
}


//Returns the height of a vertex given a single wave param
float WaveHeight(int waveNumber, float x, float y) {
    vec2 direction = (vec2(xDirection[waveNumber], yDirection[waveNumber]));
    float frequency = 2.0*pi/waveLength[waveNumber];
    float phase = speed[waveNumber] * frequency;
    float theta = dot(direction, vec2(x, y));
    return amplitude[waveNumber] * sin(theta * frequency + time * phase);
}

//Returns height of a vertex given all the active waves
// and its current x/y value
float WaveSum(float x, float y)
{
     float height = 0.0;
     for(int i = 0; i < MAXWAVES; i++)
     {
          height += WaveHeight(i, x, y);
     }
     return height;
} 


float getDy(int waveNumber, float x, float y) {
     vec2 direction = (vec2(xDirection[waveNumber], yDirection[waveNumber]));
    float frequency = 2.0*pi/waveLength[waveNumber];
    float phase = speed[waveNumber] * frequency;
    float theta = dot(direction, vec2(x, y));
    float A = amplitude[waveNumber] * direction.y * frequency;
    return A * cos(theta * frequency + time * phase);
}

float getDx(int waveNumber, float x, float y) {
     vec2 direction = (vec2(xDirection[waveNumber], yDirection[waveNumber]));
    float frequency = 2.0*pi/waveLength[waveNumber];
    float phase = speed[waveNumber] * frequency;
    float theta = dot(direction, vec2(x, y));
    float A = amplitude[waveNumber] * direction.x * frequency;
    return A * cos(theta * frequency + time * phase);
}

//Returns the normal vector for each vertex
vec3 getNormal(float x, float y) {
    float dx = 0.0;
    float dy = 0.0;
    
    //Sum for each wave
    for (int i = 0; i < MAXWAVES; i++) {
        dx += getDx(i, x, y);
        dy += getDy(i, x, y);
    }
    vec3 n = vec3(-dx, 1.0, -dy);
    return normalize(n);
}


void main() {
  //vec4 pos = position; //Grab the position from Processing bc it's read only   
  //pos.z = WaveSum(pos.x, pos.y);
  
  
  vec4 pos = position; //Grab the position from Processing bc it's read only   
  pos = vec4(GerstnerWave(normal.x, normal.z),position.w);
  
  gl_Position  = transform * pos; //Get clipping matrix for view
  
  ecVertex = vec3(modelview * pos);
  
  // Normal vector in eye coordinates
  vec3 Normal = GerstnerNormal(pos.x, pos.z);
  //vec3Normal = vec3(0,1.0,0);
  ecNormal = normalize(normalMatrix*Normal);
  //ecNormal *= -1;
  
  vec3 ecNormalInv = ecNormal * -one_float;
  
  // Light calculations
  vec3 totalAmbient = vec3(0, 0, 0);
  
  vec3 totalFrontDiffuse = vec3(0, 0, 0);
  vec3 totalFrontSpecular = vec3(0, 0, 0);
  
  vec3 totalBackDiffuse = vec3(0, 0, 0);
  vec3 totalBackSpecular = vec3(0, 0, 0);
  
  for (int i = 0; i < 8; i++) {
    if (lightCount == i) break;
    
    vec3 lightPos = lightPosition[i].xyz;
    bool isDir = lightPosition[i].w < one_float;
    float spotCos = lightSpot[i].x;
    float spotExp = lightSpot[i].y;
    
    vec3 lightDir;
    float falloff;    
    float spotf;
      
    if (isDir) {
      falloff = one_float;
      lightDir = -one_float * lightNormal[i];
    } else {
      falloff = falloffFactor(lightPos, ecVertex, lightFalloff[i]);  
      lightDir = normalize(lightPos - ecVertex);
    }
  
    spotf = spotExp > zero_float ? spotFactor(lightPos, ecVertex, lightNormal[i], 
                                              spotCos, spotExp) 
                                 : one_float;
    
    if (any(greaterThan(lightAmbient[i], zero_vec3))) {
      totalAmbient       += lightAmbient[i] * falloff;
    }
    
    if (any(greaterThan(lightDiffuse[i], zero_vec3))) {
      totalFrontDiffuse  += lightDiffuse[i] * falloff * spotf * 
                            lambertFactor(lightDir, ecNormal);
      totalBackDiffuse   += lightDiffuse[i] * falloff * spotf * 
                            lambertFactor(lightDir, ecNormalInv);
    }
    
    if (any(greaterThan(lightSpecular[i], zero_vec3))) {
      totalFrontSpecular += lightSpecular[i] * falloff * spotf * 
                            blinnPhongFactor(lightDir, ecVertex, ecNormal, shininess);
      totalBackSpecular  += lightSpecular[i] * falloff * spotf * 
                            blinnPhongFactor(lightDir, ecVertex, ecNormalInv, shininess);
    }    
  }    

     // Calculating final color as result of all lights (plus emissive term).
     // Transparency is determined exclusively by the diffuse component.
     vertColor =    vec4(totalAmbient,0) * ambient + 
                  vec4(totalFrontDiffuse, 1.0) * color;
   
                  
     backVertColor = vec4(.0,.1,.2,1.0);
                  //vec4(totalAmbient,0) * ambient + 
                  //vec4(totalBackDiffuse, 1) * color;
                  
     //Fresnel Calculations
     camVec = vec3(modelview*vec4(cameraPosition,1.0));
     
}


Fragment Shader

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif


varying vec4 vertColor; //Color from vertshader
varying vec4 backVertColor; //Color from vertshader
varying float fresnelFactor;
varying vec3 camVec;
varying vec3 waterNormal;
varying vec3 ecVertex;
varying vec3 ecNormal;


void main() {
     
     //Fresnel calculations
     float bias = 0.1;
     float power = 1.3;
     vec3 I = normalize(ecVertex - camVec);
	float fresnelFactor = bias + (1.0-bias) * pow(abs(1.0 - (dot(I, ecNormal))), power);
     vec4 fresnelCol = mix(vertColor, vec4(0.2,.5,.9,1.0), fresnelFactor);
     
     gl_FragColor = gl_FrontFacing ? fresnelCol : backVertColor;
}
1 Like

Hi! Welcome to the forum :slight_smile: I can’t currently study your code, but only wanted to say that several times when I was having unexpected results they were solved by calling hint(DISABLE_OPTIMIZED_STROKE); You could try if it makes any difference… Cheers!

1 Like