Search code examples
openglgraphicssdlsdl-2opengl-3

OpenGL - How to rotate light source without rotating object at the same time?


I'm doing the learnopengl tutorials and I'm trying one of the exercises in the Basic Lighting tutorial -

Try to move the light source around the scene over time using either sin or cos.

Although in my case, I'm using SDL which means I can't use the following equations to rotate my light source around:

lightPos.x = 1.0f + sin(glfwGetTime()) * 2.0f;
lightPos.y = sin(glfwGetTime() / 2.0f) * 1.0f;

Right now, when I rotate my object, my light source rotates with it, however I want my light source to rotate separately from my object. This is what I have in my render loop which handles the rotation of the light and the object:

void OpenGLWindow::render()
{

    lightPos.x = 1.0f + sin(SDL_GetTicks()/1000.0f) * 2.0f;
    lightPos.y = sin((SDL_GetTicks()/1000.0f) / 2.0f) * 1.0f;

    glm::mat4 model(1.0f);
    //model = glm::translate(model, lightPos);
    model = glm::rotate(model, lightPos.z, glm::vec3(0.0f, 0.0f, 1.0f));
    //model = glm::rotate(model, lightPos.y, glm::vec3(0.0f, 1.0f, 0.0f));
    model = glm::rotate(model, lightPos.x, glm::vec3(1.0f, 0.0f, 0.0f));
    int modelMatLocation = glGetUniformLocation(shader, "lightMatrix");
    glUniformMatrix4fv(modelMatLocation, 1, false, &model[0][0]);

    // NOTE: glm::translate/rotate/scale apply the transformation by right-multiplying by the
    //       corresponding transformation matrix (T). IE glm::translate(M, v) = M * T, not T*M
    //       This means that the transformation you apply last, will effectively occur first
    glm::mat4 modelMat(1.0f);
    modelMat = glm::translate(modelMat, parentEntity.position);
    modelMat = glm::rotate(modelMat, parentEntity.rotation.z, glm::vec3(0.0f, 0.0f, 1.0f));
    modelMat = glm::rotate(modelMat, parentEntity.rotation.y, glm::vec3(0.0f, 1.0f, 0.0f));
    modelMat = glm::rotate(modelMat, parentEntity.rotation.x, glm::vec3(1.0f, 0.0f, 0.0f));
    modelMat = glm::scale(modelMat, parentEntity.scale);
    int modelMatrixLoc = glGetUniformLocation(shader, "modelMatrix");
    glUniformMatrix4fv(modelMatrixLoc, 1, false, &modelMat[0][0]);
}

And this is what I have in my vertex shader:

in vec3 position;
out vec3 FragPos;
out vec3 Normal;

uniform mat4 projectionMatrix;
uniform mat4 viewingMatrix;
uniform mat4 modelMatrix;
uniform mat4 lightMatrix;

void main()
{
    vec4 transformedPosition = projectionMatrix * viewingMatrix * modelMatrix * lightMatrix * vec4(position, 1.0f);

    gl_Position = transformedPosition;
    FragPos = vec3(modelMatrix * vec4(position, 1.0));
    Normal = mat3(transpose(inverse(modelMatrix))) * position;
}

Fragment shader:

out vec4 outColor;
in vec3 Normal;
in vec3 FragPos;

uniform vec3 lightPos;
uniform vec3 objectColor;
uniform vec3 lightColor;
uniform vec3 viewPos;

void main()
{
    float ambientStrength = 0.06;
    vec3 ambient = ambientStrength * lightColor;

    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * lightColor;

    float specularStrength = 0.6;
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
    vec3 specular = specularStrength * spec * lightColor;
    vec3 result = (ambient + diffuse + specular) * objectColor;
    outColor = vec4(result, 1.0);
}

How do I rotate the light source only, without the object rotating also?


Solution

  • If you want to rotate the lightsource, then you have to transform lightPos instead of rotating the model. This is also more efficient because it requires only one matrix transformation per frame (instead of one per vertex). The code should look as follows:

    // C++
    glm::vec4 lightPos(...) // <-- The light position you currently use
    
    glm::mat4 lightM(1.0f);
    lightM = glm::rotate(lightM, lightRotation.z, glm::vec3(0.0f, 0.0f, 1.0f));
    lightM = glm::rotate(lightM, lightRotation.y, glm::vec3(0.0f, 1.0f, 0.0f));
    lightM = glm::rotate(lightM, lightRotation.x, glm::vec3(1.0f, 0.0f, 0.0f));
    
    glm::vec4 rotatedLightPos = lightM * lightPos;
    
    glUniform3f(lightPosLocation, rotatedLightPos.x, rotatedLightPos.y, rotatedLightPos.z);
    

    Vertex Shader:

    // GLSL
    in vec3 position;
    out vec3 FragPos;
    out vec3 Normal;
    
    uniform mat4 projectionMatrix;
    uniform mat4 viewingMatrix;
    uniform mat4 modelMatrix;
    
    void main()
    {
        vec4 transformedPosition = projectionMatrix * viewingMatrix * modelMatrix * vec4(position, 1.0f);
    
        gl_Position = transformedPosition;
        FragPos = vec3(modelMatrix * vec4(position, 1.0));
        Normal = mat3(transpose(inverse(modelMatrix))) * position;
    }
    

    The fragment shader stays as it is now.