2016-07-09 9 views
5

Я пытался реализовать что-то вроде игры Antichamber (более высокоточного этот трюк показано ниже) на прошлой неделе:OpenGL - как реализовать «портального рендеринга»

trick

Вот видео, что я надеюсь достичь (даже если это было сделано с Unreal Engine 4, я не использую это): https://www.youtube.com/watch?v=Of3JcoWrMZs

Я искал лучший способ сделать это, и я узнал о трафарете буфер. Между this и кодом this (функция drawPortals()) Я нашел онлайн, мне удалось почти реализовать его.

Он прекрасно работает с одним порталом в другую комнату (не перекрестный портал, то есть вы не можете пройти через него и быть телепортированы в другую комнату). В моем примере я рисую портал в простой квадратной комнате со сферой; за порталом есть еще одна сфера, которую я использовал, чтобы проверить, был ли буфер глубины работать правильно и рисунок его за порталом:

front

side

Проблемы возникают, когда я добавить еще один портал, рядом с этим. В этом случае мне удается правильно отобразить другой портал (освещение выключено, но сфера по праву имеет другой цвет, чтобы показать, что это еще одна сфера):

enter image description here

Но если повернуть камеру таким образом, что первый портал должен быть протянута через второй, то глубина первой становится неправильным, а второй портал получает обращается за первой, как это:

enter image description here

пока он должно быть примерно так:

enter image description here

Итак, это проблема. Я, вероятно, что-то неправильно сделал с буфером глубины, но я не могу найти что.

Мой код для рендеринга части в значительной степени это:

glClear(GL_DEPTH_BUFFER_BIT); 
glEnable(GL_STENCIL_TEST); 

// First portal 
glPushMatrix(); 

// Disable writing to the color and depht buffer; disable depth testing 
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 
glDepthMask(GL_FALSE); 
glDisable(GL_DEPTH_TEST); 

// Make sure that the stencil always fails 
glStencilFunc(GL_NEVER, 1, 0xFF); 

// On fail, put 1 on the buffer 
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); 

// Enable writing to the stencil buffer 
glStencilMask(0xFF); 

// Clean the buffer 
glClear(GL_STENCIL_BUFFER_BIT); 

// Finally draw the portal's frame, so that it will have only 1s in the stencil buffer; the frame is basically the square you can see in the pictures 
portalFrameObj1.Draw(); 

/* Now I compute the position of the camera so that it will be positioned at the portal's room; the computation is correct, so I'm skipping it */ 

// I'm going to render the portal's room from the new perspective, so I'm going to need the depth and color buffers again 
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 
glDepthMask(GL_TRUE); 
glEnable(GL_DEPTH_TEST); 
glClear(GL_DEPTH_BUFFER_BIT); 

// Disable writing to the stencil buffer and enable drawing only where the stencil values are 1s (so only on the portal frame previously rendered) 
glStencilMask(0x00); 
glStencilFunc(GL_EQUAL, 1, 0xFF); 

// Draw the room from this perspective 
portalRoomObj1.Draw(); 

glPopMatrix(); 


// Now the second portal; the procedure is the same, so I'm skipping the comments 
glPushMatrix(); 
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); 
glDepthMask(GL_FALSE); 
glDisable(GL_DEPTH_TEST); 

glStencilFunc(GL_NEVER, 1, 0xFF); 
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); 
glStencilMask(0xFF); 
glClear(GL_STENCIL_BUFFER_BIT); 

portalFrameObj2.Draw(); 

/* New camera perspective computation */ 

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 
glDepthMask(GL_TRUE); 
glEnable(GL_DEPTH_TEST); 
glClear(GL_DEPTH_BUFFER_BIT); 

glStencilMask(0x00); 
glStencilFunc(GL_EQUAL, 1, 0xFF); 

portalRoomObj2.Draw(); 

glPopMatrix(); 


// Finally, I have to draw the portals' frames once again but this time on the depth buffer, so that they won't get drawn over; first off, disable the stencil buffer 
glDisable(GL_STENCIL_TEST); 

