Loading...
Logo
Processing Forum
Hi!

I'm pretty sure this is a bug, so I filed it as one ( https://github.com/processing/processing/issues/2083 ).

I couldn't find anything on here or in the bugs database that is the same.

But off the odd chance it isn't a bug, I thought I'd ask here, and if it turns out I've been wrong I'll delete the bug report.

I created this example from combining the OffscreenTest and LowLevelGL examples, but I've encountered this behavior in a lot of my own code as well.

This appears in the latest version (2.0.3), running on Windows 7 32bit.

Pressing the spacebar toggles between drawing directly, and first drawing into PGraphics and then displaying that.

The two should be identical but are not!

In the method drawDirectly, the output is as expected for all alpha values.
In drawImage, alpha == 1.0f looks fine, but set this to 0.5f, and drawing is almost invisible!

EDIT:

I tested this also in Processing 2.0.1 and 2.0.2 and got different behavior showing that this is most likely a bug stemming out of recent development.

In 2.0.1 the issue is the same as reported for 2.0.3.

For 2.0.2 however, the output of drawImage() is as expected, while drawDirectly() seems to ignore alpha altogether!

End EDIT.

Sketch code to reproduce:

Copy code
  1. import java.nio.*;

    PGL pgl;
    PGraphics pg;
    PShader flatShader;
    int vertLoc, colorLoc;
    float[] vertices, colors;
    FloatBuffer vertData, colorData;
    boolean drawPGraphicsImage = true;
    float alpha = 0.5f;

    // Keys:
    // 'a': toggles between alpha == 0.5f and alpha == 1.0f.
    // spacebar: toggles between rendering directly, and into a PGraphics buffer.

    // BUG DESCRIPTION:
    // In drawDirectly, the appearance is as expected for all alpha values.
    // In drawImage, alpha == 1.0f looks fine, but set this to 0.5f, and drawing is almost invisible!

    void setup() {
    size(800, 800, OPENGL);

    pg = createGraphics(800, 800, OPENGL);

    flatShader = loadShader("frag.glsl", "vert.glsl");

    vertices = new float[12];
    vertData = allocateDirectFloatBuffer(12);

    colors = new float[12];
    colorData = allocateDirectFloatBuffer(12);
    }

    void draw() {
    updateGeometry();

    background(0);

    if(drawPGraphicsImage)
    drawImage();
    else
    drawDirectly();
    }

    void keyPressed() {
    if (key == ' ') {
    drawPGraphicsImage = !drawPGraphicsImage;
    }
    else if (key == 'a') {
    if(alpha == 0.5f)
    alpha = 1.0f;
    else
    alpha = 0.5f;
    }
    }

    // This works as expected
    void drawDirectly() {
    pgl = beginPGL();
    flatShader.bind();

    vertLoc = pgl.getAttribLocation(flatShader.glProgram, "vertex");
    colorLoc = pgl.getAttribLocation(flatShader.glProgram, "color");

    pgl.enableVertexAttribArray(vertLoc);
    pgl.enableVertexAttribArray(colorLoc);

    pgl.vertexAttribPointer(vertLoc, 4, PGL.FLOAT, false, 0, vertData);
    pgl.vertexAttribPointer(colorLoc, 4, PGL.FLOAT, false, 0, colorData);

    pgl.drawArrays(PGL.TRIANGLES, 0, 3);

    pgl.disableVertexAttribArray(vertLoc);
    pgl.disableVertexAttribArray(colorLoc);

    flatShader.unbind();

    endPGL();
    }

    // This is most likey a bug!
    void drawImage() {
    pg.beginDraw();
    pg.clear();

    pgl = pg.beginPGL();
    flatShader.bind();

    vertLoc = pgl.getAttribLocation(flatShader.glProgram, "vertex");
    colorLoc = pgl.getAttribLocation(flatShader.glProgram, "color");

    pgl.enableVertexAttribArray(vertLoc);
    pgl.enableVertexAttribArray(colorLoc);

    pgl.vertexAttribPointer(vertLoc, 4, PGL.FLOAT, false, 0, vertData);
    pgl.vertexAttribPointer(colorLoc, 4, PGL.FLOAT, false, 0, colorData);

    pgl.drawArrays(PGL.TRIANGLES, 0, 3);

    pgl.disableVertexAttribArray(vertLoc);
    pgl.disableVertexAttribArray(colorLoc);

    flatShader.unbind();

    pg.endPGL();

    pg.endDraw();
    image(pg, 0, 0, 800, 800);
    }

    void updateGeometry() {
    // Vertex 1
    vertices[0] = 0;
    vertices[1] = 0;
    vertices[2] = 0;
    vertices[3] = 1;
    colors[0] = 1;
    colors[1] = 0;
    colors[2] = 0;
    colors[3] = alpha;

    // Corner 2
    vertices[4] = width/2;
    vertices[5] = height;
    vertices[6] = 0;
    vertices[7] = 1;
    colors[4] = 0;
    colors[5] = 1;
    colors[6] = 0;
    colors[7] = alpha;

    // Corner 3
    vertices[8] = width;
    vertices[9] = 0;
    vertices[10] = 0;
    vertices[11] = 1;
    colors[8] = 0;
    colors[9] = 0;
    colors[10] = 1;
    colors[11] = alpha;

    vertData.rewind();
    vertData.put(vertices);
    vertData.position(0);

    colorData.rewind();
    colorData.put(colors);
    colorData.position(0);

    }

    FloatBuffer allocateDirectFloatBuffer(int n) {
    return ByteBuffer.allocateDirect(n * Float.SIZE/8).order(ByteOrder.nativeOrder()).asFloatBuffer();
    }

