Determining the center of the screen in a WEBGL fragment shader

Viewed 318

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.

1 Answers

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>

Related