// Disable the color buffer 
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 

glClear(GL_DEPTH_BUFFER_BIT); 

// Draw portals' frames 
portalFrameObj1.Draw(); 
portalFrameObj2.Draw(); 

// Enable the color buffer again 
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 


/* Here I draw the rest of the scene */ 

UPDATE

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

В принципе, способ, которым я рисую первый портал, таков: 1) Заполните биты портального кадра в буфере трафарета с помощью 1s; за пределами портала есть только 0s 2) Нарисуйте портал, где трафарет имеет 1 сек (так, чтобы он рисовался на портале рамы

И я повторяю это для второго портала.

Для первого портала, я получаю на шаге 1 что-то вроде этого, то (простите за глупые рисунки, я ленивый): enter image description here

Затем после шага 2:
enter image description here

Тогда я начинается со вторым порталом:
enter image description here

Но теперь, между шагом 1 и 2, я говорю трафарету рисовать только там, где биты 1s; поскольку буфер теперь очищен, я потерял следы 1-го из первого портала, поэтому, если часть кадра второго портала находится за предыдущим кадром, 1-е из вторых кадров по-прежнему будет нарисовано с комнатой второго портала, независимо от того, буфер глубины. Вид как на этой картинке: enter image description here

Я не знаю, если мне удалось объяснить это правильно ...

ответ

0

Это было некоторое время, в настоящее время. Так как не будет anwsers (возможно), я пишу об обходном пути, который я использую сейчас.

Я попытался решить проблему, используя различные функции трафаретов, а не освобождать буфер трафарета при рендеринге нового портала; идея заключалась в том, чтобы использовать тот факт, что я знал, глядя на буфер трафарета, где были предыдущие порталы, где они были визуализированы.

В конце концов, я не смог этого сделать; в примерах, которые я опубликовал в моем первоначальном вопросе, один из двух порталов всегда скрывал части другого.

Итак, я решил сделать что-то более простое: я проверяю, открыт ли портал, а затем я рисую его точно так же, как в коде, который я разместил в вопросе (так что каждый раз опуская буфер трафарета).

В псевдокоде, я делаю это:

for(var i = 0; i < PORTALS_NUMBER; i++) 
{ 
    // I get the normal to the portal's frame and its position 
    var normal = mPortalFramesNormals[i]; 
    var framePos = mPortalFrames[i].GetPosition(); 

    // I compute the scalar product between the normal and the direction vector between the camera's position and the frame's position 
    var dotProduct = normal * (currentCameraPosition - framePos); 

    // If the dot product is 0 or positive, the portal is visible 
    if(dotProduct >= 0) 
    { 
     // I render the portal 
     DrawPortal(mPortalFrames[i], mPortalRooms[i]); 
    } 
} 

glDisable(GL_STENCIL_TEST); 

// Now I draw the portals' frames in the depth buffer, so they don't get overwritten by other objects in the scene 
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 
glDepthMask(GL_TRUE); 
glEnable(GL_DEPTH_TEST); 
glClear(GL_DEPTH_BUFFER_BIT); 

for(var i = 0; i < PORTALS_NUMBER; i++) 
{ 
    mPortalFrames[i].Draw(); 
} 

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 

В моем случае, когда мне нужно, чтобы отобразить 4 порталов в общей сложности, расположенных в виде кубы (так что я знаю, что в большинстве 2 порталов непосредственно видны на время), и только одна грань кадра отображает портал, а другой - не работает, он отлично работает.

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

Если кто-то знает лучший способ сделать это, мне все равно интересно знать!

EDIT: Я узнал о других версиях функций трафарета (т.е. glStencilFuncSeparate, glStencilOpSeparate, glStencilMaskSeparate), которые позволяют делать различные вещи с задней и передней поверхностью. Мне кажется, что мне нужно будет решить проблемы, которые я описал, но я не смогу попробовать это в ближайшее время. Я просто указываю на это, если кто-то, у кого такая же проблема, бродит здесь.