使用Stencil Buffer實現Reflection
來源:互聯網
上載者:User
理解Stencil Buffer可不是件容易的事,正確靈活的運用就更有技巧性了。Stencil主要是在Reflection、Refraction、Shodow creation應用很廣。網上也有一些樣本,就Planar Reflection有固定的模式,但知其然不知其所以然就無法建立更複雜多變的情境了。下面就最簡單的Planar Reflection說說吧。
Beware that reflection will reverse vertex ordering for the faces in the object, so the face culling state for the reflection scene must be set to the reverse of the state for the original scene。必須注意的是,反射將反轉物體表面的頂點順序,所以反射情境中物體表面的culling state必須置為原始情境中culling state的相反值。 如何得到相對reflector的virtual object呢(這是相對視點獨立的操作,只考慮物體和反射面的相對位置)?這個過程可以分解成三個步驟:1、 把物體轉變到反射面(reflector)的局部座標系中,再把反射面平移至原點,並旋轉使得反射面和XY平面重合。2、 glScalef(1.0f, 1.0f, -1.0f);3、 第一步變換的逆操作,使反射面回到原來位置。相關代碼列與下面。 通過反射矩陣(Reflector Matrix)也可求得virtual object: P為反射平面上的一點,V是垂直反射面的向量。物體頂點乘以R得到反射物體。 我們可以做個小的案例。假設,反射面就是XY平面,則V=(0,0,1);取反射面上的任一點為P=(2,1,0),則P·V=0;可以求得反射矩陣R為:| 1 0 0 0 || 0 1 0 0 || 0 0 -1 0 || 0 0 0 1 |,當物體頂點乘以R時只是改變了其Z方向,和上面方法的第二步glScalef(1.0f, 1.0f, -1.0f);效果一樣。由此可以知上面兩種求virtual object效果一樣。 在得到反射物體的虛頂點(virtual vertex)後,就該對情境中的反射物體進行渲染了。主要的工作是對反射物體的剪下,使其只能在反射面內可見。方法一般有兩種:對模板緩衝(Stencil Buffer)的使用,或使用紋理圖投影到Reflector上,用Reflector的邊界對反射圖象進行剪下。使用模板緩衝對反射物體進行剪下比較好理解。//先把reflector地區的stencil buffer設定為1,其他地區為0,且不顯示出來glColorMask(0,0,0,0);glEnable(GL_STENCIL_TEST);glStencilFunc(GL_ALWAYS, 1, 1);glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);glDisable(GL_DEPTH_TEST);glPushMatrix(); glMultMatrixd(reflector->matrix()); DrawFloor(); DrawFloor2();glPopMatrix(); //渲染virtual object,使物體轉變至反射面的局部座標系中virtualObject->SetParentFrame(dynamic_cast<Frame*>(reflector));virtualObject2->SetParentFrame(dynamic_cast<Frame*>(reflector));virtualObject->SetPositionAndOrientation(object->position(), object->orientation());virtualObject2->SetPositionAndOrientation(object->position(), object->orientation()); // glScalef(1.f, 1.f, -1.f)實現反射的過程 // glPushMatrix()/glPopMatrix()實現步驟三 glPushMatrix(); glMultMatrixd(reflector->matrix()); glScalef(1.f, 1.f, -1.f); glMultMatrixd(virtualObject->matrix()); DrawObject(); glPopMatrix(); glPushMatrix(); glMultMatrixd(reflector->matrix()); glScalef(1.f, -1.f, 1.f); //注意:DrawFloor2()繪製在XZ平面,所以在Y方向反射 glMultMatrixd(virtualObject2->matrix()); DrawObject(); glPopMatrix(); glDisable(GL_STENCIL_TEST); //渲染情境的其他部分 glEnable(GL_BLEND); glColor4f(1.f, 1.f, 1.f, 0.8f); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glPushMatrix(); glMultMatrixd(reflector->matrix()); DrawFloor(); DrawFloor2(); glPopMatrix(); glDisable(GL_BLEND); glPushMatrix(); glMultMatrixd(object->matrix()); DrawObject(); glPopMatrix(); 實現後如下: 當物體穿越反射面時,對應的virtual object也會從反射面穿出。NEHE解決這種問題的方法是定義個Clip Plane(平面由方程Ax+By+Cz+D=0的係數確定),若反射面為非特殊平面,則A、B、C、D係數難以確定。可以考慮增加碰撞檢測,使其不能穿過反射面。see also:http://www.bluevoid.com/opengl/sig00/advanced00/notes/node164.html