update: 2012/04/08
reference:
1. 原文:
iPhone Development: OpenGL ES From the Ground Up, Part 4: Let There Be Light!
2. 翻譯:
從零開始學習OpenGL ES之四 – 光效
光效之二: 光源位置與點光源
A. 位置
1. 光源 3D 空間中的位置, 是光效的另一個重要屬性. 它不會影響環境元素,
但對於散射與鏡射元素而言, 只有在 OpenGL 知道了場景中物體與光的
相對位置後才能計算.
例如: 將第一個光源放置在觀察者後方的右上角
const GLfloat light0Position[] = {10.0, 10.0, 10.0, 0.0};
glLightfv(GL_LIGHT0, GL_POSITION, light0Position);
2. 光線的屬性設定: 環境, 散射, 鏡射與位置等. 如果你沒有設定其中一個元素,
那麼它就採用預設值: 黑色 {0.0, 0.0, 0.0, 1.0}. 如果你沒有定義位置, 那麼
它就處於原點.
3. 在計算散射光確定光線是怎樣反射時, 需要用到 alpha 值. 現在先將 alpha
設為1.0, 之後當討論到材質時會說明它的作用.
-----------------------------------------------------------------------------------
B. 建立點光源(聚光燈)
1. 說明: 如果你希望建立一個定向點光源 (一種指向特定方向並照亮特定角度範圍
的光源, 本質上, 它與燈泡照亮各個方向相反, 它只照亮一個錐台的範圍),
那麼你需要設定兩個額外參數: GL_SPOT_DIRECTION 與 GL_SPOT_CUTOFF.
a. 設定 GL_SPOT_DIRECTION 允許你指定光照的方向.
b. 設定 GL_SPOT_CUTOFF 定義了光的擴張範圍, 它類似於之前文章中介紹的
視野角度的計算. 窄角度將產生很小範圍的點光源, 而寬角度則產生像
泛光燈一樣的效果.
----------------------------------------------------------------------------------
2. 指定光的方向:
a. 通過指定來定義光線指向的 x, y 和 z 值以使 GL_SPOT_DIRECTION 工作.
然而, 光線並不指向你在空間中定義的那一點. 你提供的三個坐標值是向量
(vector), 而非頂點. 這是一個很細微但十分重要的區別. 一個代表向量的
資料結構與一個頂點的資料結構完全一樣 (都有三個 GLfloats,其中每一個
分別是笛卡爾的一個軸) 然而, 向量的資料是用來表示方向而不是空間中的一點.
b. 兩點可以定義一條線段, 那麼空間中的一點怎麼可能指定方向? 這是因為存在
一個隱性的第二點作為起點, 即原點. 如果你從原點畫一條線到向量定義的點,
那就是向量代表的方向. 向量還可用於表示速度和距離, 一個遠離原點的點
表示速度越快或距離更遠. 在大部分的 OpenGL 應用中, 並未使用距離原點
的距離. 實際上, 在大部分使用向量的情況下, 我們需要將向量標準化
(normaliz)為長度 1.0. 目前, 如果你希望定義一個定向光, 那麼就必須建立
一個定義了光的方向的向量.
例如: 定義一個沿 z 軸而下的光源:
const GLfloat light0Direction = {0.0, 0.0, -1.0};
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light0Direction);
c. 讓光線指向一個特定物體
(1). 開啓 OpenGLESCommon.h 檔案, 修改如下:
....
#pragma mark -
#pragma mark Vector3D
#pragma mark -
typedef Vertex3D Vector3D;
#define Vector3DMake(x,y,z) (Vector3D)Vertex3DMake(x, y, z)
#define Vector3DSet(vector,x,y,z) Vertex3DSet(vector, x, y, z)
static inline GLfloat Vector3DMagnitude(Vector3D vector)
{
return sqrtf((vector.x * vector.x) + (vector.y * vector.y) + (vector.z * vector.z));
}
static inline void Vector3DNormalize(Vector3D *vector)
{
GLfloat vecMag = Vector3DMagnitude(*vector);
if ( vecMag == 0.0 )
{
vector->x = 1.0;
vector->y = 0.0;
vector->z = 0.0;
return;
}
vector->x /= vecMag;
vector->y /= vecMag;
vector->z /= vecMag;
}
static inline Vector3D Vector3DMakeWithStartAndEndPoints(Vertex3D start, Vertex3D end)
{
Vector3D ret;
ret.x = end.x - start.x;
ret.y = end.y - start.y;
ret.z = end.z - start.z;
Vector3DNormalize(&ret);
return ret;
}
....
(2). 使用方式:
將光的位置和物體的位置傳入 Vector3DMakeWithStartAndEndPoints()
方法中, 它將傳回一個被光照射到的指定點的標準化向量. 然後, 再將其
作為 GL_SPOT_DIRECTION 的值.
----------------------------------------------------------------------------------
3. 指定光的角度:
a. 說明: 除非你限制光照的角度, 否則指定光的方向並不會產生顯著的效果.
因為當你指定 GL_SPOT_CUTOFF 值時, 它定義了中心線兩邊的角度,
所以如果你指定遮光角(cutoff angle)時, 它必須小於 180 度. 如果你的
定義為 45 度, 那麼實際上你創建了一個總角度為 90 度的點光源.
這意味著你可設定的 GL_SPOT_CUTOFF 的最大值為 180 度.
b. 例子:
限制角度為 90 度(使用 45 度遮光角):
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 45.0);
c. 備註: 還有三個光的屬性可以設置, 它們是一起配合使用的. 有機會的話, 會在
有關光衰減 (光線隨着遠離光源而減弱) 的文章中討論到. 通過調整
衰減值可以產生很漂亮的效果.
-----------------------------------------------------------------------------------
C. 設定光源
1. 開啓 ViewController.m 檔案, 修改如下:
....
-(void)setupView:(GLView *)view
{
NSLog(@"step 05. ViewController => setupView:");
const GLfloat zNear = 0.01, zFar = 1000.0, fieldOfView = 45.0; // 設定為 45 度視野
GLfloat size;
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0);
CGRect rect = view.bounds;
// 設定透視 viewport (基於視野角度計算錐台)
glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size /
(rect.size.width / rect.size.height), zNear, zFar);
// 建構一個對應的座標系統
glViewport(0, 0, rect.size.width, rect.size.height);
glMatrixMode(GL_MODELVIEW);
//@add for 啟動光效
glEnable(GL_LIGHTING);
//@add for 啟動第一個光源
glEnable(GL_LIGHT0);
//@add for setup light
//
// 定義第一個光源的環境光
// Define the ambient component of the first light
const GLfloat light0Ambient[] = {0.1, 0.1, 0.1, 1.0};
glLightfv(GL_LIGHT0, GL_AMBIENT, light0Ambient);
// 定義第一個光源的散射光
// Define the diffuse component of the first light
const GLfloat light0Diffuse[] = {0.7, 0.7, 0.7, 1.0};
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0Diffuse);
// 定義第一個光源的鏡射光與亮度
// Define the specular component and shininess of the first light
const GLfloat light0Specular[] = {0.7, 0.7, 0.7, 1.0};
const GLfloat light0Shininess = 0.4;
glLightfv(GL_LIGHT0, GL_SPECULAR, light0Specular);
glLightfv(GL_LIGHT0, GL_SHININESS, &light0Shininess);
// 定義第一個光源的位置
// Define the position of the first light
const GLfloat light0Position[] = {0.0, 10.0, 10.0, 0.0};
glLightfv(GL_LIGHT0, GL_POSITION, light0Position);
// 定義第一個光源的方向向量: 沿 z 軸而下
// Define a direction vector for the light, this one points right down the Z axis
const GLfloat light0Direction[] = {0.0, 0.0, -1.0};
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light0Direction);
// 定義第一個光源的遮光角: 限制角度為 90 度(使用 45 度遮光角)
// Define a cutoff angle. This defines a 90° field of vision, since the cutoff
// is number of degrees to each side of an imaginary line drawn from the light's
// position along the vector supplied in GL_SPOT_DIRECTION above
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 45.0);
glLoadIdentity();
}
....
2. 編譯並執行:
結果還是跟之前一樣: 白, 灰, 黑的循環效果.
沒有留言:
張貼留言
注意:只有此網誌的成員可以留言。