ShaderToy API : access the biggest library of amazing shaders on the planet

Some of the shadertoy shaders seem to use more than one buffer, is it possible to make those ones work within the Codea implementation of shaders?

@piinthesky what do you mean by “more than one buffer”? Could you provide a link to an example?

for example: https://www.shadertoy.com/view/Ms3XWN

there is a buf a, buf b, image, each with their own main()

@piinthesky I’m not a shader pro, but here’s what my brain thinks about this.

Roughly speaking, shaders only act on images - the fragments (pixels) and the vertices (of its mesh). This means that you can make all kind of crazy computations inside the shader code (like in “Buf A” from your example) but sooner or later that code has to affect either a pixel or a vertex (otherwise its worthless).

Buf A: the author seems to handle all the controls here (buttons, map setup, etc) but you also see that the data is stored in fragment color (pixels)

Buf B: handles the object drawings (ghosts, pacman, etc)

Image: combines everything

The result of all shader-steps is always an image. Codea can only run one shader per mesh. This means, that this example code will not run inside Codea without further modifications.

From what I see, I would conclude that you should try use multiple render passes. Meaning, apply the shader code from “Buf A” onto an empty image. Than use that resulting image and apply another shader onto it (code from “Buf B”). Finally take that image and apply shader code from “Image” tab onto it. The final outcome is the combination of all the steps… which is what you want.

I did some more work on this last night, I set up the remainder of the encoder / decoder to save a file to a text document, including all of the JSON which contains all the needed info for setting up multiple render buffers.

@piinthesky the buffers will each be rendered as an image in memory using setContext(bufferName). I have a few rather complex Codea projects going, and in a few of them I have already tested / solved all of the things needed to accomplish this.

Also, check out InteractiveShaderFormat.com which I am working on supporting, it’s rather brilliant way to create complex multi-pass shaders with mapped controls. I have also set up my own means to accomplish all of this using regex to parse uniforms and setup UI objects, with optional short form comments after each uniform to set the range while still retaining full GLSL_ES compatibility.


@se24vad for the most part you are correct, for the sake of clarity, here are a few specifics about the nature of ShaderToy:

shadertoy is based entirely around the fragment (aka pixel) shader, and does not use any vertex shader (there is a cool vertexShaderArt.com for a fully vertex shader based online creative tool)

the wizard - creator of ShaderToy In?igo Quilez goes by the handles “iq” or “rgba”, he explains the concept in his seminal paper “Rendering Worlds with Two Triangles” - http://www.iquilezles.org/www/material/nvscene2008/rwwtt.pdf
( if I recall correctly, one of my rather amazing friends / collaborators -Taylor, used to work with at Pixar and now makes an awesome modular synthesis app for iOS called Audulus 3 )

The general idea being to create a quad (two triangles) that make up the size of the desired render area and apply a fragment shader that does all of the actual drawing/rendering.

This idea historically comes from the “Demo Scene” world.

As ShaderToy grew, they added the ability to load textures, render to offscreen buffers (for complex feedback / layering / post FX options that are extremely difficult if not impossible without)

The 3d render technique commonly used in shaderToy is called “Ray Marching” and is rather mind blowing in its simplicity. If anyone would like, I can make a tutorial or explain further. It is one of the most facinating, powerful, and fun techniques for creating 3d images I have ever seen. If you are eager to learn more, search “Ray Marching Tutorial” or something like that on google / and for that matter there are loads of amazing tutorials right on shadertoy.

@se24vad ok thanks i get it

I’m wondering if you could outline the steps for using this. I’m curious to try it. It does seem rather a lot of work before you see anything though, if I understand correctly.

It seems like the things you have to do are:

  • Get a key to ShaderToy
  • Grab the code at the top of the forum and run it
  • Wait a long time for a file to be created and filled with a bunch of JSon
  • Make a project that uses a shader for something and include ShaderToy in it
  • Always run your project while online because ShaderToy dynamically loads the shaders off the web and you don’t actually get the shaders as files of your own.
  • Now you can experiment with different shaders

I’m almost positive I have a lot of that wrong. If you’d be so kind, can you correct me where I’m wrong?

