Search code examples
glslgradientfragment-shader

GLSL Box with shadows has a "cross" effect near its corner


The following GLSL code:

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord / iResolution.xy;
    vec2 windowSize = iResolution.xy;
    float border = 50.0;

    vec2 pixelPos = uv * windowSize;

    vec2 v = windowSize - pixelPos;
    vec2 n = normalize(vec2(1.0, -1.0));

    float col = 1.0;

    if (dot(v, n) < 0.0) {
        col = clamp(windowSize.x - pixelPos.x, 0.0, border) / border;
    } else {
        col = clamp(windowSize.y - pixelPos.y, 0.0, border) / border;
    }

    fragColor = vec4(vec3(col), 1.0);
}

Produces the following image:

enter image description here

There is a noticeable lighter line at the corner - where the right and top gradients meet. I don't really understand why this happens. When a point is right at the diagonal the values are supposed to be identical to the values near the diagonal. To illustrate my point, consider the following python code:

import numpy as np
from numpy.linalg import norm


def clamp(x, min_val, max_val):
    return np.minimum(np.maximum(x, min_val), max_val)


def normalize(x):
    return x / norm(x)


def vec2(x, y):
    return np.array([x, y])


windowSize = vec2(200, 100)
border = 50.0


def calculate(pixelPos):
    v = windowSize - pixelPos
    n = normalize(vec2(1.0, -1.0))

    col = 1.0

    if np.dot(v, n) < 0.0:
        col = clamp(windowSize[0] - pixelPos[0], 0.0, border) / border
    else:
        col = clamp(windowSize[1] - pixelPos[1], 0.0, border) / border

    return col


print(10 / border)  # expected value

pixelPos = np.array([190, 90])  # right on the diagonal
print(pixelPos, calculate(pixelPos))

pixelPos = np.array([189, 90])  # slightly above
print(pixelPos, calculate(pixelPos))

pixelPos = np.array([190, 89])  # slightly below
print(pixelPos, calculate(pixelPos))

It outputs:

0.2
[190  90] 0.2
[189  90] 0.2
[190  89] 0.2

As the above code shows, the values at the diagonal are identical to the values to the left and down of it. So if the values are identical, why does OpenGL show this light line at the corner?


Solution

  • As has been pointed out to me, turns out this is a well known optical illusion, and similar questions have been asked here on Stack Overflow:

    Here is a small python script that visualizes it more clearly:

    import numpy as np
    from numpy.linalg import norm
    import matplotlib.pyplot as plt
    
    
    def clamp(x, min_val, max_val):
        return np.minimum(np.maximum(x, min_val), max_val)
    
    
    def normalize(x):
        return x / norm(x)
    
    
    def vec2(x, y):
        return np.array([x, y])
    
    
    windowSize = vec2(200, 100)
    border = 50.0
    
    
    def calculate(pixelPos):
        v = windowSize - pixelPos
        n = normalize(vec2(1.0, -1.0))
    
        col = 1.0
    
        if np.dot(v, n) < 0.0:
            col = clamp(windowSize[0] - pixelPos[0], 0.0, border) / border
        else:
            col = clamp(windowSize[1] - pixelPos[1], 0.0, border) / border
    
        return col
    
    
    pixels = [[0.0 for _ in range(windowSize[0])] for _ in range(windowSize[1])]
    for i in range(windowSize[1]):
        for j in range(windowSize[0]):
            x = j
            y = windowSize[1] - i
            pixels[i][j] = calculate(vec2(x, y))
    
    plt.imshow(pixels)
    plt.show()
    

    enter image description here