Realtime 2D radiosity shader implementation

Hi all,

I was wondering if some of you could help me make this 2D radiosity shader work with Processing.

By “work”, I mean being able:

  • to apply the shader to basic shape primitives (rect, ellipse, triangle, arc) drawn in Processing
  • to adjust the radiosity level of each shape directly from the Processing script

The goal is to have something similar looking to the beautiful (and unfortunately not available) global illumination render showed in Thomas Diewald latest demos.

So far I’ve made minor changes to the original shader so it can be compiled by Processing

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

uniform float iTime;
uniform vec2 resolution;


float sphereSDF(vec2 p, float size) {
	return length(p) - size;
}

float boxSDF(vec2 p, vec2 size) {
	vec2 r = abs(p) - size;
    return min(max(r.x, r.y),0.) + length(max(r,vec2(0,0)));
}

vec3 colormap(float x) {
    float s = sin(x*6.28);
    if (x > 0.) {
    	return vec3(1,1,1.+s)/2.;
    } else {
        return vec3(1,1.+s,1)/2.;
    }
}

void AddObj(inout float dist, inout vec3 color, float d, vec3 c) {
    if (dist > d) {
        dist = d;
        color = c;
    }
}

void scene(in vec2 pos, out vec3 color, out float dist) {
    dist = 1e9; color = vec3(0,0,0);
    AddObj(dist, color, boxSDF(pos - vec2(-4,1), vec2(1,1)), vec3(.6,.8,1.));
    AddObj(dist, color, sphereSDF(pos - vec2(4,1), 1.), vec3(1,.9,.8));
    AddObj(dist, color, boxSDF(pos - vec2(0,3.0*sin(iTime)), vec2(1.5 ,0.5 + cos(iTime)*.3)), vec3(.4,.1,.1));
}

void trace(vec2 p, vec2 dir, out vec3 c) {
    for (;;) {
        float d;
        scene(p, c, d);
        if (d < 1e-3) return;
        if (d > 1e1) break;
        p -= dir * d;
    }
    c = vec3(0,0,0);
}

float random (in vec2 _st) {
    return fract(sin(dot(_st.xy,
        vec2(12.9898,78.233)))*
        43758.5453123);
}

#define SAMPLES 128

void main()
{
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = (gl_FragCoord.xy-(resolution.xy/2.f))/resolution.y*10.f;
    vec3 col = vec3(0,0,0);
    for (int i = 0; i < SAMPLES; i++) {
        float t = (float(i) + random(uv+float(i)+iTime)) / float(SAMPLES) * 2. * 3.1415;
        vec3 c;
        trace(uv, vec2(cos(t), sin(t)), c);
        col += c;
    }
    col /= float(SAMPLES);

    // Output to screen
    gl_FragColor = vec4(col*2.0,3.0);
}

Here all 3 shapes are coded in the shader. To draw the same shapes from Processing (and have the radiosity effect working) I guess I would need a vertex shader:

  • Am I right ?
  • What should that vertex shader look like ?

Thank you

2 Likes

I recall that Thomas Diewald did a bunch of work related to this in his PixelFlow library. Scroll down to the github repo homepage section Realtime 2D Radiosity - Global Illumination (GI) to see examples – many are also clickable videos.

2 Likes

Sorry for the late reply.

As i said, it seems there’s no working example available, only demo videos.

Ah, sorry, missed that mention and link in your original post. Sorry I don’t have an answer re: vertex shaders. Maybe @noahbuddy has an idea? – maybe or ping @nabr on the old forum.

I have been trying to find an alternative to hard-coded distance fields myself.
Unfortunately I do not have any tricks to get around it.

You may be able to load data into a texture.
Since this is for 2D, perhaps a flat rendering of where the objects are would be enough for the shader to check, although random texture access may add a bottleneck.

Inigo Quilez has a short writeup on combining rasterized data and raytracing/marching.

Rather than hard-coding, you could fill in a string array and set the shader from there.
A working example:

// \n characters are important for lines like:
//      #version, #define, etc.
// Other lines do not need them

String[] firstPart = {"#version 150 \n",
"#ifdef GL_ES                                                      \n",
"precision mediump float;                                          \n",
"precision mediump int;                                            \n",
"#endif                                                            \n",
"                                                                  \n",
"uniform float iTime;                                              \n",
"uniform vec2 resolution;                                          \n",
"                                                                  \n",
"float sphereSDF(vec2 p, float size) {                             \n",
"  return length(p) - size;                                        \n",
"}                                                                 \n",
"                                                                  \n",
"float boxSDF(vec2 p, vec2 size) {                                 \n",
"  vec2 r = abs(p) - size;                                         \n",
"    return min(max(r.x, r.y),0.) + length(max(r,vec2(0,0)));      \n",
"}                                                                 \n",
"                                                                  \n",
"vec3 colormap(float x) {                                          \n",
"    float s = sin(x*6.28);                                        \n",
"    if (x > 0.) {                                                 \n",
"      return vec3(1,1,1.+s)/2.;                                   \n",
"    } else {                                                      \n",
"        return vec3(1,1.+s,1)/2.;                                 \n",
"    }                                                             \n",
"}                                                                 \n",
"                                                                  \n",
"void AddObj(inout float dist, inout vec3 color, float d, vec3 c) {\n",
"    if (dist > d) {                                               \n",
"        dist = d;                                                 \n",
"        color = c;                                                \n",
"    }                                                             \n",
"}                                                                 \n",
};

