Draw points from texture stored locations?

edited March 2018 in GLSL / Shaders

I want to pass a shader a texture of encoded locations, and have it draw points at the decoded locations.

I have a particle system stored in a texture that's 3000x1, with the x and y locations encoded into RGBA.

Currently I'm having to use the CPU to loop through particles and use point() to draw it to a new texture. I know this is being done properly in a shader in the PixelFlow library, but I can't figure it out, studying particleRender.glsl.

How can I get a shader to replicate whats going on in draw()? It feels like it should be easy but from what I've read on the PShader page and book of shaders I can't piece it together.

Edit: I've updated with my attempt at hacking away at PixelFlow's particleRender.glsl, it isn't throwing errors, but it isn't drawing anything either. I'm not used to the way version 150 works so maybe it's something simple. I've tried lots of trouble shooting and I can't get that shader to draw anything at all.

edit2: I've worked at it for hours, only a tiny bit of progress, a little sign of life from the shader, but I have no idea what it's doing. It's drawing what seems to be a random quad.

PGraphics pgLocs; 
PShader psRender;

void setup() {
  size(800, 800, P3D);
  int totalPoints = 3000;
  pgLocs = createGraphics(totalPoints, 1, P2D);

  psRender = loadShader("pointFrag.frag", "pointVert.glsl");
  psRender.set("tex_position", pgLocs);
  psRender.set("wh_viewport", width, height);
  psRender.set("wh_position", totalPoints, 1);
  psRender.set("point_size", 4);
  psRender.set("totalPoints", totalPoints);

  randomFill();
}

void randomFill() {
  pgLocs.beginDraw(); // fill pgLocs with random locations
  for (int i = 0; i < pgLocs.width; i++) {  
    PVector loc = new PVector(random(width)/width, random(height)/height);
    pgLocs.set(i, 0, xyToRGBA(loc));
  }
  pgLocs.endDraw();
  psRender.set("tex_position",pgLocs);
}

void keyPressed() {
  randomFill();
}

void draw() {
  stroke(0);
  strokeWeight(5);
  background(55);
  // // What I wish would work
  shader(psRender);
  //ill(255,100);
  //rect(0, 0, width, height);

  pgLocs.loadPixels();
  for (int i = 0; i < pgLocs.width; i++) { // What I'd like to do in a shader instead
    color c = pgLocs.pixels[i];// //get pixel color
    PVector loc = RGBAtoXY(c); // decode location
    stroke(c);                 // set color just for fun
   point(loc.x*width, loc.y*height); // show location was stored in the texture properly
  }
  filter(psRender);
}

color xyToRGBA(PVector loc) { // pack x into r g, y into b a
  PVector l = loc.copy().mult(255);
  int xi = floor(l.x);
  int yi = floor(l.y);
  int xr = floor((l.x-xi)*255);
  int yr = floor((l.y-yi)*255);
  return (yr << 24) | (xi << 16) | (xr << 8) | yi;
}

PVector RGBAtoXY(color c) { 
  int a = ((c >> 24) & 0xff) ;
  int r = ((c >> 16) & 0xff) ;
  int g = ((c >> 8)  & 0xff) ;
  int b =  (c        & 0xff) ;
  return new PVector((r+g/255.0)/255.0, (b+a/255.0)/255.0);
}

pointFrag.frag

#version 150
uniform vec2      wh_viewport;
uniform sampler2D tex_position;
uniform sampler2D tex_sprite;
uniform vec4      col_A = vec4(1, 1, 1, 1.0);
uniform vec4      col_B = vec4(0, 0, 0, 0.0);

in vec2 location;
in float my_PointSize;
out vec4 out_frag;

void main(){
  vec2 my_PointCoord = ((location * wh_viewport) - gl_FragCoord.xy) / my_PointSize + 0.5;
  out_frag = vec4(1.0,0,0,0);
}

pointVert.glsl

#version 150

uniform float     point_size;
uniform sampler2D tex_position;
uniform vec4      col_A = vec4(1, 1, 1, 1.0);
uniform vec4      col_B = vec4(0, 0, 0, 0.0);
uniform int  totalPoints;
out vec2 location;
 out float my_PointSize;
vec2 posDecode(vec4 c) {
  float r = c.r;
  float g = c.g/255.0;
  float b = c.b;
  float a = c.a/255.0;
  return vec2((r+g), (b+a));
}

