ISF Shader with Smooth Speed

After very deep conversation with @ProjectileObjects I was able to figure out how to get smooth speed. Here is the first shader. I also want to mention that Shaders with Persistent buffers will not render properly in https://isf.video :frowning:

/*{
    "CATEGORIES": [
        "Generator",
        "Animation"
    ],
    "CREDIT": "Converted from Shadertoy by Igor Molochevski with added controls, Smooth speed sollution based on ProjectileObjects advise",
    "DESCRIPTION": "Please use DekstopApp, Web ISF App cant render thisFractal Torus with Neon Colors and Rotation Controls",
    "INPUTS": [
        {
            "DEFAULT": [
                1,
                0,
                0,
                1
            ],
            "NAME": "color1",
            "TYPE": "color"
        },
        {
            "DEFAULT": [
                0,
                1,
                0,
                1
            ],
            "NAME": "color2",
            "TYPE": "color"
        },
        {
            "DEFAULT": 0.5,
            "MAX": 3,
            "MIN": -3,
            "NAME": "rotateSpeedX",
            "TYPE": "float"
        },
        {
            "DEFAULT": 0.3,
            "MAX": 3,
            "MIN": -3,
            "NAME": "rotateSpeedY",
            "TYPE": "float"
        },
        {
            "DEFAULT": 0.4,
            "MAX": 3,
            "MIN": -3,
            "NAME": "rotateSpeedZ",
            "TYPE": "float"
        },
        {
            "DEFAULT": 1,
            "LABEL": "Animation Speed",
            "MAX": 10,
            "MIN": 0,
            "NAME": "speedControl",
            "TYPE": "float"
        }
    ],
    "ISFVSN": "2",
    "PASSES": [
        {
            "FLOAT": true,
            "HEIGHT": 1,
            "PERSISTENT": true,
            "TARGET": "timeBuffer",
            "WIDTH": 1
        },
        {
            "FLOAT": true,
            "HEIGHT": 1,
            "PERSISTENT": true,
            "TARGET": "rotateXBuffer",
            "WIDTH": 1
        },
        {
            "FLOAT": true,
            "HEIGHT": 1,
            "PERSISTENT": true,
            "TARGET": "rotateYBuffer",
            "WIDTH": 1
        },
        {
            "FLOAT": true,
            "HEIGHT": 1,
            "PERSISTENT": true,
            "TARGET": "rotateZBuffer",
            "WIDTH": 1
        },
        {
            "TARGET": "finalOutput"
        }
    ]
}
*/

#define PI 3.14159265359

mat2 rot(float a) {
    float s = sin(a), c = cos(a);
    return mat2(c, s, -s, c);
}

float sdTorus(vec3 p, vec2 t) {
    vec2 q = vec2(length(p.xz) - t.x, p.y);
    return length(q) - t.y;
}

float de(vec3 p, float effectiveTime, float effectiveRotX, float effectiveRotY, float effectiveRotZ) {
    float t = -sdTorus(p, vec2(2.3, 2.));
    p.y += 1.;
    float d = 100., s = 2.;
    p *= 0.5;
    
    // Modified rotation with XYZ controls using effective rotation values
    for(int i = 0; i < 6; i++) {
        p.xz *= rot(effectiveRotX);
        p.xy *= rot(effectiveRotY);
        p.yz *= rot(effectiveRotZ);
        p.xz = abs(p.xz);
        float inv = 1.0 / clamp(dot(p, p), 0.0, 1.0);
        p = p * inv - 1.0;
        s *= inv;
        d = min(d, length(p.xz) + fract(p.y * 0.05 + effectiveTime * 0.2) - 0.1);
    }
    return min(d / s, t);
}

float march(vec3 from, vec3 dir, float effectiveTime, float effectiveRotX, float effectiveRotY, float effectiveRotZ) {
    float td = 0.0, g = 0.0;
    vec3 p;
    for(int i = 0; i < 100; i++) {
        p = from + dir * td;
        float d = de(p, effectiveTime, effectiveRotX, effectiveRotY, effectiveRotZ);
        if(d < 0.002) break;
        g++;
        td += d;
    }
    float glow = exp(-0.07 * td * td) * sin(p.y * 10.0 + effectiveTime * 10.0);
    float pattern = smoothstep(0.3, 0.0, abs(0.5 - fract(p.y * 15.0)));
    return mix(pattern * glow, g * g * 0.00008, 0.3);
}

