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 (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_0 … 3
|
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.