String[] defaultScene = {
"void scene(in vec2 pos, out vec3 color, out float dist) {                                                     \n",
"    dist = 1e9; color = vec3(0,0,0);                                                                          \n",
"    AddObj(dist, color, boxSDF(pos - vec2(-4,1), vec2(1,1)), vec3(.6,.8,1.));                                 \n",
"    AddObj(dist, color, sphereSDF(pos - vec2(4,1), 1.), vec3(1,.9,.8));                                       \n",
"    AddObj(dist, color, boxSDF(pos - vec2(0,3.0*sin(iTime)), vec2(1.5 ,0.5 + cos(iTime)*.3)), vec3(.4,.1,.1));\n",
"}                                                                                                             \n"
};

String[] lastPart = {
"void trace(vec2 p, vec2 dir, out vec3 c) {                                              \n",
"    for (;;) {                                                                          \n",
"        float d;                                                                        \n",
"        scene(p, c, d);                                                                 \n",
"        if (d < 1e-3) return;                                                           \n",
"        if (d > 1e1) break;                                                             \n",
"        p -= dir * d;                                                                   \n",
"    }                                                                                   \n",
"    c = vec3(0,0,0);                                                                    \n",
"}                                                                                       \n",
"                                                                                        \n",
"float random (in vec2 _st) {                                                            \n",
"    return fract(sin(dot(_st.xy,                                                        \n",
"        vec2(12.9898,78.233)))*                                                         \n",
"        43758.5453123);                                                                 \n",
"}                                                                                       \n",
"                                                                                        \n",
"#define SAMPLES 128                                                                     \n",
"                                                                                        \n",
"void main()                                                                             \n",
"{                                                                                       \n",
"    // Normalized pixel coordinates (from 0 to 1)                                       \n",
"    vec2 uv = (gl_FragCoord.xy-(resolution.xy/2.f))/resolution.y*10.f;                  \n",
"    vec3 col = vec3(0,0,0);                                                             \n",
"    for (int i = 0; i < SAMPLES; i++) {                                                 \n",
"        float t = (float(i) + random(uv+float(i)+iTime)) / float(SAMPLES) * 2. * 3.1415;\n",
"        vec3 c;                                                                         \n",
"        trace(uv, vec2(cos(t), sin(t)), c);                                             \n",
"        col += c;                                                                       \n",
"    }                                                                                   \n",
"    col /= float(SAMPLES);                                                              \n",
"                                                                                        \n",
"    // Output to screen                                                                 \n",
"    gl_FragColor = vec4(col*2.0,3.0);                                                   \n",
"}                                                                                       \n"
};

String[] vert = new String[]
{
  "#version 150 \n",
  "in vec2 position;\n",
  "void main() {\n",
    "gl_Position = vec4(position,0.,1.);\n",
  "}"
};

String[] nonMovingScene = {
"void scene(in vec2 pos, out vec3 color, out float dist) {                                 \n",
"    dist = 1e9; color = vec3(0,0,0);                                                      \n",
"    AddObj(dist, color, boxSDF(pos - vec2(4,1), vec2(1,1)), vec3(.6,.8,1.));              \n",
"    AddObj(dist, color, sphereSDF(pos - vec2(-4,1), 1.), vec3(1.,1.,0.));                 \n",
"    AddObj(dist, color, sphereSDF(pos - vec2(-4,2), 1.), vec3(.1,.1,0.5 + cos(iTime)*.4));\n",
"    AddObj(dist, color, boxSDF(pos - vec2(0,3.0), vec2(1.5, 1.0)), vec3(.1,.7,.1));       \n",
"}                                                                                         \n"
};

String[] squareScene = {
"void scene(in vec2 pos, out vec3 color, out float dist) {                          \n",
"    dist = 1e9; color = vec3(0,0,0);                                               \n",
"    AddObj(dist, color, boxSDF(pos - vec2(0,3.0), vec2(1.0, 1.0)), vec3(.8,.1,.9));\n",
"}                                                                                  \n"
};

PShader shdr;
float t = 0;
float dt = 0.05;

PShader constructShader(String[] scene)
{
  String[] shadSource = (String[])concat((String[])concat(firstPart,scene), lastPart);
  return new PShader(this, vert, shadSource);
}

void mouseClicked()
{
  resetShader(); // will work without calling but I wonder about memory leaks
  // build scene, could populate a String array based on a series of shapes
  float test = random(1);
  if (test < (1.0/3))
  {
    shdr = constructShader(defaultScene);
  }
  else if (test > (2.0/3))
  {
    shdr = constructShader(nonMovingScene);
  }
  else
  {
    shdr = constructShader(squareScene);
  }
  // just rebuilt shader so set necessary variables
  shdr.set("resolution", float(width), float(height));
  shader(shdr);
}

void setup() {
  size(640, 480, P2D);
  rectMode(RADIUS); // fixes drawing area. could instead translate by -width/2,-height/2
  shdr = constructShader(defaultScene);
  shader(shdr);
}

void draw()
{
  shdr.set("iTime", t);
  t += dt;
  rect(0, 0, width, height);
}

I figured out the live-shader stuff. I was not setting things up correctly after modifying the shader.
While this still does not solve being able to simply “draw a rectangle”, it does allow changing the scene at run-time.

2 Likes