Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm trying to implement basic directional lightning in OpenGL 3.3 by emulating the logic shown in this guide: http://www.arcsynthesis.org/gltut/Illumination/Tutorial%2009.html

I do not understand what matrix to send to the vertex shader to use for calculating lightning effects. The guide says modelView matrix, but to me, and when I'm running the game, it dosn't make sense; when I orient myself in the world with mouse and keyboard, the lightning direction gets altered aswell!

Here's the items to which I fill my 'render queue', which contains all data needed to draw the models:

/* RenderItem definition */
    struct RenderItem
    {
        VertexBufferPtr mVertexBuffer;
        const Vec4 mColor;
        const Mat4 mMVP;
        const Mat3 mMVMatrix;    // I used model-view matrix here, but I dont know really which one to use

        const Vec4 mLightIntensity;
        const Vec3 mDirToLight;

       // simply extracts all the struct data into one buffer to be uploaded as UniformBuffer to shader
        void CopyUniformData(std::vector<float>& buffer) const
        {
            std::vector<float> colorValues = GetTypeValues(mColor);
            std::vector<float> mvpMatrixValues = GetTypeValues(mMVP);
            std::vector<float> mvMatrixValues = GetTypeValues(mMVMatrix);
            std::vector<float> lightIntensityMatrixValues = GetTypeValues(mLightIntensity);
            std::vector<float> dirToLightMatrixValues = GetTypeValues(mDirToLight);

            buffer.insert(buffer.end(), colorValues.begin(), colorValues.end());
            buffer.insert(buffer.end(), mvpMatrixValues.begin(), mvpMatrixValues.end());
            buffer.insert(buffer.end(), mvMatrixValues.begin(), mvMatrixValues.end());
            buffer.insert(buffer.end(), lightIntensityMatrixValues.begin(), lightIntensityMatrixValues.end());
            buffer.insert(buffer.end(), dirToLightMatrixValues.begin(), dirToLightMatrixValues.end());
        }

        RenderItem(VertexBufferPtr vertexBuffer, const Vec4& color, const Mat4& mvp, const Mat3& mv, const Vec4& lightIntensity, const Vec3& dirToLight);
    };

Here's where I create all the RenderItems for my renderqueue:

void Engine::CreateModelRenderables(const Model* model, const Mat4& viewMatrix, const Mat4& perspectiveMatrix, const Mat4& nodeTransform, std::vector<RenderItem>& renderQueue, const std::vector<LightPtr>& activeLights)
    {
        const Mat4 modelMatrix = nodeTransform * model->mTransform;       // 'nodeTransform' is the SceneNodes transform, while model->mTransform is the models transform... so this gives the model-world matrix
        const Mat4 modelViewMatrix = viewMatrix * modelMatrix;
        const Mat4 modelViewProjMatrix = perspectiveMatrix * modelViewMatrix;

        // 'activelights' is a vector, but for now only one light is supported, so I just pick the first element

        // TODO: handle multiple lights
        BOOST_FOREACH(const Mesh& mesh, model->mMeshes)
            if (activeLights.size() >= 1)
                renderQueue.push_back(RenderItem(mesh.mVertexBuffer, Vec4(1.0f, 0.3f, 0.3f, 1.0f), modelViewProjMatrix, Mat3(....???....), activeLights.front()->mLightIntensity, activeLights.front()->mLightDirection));
            else
                renderQueue.push_back(RenderItem(mesh.mVertexBuffer, Vec4(1.0f, 0.3f, 0.3f, 1.0f), modelViewProjMatrix, Mat3(....???....), Vec4(0.0f), Vec3(0.0f)));

        BOOST_FOREACH(const Model& childModel, model->mChildren)
            CreateModelRenderables(&childModel, viewMatrix, perspectiveMatrix, modelMatrix, renderQueue, activeLights);
    }

for drawing, I just loop over the renderitems, update the UniformBuffer object and glDrawElements (vertexbuffer->render):

void OpenGLRenderer::DrawRenderables(const std::vector<RenderItem>& renderQueue)
    {
        BOOST_FOREACH(const RenderItem& renderItem, renderQueue)
        {
            mUniBuffer.SetData(renderItem);

            renderItem.mVertexBuffer->Render();
        }
    }

this is my vertex shader:

