# calculating size of rectangle which fully obscures a sphere

I am learning GLSL and want to raytrace a sphere. I am rendering rectangles which sit between the camera and sphere, and do a ray-sphere intersection test in the fragment shader. It almost works:

As you can see, the rectangles are a little too small. Here is how I calculated them.

Point $C$ is the centre of the rectangle, a distance $R$ from the radius of the sphere. Taking basis vectors $i,j$ I can construct the corners as $C+(\pm i, \pm j)$

Point $C$ is a fraction $1-\frac{R}{||V||}$ along the vector $V$ from the camera.

I can direct $i$ and $j$ given the vector $V$ and the "up" vector.

How long should $i$ and $j$ be? By similar triangles, they should have length $R\times (1-\frac{R}{||V||})$.

Here is the code (I'm new to GLSL so please ignore bad habits, for now I just want to get the geometry right).

A vertex array containing four $(\pm 1, \pm 1, 0)$ vectors forms a triangle strip.

#version 300 es
in  vec2 position;
out vec3 vs_fs_position;

uniform vec3 i;
uniform vec3 j;
uniform vec3 centre;

uniform mat4 pvm; // projection * view * model

void main ()
{
vec3 p = centre + position.x * i + position.y * j;

vs_fs_position = p; // world space coordinate sent to fragment

gl_Position = pvm * vec4 (p, 1); // to screen space
}


#version 300 es
precision mediump float;
out mediump vec4 out_colour;
in  mediump vec3 vs_fs_position;

uniform mediump vec3 camera_position;
uniform mediump vec3 camera_to_sphere;

void main ()
{
vec3 direction = vs_fs_position - camera_position;

float A = dot (direction, direction);
float B = 2.0 * dot (direction, camera_to_sphere);
float C = dot (camera_to_sphere, camera_to_sphere)

float det = B * B - 4.0 * A * C;

if (det < 0.0)
out_colour = vec4 (0.0, 0.0, 0.0, 1.0);
else
out_colour = vec4 (0.5, 0.5, 0.5, 1.0);

}


Some C++ to draw it

shader .camera_position = m_camera_position;

const float EPSILON = 0.0;

for (auto & s : SPHERES)
{
auto R = s .position - m_camera_position;

float ratio = 1.0 - (s .radius + EPSILON) / length (R);

float width = ratio * s .radius;

auto & up = m_camera_up;

const float W = width * (1 + EPSILON);

glm :: vec3 i = normalize (cross (up, R)) * W;
glm :: vec3 j = normalize (cross (R,  i)) * W;

shader .camera_to_sphere = s .position - m_camera_position;

glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
}


If it runs with EPSILON=0.5, I get this output.

Theoretically, I should be able to get a perfect fit with EPSILON=0, that way I can be confident it will work at all scales/positions.

What's wrong with my calculation of the width parameter?

How long should $i$ and $j$ be? By similar triangles, they should have length $R\times (1-\frac{R}{\lVert V\rVert})$.
$\frac{R}{\sqrt{\lVert V\rVert^2-R^2}} \times \left(\lVert V\rVert-R\right)$