From Shadertoy to OPENRNDR

This is a simple example porting one program from Shadertoy. Keep in mind that it’s not a generic tutorial for porting every possible program from Shadertoy.

Shadertoy

vec2 shuffle(vec2 xy, vec2 p) {
    vec2 i = floor(fract(xy) * p) / p;
    vec2 f = fract(xy * p) / p;   
    i.x += step(1.-i.x, .5) / (p.x * 2.);    
    i.y += step(1.-i.y, .5) / (p.y * 2.);    
    return fract(i * 2. + f);
}
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {   
    vec2 uv = fragCoord/iResolution.xy;   
    uv *= .5; // scaling
    uv = shuffle(uv, vec2(30., 1.));
    uv = shuffle(uv, vec2(1., 16.));
    fragColor = texture(iChannel0, uv);
}

It is a shader inspired by this video posted by @rein in Slack. I wrote it quickly and it’s not specially good :see_no_evil: (as it doesn’t deal with aspect ratio nor it’s easily configurable). But I hope it can still serve as an example of porting a shader to OPENRNDR.

OPENRNDR

Imports
import org.openrndr.application
import org.openrndr.draw.loadImage
import org.openrndr.draw.shadeStyle
import org.openrndr.extensions.Screenshots
fun main() = application {
    configure {
        width = 1024
        height = 1024
    }

    program {
        // https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Kissing_Prairie_dog_edit_3.jpg/1024px-Kissing_Prairie_dog_edit_3.jpg
        val img = loadImage("data/images/1024px-Kissing_Prairie_dog_edit_3.jpg")

        extend {
            drawer.stroke = null
            drawer.shadeStyle = shadeStyle {
                fragmentPreamble = """
                    vec2 shuffle(vec2 xy, vec2 p) {
                        vec2 i = floor(fract(xy) * p) / p;
                        vec2 f = fract(xy * p) / p;   
                        i.x += step(1.-i.x, .5) / (p.x * 2.);    
                        i.y += step(1.-i.y, .5) / (p.y * 2.);    
                        return fract(i * 2. + f);
                    }
                """.trimIndent()
                fragmentTransform = """
                    vec2 uv = c_boundsPosition.xy;
                    uv.y = 1.-uv.y; // vert flip
                    uv = shuffle(uv, vec2(30., 1.));
                    uv = shuffle(uv, vec2(1., 30.));
                    x_fill = texture(p_img, uv);
                """.trimIndent()
                parameter("img", img)
            }
            drawer.rectangle(drawer.bounds)
        }
    }
}

It’s a very simple program that loads an image, defines a shade style and draws a full screen rectangle using that style.

The .trimIndent() is optional but added automatically by the IDE. It removes indentation white space from strings.

The fragmentTransform part is what you need to create a typical shadeStyle. That parts ends up inside the main() function in the GLSL shader. In this case I also want to define my own functions (the one called shuffle) therefore I use the fragmentPreamble to inject them above the main() function.

concept shadertoy OPENRNDR
normalized position fragCoord/iResolution.xy c_boundsPosition.xy
image uniform iChannel_03 we name it. in this case we choose img and it becomes uniform p_img inside shader code
uv vertical flip not needed uv.y = 1.-uv.y
uv scaling uv *= 0.5 I was happy with the scale
output variable fragColor x_fill. This is something special in OPENRNDR, because by default it contains the requested fill color. I overwrite it by using texture() to sample pixels from the provided image. But instead of overwriting I could do x_fill.rgb *= 1.2; to make the requested color brighter for all pixels, x_fill.rgb *= 0.8; to make them darker or x_fill.rgb *= 1.0 + 0.1 * sin(c_boundsPosition.x); to create a horizontal wavy gradient based on the requested fill color.

Don’t worry if you don’t understand shaders yet, my goal here was only to show similarities and differences between shadertoy code and a similar program written in OPENRNDR.

You can read more about shader uniforms and attributes in the OPENRNDR Guide.

4 Likes

This is a great example! I think the shader section of the guide could be improved with more details. For example, c_boundsPosition is not mentioned in the table of constants (only the examples).

:slight_smile:

You are right about the guide, there’s an issue open for that.

I use shadertoy to quickly sketch some shader-code and move to openrndr for compositing. Following code accepts one-file shadertoy shaders using iTime and iResolution. However there is an option to pass any additional required uniforms like iMouse at render time.

2 Likes

Nice! Thanks for sharing the code :slight_smile: