繼續我們的Android 3D之旅,我們將討論光效。從今天開始我們將逐漸添加光效。
光效三要素
在 OpenGL ES中,光由三個元素組成,分別是環境元素(ambient component), 散射元素(diffuse component)和 高光元素(specular component)。我們使用顏色來設定光線元素,這看上去有些奇怪,但是由於它允許你同時指定各光線元素的顏色和相對強度,這個方法工作得很好。明亮的白色光定義為白色 ({1.0, 1.0, 1.0, 1.0}),而暗白色可能定義為灰色 ({0.3, 0.3, 0.3 1.0})。 你還可以通過改變紅,綠,藍元素的百分比來調整色偏。
下圖說明了各要素產生的效果。
高光元素定義了光線直接照射並反射到觀察者從而形成了物體上的“熱點”或光澤。光點的大小取決於一些因素,但是如果你看到如上圖黃球所示一個區域明顯的光斑,那通常就是來自於一個或多個光源的高光部分。
散射元素定義了比較平均的定向光源,在物體面向光線的一面具有光澤。
環境光則沒有明顯的光源。其光線折射與許多物體,因此無法確定其來源。環境元素平均作用於場景中的所有物體的所有面。
環境光
你的光效中有越多的環境元素,那麼就越不會產生引入注目的效果。所有光線的環境元素會融合在一起產生效果,意思是場景中的總環境光效是由所有啟動光源的環境光組合在一起所決定的。如果你使用了不止一個光源,那麼最好是只指定一個光源的環境元素,而設定其他所有光源的環境因素為黑 ({0.0, 0.0, 0.0, 1.0}),從而很容易地調整場景的環境光效。
下面演示了怎樣指定一個很暗的白色光源:
float lightAmbient[] = new float[] { 0.2f, 0.3f, 0.6f, 1.0f }; //環境光 gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient, 0);
使用像這樣的很低的環境元素值使場景看上去更引入注目,但同時也意味著物體沒有面向光線的面或者有其他物體擋住的物體將在場景中看得不是很清楚。
散射光
在OpenGL ES中可以設定的第二個光線元素是 散射元素(diffuse component)。在現實世界裡,散射光線是諸如穿透光纖或從一堵白牆反射的光線。散射光線是發散的,因而參數較柔和的光,一般不會像直射光一樣產生光斑。如果你曾經觀察過職業攝影家使用攝影室燈光,你可能會看到他們使用柔光箱 或者反光傘。兩者都會穿透像白布之類的輕型材料並反射與輕型有色材料從而使光線發散以產生令人愉悅的照片。在OpenGL ES中,散射元素作用類似,它使光線均勻地散布到物體之上。然而,不像環境光,由於它是定向光,只有面向光線的物體面才會反射散射光,而場景中的所有多面體都會被環境光照射。
下面的例子演示了設定場景中的第一個散射元素:
float lightDiffuse[] = new float[] { 0.2f, 0.3f, 0.6f, 1.0f };//漫反射光 gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuse, 0);
位置
還需要設定光效的另一個重要屬性,即光源3D空間中的位置。這不會影響環境元素,但其他兩個元素由於其本性,只有在OpenGL在知道了場景中物體與光的相對位置後才能計算。例如:
float[] lightPos = new float[] {0,0,3,1}; //光源位置 gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPos, 0)
關於光效的設置還有很多,大家有興趣可以自己研究,這裡有篇不錯的文章,大家可以看看http://www.linuxidc.com/Linux/2011-09/43750p10.htm
我們今天例子的效果:
實例代碼:
- public class CubeRenderer implements Renderer {
-
- float box[] = new float[] {
- // FRONT
- -0.5f, -0.5f, 0.5f,
- 0.5f, -0.5f, 0.5f,
- -0.5f, 0.5f, 0.5f,
- 0.5f, 0.5f, 0.5f,
- // BACK
- -0.5f, -0.5f, -0.5f,
- -0.5f, 0.5f, -0.5f,
- 0.5f, -0.5f, -0.5f,
- 0.5f, 0.5f, -0.5f,
- // LEFT
- -0.5f, -0.5f, 0.5f,
- -0.5f, 0.5f, 0.5f,
- -0.5f, -0.5f, -0.5f,
- -0.5f, 0.5f, -0.5f,
- // RIGHT
- 0.5f, -0.5f, -0.5f,
- 0.5f, 0.5f, -0.5f,
- 0.5f, -0.5f, 0.5f,
- 0.5f, 0.5f, 0.5f,
- // TOP
- -0.5f, 0.5f, 0.5f,
- 0.5f, 0.5f, 0.5f,
- -0.5f, 0.5f, -0.5f,
- 0.5f, 0.5f, -0.5f,
- // BOTTOM
- -0.5f, -0.5f, 0.5f,
- -0.5f, -0.5f, -0.5f,
- 0.5f, -0.5f, 0.5f,
- 0.5f, -0.5f, -0.5f,
- };
- float lightAmbient[] = new float[] { 0.2f, 0.3f, 0.6f, 1.0f }; //環境光
- float lightDiffuse[] = new float[] { 0.2f, 0.3f, 0.6f, 1.0f };//漫反射光
- float[] lightPos = new float[] {0,0,3,1}; //光源位置
- /*
- * 因為進行光照處理,你必須告知系統你定義的模型各個面的方向,以便系統計算光影情況,方向的描述是通過向量點來描述的
- */
- float norms[] = new float[] { //法向量數組,用於描述個頂點的方向,以此說明各個面的方向
- // FRONT
- 0f, 0f, 1f, //方向為(0,0,0)至(0,0,1)即Z軸正方向
- 0f, 0f, 1f,
- 0f, 0f, 1f,
- 0f, 0f, 1f,
- // BACK
- 0f, 0f, -1f,
- 0f, 0f, -1f,
- 0f, 0f, -1f,
- 0f, 0f, -1f,
- // LEFT
- -1f, 0f, 0f,
- -1f, 0f, 0f,
- -1f, 0f, 0f,
- -1f, 0f, 0f,
- // RIGHT
- 1f, 0f, 0f,
- 1f, 0f, 0f,
- 1f, 0f, 0f,
- 1f, 0f, 0f,
- // TOP
- 0f, 1f, 0f,
- 0f, 1f, 0f,
- 0f, 1f, 0f,
- 0f, 1f, 0f,
- // BOTTOM
- 0f, -1f, 0f,
- 0f, -1f, 0f,
- 0f, -1f, 0f,
- 0f, -1f, 0f
- };
-
-
- FloatBuffer cubeBuff;
- FloatBuffer normBuff;
-
- float xrot = 0.0f;
- float yrot = 0.0f;
-
- /**
- * 將float數組轉換存儲在字節緩沖數組
- * @param arr
- * @return
- */
- public FloatBuffer makeFloatBuffer(float[] arr) {
- ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);//分配緩沖空間,一個float占4個字節
- bb.order(ByteOrder.nativeOrder()); //設置字節順序, 其中ByteOrder.nativeOrder()是獲取本機字節順序
- FloatBuffer fb = bb.asFloatBuffer(); //轉換為float型
- fb.put(arr); //添加數據
- fb.position(0); //設置數組的起始位置
- return fb;
- }
-
- public CubeRenderer() {
- // TODO Auto-generated constructor stub
- cubeBuff = makeFloatBuffer(box);//轉換float數組
- normBuff = makeFloatBuffer(norms);
- }
-
-
- protected void init(GL10 gl) {
- gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);//設置清屏時背景的顏色,R,G,B,A
-
- gl.glEnable(GL10.GL_LIGHTING); //啟用光照
- gl.glEnable(GL10.GL_LIGHT0); //開啟光源0,最多可以開啟8個光源
- //設置光照參數,也可以使用默認的,不設置
- gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient, 0);
- gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuse, 0);
- gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPos, 0);
-
- gl.glNormalPointer(GL10.GL_FLOAT, 0, normBuff);
- gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
-
- gl.glEnable(GL10.GL_DEPTH_TEST); //啟用深度緩存
- gl.glEnable(GL10.GL_CULL_FACE); //啟用背面剪裁
- gl.glClearDepthf(1.0f); // 設置深度緩存值
- gl.glDepthFunc(GL10.GL_LEQUAL); // 設置深度緩存比較函數,GL_LEQUAL表示新的像素的深度緩存值小於等於當前像素的深度緩存值(通過gl.glClearDepthf(1.0f)設置)時通過深度測試
- gl.glShadeModel(GL10.GL_SMOOTH);// 設置陰影模式GL_SMOOTH
- }
-
- @Override
- public void onSurfaceCreated(GL10 gl, EGLConfig config) {
- // TODO Auto-generated method stub
- init(gl);
- }
-
- @Override
- public void onSurfaceChanged(GL10 gl, int w, int h) {
- // TODO Auto-generated method stub
- gl.glViewport(0, 0, w, h); //設置視窗
- gl.glMatrixMode(GL10.GL_PROJECTION); // 設置投影矩陣
- gl.glLoadIdentity(); //設置矩陣為單位矩陣,相當於重置矩陣
- GLU.gluPerspective(gl, 45.0f, ((float) w) / h, 0.1f, 10f);//設置透視范圍
- }
-
- @Override
- public void onDrawFrame(GL10 gl) {
- // TODO Auto-generated method stub
- gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);// 清除屏幕和深度緩存
-
- gl.glMatrixMode(GL10.GL_MODELVIEW); //切換至模型觀察矩陣
- gl.glLoadIdentity();// 重置當前的模型觀察矩陣
- GLU.gluLookAt(gl, 0, 0, 3, 0, 0, 0, 0, 1, 0);//設置視點和模型中心位置
-
- gl.glVertexPointer(3, GL10.GL_FLOAT, 0, cubeBuff);//設置頂點數據
- gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
-
- gl.glRotatef(xrot, 1, 0, 0); //繞著(0,0,0)與(1,0,0)即x軸旋轉
- gl.glRotatef(yrot, 0, 1, 0);
-
- gl.glColor4f(1.0f, 0, 0, 1.0f); //設置顏色,紅色
- gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4); //繪制正方型FRONT面
- gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4);
-
- gl.glColor4f(0, 1.0f, 0, 1.0f);
- gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4);
- gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4);
-
- gl.glColor4f(0, 0, 1.0f, 1.0f);
- gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4);
- gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4);
-
- xrot += 0.5f;
- yrot += 0.5f;
- }
-
- }
比較上次代碼,可以看出,為一個3D模型添加光效,並不需要改動很大。只需要在實踐篇(見http://www.linuxidc.com/Linux/2011-09/43751.htm)代碼基礎上添加上述代碼即可~~