Here’s a short example that shows that illustrates how relatively simple it is to port a GLSL shader to Pixelblaze. Clearly, this isn’t the most complex shader out there, but it demonstrates the basic issues you’ll encounter while porting, and looks about the same on LEDs as it does on the screen.

(Annotated shader code at the top, scroll down to see the equivalent Pixelblaze pattern code!)

Not all shaders are appropriate for LED displays, but shadertoy.com and other shader repositories are great places to look for inspiration and mathematical tools. If you’re interested, I highly recommend The Book of Shaders as a place to start. It’s an excellent interactive tutorial – playing with GLSL for a while will actually help your Pixelblaze thinking too!

```
/************************************************************************
// Annotated GLSL fragment shader code to illustrate porting to Pixelblaze
// original https://www.shadertoy.com/view/3sSSRD
#version 120 // the required opengl version
// uniforms are constants passed into the shader by the calling program
//
uniform float time; // usually in seconds since program start
uniform vec2 mouse; // mouse screen coords
uniform vec2 resolution; // physical resolution of the display
// evaluate the gyroid function at the current point. Returns a vector
// containing the "brightness" at this point
//
vec3 cgyroid(vec3 p) {
float g = (cos(p.x) * sin(p.y) + cos(p.y) * sin(p.z) + cos(p.z) * sin(p.x));
return vec3(g);
}
// main -- this is equivalent to render2d() on the Pixelblaze
//
void main(void) {
// pixel coords are passed in screen units. Here, we normalize them
// to the 0-1 range, and translate them so the origin is at the center of
// the screen. It is also common in shaders to correct for screen aspect
// ratio since basically nobody has a square screen. Pixelblaze's mapper does
// this for us
//
vec2 uv = 2.0*gl_FragCoord.xy/resolution.xy;
float aspect = resolution.x/resolution.y;
uv.x *= aspect;
vec2 dc = uv -.5;
// scale up so the gyroid function is a little smaller on the display.
// this is arbitrary and should be changed to taste and display.
//
dc *= 8.0;
// process current point, adding color values which are functions
// of time, so we get an interesting animation.
//
vec3 col = cgyroid(vec3(dc.x,dc.y,cos(time)*5.0));
col += vec3(sin(time), sin(time * 0.5), sin(time*0.15));
// whatever RGBA color you assign to gl_FragColor at this pixel is what gets
// displayed on the screen.
//
gl_FragColor = vec4(col,1.0);
}
************************************************************************/
// Begin Pixelblaze pattern!
// preset translation and scaling. Shaders have to do this for every pixel, but
// fortunately on Pixelblaze, we can do it once at pattern startup. Note that I'm
// using a different scale value than the shader.
translate(-0.5,-0.5);
scale(12,12);
// evaluate the gyroid function at the current point. Since it's hard
// for us to return vectors, and we don't actually need to in this case, we
// just return a single value for "brightness" at this pixel.
function cgyroid(x,y,t) {
return cos(x) * sin(y) + cos(y) * sin(t) + cos(t) * sin(x);
}
// create our time base by accumulating delta, then control speed with a
// multiplier. (Be careful about overflow here)
//
// Also, precompute trig results that are the same for every pixel. Shaders
// can't do this - every pixel is completely independent. But on the Pixelblaze, we can
// (and should) move as much repetitive math out of the render() function as
// possible for better performance.
export var t = 0
export function beforeRender(delta) {
t = (t + delta/1000) % 1000;
t1 = t * 5; // speed control
s1 = sin(t1);
s2 = sin(t1 + 0.5);
s3 = sin(t1 * 0.15);
c1 = cos(t1);
}
// render current pixel, generating our gyroid value, then
// adding in the time dependent color values that we generated
// in beforeRender(). Done!
export function render2D(index,x,y) {
var v = cgyroid(x,y,c1);
r = v + s1;
g = v + s2;
b = v + s3;
rgb(r,g,b)
}
```