歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

OpenGL超級寶典學習筆記——鏡面光與法線平均

光照效果

僅僅使用環境光和漫反射光的光照效果,噴氣式飛機表面的顏色看起來比較平淡。在渲染木材,泥土,布料,紙箱上等這些表面粗糙的物體上,使用環境光和漫反射光的光照效果就基本足夠了。但是在為光滑的金屬物體建模時,為了使其顯得更加逼真,僅僅使用環境光和漫反射光是不夠的,還需要鏡面光的效果。

鏡面亮點

鏡面光照和材料屬性可以為物體表面添加光澤和亮斑的效果。當入射光與觀察者的角度較小時,可以看到鏡面加亮的效果。鏡面亮點就是幾乎所有的光照射在物體表面上並被反射開來。添加鏡面光的成分:

GLfloat ambientLight = {0.3f, 0.3f, 0.3f, 1.0f};
Glfloat diffuseLight = {0.7f, 0.7f, 0.7f, 1.0f};
//specular light 
GLfloat specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
....
//Enable Lighting
glEnable(GL_LIGHTING);
//set up and enable light0glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glEnable(GL_LIGHT0);

specular[]為鏡面光成分指定了一個非常亮的白光。模擬太陽當空照的效果。下面的語句就是指定鏡面光的成分。

glLightfv(GL_LIGHT0, GL_SPECULAR, specular);

僅僅添加鏡面光的效果,我們不會再噴氣式飛機上看到什麼變換。我們還需要為其指定材料的鏡面光反射屬性。

鏡面發射

給材料添加鏡面反射屬性的代碼段如下:

GLfloat specref[] = {1.0f, 1.0f, 1.0f, 1.0f};
....//Enable color tracking 
glEnable(GL_COLOR_MATERIAL);
//set material properties to follow 
 glColorglColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
//使後面的材料具有完全的鏡面反射效果以及強光澤 
glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
glMateriali(GL_FRONT, GL_SHININESS, 128);
代碼在設置鏡面反射屬性之前,為材料的環境光和漫反射光屬性開啟顏色追蹤。材料的鏡面光反射屬性我們另外單獨指定了一個固定不變的值。指定材料的鏡面光反射屬性為(1.0f, 1.0f, 1.0f, 1.0f)代表著完全發射入射的鏡面光。在glMaterialfv(GL_FRONT, GL_SPECULAR, specref);之後的多邊形將采用此材料屬性。 在沒有重新調用glMaterial之前,所有的材料都將具有此屬性。  

鏡面指數

在上面的例子中在強烈的鏡面光照射和材料鏡面反射(完全發射)的效果下導致飛機顯示為純白,只有遠離光源的表面除外(未受到光照,顯示黑色)。

glMateriali(GL_FRONT, GL_SHININESS, 128);

GL_SHININESS屬性設置材料的鏡面指數,指定了鏡面加亮的大小和集中性。如果指定為0,即沒有聚焦,即整個多邊形均勻加亮。通過設置這個值可以縮小鏡面加亮的范圍,增加鏡面加亮的集中度,制造亮點的效果。這個值越小,表示材質越粗糙,點光源發射的光線照射到上面,產生較大的亮點。這個值越大,表示材質越類似於鏡面,光源照射到上面後,產生較小的亮點。這個參數值的范圍為1-128。

設置為128的效果如下:

設置為0時效果如下:(被照射到的多邊形整個都加亮了,沒被照射到的顯示黑色)

設置環境的代碼如下:
void SetupRC()
{
  // Light values and coordinates 
  GLfloat  ambientLight[] = { 0.3f, 0.3f, 0.3f, 1.0f };
  GLfloat  diffuseLight[] = { 0.7f, 0.7f, 0.7f, 1.0f };  
  GLfloat  specular[] = {1.0f, 1.0f, 1.0f, 1.0f};  
  glEnable(GL_DEPTH_TEST);   
  // Hidden surface removal  
  glFrontFace(GL_CCW);        
  // Counter clock-wise polygons face out  
  glEnable(GL_CULL_FACE);        
 
  // Enable lighting  
  glEnable(GL_LIGHTING);  
  // Setup and enable light 0  
  glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight);  
  glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight);  
  glLightfv(GL_LIGHT0, GL_SPECULAR, specular);  
  glEnable(GL_LIGHT0);  
  GLfloat specref[] = {1.0f, 1.0f, 1.0f, 1.0f};  
  // Enable color tracking  
  glEnable(GL_COLOR_MATERIAL);  
  // Set Material properties to follow glColor values  
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);    
  glMaterialfv(GL_FRONT, GL_SPECULAR, specref);  
  glMateriali(GL_FRONT, GL_SHININESS, 128);  
  // Light blue background  
  glClearColor(0.0f, 0.0f, 1.0f, 1.0f );  
  glEnable(GL_NORMALIZE);
}

法線平均

