Search code examples
pythonopenglpyglet

How to achieve a threshold effect in OpenGL without fragment shaders?


I suppose it is possible with blend functions.

Threshold: all pixels with colors under threshold value go to zero; others go to one.

Here's a basic code.

import pyglet
from pyglet.gl import *

# Set the threshold value
threshold = 0.5

# Window creation
window = pyglet.window.Window(500, 500)


@window.event
def on_draw():
    # Draw a square black to white gradient
    pyglet.graphics.draw_indexed(4, pyglet.gl.GL_TRIANGLES, [0, 1, 2, 2, 3, 0],
                                 ('v2i', (100, 100, 100, 400, 400, 400, 400, 100)),
                                 ('c4f', (0, 0, 0, 1) * 2 + (1, 1, 1, 1) * 2))

    # Overlay a small square on top
    glEnable(GL_BLEND)
    glBlendEquation(GL_MAX)
    glBlendFunc(GL_ONE, GL_DST_COLOR)

    pyglet.graphics.draw_indexed(4, pyglet.gl.GL_TRIANGLES, [0, 1, 2, 2, 3, 0],
                                 ('v2i', (200, 200, 200, 300, 300, 300, 300, 200)),
                                 ('c4f', (threshold, threshold, threshold, 1) * 4))


pyglet.app.run()

Desired result: threshold


Solution

  • It's possible, but it's not going to be much fun, and will require a few passes. If you know the underlying colour values at the vertices of the square you are drawing on top, then pump those colour values into the alpha colour channel of the square you are drawing. If you can't determine those colour values, you're out of luck.

    You can then use glAlphaFunc and enable GL_ALPHA_TEST. This can be used to restrict the drawing to alpha values under a given threshold. In of itself, this isn't going to be very useful (since the quad drawn will have interpolated colour values that match the gradient underneath). However, if instead of drawing the geometry, you instead write into the stencil buffer, you will have a stencil ref for one half of the quad. Re-draw the quad (as a black quad), this time setting the stencil test to pass only when the stencil ref is matched. That will have taken care of one half of the quad.

    Now clear the stencil buffer, set the alpha test to draw only when the alpha is over the given threshold, and repeat the above steps for the second half of the quad (the white bit).

    This will be pretty grim, fairly convoluted, but it will work. To do the same thing in a fragment shader would only take about 4 or 5 lines of code though.