How to animate this simple shader

Viewed 1676

I am new to shader. I wrote this extremely simple fragment shader, and it looks like this(with the orange color on the top and red color at the bottom)

What I want to achieve: I want to animate these two colors. I want the top orange color translates to bottom and then go back to top, and the bottom red color translate to top and then go back to bottom. (To simulate the sunset and sunrise effect)

Thanks!

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

vec3 colorA = vec3(0.905,0.045,0.045);
vec3 colorB = vec3(0.995,0.705,0.051);

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;

    vec3 pct = vec3(st.y);

    vec3 color = vec3(0.0);

    color = mix(colorA, colorB, pct);

    gl_FragColor = vec4(color,1);
}

enter image description here

2 Answers

The easiest way to do so is to add an offset to st.y. The offset has to be in the range [-1.0, 1.0]. This can be achieved by sin. The argument to sin has to depend on u_time.
Note, sin calculates the sine function of an angle in radians, so the argument has to be scaled by 2.0 * PI (~ 2.0*3.1415). The result has to be clamped (clamp) to the range [0.0, 1.0].

e.g.:

float interval = 2000.0; // 2000 milliseconds = 2 seconds
float p = clamp(st.y + sin(2.0*3.1415 * u_time / interval), 0.0, 1.0);

vec3 pct = vec3(p); 

Note the example works in case when u_time is a value in milliseconds. If u_time would be a value in seconds, then you've to scale the interval by *1000.0' (e.g 2.0 instead of 2000.0).

var container, camera, scene, renderer, uniforms;

init();
animate();

function init() {
    container = document.getElementById( 'container' );

    camera = new THREE.Camera();
    camera.position.z = 1;

    scene = new THREE.Scene();

    var geometry = new THREE.PlaneBufferGeometry( 2, 2 );

    uniforms = {
        u_time: { type: "f", value: 1.0 },
        u_resolution: { type: "v2", value: new THREE.Vector2() },
        u_mouse: { type: "v2", value: new THREE.Vector2() }
    };

    var material = new THREE.ShaderMaterial( {
        uniforms: uniforms,
        vertexShader: document.getElementById( 'vertexShader' ).textContent,
        fragmentShader: document.getElementById( 'fragmentShader' ).textContent
    } );

    var mesh = new THREE.Mesh( geometry, material );
    scene.add( mesh );

    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio( window.devicePixelRatio );

    container.appendChild( renderer.domElement );

    onWindowResize();
    window.addEventListener( 'resize', onWindowResize, false );

    document.onmousemove = function(e){
    uniforms.u_mouse.value.x = e.pageX
    uniforms.u_mouse.value.y = e.pageY
    }
}

function onWindowResize( event ) {
    renderer.setSize( window.innerWidth, window.innerHeight );
    uniforms.u_resolution.value.x = renderer.domElement.width;
    uniforms.u_resolution.value.y = renderer.domElement.height;
}

function animate(delta_ms) {
    requestAnimationFrame( animate );
    render(delta_ms);
}

function render(delta_ms) {
    uniforms.u_time.value = delta_ms;
    renderer.render( scene, camera );
}
<script id="vertexShader" type="x-shader/x-vertex">
void main() {
    gl_Position = vec4( position, 1.0 );
}
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

vec3 colorA = vec3(0.905,0.045,0.045);
vec3 colorB = vec3(0.995,0.705,0.051);

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;

    float interval = 2000.0; // 2000 milliseconds = 2 seconds
    float p = clamp(st.y + sin(2.0*3.1415 * u_time / interval), 0.0, 1.0);

    vec3 pct = vec3(p);

    vec3 color = vec3(0.0);

    color = mix(colorA, colorB, pct);

    gl_FragColor = vec4(color,1);
}
</script>

<div id="container"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/104/three.min.js"></script>

Something like that, with additional mixing of colours by sin function of time:

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

vec3 colorA = vec3(0.905,0.045,0.045);
vec3 colorB = vec3(0.995,0.705,0.051);

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;

    float sinF = sin(u_time) * 0.5 + 0.5;

    vec3 colorTop = mix(colorA, colorB, sinF);
    vec3 colorBottom = mix(colorB, colorA, sinF);

    vec3 pct = vec3(st.y);

    vec3 color = vec3(0.0);

    color = mix(colorTop, colorBottom, pct);

    gl_FragColor = vec4(color,1);
}
Related