glsltextureswarp

Move pixel with texture direction and texture velocity / GLSL


I write a little program to explain simply my problem, I try to change the pixel position of picture with a texture one where the component x is the direction, and where the other represent the velocity. The final objective is to use my data from CPU where are compute a NAVIER-STROKE fluid to move the pixel in GLSL. The CPU code is in Processing java library. I try to undestand what is buggy in my code, but I don't understand how work the pixel translation. in the first I transform my direction in value color from 0 to 255 in the CPU and after in the GPU transform this one in vectorial direction, and multiply this one by the velocity and scale this one in 1x1 but that's don't work... sorry if my explication is not really understable, but english is not really fluent.

link to the sketch

Processing :

PImage tex_velocity, tex_direction ;
PShader warping;
PImage img ;
int grid_w, grid_h ;
void setup() {
  size(600,375,P2D);
  // img = loadImage("pirate_small.jpg");
  img = loadImage("puros_girl_small.jpg");
  grid_w = 60 ;
  grid_h = 37 ;
  tex_velocity = createImage(grid_w,grid_h,RGB);
  tex_direction = createImage(grid_w,grid_h,RGB);  
  warping = loadShader("shader/warp/rope_warp_frag.glsl");
  noise_img(tex_velocity, 20, .1, .1); // max translate for the pixel
    noise_img(tex_direction, 360, .1, .1); // degree direction
}

void draw() {

    println(frameRate);
    if(frameCount%30 == 0) {
        noise_img(tex_velocity, 20, .1, .1); // max translate for the pixel
        noise_img(tex_direction, 360, .1, .1); // degree direction
    }

    warping.set("mode", 0) ;
    warping.set("texture",img);
    warping.set("roof_component_colour",g.colorModeX);
    warping.set("wh_ratio",1f/grid_w, 1f/grid_h);

  warping.set("vel_texture",tex_velocity);
  warping.set("dir_texture",tex_direction);

  shader(warping);

  image(img,0,0);
  resetShader();
  image(tex_velocity,5,5);
  image(tex_direction,grid_w +15 ,5 );
}


float x_offset, y_offset ;
void noise_img(PImage dst, int max, float ratio_x, float ratio_y) {
    noiseSeed((int)random(10000));
    for(int x = 0 ; x < dst.width ; x++) {
        x_offset += ratio_x ;
        for(int y = 0 ; y < dst.height ; y++) {
            y_offset += ratio_y ;
            float v = map(noise(x_offset,y_offset),0,1,0,max);
            v = (int)map(v,0,max,0,g.colorModeX);
            int c = color(v,v,v,g.colorModeA) ;
            dst.set(x,y,c);
        }
    }
}

GLSL

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

#define PROCESSING_TEXTURE_SHADER

#define PI 3.1415926535897932384626433832795

varying vec4 vertTexCoord;
uniform sampler2D texture;

uniform int mode;
uniform float roof_component_colour;

uniform sampler2D vel_texture;
uniform sampler2D dir_texture;
uniform vec2 wh_ratio;




float map(float value, float start1, float stop1, float start2, float stop2) {
  float result = start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1));
  return result;
}

vec2 cartesian_coord(float angle) {
  float x = cos(angle);
  float y = sin(angle);
  return vec2(x,y);
}


vec2 translate(float fdir, float fvel) {
  float angle_in_radian = map(fdir, 0, roof_component_colour, -PI, PI);
  vec2 dir_cart = cartesian_coord(angle_in_radian);
  return dir_cart *fvel ;
}



void main() {
  vec2 ratio = gl_FragCoord.xy *wh_ratio;

  vec4 vel = texture2D(vel_texture, ratio);
  vec4 dir = texture2D(dir_texture, ratio);

  // rendering picture ;
  if(mode == 0) {
    float direction = dir.x;
    float velocity = vel.x;
    vec2 translation = translate(direction,velocity);

    // not bad, but totaly wrong
   //  vec2 coord_dest = vertTexCoord.st +translation 

   vec2 coord_dest = vertTexCoord.st *ratio +translation ;
   // not bad, but totaly wrong
    vec2 coord_dest = vertTexCoord.st *ratio +translation ;
    vec4 tex_colour = texture2D(texture, coord_dest);

    gl_FragColor = tex_colour;
  }


  // velocity
  if(mode == 1 ) {
    gl_FragColor = texture2D(vel_texture, vertTexCoord.st);;
  }
  // direction force field
  if(mode == 2) {
    gl_FragColor = texture2D(dir_texture, vertTexCoord.st);;
  }
}

enter image description here


Solution

  • The texture format is GL_RGBA8, this means each color channel is stored to a byte in, which is a integral data tyoe in rage from 0 to 255.
    But when you read texts from the texture sampler, the you will get a floating point value in the range from 0.0 to 1.0. (see glTexImage2D - GL_RGBA).

    In the fragment shader you have to map the color channel (in [0, 1]), which you read from the texture sampler, to the range from -PI to PI. For this you can use the GLSL function mix, which does a linear interpolation between 2 values:

    vec2 translate(float fdir, float fvel) // fdir, fvel in [0.0, 1.0] 
    {
      float angle = mix(-PI, PI, fdir);
      return vec2(cos(angle), sin(angle)) * fvel;
    }
    

    The texture coordinates are in range [0, 1]. You have to transform the translation to texture coordinates. For this you have to know the size of your image texture:

    vec2 wh_ratio;     // 1f/grid_w, 1f/grid_h
    vec2 imageTexSize; // size of "texture"
    
    vec2 scale = imageTexSize * wh_ratio;
    vec2 coord_dest = vertTexCoord.st + translation / scale;