Search code examples
glslshaderglsles

Problem going from Shadershop functions to glsl functions


It's a bit related to how to convert shadershop formula into glsl . Only the above answer does not provide any explanation.

What I try is:

enter image description here

Where SineV is:

enter image description here

and SineH is:

enter image description here

This is what I have so far:

#ifdef GL_ES
precision mediump float;
#endif

float scale = 5.0;


uniform vec2 u_resolution;

float sineV(float x) {
    x *= scale;
    return  (sin( x / 0.18 ) + sin( (x - -1.0) / 0.35 ) + 0.25);
}

float sineH(float x) {
    x *= scale;
    return (sin( (x - 0.18) / 0.37 ) + sin( ((x - 0.18) - -0.31) / 0.45 ) + -0.59) * 0.75 + 0.1;
}

mat2 inverse(mat2 m) {
    float det_m = m[0][0]*m[1][1] - m[0][1]*m[1][0];
    mat2  inv_m = mat2(m[1][1], -m[0][1], -m[1][0], m[0][0]) / det_m;
    return inv_m;
}



void main() {

    mat2 m = mat2(0.0, -1.0,
                  1.0, 0.0);

    vec2 st = gl_FragCoord.xy / u_resolution.xy;
    float x1 = st.x;
    float x2 = st.y;

    vec2  x1x2 = inverse(m) * vec2(x1, x2);

    vec2 tmp = m * x1x2;

    float x = sineV(x1x2.x - x1x2.y) + sineH(tmp.x - tmp.y);

    vec3 color = vec3( x, x, abs(x) );
    gl_FragColor = vec4(color, 1.0);
}

But I reached a point where it is just guessing and trial and error. Hope someone can help.


Solution

  • The matrix in shadershop transforms the input tuple (x1, x2) to (u, v) coordinates. The fragment shader is executed for each fragment, each fragment is associated to different (u, v) coordinates. You've to calculate the x1 and x2 corresponding to the actual (u, v) coordinate of the fragment. So you've to use the inverse matrix:

    (u, v)  = m * (x1, x2)
    (x1, x2 = inverse(m) * (u, v)
    

    The shader has to sum up result of the f(x1) and the result of the f(x2):

    x = f(x1) + f(x2)
    

    The corresponding glsl code is:

    vec2  x1x2 = inverse(m) * st.xy;
    float x    = sineH(x1x2.x) + sineV(x1x2.y);
    

    The mapping of x to the white, blue and black color can be achieved by the empirical formula (I found this out by trial and error):

    vec3 color = vec3(x, x, abs(x));
    

    For the full shader code see the example (note, the result is stretched to the canvas):

    (function loadscene() {    
    
    var canvas, gl, vp_size, prog, bufObj = {};
    
    function initScene() {
    
        canvas = document.getElementById( "ogl-canvas");
        gl = canvas.getContext( "experimental-webgl" );
        if ( !gl )
          return;
    
        progDraw = gl.createProgram();
        for (let i = 0; i < 2; ++i) {
            let source = document.getElementById(i==0 ? "draw-shader-vs" : "draw-shader-fs").text;
            let shaderObj = gl.createShader(i==0 ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER);
            gl.shaderSource(shaderObj, source);
            gl.compileShader(shaderObj);
            let status = gl.getShaderParameter(shaderObj, gl.COMPILE_STATUS);
            if (!status) alert(gl.getShaderInfoLog(shaderObj));
            gl.attachShader(progDraw, shaderObj);
            gl.linkProgram(progDraw);
        }
        status = gl.getProgramParameter(progDraw, gl.LINK_STATUS);
        if ( !status ) alert(gl.getProgramInfoLog(progDraw));
        progDraw.inPos = gl.getAttribLocation(progDraw, "inPos");
        progDraw.u_resolution = gl.getUniformLocation(progDraw, "u_resolution");
        gl.useProgram(progDraw);
    
        var pos = [ -1, -1, 1, -1, 1, 1, -1, 1 ];
        var inx = [ 0, 1, 2, 0, 2, 3 ];
        bufObj.pos = gl.createBuffer();
        gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
        gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( pos ), gl.STATIC_DRAW );
        bufObj.inx = gl.createBuffer();
        bufObj.inx.len = inx.length;
        gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
        gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( inx ), gl.STATIC_DRAW );
        gl.enableVertexAttribArray( progDraw.inPos );
        gl.vertexAttribPointer( progDraw.inPos, 2, gl.FLOAT, false, 0, 0 ); 
        
        gl.enable( gl.DEPTH_TEST );
        gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
    
        window.onresize = resize;
        resize();
        requestAnimationFrame(render);
    }
    
    function resize() {
        vp_size = [window.innerWidth, window.innerHeight];
        //vp_size = [256, 256]
        canvas.width = vp_size[0];
        canvas.height = vp_size[1];
    }
    
    function render(deltaMS) {
    
        gl.viewport( 0, 0, canvas.width, canvas.height );
        gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
       
        gl.uniform2f(progDraw.u_resolution, canvas.width, canvas.height);
        gl.drawElements( gl.TRIANGLES, bufObj.inx.len, gl.UNSIGNED_SHORT, 0 );
        
        requestAnimationFrame(render);
    }  
    
    initScene();
    
    })();
    <script id="draw-shader-vs" type="x-shader/x-vertex">
    precision mediump float;
    
    attribute vec2 inPos;
    
    varying vec2 ndcPos;
    
    void main()
    {
        gl_Position = vec4( inPos.xy, 0.0, 1.0 );
    }
    </script>
    
    <script id="draw-shader-fs" type="x-shader/x-fragment">
    precision mediump float;
    
    uniform vec2  u_resolution;
    
    float scale = 5.0;
    
    float sineV(float x) {
        x *= scale;
        return  (sin( x / 0.18 ) + sin( (x - -1.0) / 0.35 ) + 0.25);
    }
    
    float sineH(float x) {
        x *= scale;
        return (sin( (x - 0.18) / 0.37 ) + sin( ((x - 0.18) - -0.31) / 0.45 ) + -0.59) * 0.75 + 0.1;
    }
    
    mat2 inverse(mat2 m) {
        float det_m = m[0][0]*m[1][1] - m[0][1]*m[1][0];
        mat2  inv_m = mat2(m[1][1], -m[0][1], -m[1][0], m[0][0]) / det_m;
        return inv_m;
    }
    
    void main()
    {
        vec2  st     = 2.0 * gl_FragCoord.xy / u_resolution.xy - 1.0;
        mat2  m      = mat2(0.0, -1.0, 1.0, 0.0);
        vec2  x1x2   = inverse(m) * st.xy;
        float x      = sineH(x1x2.x) + sineV(x1x2.y);
    
        vec3 color   = vec3(x, x, abs(x));
        gl_FragColor = vec4(color, 1.0);
    }
    </script>
    
    <canvas id="ogl-canvas" style="border: none"></canvas>