由四邊形和三角形組成的球體,如果為其的每一個表面都單獨指定了法線,那這個球體看起來就是充滿稜角。如果我們為每個頂點指定“真實的”法線,那麼OpenGL光照計算的所產生的值將會平滑地分布在多邊形表面上。這些平面的多邊形經過著色之後就像平滑的表面一樣。

在理論上,如果我們使用足夠多的多邊形來繪制球體,那球體表面就會顯得平滑,這些多邊形就近似於真實的表面。就像使用足夠多的小短線來模擬平滑的曲線一樣。如果把每個頂點都當成真實的表面上的頂點的話,那麼這個表面的實際法線值就代表著真實表面的法線。在球體中,法線從球體的中心指向各個頂點。

下圖圖5.30中的,每一個平面片段都有法線垂直指向它的表面。就像在噴氣式飛機中的例子一樣。在圖5.31中法線並不正交於多邊形的平面,而是正交於真實球體的表面,或者球體表面的切線。

在球體中,計算真實法線較為簡單,球體中心和多邊形頂點的連線即是。但在一些復雜的物體中,就沒那麼簡單了。需要取得共享一個頂點的多邊形的法線,對其進行平均,來獲得更加真實的效果。

綜合例子

設置光源位置

//光源位置 GLfloat lightPos[] = {0.0f, 0.0f, 70.0f, 1.0f};
..
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);

lightPos數組的前三個值x,y,z指定了光源的位置或者方向。如果最後一個值為1.0,說明光源的真實位置就位於lightPos上,是位置性光源,光線從光源發散開來。如果最後一個值為0.0則代表光在無限遠處,光源是方向性光源,所有光線是平行的。  

創建聚光燈

GLfloat ambientLight[] = {0.5f, 0.5f, 0.5f, 1.0f};
GLfloat specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
GLfloat specref[] = {1.0f, 1.0f, 1.0f, 1.0f};
//光源位置 
GLfloat lightPos[] = {0.0f, 0.0f, 70.0f, 1.0f};
//聚光燈光照方向朝z軸負方向 
GLfloat spotDir[] = {0.0f, 0.0f, -1.0f};
void SetupRC()
{  
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);  
  //開啟深度測試  
  glEnable(GL_DEPTH_TEST);  
 
  //剔除掉不必要的背面  
  glFrontFace(GL_CCW);  
  glEnable(GL_CULL_FACE);  
  glCullFace(GL_BACK);  
 
  //開啟光照  
  glEnable(GL_LIGHTING);  
 
  //設置light0光照參數  
  glLightfv(GL_LIGHT0, GL_AMBIENT_AND_DIFFUSE, ambientLight);  
  glLightfv(GL_LIGHT0, GL_SPECULAR, specular);  
 
  //設置光源位置和方向  
  glLightfv(GL_LIGHT0, GL_POSITION, lightPos);  
  glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spotDir);  
  glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 80.0f);  
 
  //設置全局環境光  
  glLightModelfv(GL_AMBIENT, ambientLight);  
  //開啟light0  
  glEnable(GL_LIGHT0);    
 
  //開啟和設置顏色追蹤  
  glEnable(GL_COLOR_MATERIAL);  
 
  //設置多邊形正面的材料的環境光和漫反射光屬性為顏色追蹤  
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); 
 
  //設置鏡面光的反射屬性  
  glMaterialfv(GL_FRONT, GL_SPECULAR, specref);  
  glMateriali(GL_FRONT, GL_SHININESS, 128);  
}

glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 80.0f);設置聚光燈的半夾角,使得聚光燈發射出一個光錐。在此光錐之外的物體不會被照射到。
 

在這個例子中,我們使用一個黃色的小燈泡的方式來模擬光源的位置,通過方向鍵可以調整其位置。並通過右鍵菜單來調整,物體的著色模式,和球體的近似程度(由多少多邊形組成球體)。

GL_FLAT著色模式

GL_SMOOTH著色模式

完整代碼示例:

