Search code examples
javascriptshaderwebglp5.jsfragment-shader

Determining the center of the screen in a WEBGL fragment shader


I started learning WebGL a couple of weeks ago and as I am trying to learn by practice, I stumbled upon a simple example of a shader that I could implement using p5.js.

In this example, I am creating concentric circles starting from the center of the screen, using this fragment, where u_resolution and u_time are uniforms passed down from p5 script as [windowWidth, windowHeight] and respectively frameCount as below:

void main(void) {
    float maxAxis = max(u_resolution.x, u_resolution.y);
    vec2 uv = gl_FragCoord.xy / maxAxis;
    vec2 center = u_resolution / maxAxis;
    
    gl_FragColor = vec4(
        vec3(sin(u_time + distance(uv, center) * 255.0)),
        1.0);
    }

Using this example, I can achieve what I want, but I cannot understand why I cannot calculate the center of the fragment using the formula:

vec2 center = vec2(u_resolution.x * 0.5, u_resolution.y * 0.5);

If I do this, then it will mess up the whole rendering.

Is there a coordinate system mismatch that I am missing, or something else?

For a better explanation, I included a snippet of the original experiment that I am doing in CodePen right here.


Solution

  • uv and center are in range [0.0, 1.0]. There for the center of the viewport is:

    vec2 center = vec2(u_resolution.x * 0.5, u_resolution.y * 0.5);

    vec2 center = 0.5 * u_resolution / maxAxis;
    

    let myShaderIn, myShaderOut;
    let isPlaying = true;
    
    const vertexShader = document.getElementById("vert-shader").textContent;
    const fragmentShaderStyleIn = document.getElementById("frag-shader-style-in")
        .textContent;
    
    function setup() {
        const canvas = createCanvas(windowWidth, windowHeight, WEBGL);
        // canvas.mousePressed(toggleSound);
        rectMode(CENTER);
    
        // shaders
        myShaderIn = createShader(vertexShader, fragmentShaderStyleIn);
    
        // register shaders
        shader(myShaderIn);
    
        // shapes setup
        noStroke();
    }
    
    function draw() {
        background(0);
        drawEllipse();
    }
    
    function drawEllipse() {
        myShaderIn.setUniform("u_resolution", [float(width), float(height)]);
        myShaderIn.setUniform("u_time", float(frameCount));
    
        shader(myShaderIn);
        
        ellipse(0, 0, width/2);
    }
    
    function windowResized() {
        resizeCanvas(windowWidth, windowHeight);
        clear();
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
    
    <!-- vertex shader -->
    <script type="x-shader/x-vertex" id="vert-shader">
        #ifdef GL_FRAGMENT_PRECISION_HIGH
            precision highp float;
        #else
            precision mediump float;
        #endif
        
        attribute vec3 aPosition;
        
        uniform mat4 uProjectionMatrix;
        uniform mat4 uModelViewMatrix;
    
        void main() {
            vec4 newPosition = vec4(aPosition, 1.0);
            gl_Position = uProjectionMatrix * uModelViewMatrix * newPosition;
        }
    </script>
    
    <!-- fragment shader -->
    <script type="x-shader/x-fragment" id="frag-shader-style-in">
        #ifdef GL_FRAGMENT_PRECISION_HIGH
            precision highp float;
        #else
            precision mediump float;
        #endif
    
        uniform vec2 u_resolution; // canvas size (width, height)
        uniform float u_time;       // time in seconds since load
    
        void main(void) {
        float maxAxis = max(u_resolution.x, u_resolution.y);
        // If you want to map the pixel coordinate values to the range 0 to 1, you divide by resolution.
        
        /*With vec4 gl_FragCoord, we know where a thread is working inside the billboard. 
        In this case we don't call it uniform because it will be different from thread to thread, 
        instead gl_FragCoord is called a varying. */
        vec2 uv = gl_FragCoord.xy / maxAxis;
        vec2 center = 0.5 * u_resolution / maxAxis;
    
        gl_FragColor = vec4(
            vec3(sin(u_time * 0.1 + distance(uv, center) * 255.0)),
            1.0);
    }
    </script>