void main(){
  float x = gl_VertexID/ float(totalPoints);
  vec4 color = vec4(texture2D(tex_position, vec2(x,1.0)));
  location = posDecode(color);
  location.y = 1-location.y;
  gl_Position  = vec4(location * 2.0 - 1.0, 0, 1); //vec4(location.x,location.y,0,1);//
  gl_PointSize = point_size;
  my_PointSize = point_size;
}

edit3: Still no luck figuring it out. I found more of the puzzle in pixelflow. These calls seem important gl.glEnable(GL3.GL_PROGRAM_POINT_SIZE); gl.glDrawArrays(GL2.GL_POINTS, 0, num_points);

This is what I'm trying to implement this into. I think I could do a million path finders instead of tens of thousands.

Answers

  • edited March 2018

    I got it working. I'm using a magic number, I have no idea where it comes from and it's probably because I'm doing something wrong/improperly. Any ideas where this 1.731875 number in my vert shader comes from? It's needed to match the scale. Also none of my .set("var",var) type stuff is working with my shader, any idea what could cause that?

    // pointvert.glsl
     #define PROCESSING_POINT_SHADER
    uniform vec2 wh;
    uniform float fix;
    uniform mat4 projection;
    uniform mat4 modelview;
    uniform sampler2D tex_position;
    
    attribute vec4 position;
    attribute vec4 color;
    attribute vec2 offset;
    
    varying vec4 vertColor;
    
    vec2 posDecode(vec4 c) {
        return vec2(c.r + c.g/255.0, c.b + c.a/255.0);
    }
    void main() {
    
     vec4 colord = texelFetch(tex_position, ivec2(position.x, 0),0);
     vertColor = colord;
     vec4 loc = vec4(posDecode(colord),0,0); // decoded location from texture
    
      vec4 pos = modelview * vec4(0,0,position.zw); 
      vec4 clip = projection * pos;
    
      vec4 locAdjusted =  clip + projection * vec4(offset, 0, 0);
    
      vec2 screenWH = vec2(1000,1000); // setting this with wh from processing isn't working?
      screenWH *= 1.731875; // What is this magic numbers? Doesn't seem to relate to the screen size in a clear ratio.
      loc.x *= screenWH.x;  
      loc.y *= screenWH.y;
      locAdjusted.x += loc.x; 
      locAdjusted.y -= loc.y;
    
      gl_Position = locAdjusted;
    }
    

    .....

    //pointfrag.glsl
    #ifdef GL_ES
    precision mediump float;
    precision mediump int;
    #endif
    
    varying vec4 vertColor;
    
    void main() {  
      gl_FragColor = vertColor;
    }
    

    ....

    sketch

    // PRESS ANY KEY TO TOGGLE SHADER/CPU RENDERING
    // CLICK FOR FRESH DATA
    
    PShape psPoints; // vertex list pointing to every pixel/encoded location in pgLoc
    PShader pointShader;
    PImage pgLocs;
    int totalPoints = 5000;
    
    void setup() {
      size(1000, 1000, P3D);
      pgLocs = createImage(totalPoints,1, ARGB); 
    
      pointShader = loadShader("pointfrag.glsl", "pointvert.glsl");
      float[] f= new float[2];
      f[0] = width; 
      f[1] = height;
      pointShader.set("wh", width,height);  // this isn't working, no idea why
      randomFill();
      initShape();
    }
    
    void randomFill() {
      pgLocs.loadPixels();
      for (int i = 0; i < totalPoints; i++) {  
        PVector loc = new PVector(random(width)/width, random(height)/height);
        pgLocs.pixels[i] = xyToRGBA(loc);
      }
      pgLocs.pixels[0] = xyToRGBA(new PVector(((float)mouseX)/width, ((float)mouseY)/height));
      pgLocs.updatePixels();
      pointShader.set("tex_position", pgLocs);
    }
    
    void initShape() {
      // creates a shape with verticies with the pixel locations for the encoded locations in pgLocs;
      // this triggers the vert shader for each data point without needing gl_VertexID
      psPoints = createShape();
      psPoints.beginShape(POINTS);
      psPoints.stroke(255);
      psPoints.strokeWeight(10);
      for (int i = 0; i < totalPoints; i++) {
        psPoints.vertex(i*1.00+0.00, 0.0);
      }
      psPoints.endShape();
    }
    
    boolean useShader = true;
    void keyPressed() {
      useShader = !useShader;
    }
    
    void draw() {    
      pointShader.set("fix", 800);
      initShape();
      background(20);
      if (mousePressed) {   
        randomFill();
        initShape();
      }  
      if (!useShader) { // CPU implementation
        resetShader(POINTS);
        strokeWeight(10);
        pgLocs.loadPixels();
        for (int i = 0; i < totalPoints; i++) { // What I'd like to do in a shader instead
          color c = pgLocs.pixels[i];// //get pixel color
          PVector loc = RGBAtoXY(c); // decode location
          stroke(c);                 // set color just for fun
          point(loc.x*width, loc.y*height); // show location was stored in the texture properly
        }
        text("CPU render", 10, 10);
      } 
      else {  // Shader implementation
        shader(pointShader, POINTS);
        stroke(255);
        shape(psPoints); // sends a list of point verticies, pointing to every pixel in pgLoc
        text("Shader render", 10, 30);
      }
    }
    color xyToRGBA(PVector loc) { // pack x into r g, y into b a
      PVector l = loc.copy().mult(255);
      int xi = floor(l.x);
      int yi = floor(l.y);
      int xr = floor((l.x-xi)*255);
      int yr = floor((l.y-yi)*255);
      return (yr << 24) | (xi << 16) | (xr << 8) | yi;
    }
    
    PVector RGBAtoXY(color c) { 
      int a = ((c >> 24) & 0xff) ;
      int r = ((c >> 16) & 0xff) ;
      int g = ((c >> 8)  & 0xff) ;
      int b =  (c        & 0xff) ;
      return new PVector((r+g/255.0)/255.0, (b+a/255.0)/255.0);
    }
    
  • Answer ✓

    To push values into the shader's "wh" variable, cast "width" and "height" to floats. (or send f[0] and f[1] for width and height respectively)

    Vec'n' types expect to have floating point values. There are integer vectors uvec'n' and ivec'n' but that is a whole other can of worms. ;)

    https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)#Vectors

    As for 1.731875, it is nearly the square root of 3 which would also be the length of a vector (1,1,1)... but that is all I have without guessing.

  • import com.jogamp.opengl.*;
    import  com.jogamp.opengl.GL4;
    import com.jogamp.opengl.util.GLBuffers;
    import java.nio.FloatBuffer;
    import java.nio.IntBuffer;
    
    IntBuffer Tex;
    FloatBuffer mBuf;
    
    PJOGL pgl;
    GL4 gl4 ;
    
    final int twidth = 1;
    
    PShader shdr;
    
    void settings() {
      size(640, 360, P3D );
      PJOGL.profile = 4;
    }
    
    void setup() {
    
      /* //fragment.glsl
      #version 430
      layout(binding = 0) uniform sampler1D tex;
      layout(location = 0) uniform vec3 iResolution;
      layout(location = 0) out vec4 fragColor;
      void main() {
      vec2 uvv = (gl_FragCoord.xy*2.-iResolution.xy)/iResolution.z;
      float arr = texelFetch( tex ,0,0) .r;
      float cir=arr/length(uvv);
      fragColor = vec4(vec3(cir),1.);
      }
      */
    
      /* //vertex.glsl
      #version 430
      in vec2 position;
      void main(){
      gl_Position = vec4(position.xy,0.,1.);
      }
      */
    
      shdr = loadShader("fragment.glsl", "vertex.glsl");
    
    
    
      shader(shdr);
      shdr.set("iResolution", new PVector(width, height, Math.min(width, height)));
    
      Tex = GLBuffers.newDirectIntBuffer(1);
    
      pgl = (PJOGL) beginPGL();  
      gl4 = pgl.gl.getGL4();
    
      gl4.glGenTextures(1, Tex);
    
      gl4.glBindTexture(GL4.GL_TEXTURE_1D, Tex.get(0));
    
      mBuf = GLBuffers.newDirectFloatBuffer(twidth);
    
      shdr.bind();
      gl4.glTexImage1D( GL4.GL_TEXTURE_1D, 0, GL4.GL_R32F, twidth, 0, GL4.GL_RED, GL4.GL_FLOAT, mBuf );
      shdr.unbind();
      endPGL();
    }
    
    void draw() {
      pgl = (PJOGL) beginPGL(); 
    
      float arr = 1./random(2, 24);
    
      gl4 = pgl.gl.getGL4();
      shdr.bind();
      if (frameCount%12==0) mBuf.put( arr ).rewind();
      println(arr);
    
      gl4.glTexSubImage1D( GL4.GL_TEXTURE_1D, 0, 0, twidth, GL4.GL_RED, GL4.GL_FLOAT, mBuf );
      shdr.unbind();
      endPGL();
      rect(0, 0, width, height);
    }
    
Sign In or Register to comment.