Последние пару дней я работал над освещением с помощью системы прямого рендеринга. Пока все хорошо, но когда я перемещаю сетку, я рендеринг от глобального центра (0, 0, 0) или вращаю его, все огни (кроме внешнего освещения) начинают «мерцать» (но только на этом сетка, которая находится вдали от (0, 0, 0)). Когда у меня есть вторая сетка, положение которой является глобальным центром, источники света обычно действуют на нее.
Возможно, стоит упомянуть, что я могу работать только в режиме выпуска, поскольку работа в режиме отладки дает мне странное исключение. Я считаю, что это как-то связано с библиотеками SFML …
Вот как это выглядит, когда я освещаю объект направленным светом (обратите внимание, что сетка с серой кирпичной текстурой не показывает этот странный эффект):
https://www.dropbox.com/s/yt3ozxyeg8e6psa/Screenshot%20%284%29.png?dl=0
И с прожектором:
https://www.dropbox.com/s/1yk2j0yg4fca6pz/Screenshot%20%285%29.png?dl=0
Я использую GLEW 1.10.0 для загрузки функций OpenGL и SFML 2.1 для оконных вещей. Моя IDE — Visual Studio 2013 Ultimate, и я работаю на Windows 8.1 с последними графическими драйверами Nvidia.
Вот код, который делает смешивание:
void RenderingEngine::Render(GameObject* gameObject)
{
Clear();
m_baseLights.clear();
gameObject->AddToRenderingEngine(this);
gameObject->Render(this, *m_ambientShader);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
glDepthMask(GL_FALSE);
glDepthFunc(GL_EQUAL);
for (uInt i = 0; i < m_baseLights.size(); i++)
gameObject->Render(this, *m_baseLights[i]->GetShader());
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
}
И вот пара других важных методов:
void GameObject::Render(RenderingEngine* renderingEngine, const Shader& shader)
{
for (uInt i = 0; i < m_children.size(); i++)
m_children[i]->Render(renderingEngine, shader);
for (uInt i = 0; i < m_components.size(); i++)
// NOTE: This is where MeshRenderer::Render(...) etc. get called
m_components[i]->Render(renderingEngine, shader);
}// NOTE: MeshRenderer inherits GameComponent
void MeshRenderer::Render(RenderingEngine* renderingEngine, const Shader& shader)
{
shader.Bind();
shader.UpdateUniforms(*renderingEngine, m_material, GetParentObject().GetTransform());
m_mesh.Draw();
}void Shader::Bind() const
{
glUseProgram(m_program);
}// NOTE: SpotShader is one of many classes that inherits Shader and implements its pure virtual method UpdateUniforms(...)
void SpotShader::UpdateUniforms(const RenderingEngine& renderingEngine, const Material& material, const Transform& transform) const
{
material.GetTexture()->Bind(0);
SetUniformMat4f("viewProjection", renderingEngine.GetMainCamera().GetViewProjection());
SetUniformMat4f("transform", transform.GetTransform());
SetUniformSpotLight("spotLight", *m_spotLight);
SetUniformf("specularIntensity", material.GetSpecularIntensity());
SetUniformf("specularExponent", material.GetSpecularExponent());
SetUniformVec3f("cameraPosition", renderingEngine.GetMainCamera().GetParentObject().GetTransform().GetTranslation());
}void Mesh::Draw() const
{
glBindVertexArray(m_vertexArrayObject);
glDrawElements(GL_TRIANGLES, m_drawCount, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
Иногда это так, но реальный ответ на эту проблему находится в разделе Edit2.
Я заметил такой эффект в своем приложении. Я считаю, что это как-то связано с точностью чисел на GPU. Чем дальше от 0,0,0, тем больше эта ошибка. Одним из решений этой проблемы является сокращение масштабов мира. Если, например. 1 метр в вашем мире — это значение 1,0, вы можете уменьшить его до 0,1. Это, однако, не реальное решение, просто обходной путь.
Есть еще одна вещь. Я помню, что в моем случае эта ошибка присутствовала только при запуске приложения через Visual Studio. Попробуйте запустить его из .exe и посмотрите, сохраняется ли проблема. Так как он не присутствовал в релизной версии моей игры, я просто проигнорировал ее и предположил, что она как-то связана с режимом отладки видеокарты.
Редактировать:
Хорошо, еще одна вещь приходит на ум. Почему вы используете GL_EQUAL вместо GL_LEQUAL? Я знаю, что вы думаете, что, поскольку вы рисуете одну и ту же модель дважды, ее позиция должна быть одинаковой, но вы абсолютно уверены в этом? В этом случае нет веской причины предпочитать GL_EQUAL GL_LEQUAL. Попробуйте изменить его и посмотрите, поможет ли это. Отказ от 0,0,0 объяснил бы это, поскольку 0,0,0 — единственное место, где к модели не применяются преобразования. Каждое преобразование может привести к неточностям, которые могут быть причиной неудачи вашего Z-теста.
Edit2:
Это предложение может сделать вещи лучше, но это не исправит их полностью. Это всего лишь предположение, но позвольте мне рассказать вам, что происходит в функции gameObject-> Render. Вы нажимаете на матрицу, рисуете, а затем POP на матрицу, верно? Теперь вы думаете, что у вас точно такая же трансформация сейчас? Вы нет! Это почти то же самое. Чтобы все заработало, вам нужно изменить дизайн вашей программы. Не нажимайте / не POP какие-либо матрицы между рисованием одного и того же объекта, и вы должны хорошо использовать сравнение GL_EQUAL.
Edit3:
Мой последний совет: убедитесь, что матрица преобразования, которую вы используете, состоит в том, чтобы каждый проход был одинаковым. Если вы не можете найти причину, по которой все может быть по-другому, используйте этот простой взлом. Используйте небольшое значение смещения, которое вы передаете шейдеру как форму. Увеличивайте это значение немного в каждом проходе (поэкспериментируйте, что должно быть. Я думаю, что что-то очень маленькое подойдет для триха). Затем, когда вы преобразуете каждую вершину с помощью преобразования или проекции вида, вычтите это смещение из вашей координаты Z. Таким образом, каждый свет будет нарисован немного раньше предыдущего.
Edit4:
Другая проблема (которая здесь имела место) — это GLSL и C ++ могут выполнять матричное умножение немного по-разному, поэтому выполнение одной и той же операции в шейдере и в C ++ может привести к различным эффектам, что приводит к появлению артефактов.