Vertex of shapes in shader

edited March 2014 in GLSL / Shaders

I wonder why the vertexes for shapes that was drawn direct on the sketch instead of creating an PShape, sends other vertexes to the shader then the one that was drawn on a PShape. For example, when I change the per pixel lightning example from http://www.processing.org/tutorials/pshader/ and change the the filter:

void main() {
  gl_Position = transform * vertex;
  vec3 ecVertex = vec3(modelview * vertex);

  ecNormal = normalize(normalMatrix * normal);
  lightDir = normalize(lightPosition.xyz - ecVertex);
  vertColor = sin(vertex);
}

so the color is generated by the position. This works as expected. But when I change the sketch so it draws the can in every draw call like this:

void createCan(float r, float h, int detail) {

  beginShape(QUAD_STRIP);
  noStroke();
  for (int i = 0; i <= detail; i++) {
    float angle = TWO_PI / detail;
    float x = sin(i * angle);
    float z = cos(i * angle);
    float u = float(i) / detail;
    vertex(x * r, -h/2, z * r, u, 0);
    vertex(x * r, +h/2, z * r, u, 1);    
  }
  endShape(); 
}

the colors start to flicker and change in every loop step. Can someone explain this?

Answers

  • Hi, before going into the flickering issue, I'd suggest to add to the immediate mode code the normal(x, 0, z) call, otherwise the renderer will auto-compute the normals per each triangle, which won't give accurate lighting results for a cylinder.

    About the flickering: this is a bit tricky but not the result of a bug... it is in fact due to the way the renderer optimizes geometry buffers in the immediate mode. In order to pack as much geometry in a single VBO as possible, the geometry transformations are applied on the CPU side before copying the VBO to the video memory, and so the transform matrix (more specifically the modelview component) only contains the camera transformations (but none of the rotation and translation components you apply in the draw function). When drawing a PShape, the vertex coordinates are sent to the GPU as they are fed through the Processing API.

    This optimization is disabled when you need to draw strokes in 3D so they look correct when you stack flat geometry, by using hint(DISABLE_OPTIMIZED_STROKE), so that a side effect of this hint is that the flickering in your code goes away. All of this is a bit tricky, and a consequence of the all the things going on behind the scenes to make rendering fast, maybe this sketch helps understanding the different settings (same shader code as you posted):

    PShape can;
    float angle;
    
    PShader pixlightShader;
    
    boolean usePShape = false;
    boolean optimStroke = true;
    
    void setup() {
      size(640, 360, P3D);
      can = createCan(100, 200, 32);
      pixlightShader = loadShader("pixlightfrag.glsl", "pixlightvert.glsl");
    
      if (usePShape) println("using pshape");
      else println("using immediate mode");  
      if (optimStroke) println("enabled optimized stroke");
      else println("disabled optimized stroke");  
    }
    
    void draw() {    
      background(0);
    
      shader(pixlightShader);
    
      pointLight(255, 255, 255, width/2, height, 200);
    
      translate(width/2, height/2);
      rotateY(angle);
      if (usePShape) shape(can);
      else drawCan(100, 200, 32);
    
      angle += 0.01;
    }
    
    PShape createCan(float r, float h, int detail) {
      textureMode(NORMAL);
      PShape sh = createShape();
      sh.beginShape(QUAD_STRIP);
      sh.noStroke();
      for (int i = 0; i <= detail; i++) {
        float angle = TWO_PI / detail;
        float x = sin(i * angle);
        float z = cos(i * angle);
        float u = float(i) / detail;
        sh.normal(x, 0, z);
        sh.vertex(x * r, -h/2, z * r, u, 0);
        sh.vertex(x * r, +h/2, z * r, u, 1);    
      }
      sh.endShape(); 
      return sh;
    }
    
    void drawCan(float r, float h, int detail) { 
      beginShape(QUAD_STRIP);
      noStroke();
      for (int i = 0; i <= detail; i++) {
        float angle = TWO_PI / detail;
        float x = sin(i * angle);
        float z = cos(i * angle);
        float u = float(i) / detail;
        normal(x, 0, z);
        vertex(x * r, -h/2, z * r, u, 0);
        vertex(x * r, +h/2, z * r, u, 1);   
      }
      endShape();
    }
    
    void keyPressed() {
      if (key == 'p') {
        usePShape = !usePShape;
        if (usePShape) println("using pshape");
        else println("using immediate mode");
      } else if (key == 'o') {
        optimStroke = !optimStroke;
        if (optimStroke) {
          hint(ENABLE_OPTIMIZED_STROKE);
          println("enabled optimized stroke");
        } else { 
          hint(DISABLE_OPTIMIZED_STROKE);
          println("disabled optimized stroke");
        }
      }  
    }
    
  • Thanks for the explanation.

  • edited February 2018

    This was tricky indeed!

    I just gave a workshop on shaders and left scratching my head about why creating shader patterns that only depend on vertex positions are not anchored to the object, but rotate in strange ways if the object rotates.

    It is solved with hint(DISABLE_OPTIMIZED_STROKE);

    Here you can see a working example online.

    Here the program that fails to work properly without the hint:

    import peasy.PeasyCam;
    
    PeasyCam cam;
    PShader fx;
    
    void setup() {
      size(600, 600, P3D);
      cam = new PeasyCam(this, 400);
      fx = loadShader("frag1.glsl", "vert1.glsl");
      hint(DISABLE_OPTIMIZED_STROKE);
    }
    
    void draw() {
      background(0);
      shader(fx);
      box(300);
    }
    

    Vertex

    uniform mat4 transformMatrix;
    attribute vec4 position;     
    varying vec3 pos;
    
    void main() {
      gl_Position = transformMatrix * position;
      pos = vec3(position) * 0.01;
    }
    

    Fragment

    #ifdef GL_ES
    precision mediump float;
    precision mediump int;
    #endif
    
    varying vec3 pos;
    
    void main() {
      float c = .5 + .5*sin(17.*(pos.x + pos.y + pos.z));
      c = step(.5, c);
      gl_FragColor = vec4(c, c, c, 1.);
    }
    
Sign In or Register to comment.