#include "gltools.h"
GLfloat ambientLight[] = {0.5f, 0.5f, 0.5f, 1.0f};
GLfloat specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
GLfloat specref[] = {1.0f, 1.0f, 1.0f, 1.0f};
//光源位置 
GLfloat lightPos[] = {0.0f, 0.0f, 70.0f, 1.0f}; 
//聚光燈光照方向朝z軸負方向
GLfloat spotDir[] = {0.0f, 0.0f, -1.0f}; 
//用於旋轉 
static GLfloat xRot = 0.0f; 
static GLfloat yRot = 0.0f; 
#define FLAT_MODE 1 
#define SMOOTH_MODE 2 
#define LOW_LEVEL 3 
#define MEDIUM_LEVEL 4 
#define HIGH_LEVEL 5 
int iShade = FLAT_MODE; 
int iLevel = LOW_LEVEL; 
void SetupRC()
{
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 
  //開啟深度測試 
  glEnable(GL_DEPTH_TEST); 
  //剔除掉不必要的背面 
  glFrontFace(GL_CCW);
  glEnable(GL_CULL_FACE);
  glCullFace(GL_BACK); 
  //開啟光照 
  glEnable(GL_LIGHTING); 
  //設置light0光照參數 
  glLightfv(GL_LIGHT0, GL_AMBIENT_AND_DIFFUSE, ambientLight);
  glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
  //設置光源位置和方向 
  glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
  glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spotDir);
  glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 80.0f); 
  //設置全局環境光 
  glLightModelfv(GL_AMBIENT, ambientLight); 
  //開啟light0 
  glEnable(GL_LIGHT0); 
  //開啟和設置顏色追蹤 
  glEnable(GL_COLOR_MATERIAL); 
  //設置多邊形正面的材料的環境光和漫反射光屬性為顏色追蹤 
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); 
  //設置鏡面光的反射屬性 
  glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
  glMateriali(GL_FRONT, GL_SHININESS, 128);

  glEnable(GL_NORMALIZE);
} 

void RenderScene()
{ if (iShade == FLAT_MODE)
  {
    glShadeModel(GL_FLAT);
  } else {
    glShadeModel(GL_SMOOTH);
  }
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
  //矩陣壓棧 
  glPushMatrix();

    glRotatef(xRot, 1.0f, 0.0f, 0.0f);
    glRotatef(yRot, 0.0f, 1.0f, 0.0f); 
   //旋轉後重新設置光源位置
   glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
    glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spotDir);

    glTranslatef(lightPos[0], lightPos[1], lightPos[2]);
    glColor3ub(255, 0, 0);
    glutSolidCone(4.0, 6.0, 15, 15);

    glPushAttrib(GL_LIGHTING_BIT); 
   //關閉光照畫小黃球 
      glDisable(GL_LIGHTING);
      glColor3ub(255, 255, 0);
      glutSolidSphere(3.0, 15, 15);
    glPopAttrib(); 
	//矩陣出棧
	glPopMatrix();
  glColor3ub(0, 0, 255); 
  if (iLevel == LOW_LEVEL)
  {
    glutSolidSphere(30.0f, 8, 8);
  } else if (iLevel == MEDIUM_LEVEL)
  {
    glutSolidSphere(30.0f, 10, 10);
  } else {
    glutSolidSphere(30.0f, 15, 15);
  }

  glutSwapBuffers();
}

 void ChangeSize(int w, int h)
{ 
if (h == 0)
  {
    h = 1;
  } 
  //設置視口 
  glViewport(0, 0, w, h);
  GLfloat faspect = (GLfloat)w/(GLfloat)h; 
  //透視投影變換 
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(35.0, faspect, 1.0, 350.0); 
  //模型視圖矩陣 
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity(); 
  //先往顯示器裡平移 
  glTranslatef(0.0f, 0.0f, -250.0f);

  glutPostRedisplay();
} 

void SpecialKeys(int keys, int x, int y)
{ 
//改變旋轉角度 
  if (keys == GLUT_KEY_UP)
  {
    xRot -= 5.0f;
  } if (keys == GLUT_KEY_DOWN)
  {
    xRot += 5.0f;
  } if (keys == GLUT_KEY_LEFT)
  {
    yRot += 5.0f;
  } if (keys == GLUT_KEY_RIGHT)
  {
    yRot -= 5.0f;
  } if (xRot > 356.0f)
  {
    xRot = 0.0f;
  } else if (xRot < -1.0f)
  {
    xRot = 355.0f;
  } if (yRot > 356.0f)
  {
    yRot = 0.0f;
  } else if (yRot < -1.0f)
  {
    yRot = 355.0f;
  }

  glutPostRedisplay();
} 

void ProcessMenu(int key)
{ 
switch(key)
  { case 1:
    iShade = FLAT_MODE; break; case 2:
    iShade = SMOOTH_MODE; break; case 3:
    iLevel = LOW_LEVEL; break; case 4:
    iLevel = MEDIUM_LEVEL; break; case 5:
    iLevel = HIGH_LEVEL; break; default: break;
  }
  glutPostRedisplay();
} 

int main(int args, char *arv[])
{
  glutInit(&args, arv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowSize(800, 600);
  glutCreateWindow("spot light");
  glutDisplayFunc(RenderScene);
  glutReshapeFunc(ChangeSize);
  glutSpecialFunc(SpecialKeys); 
  //創建菜單
  int menuID = glutCreateMenu(ProcessMenu);
  glutAddMenuEntry("Flag Mode", 1);
  glutAddMenuEntry("SMOOTH Mode", 2);
  glutAddMenuEntry("Low Level", 3);
  glutAddMenuEntry("Midum Level", 4);
  glutAddMenuEntry("High Level", 5);
  glutAttachMenu(GLUT_RIGHT_BUTTON);
  SetupRC();
  glutMainLoop(); 
  return 0;
}

 

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