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 \
} \n";
the fragment shader simply passes-through the frag_color..
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: 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, its just the lightning that is wierd...
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;
}