const std::string gVertexShader =   "#version 330                                               \n  \
                                                                                                    \n  \
                                        layout(std140) uniform DefaultUniform                       \n  \
                                        {                                                           \n  \
                                            vec4 Color;                                             \n  \
                                            mat4 MVPMatrix;                                         \n  \
                                            mat3 MVMatrix;                                          \n  \
                                                                                                    \n  \
                                            vec4 LightIntensity;                                    \n  \
                                            vec3 DirToLight;                                        \n  \
                                        };                                                          \n  \
                                                                                                    \n  \
                                        layout(location = 0) in vec3 vert_position;                 \n  \
                                        layout(location = 1) in vec3 vert_normal;                   \n  \
                                        out vec4 frag_color;                                        \n  \
                                                                                                    \n  \
                                        void main()                                                 \n  \
                                        {                                                           \n  \
                                            gl_Position = MVPMatrix * vec4(vert_position, 1.0f);    \n  \
                                                                                                    \n  \
                                            vec3 normCamSpace = normalize(MVMatrix * vert_normal);  \n  \
                                            float angIncidence = dot(normCamSpace, DirToLight);     \n  \
                                            angIncidence = clamp(angIncidence, 0, 1);               \n  \
                                                                                                    \n  \
                                            frag_color = LightIntensity * Color * angIncidence;     \n  \
                                        }   

the fragment shader simply passes-through the frag_color..

Here is my camera class used to generate the viewMatrix through getCameraTransform(), in case there might be something wrong there? I don't think so though, as I can move around in the world with WASD + mouse just as expected

Camera::Camera() : mTranslation(0.0f), mHorizontalAngle(0.0f), mVerticalAngle(0.0f)
    {
    }

    Camera::~Camera()
    {
    }


    void Camera::SetPosition(const Vec3& position)
    {
        mTranslation = position;
    }

    void Camera::TranslateCamera(const Vec3& translateVec)
    {
        mTranslation += translateVec;
    }

    void Camera::RotateCamera(const float offsetHorizontalAngle, const float offsetVerticalAngle)
    {
        mHorizontalAngle += offsetHorizontalAngle;
        mVerticalAngle   += offsetVerticalAngle;
    }


    Vec3 Camera::Forward() const
    {
        Vec4 forward = Inverse(Orientation()) * Vec4(0, 0, 1, 1);

        return Vec3(forward);
    }

    Vec3 Camera::Right() const
    {
        Vec4 right = Inverse(Orientation()) * Vec4(-1, 0, 0, 1);

        return Vec3(right);
    }

    Mat4 Camera::Orientation() const
    {
        Quaternion rotation;
        rotation = AngleAxisToQuaternion(mVerticalAngle, Vec3(1.0f, 0.0f, 0.0f));
        rotation = rotation * AngleAxisToQuaternion(mHorizontalAngle, Vec3(0.0f, 1.0f, 0.0f));

        return QuaternionToMat4(rotation);
    }


    Mat4 Camera::GetCameraTransform() const
    {
        Mat4 viewMatrix(1.0f);
        viewMatrix = Orientation() * Translate(viewMatrix, mTranslation);

        return viewMatrix;
    }   

SO, my question is, what matrix should the MVMatrix be in the vertex shader? The guide says the 'modelToCameraMatrix', which I suppose is my modelViewMatrix in Engine::CreateModelRenderables(), but I don't want the lights direction to change when I re-orient my camera!

EDIT: Alright, I tried to perform the lighting in world space; so I pass the 'modelMatrix ' as the 'mMVMatrix' in the shader and change nothing else. What happens is, lighting is only applied when changing the X-value of LightDir; changing Y/Z values have no effect. Why is this?

share|improve this question
    
Did you not read the part where it talks about the need to transform the light from world-space into camera-space? –  Nicol Bolas Mar 22 '13 at 7:29
add comment

3 Answers

From the linked article:

You certainly could use world space to do lighting. However, for our purposes, we will use camera space. The reason for this is partially illustrative: in later tutorials, we are going to do lighting in some rather unusual spaces. By using camera space, it gets us in the habit of transforming both our light direction and the surface normals into different spaces.

So feel free to use the model-to-world matrix instead of model-to-camera matrix. If you are going to use model-to-camera matrix as the article suggests, you also need to transform your lights to the camera space in the shader.

share|improve this answer
    
Alright I tried doing the model-to-world matrix; updated question –  KaiserJohaan Mar 22 '13 at 17:07
add comment

I'm not sure what you're trying to do there but try and send the light's world matrix to the shader, after all, all light effects are relative to that space. The only situation where the position of your camera interferes with your lighting calculations is when you implement specularity.

share|improve this answer
add comment
up vote 0 down vote accepted

Alright, I solved it.

First I did as people mentioned; use world space instead, but I got some very strange unexplainable lighting effects.

After some experimentation I realised I can do the lighting calculations in world space just fine if I supply a mat4 model-to-world matrix rather than a mat3 model-to-world matrix to the vertexshader for the normal. I believe it has something to do with translation, since the objects in question were translated away abit from the camera, but if anyone has a complete answer I would be happy to hear it.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.