1. 程式人生 > >OpenGL陰影貼圖

OpenGL陰影貼圖


2. 設定光源為觀察點, 得到其投影矩陣和模型檢視矩陣, 繪製模型, 得到其深度紋理
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
     gluPerspective(fieldOfView, 1.0f, nearPlane, nearPlane + 300.0f);
     glGetFloatv(GL_PROJECTION_MATRIX, lightProjection);

     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
     gluLookAt(lightPos[0], lightPos[1], lightPos[2], 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
     glGetFloatv(GL_MODELVIEW_MATRIX, lightModelview);
     glViewport(0, 0, shadowSize, shadowSize);
     glClear(GL_DEPTH_BUFFER_BIT);
     glShadeModel(GL_FLAT);
     glDisable(GL_LIGHTING);
     glDisable(GL_COLOR_MATERIAL);
     glDisable(GL_NORMALIZE);
     glColorMask(0, 0, 0, 0);
     glEnable(GL_POLYGON_OFFSET_FILL);
     DrawModels();
     glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 0, 0, shadowSize, shadowSize, 0);
     glShadeModel(GL_SMOOTH);
     glEnable(GL_LIGHTING);
     glEnable(GL_COLOR_MATERIAL);
     glEnable(GL_NORMALIZE);
     glColorMask(1, 1, 1, 1);
     glDisable(GL_POLYGON_OFFSET_FILL);
3. 設定紋理矩陣

     glMatrixMode(GL_TEXTURE);
     glLoadIdentity();
     glTranslatef(0.5f, 0.5f, 0.5f);
     glScalef(0.5f, 0.5f, 0.5f);
     // Proj * Model 將紋理座標轉到世界空間
     glMultMatrixf(lightProjection);
     glMultMatrixf(lightModelview);
    
     這是最重要的一個步驟, 後面通過glTexGen()函式生成四個座標(s, t, r, q). 該四個座標經過轉換後為場景中模型的場景空間座標
     現在我們需要將該座標轉換成以光源為原點的笛卡爾座標系中的座標
     乘以lightModelview矩陣則會導致 (s, t, r, q)座標轉換到以光源為原點的笛卡爾座標系中的座標
     而後lightProjection矩陣則得到投影裁剪座標, x, y, z的範圍都為[-1.0, 1.0]
     經過scale, 範圍為[-0.5, 0.5], glTranslate則為[0.0, 1.0], 這樣其就可以與深度陰影紋理一一對應了

4. 首先繪製圖形的陰影部分, 即用很暗淡的環境過繪製一遍物體
          glLightfv(GL_LIGHT0, GL_AMBIENT, lowAmbient);
          glLightfv(GL_LIGHT0, GL_DIFFUSE, lowDiffuse);
          DrawModels();    
5. 啟用Alpha測試, 設定紋理引數, 啟用紋理生成, 而後繪製模型
     在繪製模型的時候模型上的一點生成紋理座標(s, t, r, q), 該紋理座標也對應於場景空間的座標, 經過轉換為(s', t', r', q')為光源觀察點的裁剪座標
     由於呼叫了 GL_COMPARE_R_TO_TEXTURE 比較模式, 則 r' 和 深度陰影紋理的 D值進行比較, 預設比較模式為GL_LEQUAL
     由於 r' 也是場景空間中該點在光源觀察點的深度值. 當 r' < D 時, 場景中該點的紋理值為{1.0f, 1.0f, 1.0f, 1.0f}, 否則為(0.0f, 0.0f, 0.0f, 0.0f)
     由於啟用了 GL_ALPHA_TEST 和 GL_MODULATE, 所以當紋理為(1.0f, 1.0f, 1.0, 1.0)時, 經過光照和紋理, 該點畫素的alpa必然為1.0, 繪製該點. 其他地方則不繪製

          glAlphaFunc(GL_GREATER, 0.90);
          glEnable(GL_ALPHA_TEST);
          glEnable(GL_TEXTURE_2D);
          glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
          glEnable(GL_TEXTURE_GEN_S);
          glEnable(GL_TEXTURE_GEN_T);
          glEnable(GL_TEXTURE_GEN_R);
          glEnable(GL_TEXTURE_GEN_Q);
          DrawModels();
6. 最後禁用紋理生成
          glDisable(GL_ALPHA_TEST);
          glDisable(GL_TEXTURE_2D);
          glDisable(GL_TEXTURE_GEN_S);
          glDisable(GL_TEXTURE_GEN_T);
          glDisable(GL_TEXTURE_GEN_R);
          glDisable(GL_TEXTURE_GEN_Q);

總結: 陰影深度紋理的本質就是生成一張深度圖, 描述場景中最靠近光源的各個點. 而後通過紋理生成的方法, 場景中每個點都生成一個紋理座標(s, t, r, q)
     該座標經過轉換為光源觀察點的裁剪座標(s', t', r', q'), 而後 r' 和深度紋理對應的值進行比較, 而後通過比較, 設定場景中該點的紋理值是(1.0, 1.0, 1.0, 1.0),
     還是(0.0, 0.0, 0.0, 0.0), 凡是紋理值為(1.0, 1.0, 1.0, 1.0)則是光照區域, 否則就是陰影區域
     另外本例的紋理生成方式可以使用 GL_EYE_* 方式, 也可以使用 GL_OBJECT_* 方式, 前者要和 DrawModel()  在一起, 這樣視覺空間座標就會自動轉換成場景空間座標
     不過在使用 GL_OBJECT_* 容易出現陰影重疊問題, 這個問題我百思不得其解


glTexGeni 函式
     控制紋理座標的生成
          void glTexGeni(GLenum coord, GLenum pname, GLint param);    
          coord: GL_S, GL_T, GL_R, GL_Q.
          pname: GL_TEXTURE_GEN_MODE
          param: GL_OBJECT_LINEAR, GL_EYE_LINEAR, GL_SPHERE_MAP, GL_NORMAL_MAP, GL_REFLECTION_MAP
          GL_OBJECT_LINEAR:
               使用函式 g = p1x0 + p2y0 + p3z0 + p4w0
                    g 是最後算出的座標值, p1, p2, p3, p4 是給出的平面方程引數, x0, y0, z0, w0 則是物體的場景座標
          GL_EYE_LINEAR:
               使用函式 g = p1'xe + p2'ye + p3'ze + p4'we
                    [p1' p2' p3' p4'] = (p1 p2 p3 p4)M', M'為逆矩陣
               xe, ye, ze, we 是頂點的視覺座標, M 是呼叫 glTexGen 的模型檢視矩陣, 注意當矩陣不可逆時, 會產生不可預料的結果
               注意, p1, p2, p3, p4 定義了一個視覺座標系中的一個平面, 模型檢視矩陣可以應用於這些引數
          啟用和禁止 GL_TEXTURE_GEN_S, GL_TEXTURE_GEN_T, GL_TEXTURE_GEN_R, GL_TEXTURE_GEN_Q