Almost done with the new version, just need to finish the function for viewing one immediately. Currently have 3 main functions in the API that are:

    ShaderToy:init(" '') -- INSERT YOUR API KEY string, make one by creating an account on shadertoy.com and in your account making a key, copy paste the string into these quotes.
    ShaderToy:saveShader('lsjBWG') -- saves a shaderToy json to a txt file in documents
    ShaderToy:tabShader('XtXyDN')  -- makes a tab with this shader

Here is the current version, the above functions work and I may be able to finish the immediate viewer for it tonight.

ShaderToy = class()

function ShaderToy:init(API_key, SHADER)
    self.decoded = {}
    self.encoded = ''
    self.key = API_key or ''
    
    if SHADER ~= nil and self.key ~= nil then
        self:getShader(SHADER)
    end
    
    --[[
    self.list = readText("Documents:ShaderToyListFull")
    if self.list == nil then
        ShaderToy:getList()
    end
    -- ]]
end


function ShaderToy:setKey(key)
    if key ~= nil and type(key) == string then
        self.key = key
    end
end

--------------- LIST ---------------------

function ShaderToy:getList(search, key)
    http.request('https://www.shadertoy.com/api/v1/shaders?key='..self.key,
    function(data, status, headers)
        ShaderToy:gotList(data, status, headers)
    end )
end

function ShaderToy:gotList(data, status, headers)
    if data ~= nil then
        --print(data)
        --local list = json.decode(data)
        --tablePrint(list)
        saveText("Documents:ShaderToyListFull",data)        
    end
end

function ShaderToy:loadList(file)
    if file ~= nil then
        
    end
end

------------------ SHADER ---------------------

function ShaderToy:getShader(sss)
    if sss ~= nil then
        http.request( "https://www.shadertoy.com/api/v1/shaders/"..sss.."?key="..self.key,
        function(data, status, headers) 
            ShaderToy:decoder(data, status, headers) 
        end )
    else
        print('shader == nil')
    end
end

function ShaderToy:saveShader(sss, destination)
    if sss ~= nil then
        http.request( "https://www.shadertoy.com/api/v1/shaders/"..sss.."?key="..self.key,
        function(data, status, headers) 
            ShaderToy:decoder(data, status, headers)
            ShaderToy:encoder(self.decoded)
            local destFile = destination or "Dropbox:ShaderToyTest"
            saveText(destFile, self.encoded)
        end )
    else
        print('shader == nil')
    end
end

function ShaderToy:tabShader(sss, destination)
    if sss ~= nil then
        http.request( "https://www.shadertoy.com/api/v1/shaders/"..sss.."?key="..self.key,
        function(data, status, headers) 
            ShaderToy:decoder(data, status, headers)
            ShaderToy:encoder(self.decoded)
            local destTab = destination or self.decoded.Shader.info.id or sss or 'tab'
            --tableTab(destTab)
            saveProjectTab(destTab, sss.. '=' .. '[['..self.encoded..']]')
        end )
    else
        print('shader == nil')
    end
end

------------------ JSON ---------------------

function ShaderToy:encoder(t)
    local stateTable = {
        indent = true,
        keyorder = {},
        level = 4,
        --buffer    =   -- array to store strings for the result,
        --bufferlen =   -- index of last element of buffer,
        --exception =   -- function called when encoder can not encode a given value 
                        -- will be given the parameters, reason, value, state, and defaultmessage.
    }
    if t ~= nil then
        self.encoded = json.encode(t, stateTable)
    end
end

function ShaderToy:decoder(data, status, headers)
    if data ~= nil then 
        self.decoded = json.decode(data) 
        tablePrint(self.decoded)
    else -- if data == nil then
        print('data == nil')
        return false
    end
end

-------------------- TABLE ------------------------

function tableTab(t)
    if t ~= nil then
        for k,v in pairs(t) do
            if type(v) == 'table' then
                tableTab(v)
            else
                -- pp = pp..k..":"..v
                print(k..':'..v)
            end
        end
    end
end

function tablePrint(t)
    if t ~= nil then
        for k,v in pairs(t) do
            if type(v) == 'table' then
                tablePrint(v)
            else
                -- pp = pp..k..":"..v
                print(k..':'..v)
            end
        end
    end
end

--------------------------------------------

function ShaderToy:draw()
    -- Codea does not automatically call this method
end

function ShaderToy:touched(touch)
    -- Codea does not automatically call this method
end

--------------------------------------------

function drawList()
    if st~=nil then
        idx = math.floor(math.fmod(ElapsedTime*60, 5000)+2)
        sss = sss..st[idx]
        text(sss, WIDTH/2, HEIGHT/2)
    end
end

function initText()
    textMode(CENTER)
    stroke(100,100,100)
    fill(100,100,100)
    strokeWidth(1)
    fontSize(8)
    textAlign(CENTER)
    textWrapWidth(WIDTH*.5)
    noSmooth()
end

------------------------------------------------------------------------------------------------------------------------------------
--https://raw.githubusercontent.com/beautypi/shadertoy-iOS-v2/master/shadertoy/shaders/fragment_base_uniforms.glsl
fragment_base_uniforms = [[
#version 300 es

precision highp float;
precision highp int;
precision highp sampler2D;

uniform vec3      iResolution;                  // viewport resolution (in pixels)
uniform float     iGlobalTime;                  // shader playback time (in seconds)
uniform vec4      iMouse;                       // mouse pixel coords
uniform vec4      iDate;                        // (year, month, day, time in seconds)
uniform float     iSampleRate;                  // sound sample rate (i.e., 44100)
uniform vec3      iChannelResolution[4];        // channel resolution (in pixels)
uniform float     iChannelTime[4];              // channel playback time (in sec)

uniform vec2      ifFragCoordOffsetUniform;     // used for tiled based hq rendering
uniform float     iTimeDelta;                   // render time (in seconds)
uniform int       iFrame;                       // shader playback frame
]]



--https://raw.githubusercontent.com/beautypi/shadertoy-iOS-v2/master/shadertoy/shaders/fragment_main_sound.glsl
fragment_main_sound = [[
out vec4 glFragColor;

void main()  {
    float t = ifFragCoordOffsetUniform.x + (((iResolution.x-0.5+gl_FragCoord.x)/11025.) + (iResolution.y-.5-gl_FragCoord.y)*(iResolution.x/11025.));
    vec2 y = mainSound( t );
    vec2 v  = floor((0.5+0.5*y)*65536.0);
    vec2 vl = mod(v,256.0)/255.0;
    vec2 vh = floor(v/256.0)/255.0;
    glFragColor = vec4(vl.x,vh.x,vl.y,vh.y);
}
]]

--https://raw.githubusercontent.com/beautypi/shadertoy-iOS-v2/master/shadertoy/shaders/fragment_main_copy.glsl
fragment_main_copy = [[
#version 300 es

precision highp float;
precision highp int;

uniform highp sampler2D sourceTexture;
uniform vec2 sourceResolution;
uniform vec2 targetResolution;

out vec4 glFragColor;

void main()  {
    vec2 fragCoordScaled = gl_FragCoord.xy / targetResolution;
    fragCoordScaled *= targetResolution / sourceResolution;
    
    if( fragCoordScaled.x >= 1. || fragCoordScaled.y >= 1. ) discard;
    
    // gl_FragColor = vec4( fragCoordScaled, 0,1); //
    glFragColor = texture( sourceTexture, fragCoordScaled );
}
]]

--https://raw.githubusercontent.com/beautypi/shadertoy-iOS-v2/master/shadertoy/shaders/vertex_main.glsl
vertex_main = [[
#version 300 es

precision highp float;
precision highp int;

in vec3 position;

void main() {
    gl_Position.xyz = position;
    gl_Position.w = 1.0;
}
]]

--https://raw.githubusercontent.com/beautypi/shadertoy-iOS-v2/master/shadertoy/shaders/fragment_main_image.glsl
fragment_main_image = [[
out vec4 glFragColor;

void main()  {
    mainImage(glFragColor, gl_FragCoord.xy + ifFragCoordOffsetUniform );
}
]]