Отображение теней Cubemap не работает
Я пытаюсь создать всенаправленное / точечное освещение в openGL версии 3.3. Я искал в Интернете и на этом сайте, но до сих пор я не смог сделать это. Из моего понимания я должен
Генерация кадрового буфера с использованием компонента глубины
Создайте кубическую карту и привяжите ее к указанному кадровому буферу
Рисование к отдельным частям кубической карты в соответствии с перечислениями GL_TEXTURE_CUBE_MAP_*
Нарисуйте сцену как обычно, и сравните значение глубины фрагментов с теми, что в кубической карте
Теперь я прочитал, что лучше использовать расстояния от источника света до фрагмента, а не хранить глубину фрагмента, так как это облегчает поиск кубической карты (что-то о том, что нет необходимости проверять каждую отдельную текстуру?)
Моя текущая проблема заключается в том, что свет, который выходит, фактически находится в сфере и не генерирует тени. Другая проблема заключается в том, что кадровый буфер жалуется на неполность, хотя у меня сложилось впечатление, что кадровый буфер не нуждается в визуальном буфере, если он рендерится в текстуру.
Вот моя инициализация кадрового буфера и карты куба:
framebuffer = 0;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glGenTextures(1, &shadowTexture);
glBindTexture(GL_TEXTURE_CUBE_MAP, shadowTexture);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);GL_COMPARE_R_TO_TEXTURE);
for(int i = 0; i < 6; i++){
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i , 0,GL_DEPTH_COMPONENT16, 800, 800, 0,GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
}
glDrawBuffer(GL_NONE);
Shadow Vertex Shader
void main(){
gl_Position = depthMVP * M* vec4(position,1);
pos =(M * vec4(position,1)).xyz;
}
Shadow Fragment Shader
void main(){
fragmentDepth = distance(lightPos, pos);
}
Вершинный шейдер (вырезаны несвязанные биты)
uniform mat4 depthMVP;
void main() {
PositionWorldSpace = (M * vec4(position,1.0)).xyz;
gl_Position = MVP * vec4(position, 1.0 );
ShadowCoord = depthMVP * M* vec4(position, 1.0);
}
Фрагмент шейдера (несвязанный вырез кода)
uniform samplerCube shadowMap;
void main(){
float bias = 0.005;
float visibility = 1;
if(texture(shadowMap, ShadowCoord.xyz).x < distance(lightPos, PositionWorldSpace)-bias)
visibility = 0.1
}
Теперь, когда вы, вероятно, думаете, что такое глубина MVP? Матрица проекции глубины в настоящее время является ортогональной проекцией с диапазонами [-10, 10] в каждом направлении. Ну, они определены так:
glm::mat4 depthMVP = depthProjectionMatrix* ??? *i->getModelMatrix();
Проблема в том, что я не знаю, что??? значение должно быть. Раньше это была матрица камеры, однако я не уверен, что именно так и должно быть. Затем выполняется рисование кода для сторон кубической карты следующим образом:
for(int loop = 0; loop < 6; loop++){
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X+loop, shadowTexture,0);
glClear( GL_DEPTH_BUFFER_BIT);
for(auto i: models){
glUniformMatrix4fv(modelPos, 1, GL_FALSE, glm::value_ptr(i->getModelMatrix()));
glm::mat4 depthMVP = depthProjectionMatrix*???*i->getModelMatrix();
glUniformMatrix4fv(glGetUniformLocation(shadowProgram, "depthMVP"),1, GL_FALSE, glm::value_ptr(depthMVP));
glBindVertexArray(i->vao);
glDrawElements(GL_TRIANGLES, i->triangles, GL_UNSIGNED_INT,0);
}
}
Наконец сцена прорисовывается нормально (я избавлю вас от деталей). Перед вызовами для рисования на кубическую карту я устанавливаю кадровый буфер на тот, который я сгенерировал ранее, и меняю область просмотра на 800 на 800. Я изменяю кадровый буфер обратно на 0 и сбрасываю область просмотра на 800 на 600, прежде чем я сделаю нормальное рисование. Любая помощь по этому вопросу будет принята с благодарностью.
Обновление 1
После некоторой настройки и исправления ошибок, это результат, который я получаю. Я исправил ошибку с не работающей глубиной MVP, я рисую здесь расстояние, которое хранится в кубической карте. http://imgur.com/JekOMvf
По сути, происходит то, что он рисует одну и ту же одностороннюю проекцию с каждой стороны. Это имеет смысл, поскольку мы используем одну и ту же матрицу вида для каждой стороны, однако я не уверен, какую матрицу вида я должен использовать. Я думаю, что они должны быть матрицами lookAt(), которые расположены в центре, и смотреть в направлении стороны карты куба. Однако возникает вопрос, каким образом я должен использовать эти несколько проекций в моем главном колл-дро.
Обновление 2
Я пошел дальше и создал эти матрицы, однако я не уверен, насколько они действительны (они были скопированы с веб-сайта для кубических карт DX, поэтому я инвертировал координату Z).
case 1://Negative X
sideViews[i] = glm::lookAt(glm::vec3(0), glm::vec3(-1,0,0),glm::vec3(0,-1,0));
break;
case 3://Negative Y
sideViews[i] = glm::lookAt(glm::vec3(0), glm::vec3(0,-1,0),glm::vec3(0,0,-1));
break;
case 5://Negative Z
sideViews[i] = glm::lookAt(glm::vec3(0), glm::vec3(0,0,-1),glm::vec3(0,-1,0));
break;
case 0://Positive X
sideViews[i] = glm::lookAt(glm::vec3(0), glm::vec3(1,0,0),glm::vec3(0,-1,0));
break;
case 2://Positive Y
sideViews[i] = glm::lookAt(glm::vec3(0), glm::vec3(0,1,0),glm::vec3(0,0,1));
break;
case 4://Positive Z
sideViews[i] = glm::lookAt(glm::vec3(0), glm::vec3(0,0,1),glm::vec3(0,-1,0));
break;
Вопрос все еще стоит, на что я должен переводить часть представления deepMVP, поскольку это 6 отдельных матриц. Вот скриншот того, как он в настоящее время выглядит, с тем же фрагментным шейдером (т.е. фактически рендерингом теней) http://i.imgur.com/HsOSG5v.png Как вы можете видеть, тени кажутся хорошими, однако позиционирование очевидно вопрос. Матрица представления, которую я использовал для генерации этого, была просто обратным переводом положения камеры (как это сделала бы функция lookAt()).
Обновление 3
Код, в том виде, в каком он сейчас стоит: Shadow Vertex
void main(){
gl_Position = depthMVP * vec4(position,1);
pos =(M * vec4(position,1)).xyz;
}
Фрагмент тени
void main(){
fragmentDepth = distance(lightPos, pos);
}
Главная вершина
void main(){
PositionWorldSpace = (M*vec4(position, 1)).xyz;
ShadowCoord = vec4(PositionWorldSpace - lightPos, 1);
}
Главный фраг
void main(){
float texDist = texture(shadowMap, ShadowCoord.xyz/ShadowCoord.w).x;
float dist = distance(lightPos, PositionWorldSpace);
if(texDist < distance(lightPos, PositionWorldSpace)
visibility = 0.1;
outColor = vec3(texDist);//This is to visualize the depth maps
}
Используемая перспективная матрица
glm::mat4 depthProjectionMatrix = glm::perspective(90.f, 1.f, 1.f, 50.f);
Все в настоящее время работает, вроде. Данные, которые хранит текстура (то есть расстояние), сохраняются странным образом. Кажется, что это нормализовано, так как все значения находятся в диапазоне от 0 до 1. Кроме того, вокруг зрителя есть область 1x1x1, у которой нет проекции, но это связано с усечением, и я думаю, что это будет легко исправить (например, смещение камеры назад.5 в центр).
1 ответ
Если вы оставите глубину фрагмента для OpenGL, чтобы определить, можете ли вы воспользоваться аппаратной иерархической Z-оптимизацией. По сути, если вы когда-нибудь напишите gl_FragDepth
в фрагментном шейдере (без использования новомодного консервативного расширения GLSL глубины) это предотвращает аппаратную оптимизацию, называемую иерархическим Z. Для краткости Hi-Z - это метод, при котором растеризация для некоторых примитивов может быть пропущена на основе того, что значения глубины для всего Примитив находится за значениями уже в буфере глубины. Но это работает, только если ваш шейдер никогда не записывает произвольное значение в gl_FragDepth
,
Если вместо того, чтобы записать расстояние фрагмента от источника света до карты куба, вы придерживаетесь традиционной глубины, теоретически вы должны получить более высокую пропускную способность (поскольку пропущенные примитивы могут быть пропущены) при написании карт теней.
Затем в фрагментном шейдере, где вы берете карту куба глубины, вы должны преобразовать значения расстояния в значения глубины, используя следующий фрагмент кода (где f и n - расстояния в дальнем и ближнем плане, которые вы использовали при создании куба глубины). карта):
float VectorToDepthValue(vec3 Vec)
{
vec3 AbsVec = abs(Vec);
float LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z));
const float f = 2048.0;
const float n = 1.0;
float NormZComp = (f+n) / (f-n) - (2*f*n)/(f-n)/LocalZcomp;
return (NormZComp + 1.0) * 0.5;
}
Код заимствован из SO вопроса: Всенаправленное отображение теней с глубиной куба
Таким образом, применяя этот дополнительный бит кода к вашему шейдеру, получится что-то вроде этого:
void main () {
float shadowDepth = texture(shadowMap, ShadowCoord.xyz/ShadowCoord.w).x;
float testDepth = VectorToDepthValue(lightPos - PositionWorldSpace);
if (shadowDepth < testDepth)
visibility = 0.1;
}