Search code examples
javascriptmathshaderwebgl

Why is this shader circle so much smaller than the passed radius?


I'm developing a GLSL shader that draws out a circle based on a given center position and radius. For some reason I don't understand, the circle's radius does not match what I am passing. That is, when I pass in 100 for u_radius, the radius is instead 56. I tried just doubling the value in the shader, and while it's close with that, it's still slightly inaccurate. Does anyone have any clue what could be causing the discrepancy?

precision mediump float;

varying vec4 v_pos;

uniform mat3 u_matrix;
uniform vec2 u_center; // the center of the circle in world coordinates

uniform float u_aspect; // aspect ratio. 1.7778 for my monitor (1920x1080)
uniform float u_radius; // radius. passing in 100

uniform vec2 u_canvasSize; // [ 1920, 1080 ]

void main() {
  vec4 c = vec4((u_matrix * vec3(u_center, 1)).xy, 0, 1); // center

  vec2 onePix = vec2(1.0, 1.0) / u_canvasSize; // the size of one pixel in clip-space

  float onePixLength = sqrt(onePix.x * onePix.x + onePix.y * onePix.y); // magnitude of one pixel

  float r = onePixLength * u_radius; // radius converted to clip-space

  vec2 dist = (v_pos.xy - c.xy) * vec2(u_aspect, 1.0); // distance from center to current shader point

  if (dist.x * dist.x + dist.y * dist.y > r * r) {
    discard;
  }

  gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}

Solution

  • It is because normalized device space coordinates are in range [-1.0, 1.0]. Therefore a factor of 2 is missing when calculating the pixel size:

    vec2 onePix = vec2(1.0, 1.0) / u_canvasSize;

    vec2 onePix = vec2(2.0) / u_canvasSize;
    

    Additionally, you need to calculate the side length of a pixel instead of the diagonal length. The width (x-dimension) is scaled by the aspect ratio. Therefore, you need to calculate the height of a pixel:

    float onePixLength = sqrt(onePix.x * onePix.x + onePix.y * onePix.y);

    float onePixLength = onePix.y;
    

    Note, onePix.x * u_aspect is equal to onePix.y.