php open gl,Open GL 资料 01
publicclassMyGLSurfaceViewextendsGLSurfaceView?{
privatefinalfloatTOUCH_SCALE_FACTOR?=180.0f?/320;
/**
*?具體實現的渲染器
*/
privateOPhoneOglesDevRenderer?mRenderer;
/**
*?記錄上次觸屏位置的坐標
*/
privatefloatmPreviousX,?mPreviousY;
publicMyGLSurfaceView(Context?context)?{
super(context);
//?設置渲染器
mRenderer?=newOPhoneOglesDevRenderer(context);
setRenderer(mRenderer);
//?設置渲染模式為主動渲染
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
publicvoidonPause()?{
super.onPause();
}
publicvoidonResume()?{
super.onResume();
}
/**
*?響應觸屏事件
*/
@Override
publicbooleanonTouchEvent(MotionEvent?e)?{
floatx?=?e.getX();
floaty?=?e.getY();
switch(e.getAction())?{
caseMotionEvent.ACTION_MOVE:
floatdx?=?x?-?mPreviousX;
floatdy?=?y?-?mPreviousY;
mRenderer.mAngleX?+=?dx?*?TOUCH_SCALE_FACTOR;
mRenderer.mAngleY?+=?dy?*?TOUCH_SCALE_FACTOR;
requestRender();
}
mPreviousX?=?x;
mPreviousY?=?y;
returntrue;
}
}
OpenGL ES開發簡要框架
開發OpenGL ES程序,首要做的就是設置視口,設置投影矩陣,設置模型視圖矩陣等。對于設置模型視圖矩陣,我們通常會分別設置相機矩陣和模型矩陣。對于一些全局性的設置,我們通常只需要執行一次;而對于那些需要動態改變的屬性,則應該在相應事件發生時或者逐幀進行動態更新。GLSurfaceView.Renderer接口提供了監視繪圖表面創建、改變以及逐幀更新的方法,分別是:
/**
*?創建繪圖表面時調用
*/
@Override
publicvoidonSurfaceCreated(GL10?gl,?EGLConfig?config)
/**
*?當繪圖表面尺寸發生改變時調用
*/
@Override
publicvoidonSurfaceChanged(GL10?gl,intwidth,intheight)
/**
*?逐幀渲染
*/
@Override
publicvoidonDrawFrame(GL10?gl)
/** * 創建繪圖表面時調用 */ @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) /** * 當繪圖表面尺寸發生改變時調用 */ @Override public void onSurfaceChanged(GL10 gl, int width, int height) /** * 逐幀渲染 */ @Override public void onDrawFrame(GL10 gl)
通常,我們在onSurfaceCreated()中通過調用glHint()函數來設置渲染質量與速度的平衡,設置清屏顏色,著色模型,啟用背面剪裁和深度測試,以及禁用光照和混合等全局性設置。相關代碼如下:
publicvoidonSurfaceCreated(GL10?gl,?EGLConfig?config)?{
//全局性設置
gl.glDisable(GL10.GL_DITHER);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,?GL10.GL_FASTEST);
//設置清屏背景顏色
gl.glClearColor(0.5f,0.5f,0.5f,1);
//設置著色模型為平滑著色
gl.glShadeModel(GL10.GL_SMOOTH);
//啟用背面剪裁
gl.glEnable(GL10.GL_CULL_FACE);
gl.glCullFace(GL10.GL_BACK);
//啟用深度測試
gl.glEnable(GL10.GL_DEPTH_TEST);
//禁用光照和混合
gl.glDisable(GL10.GL_LIGHTING);
gl.glDisable(GL10.GL_BLEND);
}
在onSurfaceChanged中,我們會根據繪圖表面尺寸的改變,來即時改變視口大小,以及重新設置投影矩陣。相關代碼如下:
publicvoidonSurfaceChanged(GL10?gl,intwidth,intheight)?{
//設置視口
gl.glViewport(0,0,?width,?height);
//設置投影矩陣
floatratio?=?(float)?width?/?height;//屏幕寬高比
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl,45.0f,?ratio,1,5000);
//每次修改完GL_PROJECTION后,最好將當前矩陣模型設置回GL_MODELVIEW
gl.glMatrixMode(GL10.GL_MODELVIEW);
}
}
在onDrawFrame中,需要編寫的是每幀實際渲染的代碼,包括清屏,設置模型視圖矩陣,渲染模型,以及相應的update函數。相關代碼如下:
publicvoidonDrawFrame(GL10?gl)?{
//一般的opengl程序,首先要做的就是清屏
gl.glClear(GL10.GL_COLOR_BUFFER_BIT?|?GL10.GL_DEPTH_BUFFER_BIT);
//緊接著設置模型視圖矩陣
setUpCamera(gl);
//渲染物體
drawModel(gl);
//更新時間
updateTime();
}
設置模型視圖矩陣(即GL_MODELVIEW矩陣)時,我們通常分別設置相機和物體矩陣。在設置相機矩陣時,我們可以通過調用
GLU.gluLookAt (GL10 gl, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ) 傳入視點位置(eyeX, eyeY, eyeZ)、被觀察體的中心位置(centerX, centerY, centerZ)以及相機向上方向的向量(upX, upY, upZ)。相關代碼如下:
/**
*?設置相機矩陣
*?@param?gl
*/
privatevoidsetUpCamera(GL10?gl)?{
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
GLU.gluLookAt(gl,?mfEyeX,?mfEyeY,?mfEyeZ,?mfCenterX,?mfCenterY,?mfCenterZ,0,1,0);
}
OpenGL ES中采用的是矩陣堆棧體系。對于模型視圖矩陣,堆棧深度至少為16;對于投影矩陣或者紋理矩陣,則至少為2。由于OpenGL ES中的矩陣操作,都是針對當前棧頂的矩陣,因此很多時候需要配對使用glPushMatrix()和glPopMatrix()來進行保存和恢復矩陣現場。在本例中,渲染模型之前,我們首先使用glPushMatrix()來復制當前模型視圖矩陣,并將其推入到棧頂,之后所有的矩陣操作均針對該矩陣。然后我們通過調用glRotate()函數,進行適當的旋轉,在渲染模型完畢之后,通過調用glPopMatrix()將當前矩陣彈出,恢復之前的矩陣現場。相關代碼如下:
/**
*?渲染模型
*?@param?gl
*/
privatevoiddrawModel(GL10?gl)?{
gl.glPushMatrix();
{
//首先對模型進行旋轉
gl.glRotatef(mfAngleX,1,0,0);//繞X軸旋轉
gl.glRotatef(mfAngleY,0,1,0);//繞Y軸旋轉
if(mModel.containsAnimation())?{
//如果模型有動畫,那么按時間就更新動畫
if(mMsPerFrame?>0)?{
mModel.animate(mMsPerFrame?*0.001f);//將毫秒數轉化為秒,?/1000
}
mModel.fillRenderBuffer();//更新頂點緩存
}
mModel.render(gl);//渲染模型
mModel.renderJoints(gl);//渲染關節,骨骼
}
gl.glPopMatrix();
}
OpenGL ES中支持三種渲染圖元:點(GL_POINTS)、線(GL_LINES)和三角形(GL_TRIANGLES)。在本例子中,模型實體采用三角形渲染(對應函數mModel.render(gl)),而對于有骨骼信息的模型,會使用點和線來渲染骨骼輔助信息(對應函數mModel.renderJoints(gl))。OpenGL ES拋棄了OpenGL中傳統但低效的glBegin()、glEnd()的渲染方式,采用了更為高效的批量渲染模式,使用java.nio.Buffer對象來存儲渲染數據,之后通過調用glVertexPointer()、glNormalPointer()、glColorPointer()以及glTextureCoordPointer()傳入Buffer對象來分別設置頂點位置、法線、顏色和紋理坐標渲染數據。在設置渲染數據的同時,需要通過調用glEnableClientState()函數,分別傳入GL_VERTEX_ARRAY、GL_NORMAL_ARRAY、GL_COLOR_ARRAY和GL_TEXTURE_COORD_ARRAY來通知底層引擎啟用相應渲染屬性數據。這四個渲染屬性并非要全部設置,而是可以根據需要只是啟用其中的某幾個。在本例中,渲染模型實體時,僅啟用了頂點位置數據和紋理坐標數據;在渲染點線的骨骼輔助信息時,則僅僅啟用了頂點位置數據。對于那些沒有被啟用的渲染屬性,必須要確保其當前處于為非活動狀態(即調用glDisableClientState()),否則就可能會對渲染結果造成一定影響,或者白白加重底層管線運算負擔。
另外需要注意的是OPhone中要傳入gl*Pointer()函數的Buffer對象必須要為direct模式申請的,這樣可以確保緩存對象放置在Native的堆中,以免受到Java端的垃圾回收機制的影響。對于FloatBuffer、ShortBuffer和IntBuffer等多字節的緩存對象,它們的字節順序必須設置為nativeOrder,否則會極大降低程序執行效率。
在設置好各個渲染屬性的數據之后,就要通過調用glDrawArrays()或者glDrawElements()來進行數據的最終提交渲染。前者表示傳入的數據是最終要渲染的數據,可以直接渲染,而后者會根據傳入的索引,由底層重組最終要真正渲染的數據。相比之下,后者可以節省更多的內存。下面的代碼展示了以三角形來渲染模型實體,啟用頂點位置數據和紋理坐標數據,未啟用法線和顏色數據:
/**
*?渲染實體模型
*?@param?gl
*/
publicvoidrender(GL10?gl)?{
gl.glPushMatrix();
{
//設置默認顏色
gl.glColor4f(1.0f,0.5f,0.5f,1.0f);
//啟用客戶端狀態
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//遍歷所有的MS3D?Group,渲染每一個Group
for(inti?=0;?i
if(mpGroups[i].getTriangleCount()?==0)?{
//如果該Group包含的三角形個數為零,則直接跳過
continue;
}
//得到相應紋理
TextureInfo?tex?=?mpTexInfo[i?%?mpTexInfo.length];
if(tex?!=null)?{
//如果紋理不為空,則綁定相應紋理
gl.glBindTexture(GL10.GL_TEXTURE_2D,?tex.mTexID);
//啟用紋理貼圖
gl.glEnable(GL10.GL_TEXTURE_2D);
//綁定紋理坐標數據
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glTexCoordPointer(2,?GL10.GL_FLOAT,0,
mpBufTextureCoords[i]);
}else{
//如果紋理為空,禁用紋理貼圖
//禁用紋理客戶端狀態
gl.glDisable(GL10.GL_TEXTURE_2D);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
//綁定頂點數據
gl.glVertexPointer(3,?GL10.GL_FLOAT,0,?mpBufVertices[i]);
//提交渲染
gl.glDrawArrays(GL10.GL_TRIANGLES,0,?mpGroups[i]
.getTriangleCount()?*3);
}
//渲染完畢,重置客戶端狀態
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glDisable(GL10.GL_TEXTURE_2D);
}
gl.glPopMatrix();
}
程序中渲染骨骼關節輔助信息的部分,就是以點和線的模型進行渲染,相關代碼如下
/**
*?渲染骨骼幫助信息
*?@param?gl
*/
publicvoidrenderJoints(GL10?gl)?{
if(!containsJoint())?{
return;
}
//為保證骨骼始終可見,暫時禁用深度測試
gl.glDisable(GL10.GL_DEPTH_TEST);
//設置點和線的寬度
gl.glPointSize(4.0f);
gl.glLineWidth(2.0f);
//僅僅啟用頂點數據
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//渲染骨骼連線
gl.glColor4f(1.0f,0.0f,0.0f,1.0f);//設置顏色
gl.glVertexPointer(3,?GL10.GL_FLOAT,0,?mBufJointLinePosition);
//提交渲染
gl.glDrawArrays(GL10.GL_LINES,0,?mJointLineCount);
//渲染關節點
gl.glColor4f(1.0f,1.0f,0.0f,1.0f);//設置顏色
gl.glVertexPointer(3,?GL10.GL_FLOAT,0,?mBufJointPointPosition);
//提交渲染
gl.glDrawArrays(GL10.GL_POINTS,0,?mJointPointCount);
//重置回默認狀態
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glPointSize(1.0f);
gl.glLineWidth(1.0f);
gl.glEnable(GL10.GL_DEPTH_TEST);
}
紋理操作??????? 在前面的代碼中,我們看到了啟用、綁定紋理等操作。紋理映射是3D中非常重要的一塊,如果沒有紋理,整個3D世界就會只是一些單純的色塊。OPhone中目前支持2D紋理映射(貼圖尺寸必須要為2的N次方),并支持2個以上的紋理貼圖單元。由于紋理數據存儲在OpenGL ES服務器端(可以理解為GPU端,即Graphics Process Unit,圖形處理單元),因此需要我們從客戶端(即外部的應用程序端)將像素數據傳入,由底層將這些像素轉換成更為高效的、對硬件更為友好的紋素格式。OpenGL ES中的每一個紋理都被當作一個紋理對象,它除了包括紋理像素數據之外,還包括該紋理的其他屬性,比如名字、過濾模式、混合模式等。開發者需要首先向底層申請一個紋理名稱,之后上傳紋理像素數據,以及設置其他屬性。下面的代碼向我們展示了如何在OPhone中創建一個紋理對象:
/**
*?創建一個紋理對象
*?@param?context?-?應用程序環境
*?@param?gl?-?opengl?es對象
*?@param?resID?-?R.java中的資源ID
*?@param?wrap_s_mode?-?紋理環繞S模式
*?@param?wrap_t_mode?-?紋理環繞T模式
*?@return?申請好的紋理ID
*/
publicstaticintgetTexture(Context?context,?GL10?gl,intresID,
intwrap_s_mode,intwrap_t_mode)?{
//申請一個紋理對象ID
int[]?textures?=newint[1];
gl.glGenTextures(1,?textures,0);
//綁定這個申請來的ID為當前紋理操作對象
inttextureID?=?textures[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D,?textureID);
//設置當前紋理對象的過濾模式
gl.glTexParameterf(GL10.GL_TEXTURE_2D,?GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D,?GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_LINEAR);
//設置環繞模式
gl.glTexParameterf(GL10.GL_TEXTURE_2D,?GL10.GL_TEXTURE_WRAP_S,
wrap_s_mode);
gl.glTexParameterf(GL10.GL_TEXTURE_2D,?GL10.GL_TEXTURE_WRAP_T,
wrap_t_mode);
//設置混合模式
gl.glTexEnvf(GL10.GL_TEXTURE_ENV,?GL10.GL_TEXTURE_ENV_MODE,
GL10.GL_REPLACE);
//開始載入紋理
InputStream?is?=?context.getResources().openRawResource(resID);
Bitmap?bitmap;
try{
bitmap?=?BitmapFactory.decodeStream(is);
}finally{
try{
is.close();
}catch(IOException?e)?{
//?Ignore.
}
}
//綁定像素數據到紋理對象
GLUtils.texImage2D(GL10.GL_TEXTURE_2D,0,?bitmap,0);
bitmap.recycle();
returntextureID;
}
創建好紋理對象之后,在使用時,需要首先通過調用gl.glEnable(GL10.GL_TEXTURE_2D)來通知底層開啟紋理貼圖操作,之后綁定相應的紋理ID到當前紋理貼圖單元,同時通過調用glTexCoordPointer()來設置好相應的紋理坐標信息,最終提交渲染時,底層就會自動進行紋理映射操作。當紋理不再被使用時,可以通過調用glDeleteTextures()來將其刪除。
輸入事件響應
我們可以重載GLSurfaceView的onTouchEvent()方法,從而監測用戶對屏幕的觸摸事件。本例中,我們根據觸摸位置的改變,來對模型進行繞Y軸和X軸的旋轉。如果有需要,開發者還可以重載鍵盤按鍵onKeyDown()方法。值得注意的是,由于這些事件和渲染線程是分別獨立的線程,因此有些操作如果需要確保在渲染線程內部執行的話,可以調用queueEvent (Runnable)來將該操作附加到渲染線程操作隊列中。相關代碼如下:
/**
*?響應觸屏事件
*/
@Override
publicbooleanonTouchEvent(MotionEvent?e)?{
floatx?=?e.getX();
floaty?=?e.getY();
switch(e.getAction())?{
caseMotionEvent.ACTION_MOVE:
floatdx?=?x?-?mPreviousX;
floatdy?=?y?-?mPreviousY;
mRenderer.mfAngleY?+=?dx?*?TOUCH_SCALE_FACTOR;
mRenderer.mfAngleX?+=?dy?*?TOUCH_SCALE_FACTOR;
requestRender();
}
mPreviousX?=?x;
mPreviousY?=?y;
returntrue;
}
總結
以上是生活随笔為你收集整理的php open gl,Open GL 资料 01的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度之眼课程打卡-python入门05
- 下一篇: 围棋棋盘怎么编程python_围_围是什