歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> OpenGL超級寶典學習筆記——陰影

OpenGL超級寶典學習筆記——陰影

日期:2017/3/1 9:32:24   编辑:Linux編程

陰影

(又稱影子背影),光線被不透明物體阻檔而產生的黑暗范圍,與光源的方向相反。影的橫切面是二維輪廓、阻檔光線物體的倒轉投影。影的大小、形狀隨光線的入射角而改變。(維基百科)在場景中添加陰影可以使得場景更加逼真。下面兩幅圖是有無陰影效果的對比:

如何制造陰影?

簡單地做法,我們可以通過把源物體壓平在物體表面所處的平面上,來制造陰影效果。然後被壓平的物體用黑色或深色進行繪制。通過操作高級矩陣的方式,來把物體壓平到其他表面上。

被壓平的效果

我們需要壓平的模型視圖投影矩陣,以使得所有被繪制的物體都會被壓平到陰影所處的二維平面上。無論物體朝哪個方向,都會被投影到陰影所在的平面上。在此需要考慮兩種情況,光源離物體的距離以及光源的照射方向。光源的方向決定了陰影的形狀和大小。就像在一天中的不同時段,人站在太陽底下,影子會隨著太陽的位置而變化。

在math3d庫中,有一個函數m3dMakePlanarShadowMatrix,它接受的參數包括一個陰影所處平面的平面方程(3個點不能位於一條直線上),光源的位置,返回的是一個變換矩陣。用當前的模型視圖矩陣乘以這個矩陣,隨後的所有被繪制的物體都會被壓平到這個投影平面上。



void m3dMakePlanarShadowMatrix(M3DMatrix44f proj, const M3DVector4f planeEq, const M3DVector3f vLightPos)
{
// These just make the code below easier to read. They will be
// removed by the optimizer.
float a = planeEq[0];
float b = planeEq[1];
float c = planeEq[2];
float d = planeEq[3];
float dx = -vLightPos[0];
float dy = -vLightPos[1];
float dz = -vLightPos[2];
// Now build the projection matrix
proj[0] = b * dy + c * dz;
proj[1] = -a * dy;
proj[2] = -a * dz;
proj[3] = 0.0;
proj[4] = -b * dx;
proj[5] = a * dx + c * dz;
proj[6] = -b * dz;
proj[7] = 0.0;
proj[8] = -c * dx;
proj[9] = -c * dy;
proj[10] = a * dx + b * dy;
proj[11] = 0.0;
proj[12] = -d * dx;
proj[13] = -d * dy;
proj[14] = -d * dz;
proj[15] = a * dx + b * dy + c * dz;
// Shadow matrix ready
}

陰影例子

為了演示陰影的效果,我們把噴氣式飛機懸空,然後在其上面放置一個光源,制造一個飛機投影到地面的陰影效果。

下面的例子,我們在SetupRC中創建一個陰影矩陣,並用全局變量保存它。在RenderScene中使用。

 
GLfloat lightPos = {-75.0f, 150.0f, -50.0f, 0.0f};
...
//陰影矩陣
M3DMatrix44f shadowMat;
...
void SetupRC()
{
  //投影平面上的三個不處於同一直線上的點
  M3DVector3f points[3] = {{-30.0f, -149.0f, -20.0f},
    {-30.0f, -149.0f, 20.0f},
    {40.0f, -149.0f, 20.0f}};
  glEnable(GL_DEPTH_TEST);
  glFrontFace(GL_CCW);
  glEnable(GL_CULL_FACE);
  //Enable lighting
  glEnable(GL_LIGHTING);
....
  //構造投影平面
  M3DVector4f vPlaneEquation;
  m3dGetPlaneEquation(vPlaneEquation, points[0], points[1], points[2]);
  //計算投影矩陣
  m3dMakePlanarShadowMatrix(shadowMat, vPlaneEquation, lightPos);
  ...
}
 

在RenderScene中,我們先繪制地面,然後畫噴氣式飛機。然後恢復模型視圖矩陣乘以陰影矩陣,然後我們再次繪制噴氣式飛機(此過程要關掉深度測試)。飛機的投影和地面處於同一個位置,在一般情況下後面畫的那個物體會疊在之前的物體上面被顯示。但有的時候,會導致z-figinting,從而產生混亂。

代碼如下:

void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//繪制地板
glBegin(GL_QUADS);
glColor3ub(0, 32, 0);
glVertex3f(400.0f, -150.0f, -200.0f);
glVertex3f(-400.0f, -150.0f, -200.0f);
glColor3ub(0, 235, 0);
glVertex3f(-400.0f, -150.0f, 200.0f);
glVertex3f(400.0f, -150.0f, 200.0f);
glEnd();
//畫懸空的噴氣式飛機
glPushMatrix();
//開啟光照
glEnable(GL_LIGHTING);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
//旋轉
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
DrawJet(0);
//關閉光照
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glPopMatrix();
//畫陰影
glPushMatrix();
//乘以壓平的陰影矩陣
glMultMatrixf((GLfloat*)shadowMat);
//旋轉
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
DrawJet(1);
glPopMatrix();
//畫光源位置,用小黃球模擬
glPushMatrix();
glTranslatef(lightPos[0], lightPos[1], lightPos[2]);
glColor3ub(255, 255, 0);
glutSolidSphere(3.0, 15, 15);
glPopMatrix();
glEnable(GL_DEPTH_TEST);
// Display the results
glutSwapBuffers();
}
運行的效果
 

OpenGL超級寶典 第4版 中文版PDF+英文版+源代碼 見 http://www.linuxidc.com/Linux/2013-10/91413.htm

OpenGL編程指南(原書第7版)中文掃描版PDF 下載 http://www.linuxidc.com/Linux/2012-08/67925.htm

OpenGL 渲染篇 http://www.linuxidc.com/Linux/2011-10/45756.htm

Ubuntu 13.04 安裝 OpenGL http://www.linuxidc.com/Linux/2013-05/84815.htm

OpenGL三維球體數據生成與繪制【附源碼】 http://www.linuxidc.com/Linux/2013-04/83235.htm

Ubuntu下OpenGL編程基礎解析 http://www.linuxidc.com/Linux/2013-03/81675.htm

如何在Ubuntu使用eclipse for c++配置OpenGL http://www.linuxidc.com/Linux/2012-11/74191.htm

更多《OpenGL超級寶典學習筆記》相關知識 見 http://www.linuxidc.com/search.aspx?where=nkey&keyword=34581

Copyright © Linux教程網 All Rights Reserved