Replies(5)

If you set the blend mode as replace when drawing the offscreen pg buffer:
Copy code
  1.   blendMode(REPLACE);
  2.   image(pg, 0, 0, 800, 800);
  3.   blendMode(BLEND);
then the two outputs are identical. This is discussed in this issue.
Hi, thank you for the quick reply!

The bug report is closed, so I'll carry on here with my question, since REPLACE does not give me the output I need.

REPLACE treats the area of the buffer that I didn't draw into as opaque, unlike the default BLEND.

I remember in OpenGL this is dealt with by setting a particular set of source and destination blending settings ( glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA) I think but I may be wrong...), but in Processing blending is set differently so I don't know how to get the result I'm after. I hope I can avoid mixing both straight Processing and OpenGL voodoo :)

Following is an example sketch of what I mean. By pressing the spacebar, you switch between drawing into PGraphics or directly. By pressing 'r' when showing the PGraphics output you see the undesired output from using REPLACE.

To sum up, what I want is that when you run the sketch, and press spacebar, the output is the exact same!

Copy code
  1. import java.nio.*;
  2. PGL         pgl;
  3. PGraphics   pg_1, pg_2;
  4. PShader     flatShader;
  5. int         vertLoc, colorLoc;
  6. float[]     vertices, colors;
  7. FloatBuffer vertData, colorData;
  8. int         vertLoc2;
  9. float[]     vertices2;
  10. FloatBuffer vertData2;
  11. boolean     drawPGraphicsImage = true;
  12. boolean     blend = true;
  13. float       alpha = 0.5f;
  14. float angle;
  15. float jitter;
  16. // Keys:
  17. // 'a':       toggles between alpha == 0.5f and alpha == 1.0f.
  18. // spacebar:  toggles between rendering directly, and into a PGraphics buffer.
  19. // blend:     toggles between REPLACE and BLEND when drawing PGraphics
  20. void setup() {
  21.   size(800, 800, OPENGL);
  22.   pg_1 = createGraphics(800, 800, OPENGL);
  23.   pg_2 = createGraphics(800, 800, OPENGL);
  24.  
  25.   flatShader = loadShader("frag.glsl", "vert.glsl");
  26.   vertices = new float[12];
  27.   vertData = allocateDirectFloatBuffer(12);
  28.   colors = new float[12];
  29.   colorData = allocateDirectFloatBuffer(12);
  30.  
  31.   vertices2 = new float[12];
  32.   vertData2 = allocateDirectFloatBuffer(12);
  33. }
  34. void draw() {
  35.   updateGeometry();
  36.   updateGeometry2();
  37.  
  38.   background(0);
  39.  
  40.   if(drawPGraphicsImage)
  41.     drawImage();
  42.   else
  43.     drawDirectly();
  44. }
  45. void keyPressed() {
  46.   if (key == ' ') {
  47.     drawPGraphicsImage = !drawPGraphicsImage;
  48.   }
  49.   else if (key == 'r') {
  50.     blend = !blend;
  51.   }
  52.   else if (key == 'a') {
  53.     if(alpha == 0.5f)
  54.       alpha = 1.0f;
  55.     else
  56.       alpha = 0.5f;
  57.   }
  58. }
  59. // This works as expected
  60. void drawDirectly() {
  61.   pgl = beginPGL();
  62.   flatShader.bind();
  63.   vertLoc = pgl.getAttribLocation(flatShader.glProgram, "vertex");
  64.   colorLoc = pgl.getAttribLocation(flatShader.glProgram, "color");
  65.   pgl.enableVertexAttribArray(vertLoc);
  66.   pgl.enableVertexAttribArray(colorLoc);
  67.   pgl.vertexAttribPointer(vertLoc, 4, PGL.FLOAT, false, 0, vertData);
  68.   pgl.vertexAttribPointer(colorLoc, 4, PGL.FLOAT, false, 0, colorData);
  69.   pgl.drawArrays(PGL.TRIANGLES, 0, 3);
  70.   pgl.disableVertexAttribArray(vertLoc);
  71.   pgl.disableVertexAttribArray(colorLoc);
  72.   flatShader.unbind(); 
  73.   endPGL();
  74.  
  75.   pgl = beginPGL();
  76.   flatShader.bind();
  77.   vertLoc2 = pgl.getAttribLocation(flatShader.glProgram, "vertex");
  78.   colorLoc = pgl.getAttribLocation(flatShader.glProgram, "color");
  79.   pgl.enableVertexAttribArray(vertLoc2);
  80.   pgl.enableVertexAttribArray(colorLoc);
  81.   pgl.vertexAttribPointer(vertLoc2, 4, PGL.FLOAT, false, 0, vertData2);
  82.   pgl.vertexAttribPointer(colorLoc, 4, PGL.FLOAT, false, 0, colorData);
  83.   pgl.drawArrays(PGL.TRIANGLES, 0, 3);
  84.   pgl.disableVertexAttribArray(vertLoc2);
  85.   pgl.disableVertexAttribArray(colorLoc);
  86.   flatShader.unbind(); 
  87.   endPGL();
  88. }
  89. // This is most likey a bug!
  90. void drawImage() {
  91.   pg_1.beginDraw();
  92.   pg_1.clear();
  93.   pgl = pg_1.beginPGL();
  94.   flatShader.bind();
  95.   vertLoc = pgl.getAttribLocation(flatShader.glProgram, "vertex");
  96.   colorLoc = pgl.getAttribLocation(flatShader.glProgram, "color");
  97.   pgl.enableVertexAttribArray(vertLoc);
  98.   pgl.enableVertexAttribArray(colorLoc);
  99.   pgl.vertexAttribPointer(vertLoc, 4, PGL.FLOAT, false, 0, vertData);
  100.   pgl.vertexAttribPointer(colorLoc, 4, PGL.FLOAT, false, 0, colorData);
  101.   pgl.drawArrays(PGL.TRIANGLES, 0, 3);
  102.   pgl.disableVertexAttribArray(vertLoc);
  103.   pgl.disableVertexAttribArray(colorLoc);
  104.   flatShader.unbind(); 
  105.   pg_1.endPGL();
  106.   pg_1.endDraw();
  107.  
  108.   // pg_2:
  109.   pg_2.beginDraw();
  110.   pg_2.clear();
  111.   pgl = pg_2.beginPGL();
  112.   flatShader.bind();
  113.   vertLoc = pgl.getAttribLocation(flatShader.glProgram, "vertex");
  114.   colorLoc = pgl.getAttribLocation(flatShader.glProgram, "color"); 
  115.   pgl.enableVertexAttribArray(vertLoc);
  116.   pgl.enableVertexAttribArray(colorLoc);
  117.   pgl.vertexAttribPointer(vertLoc, 4, PGL.FLOAT, false, 0, vertData2);
  118.   pgl.vertexAttribPointer(colorLoc, 4, PGL.FLOAT, false, 0, colorData);
  119.   pgl.drawArrays(PGL.TRIANGLES, 0, 3);
  120.   pgl.disableVertexAttribArray(vertLoc);
  121.   pgl.disableVertexAttribArray(colorLoc);
  122.   flatShader.unbind(); 
  123.   pg_2.endPGL();
  124.   pg_2.endDraw();
  125.    
  126.   // output pg_1 to screen
  127.   image(pg_1, 0, 0, 800, 800);
  128.  
  129.   if(blend)
  130.     blendMode(BLEND);
  131.   else
  132.     blendMode(REPLACE);
  133.  
  134.   // output rotated pg_2 to screen
  135.   image(pg_2, 0, 0, 800, 800);
  136.  
  137.   blendMode(BLEND);
  138. }
  139. void updateGeometry() {
  140.   // Vertex 1
  141.   vertices[0] = 0;
  142.   vertices[1] = 0;
  143.   vertices[2] = 0;
  144.   vertices[3] = 1;
  145.   colors[0] = 1;
  146.   colors[1] = 0;
  147.   colors[2] = 0;
  148.   colors[3] = alpha;
  149.   // Corner 2
  150.   vertices[4] = width/2;
  151.   vertices[5] = height;
  152.   vertices[6] = 0;
  153.   vertices[7] = 1;
  154.   colors[4] = 0;
  155.   colors[5] = 1;
  156.   colors[6] = 0;
  157.   colors[7] = alpha;
  158.  
  159.   // Corner 3
  160.   vertices[8] = width;
  161.   vertices[9] = 0;
  162.   vertices[10] = 0;
  163.   vertices[11] = 1;
  164.   colors[8] = 0;
  165.   colors[9] = 0;
  166.   colors[10] = 1;
  167.   colors[11] = alpha;
  168.  
  169.   vertData.rewind();
  170.   vertData.put(vertices);
  171.   vertData.position(0);
  172.  
  173.   colorData.rewind();
  174.   colorData.put(colors);
  175.   colorData.position(0); 
  176. }
  177. void updateGeometry2() {
  178.   // Vertex 1
  179.   vertices2[0] = width;
  180.   vertices2[1] = height;
  181.   vertices2[2] = 0;
  182.   vertices2[3] = 1;
  183.   // Corner 2
  184.   vertices2[4] = 0;
  185.   vertices2[5] = height;
  186.   vertices2[6] = 0;
  187.   vertices2[7] = 1;
  188.   // Corner 3
  189.   vertices2[8] = width/2;
  190.   vertices2[9] = 0;
  191.   vertices2[10] = 0;
  192.   vertices2[11] = 1;
  193.  
  194.   vertData2.rewind();
  195.   vertData2.put(vertices2);
  196.   vertData2.position(0); 
  197. }
  198. FloatBuffer allocateDirectFloatBuffer(int n) {
  199.   return ByteBuffer.allocateDirect(n * Float.SIZE/8).order(ByteOrder.nativeOrder()).asFloatBuffer();
  200. }
