1

I am trying to implement a simple artificial 2D lighting. I am not using an algorithm like Phong's. However, I am having some difficulty in ensuring that my lighting do not stretch/squeeze whenever the window resize. Any tips and suggestions will be appreciated. I have tried converting my radius into a vec2 so that I can scale them accordingly based on the aspect ratio, however it doesnt work properly. Also, I am aware that my code is not the most efficient, any feedback is also appreciated as I am still learning! :D

I have an orthographic projection matrix transforming the light position so that it will be at the correct spot in the viewport, this fixed the position but not the radius (as I am calculating per fragment). How would I go about transforming the radius based on the aspect ratio?

void LightSystem::Update(const OrthographicCamera& camera)
{
    std::vector<LightComponent> lights;
    for (auto& entity : m_Entities)
    {
        auto& light = g_ECSManager.GetComponent<LightComponent>(entity);
        auto& trans = g_ECSManager.GetComponent<TransformComponent>(entity);
        if (light.lightEnabled)
        {
            light.pos = trans.Position;
            glm::mat4 viewProjMat = camera.GetViewProjectionMatrix();
            light.pos = viewProjMat * glm::vec4(light.pos, 1.f);
            // Need to store all the light atrributes in an array
            lights.emplace_back(light);
        }
        // Create a function in Render2D.cpp, pass all the arrays as a uniform variable to the shader, call this function here
        glm::vec2 res{ camera.GetWidth(), camera.GetHeight() };
        Renderer2D::DrawLight(lights, camera, res);

    }
}

Here is my shader:

#type fragment
#version 330 core
layout (location = 0) out vec4 color;

#define MAX_LIGHTS 10
uniform struct Light
{
    vec4 colour;
    vec3 position;
    float radius;
    float intensity;
} allLights[MAX_LIGHTS];

in vec4 v_Color;
in vec2 v_TexCoord;
in float v_TexIndex;
in float v_TilingFactor;
in vec4 fragmentPosition;

uniform sampler2D u_Textures[32];
uniform vec4 u_ambientColour;
uniform int numLights;
uniform vec2 resolution;

vec4 calculateLight(Light light)
{
    float lightDistance = length(distance(fragmentPosition.xy, light.position.xy));

    //float ar = resolution.x / resolution.y;

    if (lightDistance >= light.radius)
    {
        return vec4(0, 0, 0, 1); //outside of radius make it black
    }
    return light.intensity * (1 - lightDistance / light.radius) * light.colour;
}

void main()
{   
    vec4 texColor = v_Color;

    vec4 netLightColour = vec4(0, 0, 0, 1);
    if (numLights == 0)
        color = texColor;
    else
    {
        for(int i = 0; i < numLights; ++i) //Loop through lights
            netLightColour += calculateLight(allLights[i]) + u_ambientColour;
        color = texColor * netLightColour;
    }
}

tomatto
  • 149
  • 10
  • 1
    You must use an orthographic projection matrix in the vertex shader. Modify the `fragmentPosition` by the projection matrix. – Rabbid76 Sep 25 '21 at 07:56

2 Answers2

1

You must use an orthographic projection matrix in the vertex shader. Modify the clip space position through the projection matrix.

Alternatively, consider the aspect ratio when calculating the distance to the light:

float aspectRatio = resolution.x/resolution.y;
vec2 pos = fragmentPosition.xy * vec2(aspectRatio, 1.0);
float lightDistance = length(distance(pos.xy, light.position.xy));
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Hi, I did use orthographic projection matrix in the vertex shader but all that did was to ensure that my light position was at the correct spots, however the radius (not being an actual vertex) were still getting stretched. – tomatto Sep 25 '21 at 08:50
  • @ToM4tto I can't see your shader so I can't help you. – Rabbid76 Sep 25 '21 at 08:51
  • @ToM4tto Did you even try the code snippet in my answer? – Rabbid76 Sep 25 '21 at 09:08
  • Hi, sorry, I did try it, but I wasn't able to render the objects within the viewframe. – tomatto Sep 25 '21 at 12:40
  • @ToM4tto Do you set the uniform variable `resolution`? – Rabbid76 Sep 25 '21 at 13:00
  • Yep I did, turns out it was a careless naming error, however the light position are not 100% correct, I have attached a second image with red lights. – tomatto Sep 25 '21 at 14:14
  • @ToM4tto Try `fragmentPosition.xy * vec2(resolution.x/resolution.y, 1.0)` – Rabbid76 Sep 25 '21 at 14:18
  • Thanks for the help, for some reason, the light source position moves with the camera and still stretches with the window – tomatto Sep 25 '21 at 15:22
0

I'm going to compile all the answers for my question, as I had done a bad job in asking and everything turned out to be a mess.

As the other answers suggest, first I had to use an orthographic projection matrix to ensure that the light source position was displayed at the correct position in the viewport.

Next, from the way I did my lighting, the projection matrix earlier would not fix the stretch effect as my light wasn't an actual circle object made with actual vertices. I had to turn radius into a vec2 type, representing the radius vectors along x and y axis. This is so that I can then modify the vectors based on the aspect ratio:

if (aspectRatio > 1.0)
    light.radius.x /= aspectRatio;
else
    light.radius.x /= aspectRatio;

I had posted another question here, to modify my lighting algorithm to support an ellipse shape. This allowed me to then perform the scalings needed to counter the stretching along x/y axis whenever my aspect ratio changed. Thank you all for the answers.

tomatto
  • 149
  • 10