void main() {
    vec4 fragColor = vec4(0.0);
    
    if (PASSINDEX == 0) {
        // First pass: Update the accumulated time in the persistent buffer
        vec4 prevData = texture2D(timeBuffer, vec2(0.5, 0.5));
        
        // Extract previous accumulated time
        float accumulatedTime = prevData.r;
        
        // Calculate new accumulated time
        float newTime;
        
        if (FRAMEINDEX == 0) {
            // Initialize time on first frame
            newTime = 0.0;
        } else {
            // Accumulate time based on speed and frame delta
            newTime = accumulatedTime + speedControl * TIMEDELTA;
        }
        
        // Store the accumulated time
        fragColor = vec4(newTime, 0.0, 0.0, 1.0);
    }
    else if (PASSINDEX == 1) {
        // Second pass: Update the accumulated X rotation
        vec4 prevData = texture2D(rotateXBuffer, vec2(0.5, 0.5));
        vec4 timeData = texture2D(timeBuffer, vec2(0.5, 0.5));
        
        // Extract previous accumulated rotation
        float accumulatedRot = prevData.r;
        float effectiveTime = timeData.r;
        
        // Calculate new accumulated rotation
        float newRot;
        
        if (FRAMEINDEX == 0) {
            // Initialize on first frame
            newRot = 0.0;
        } else {
            // Accumulate rotation based on speed and frame delta
            newRot = accumulatedRot + rotateSpeedX * TIMEDELTA * speedControl;
        }
        
        // Store the accumulated rotation
        fragColor = vec4(newRot, 0.0, 0.0, 1.0);
    }
    else if (PASSINDEX == 2) {
        // Third pass: Update the accumulated Y rotation
        vec4 prevData = texture2D(rotateYBuffer, vec2(0.5, 0.5));
        vec4 timeData = texture2D(timeBuffer, vec2(0.5, 0.5));
        
        // Extract previous accumulated rotation
        float accumulatedRot = prevData.r;
        float effectiveTime = timeData.r;
        
        // Calculate new accumulated rotation
        float newRot;
        
        if (FRAMEINDEX == 0) {
            // Initialize on first frame
            newRot = 0.0;
        } else {
            // Accumulate rotation based on speed and frame delta
            newRot = accumulatedRot + rotateSpeedY * TIMEDELTA * speedControl;
        }
        
        // Store the accumulated rotation
        fragColor = vec4(newRot, 0.0, 0.0, 1.0);
    }
    else if (PASSINDEX == 3) {
        // Fourth pass: Update the accumulated Z rotation
        vec4 prevData = texture2D(rotateZBuffer, vec2(0.5, 0.5));
        vec4 timeData = texture2D(timeBuffer, vec2(0.5, 0.5));
        
        // Extract previous accumulated rotation
        float accumulatedRot = prevData.r;
        float effectiveTime = timeData.r;
        
        // Calculate new accumulated rotation
        float newRot;
        
        if (FRAMEINDEX == 0) {
            // Initialize on first frame
            newRot = 0.0;
        } else {
            // Accumulate rotation based on speed and frame delta
            newRot = accumulatedRot + rotateSpeedZ * TIMEDELTA * speedControl;
        }
        
        // Store the accumulated rotation
        fragColor = vec4(newRot, 0.0, 0.0, 1.0);
    }
    else if (PASSINDEX == 4) {
        // Final pass: Render the fractal using the accumulated values
        vec4 timeData = texture2D(timeBuffer, vec2(0.5, 0.5));
        vec4 rotXData = texture2D(rotateXBuffer, vec2(0.5, 0.5));
        vec4 rotYData = texture2D(rotateYBuffer, vec2(0.5, 0.5));
        vec4 rotZData = texture2D(rotateZBuffer, vec2(0.5, 0.5));
        
        // Get the accumulated values
        float effectiveTime = timeData.r;
        float effectiveRotX = rotXData.r;
        float effectiveRotY = rotYData.r;
        float effectiveRotZ = rotZData.r;

        vec2 uv = (gl_FragCoord.xy / RENDERSIZE.xy) - 0.5;
        uv.x *= RENDERSIZE.x / RENDERSIZE.y;
        
        float t = effectiveTime * 0.5;
        vec3 from = vec3(cos(t), 0.0, -3.3);
        vec3 dir = normalize(vec3(uv, 0.7));
        
        // Apply rotation controls to camera using effectiveRotX
        dir.xy *= rot(0.5 * sin(effectiveRotX));
        
        float intensity = march(from, dir, effectiveTime, effectiveRotX, effectiveRotY, effectiveRotZ);
        
        // Color blending with neon effect - properly using color2
        vec3 finalColor = mix(
            color1.rgb * intensity * 2.0,
            color2.rgb * (1.0 - intensity) * 1.5,
            smoothstep(0.2, 0.8, intensity)
        );
        
        // Add glow based on color2
        finalColor += color2.rgb * 0.5 * pow(intensity, 3.0);
        
        fragColor = vec4(finalColor, 1.0);
    }
    
    // Output the final color
    gl_FragColor = fragColor;
}

There are definitely some limitations with ISF shaders on the web. Additionally, if someone needs OpenGL 4 they will want to enable it in the ISF Editor Preferences. So you may want to toggle that before you consider your code a failure.

1 Like