Copy code
    blendMode(BLEND) (which is the default blending mode in P2D and P3D) just calls  glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA). However, I think that a problem in your code is that the triangle pixels are being alpha-blended twice, first with the black background in the pg object and then with the main surface, and this leads to a fainter color. 

    If we follow the notation of the article on blending from the opengl wiki, the alpha value of the triangle pixels in the pg surfaces would be:

    Oa = sa * Sa + da * Da

    where sa = Sa = 0.5, da = 1 - 0.5, Da = 0, because the source and destination blending factors are the source alpha and one minus source alpha (the equation above) and source alpha is simply the alpha of the triangles. So you get:

    Oa = 0.25 

    Is this correct?
    Hi, thank you for the quick and thorough reply!

    From your response and from reading the link, I see what is going on now, and I see two options for getting what I'm after:

    A: knowing from advance the number of times the drawing will be made, and calculate an initial alpha value to counter that, so that the final transparency is the desired one, in this case 0.5.

    B: from reading the OpenGL wiki article, I found a mention of this:

    It is often useful to perform premultiplied alpha blending. This means that texture values have multiplied the alpha with the texel before accessing it (or in the shader). So you don't want to multiply the source color by the source alpha, as this has already been done for you by the texture. So the setup for this is as follows:

    glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
    glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);

    ...which to me sounds like what I'm after. While I made some attempts to make use of these functions before drawing my images it didn't work, obviously(?) because Processing is doing a lot behind the scenes which prevents jumping back and forth between opengl calls and Processing calls.

    The last thing that I might do, is fetch and read the relevant code of Processing 2.0.2, since it does something wrong, but closer to what I want for some reason, probably due to a bug in blendMode that has since been fixed in 2.0.3 :)

    Also thank you for not telling me to STFU and RTFM but instead giving me a good reply when of course what I've been talking about is not a bug at all, your pedagogic attitude is what makes Processing's forum a good place to be at!

    Best,
    Onar3D
    The blending calculations that the renderer does behind the scenes are quite straightforward, if you look at the blendModeImpl() function in PGraphicsOpenGL all it does is basically to call glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), so you should be able to define your own custom blending mode by using those other gl calls, maybe there is something else you need to do in the shader code? I can look at it in more detail later.