OpenGL Как записать в буфер трафарета, если тест трафарета завершился неудачно, а тест глубины прошел успешно?

Я пытаюсь реализовать в своей программе эффект, в котором я визуализирую силуэт объекта, когда он находится позади других объектов, аналогично тому, как это делается здесь: https://i.pinimg.com/originals/87/8f/c4/878fc47a5bd6d62dfbaec35520cb4d9f

Я хочу записать в буфер трафарета фрагменты объекта куба, которые проходят тест трафарета, но не проходят тест глубины.

Это моя реализация:

glBindFramebuffer(GL_FRAMEBUFFER, this->screenFBO);
glEnable(GL_DEPTH_TEST);
glStencilOp(GL_KEEP,    // stencil fail
            GL_REPLACE, // stencil pass, depth fail
            GL_KEEP);   // stencil pass, depth pass

glClearStencil(0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

glStencilMask(0x00);
glDisable(GL_STENCIL_TEST);
this->renderEntity(entityBuffer[0]); // skybox
glEnable(GL_STENCIL_TEST);

glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilMask(0xFF);

this->renderEntity(entityBuffer[3]); // white cube

glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00);
// render rest of the models

Буфер кадра готов, буфер трафарета работает, как и предполагалось, поскольку я уже тестировал с помощью учебника из https://learnopengl.com/Advanced-OpenGL/Stencil-testing

Но, как я его настроил, он ничего не отображает в буфере трафарета, что я могу проверить через RenderDoc.

Вывод кода (куб остается за сферой)

Что я делаю неправильно? и заранее спасибо за уделенное время!


person Joshua Micheletti    schedule 25.11.2020    source источник
comment
Мне удалось заставить эффект работать, хотя для этого мне пришлось сначала визуализировать круг, чтобы создать маску в буфере трафарета, затем отключить буфер глубины и нарисовать сначала куб силуэта, где буфер трафарета равен 1, а затем нормальный куб, где буфер трафарета равен 0, хотя при этом я теряю информацию о глубине объекта   -  person Joshua Micheletti    schedule 26.11.2020


Ответы (1)


Наконец-то он полностью заработал! Решением для меня было сначала нарисовать все в буфере цвета и буфере глубины, но не в буфере трафарета (в основном просто включить тест глубины перед рисованием и установить glStencilMask (0)). Затем нарисуйте элемент, который я хочу выделить, убедившись, что я запишите его в буфер трафарета с помощью glStencilMask (255) и убедитесь, что он проходит тест трафарета с помощью glStencilFunc (GL_ALWAYS, 1, 255)

Затем отключите тест глубины для визуализации блестящего контура / формы для визуализации через другие объекты с помощью glStencilMask (0) и убедитесь, что он не пройдет проверку трафарета при визуализации поверх другого куба, используя glStencilFunc (GL_NOTEQUAL , 1, 255)

Визуализируйте блестящий куб, снова включите тест глубины, и он работает!

Также убедитесь, что для glStencilOp установлено значение: glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE);

Таким образом, буфер трафарета будет записан только тогда, когда запись элемента в буфер трафарета не будет перекрыта другими объектами (не пройден тест глубины).

Для меня самой большой проблемой было выяснить, почему буфер трафарета будет обновляться, даже если мой объект будет закрыт другими объектами, и о чем я не подумал, так это о том, что я рендерил его раньше других объектов, поэтому было нечего перекрывать это когда он был визуализирован! поэтому убедитесь, что вы сначала визуализируете свою сцену, а затем ваши объекты, которые будут иметь это свойство, чтобы вы могли использовать функции glStencilOp

glBindFramebuffer(GL_FRAMEBUFFER, this->screenFBO);
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
glStencilOp(GL_KEEP,    // stencil fail
            GL_KEEP, // stencil pass, depth fail
            GL_REPLACE);   // stencil pass, depth pass

glStencilMask(255);
glClearStencil(0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

glStencilMask(0);

this->renderEntity(entityBuffer[0]); // skybox

glStencilMask(0);
glStencilFunc(GL_ALWAYS, 1, 255);
this->renderEntity(entityBuffer[6]); // sphere
this->renderEntity(entityBuffer[1]);
this->renderEntity(entityBuffer[2]);
this->renderEntity(entityBuffer[4]);
this->renderEntity(entityBuffer[5]);
this->renderEntity(entityBuffer[7]);
this->renderEntity(entityBuffer[8]);
this->renderEntity(entityBuffer[9]);
this->renderEntity(entityBuffer[10]);
this->renderEntity(entityBuffer[11]);
this->renderEntity(entityBuffer[12]);

glStencilFunc(GL_ALWAYS, 1, 255);
glStencilMask(255);
this->renderEntity(entityBuffer[3]); // normal cube

glStencilMask(0);
glStencilFunc(GL_NOTEQUAL, 1, 255);

glDisable(GL_DEPTH_TEST);
entityBuffer[3]->setShader(5);
entityBuffer[3]->setScale(glm::vec3(1.05));
this->renderEntity(entityBuffer[3]); // white cube
entityBuffer[3]->setScale(glm::vec3(1.0));
entityBuffer[3]->setShader(0);
glEnable(GL_DEPTH_TEST);

результат можно увидеть здесь

person Joshua Micheletti    schedule 26.11.2020