php open gl,Open GL 资料 01
publicclassMyGLSurfaceViewextendsGLSurfaceView?{
privatefinalfloatTOUCH_SCALE_FACTOR?=180.0f?/320;
/**
*?具體實現(xiàn)的渲染器
*/
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開發(fā)簡要框架
開發(fā)OpenGL ES程序,首要做的就是設置視口,設置投影矩陣,設置模型視圖矩陣等。對于設置模型視圖矩陣,我們通常會分別設置相機矩陣和模型矩陣。對于一些全局性的設置,我們通常只需要執(zhí)行一次;而對于那些需要動態(tài)改變的屬性,則應該在相應事件發(fā)生時或者逐幀進行動態(tài)更新。GLSurfaceView.Renderer接口提供了監(jiān)視繪圖表面創(chuàng)建、改變以及逐幀更新的方法,分別是:
/**
*?創(chuàng)建繪圖表面時調(diào)用
*/
@Override
publicvoidonSurfaceCreated(GL10?gl,?EGLConfig?config)
/**
*?當繪圖表面尺寸發(fā)生改變時調(diào)用
*/
@Override
publicvoidonSurfaceChanged(GL10?gl,intwidth,intheight)
/**
*?逐幀渲染
*/
@Override
publicvoidonDrawFrame(GL10?gl)
/** * 創(chuàng)建繪圖表面時調(diào)用 */ @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) /** * 當繪圖表面尺寸發(fā)生改變時調(diào)用 */ @Override public void onSurfaceChanged(GL10 gl, int width, int height) /** * 逐幀渲染 */ @Override public void onDrawFrame(GL10 gl)
通常,我們在onSurfaceCreated()中通過調(diào)用glHint()函數(shù)來設置渲染質(zhì)量與速度的平衡,設置清屏顏色,著色模型,啟用背面剪裁和深度測試,以及禁用光照和混合等全局性設置。相關代碼如下:
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中,我們會根據(jù)繪圖表面尺寸的改變,來即時改變視口大小,以及重新設置投影矩陣。相關代碼如下:
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函數(shù)。相關代碼如下:
publicvoidonDrawFrame(GL10?gl)?{
//一般的opengl程序,首先要做的就是清屏
gl.glClear(GL10.GL_COLOR_BUFFER_BIT?|?GL10.GL_DEPTH_BUFFER_BIT);
//緊接著設置模型視圖矩陣
setUpCamera(gl);
//渲染物體
drawModel(gl);
//更新時間
updateTime();
}
設置模型視圖矩陣(即GL_MODELVIEW矩陣)時,我們通常分別設置相機和物體矩陣。在設置相機矩陣時,我們可以通過調(diào)用
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中的矩陣操作,都是針對當前棧頂?shù)木仃?#xff0c;因此很多時候需要配對使用glPushMatrix()和glPopMatrix()來進行保存和恢復矩陣現(xiàn)場。在本例中,渲染模型之前,我們首先使用glPushMatrix()來復制當前模型視圖矩陣,并將其推入到棧頂,之后所有的矩陣操作均針對該矩陣。然后我們通過調(diào)用glRotate()函數(shù),進行適當?shù)男D(zhuǎn),在渲染模型完畢之后,通過調(diào)用glPopMatrix()將當前矩陣彈出,恢復之前的矩陣現(xiàn)場。相關代碼如下:
/**
*?渲染模型
*?@param?gl
*/
privatevoiddrawModel(GL10?gl)?{
gl.glPushMatrix();
{
//首先對模型進行旋轉(zhuǎn)
gl.glRotatef(mfAngleX,1,0,0);//繞X軸旋轉(zhuǎn)
gl.glRotatef(mfAngleY,0,1,0);//繞Y軸旋轉(zhuǎn)
if(mModel.containsAnimation())?{
//如果模型有動畫,那么按時間就更新動畫
if(mMsPerFrame?>0)?{
mModel.animate(mMsPerFrame?*0.001f);//將毫秒數(shù)轉(zhuǎn)化為秒,?/1000
}
mModel.fillRenderBuffer();//更新頂點緩存
}
mModel.render(gl);//渲染模型
mModel.renderJoints(gl);//渲染關節(jié),骨骼
}
gl.glPopMatrix();
}
OpenGL ES中支持三種渲染圖元:點(GL_POINTS)、線(GL_LINES)和三角形(GL_TRIANGLES)。在本例子中,模型實體采用三角形渲染(對應函數(shù)mModel.render(gl)),而對于有骨骼信息的模型,會使用點和線來渲染骨骼輔助信息(對應函數(shù)mModel.renderJoints(gl))。OpenGL ES拋棄了OpenGL中傳統(tǒng)但低效的glBegin()、glEnd()的渲染方式,采用了更為高效的批量渲染模式,使用java.nio.Buffer對象來存儲渲染數(shù)據(jù),之后通過調(diào)用glVertexPointer()、glNormalPointer()、glColorPointer()以及glTextureCoordPointer()傳入Buffer對象來分別設置頂點位置、法線、顏色和紋理坐標渲染數(shù)據(jù)。在設置渲染數(shù)據(jù)的同時,需要通過調(diào)用glEnableClientState()函數(shù),分別傳入GL_VERTEX_ARRAY、GL_NORMAL_ARRAY、GL_COLOR_ARRAY和GL_TEXTURE_COORD_ARRAY來通知底層引擎啟用相應渲染屬性數(shù)據(jù)。這四個渲染屬性并非要全部設置,而是可以根據(jù)需要只是啟用其中的某幾個。在本例中,渲染模型實體時,僅啟用了頂點位置數(shù)據(jù)和紋理坐標數(shù)據(jù);在渲染點線的骨骼輔助信息時,則僅僅啟用了頂點位置數(shù)據(jù)。對于那些沒有被啟用的渲染屬性,必須要確保其當前處于為非活動狀態(tài)(即調(diào)用glDisableClientState()),否則就可能會對渲染結(jié)果造成一定影響,或者白白加重底層管線運算負擔。
另外需要注意的是OPhone中要傳入gl*Pointer()函數(shù)的Buffer對象必須要為direct模式申請的,這樣可以確保緩存對象放置在Native的堆中,以免受到Java端的垃圾回收機制的影響。對于FloatBuffer、ShortBuffer和IntBuffer等多字節(jié)的緩存對象,它們的字節(jié)順序必須設置為nativeOrder,否則會極大降低程序執(zhí)行效率。
在設置好各個渲染屬性的數(shù)據(jù)之后,就要通過調(diào)用glDrawArrays()或者glDrawElements()來進行數(shù)據(jù)的最終提交渲染。前者表示傳入的數(shù)據(jù)是最終要渲染的數(shù)據(jù),可以直接渲染,而后者會根據(jù)傳入的索引,由底層重組最終要真正渲染的數(shù)據(jù)。相比之下,后者可以節(jié)省更多的內(nèi)存。下面的代碼展示了以三角形來渲染模型實體,啟用頂點位置數(shù)據(jù)和紋理坐標數(shù)據(jù),未啟用法線和顏色數(shù)據(jù):
/**
*?渲染實體模型
*?@param?gl
*/
publicvoidrender(GL10?gl)?{
gl.glPushMatrix();
{
//設置默認顏色
gl.glColor4f(1.0f,0.5f,0.5f,1.0f);
//啟用客戶端狀態(tài)
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//遍歷所有的MS3D?Group,渲染每一個Group
for(inti?=0;?i
if(mpGroups[i].getTriangleCount()?==0)?{
//如果該Group包含的三角形個數(shù)為零,則直接跳過
continue;
}
//得到相應紋理
TextureInfo?tex?=?mpTexInfo[i?%?mpTexInfo.length];
if(tex?!=null)?{
//如果紋理不為空,則綁定相應紋理
gl.glBindTexture(GL10.GL_TEXTURE_2D,?tex.mTexID);
//啟用紋理貼圖
gl.glEnable(GL10.GL_TEXTURE_2D);
//綁定紋理坐標數(shù)據(jù)
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glTexCoordPointer(2,?GL10.GL_FLOAT,0,
mpBufTextureCoords[i]);
}else{
//如果紋理為空,禁用紋理貼圖
//禁用紋理客戶端狀態(tài)
gl.glDisable(GL10.GL_TEXTURE_2D);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
//綁定頂點數(shù)據(jù)
gl.glVertexPointer(3,?GL10.GL_FLOAT,0,?mpBufVertices[i]);
//提交渲染
gl.glDrawArrays(GL10.GL_TRIANGLES,0,?mpGroups[i]
.getTriangleCount()?*3);
}
//渲染完畢,重置客戶端狀態(tài)
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glDisable(GL10.GL_TEXTURE_2D);
}
gl.glPopMatrix();
}
程序中渲染骨骼關節(jié)輔助信息的部分,就是以點和線的模型進行渲染,相關代碼如下
/**
*?渲染骨骼幫助信息
*?@param?gl
*/
publicvoidrenderJoints(GL10?gl)?{
if(!containsJoint())?{
return;
}
//為保證骨骼始終可見,暫時禁用深度測試
gl.glDisable(GL10.GL_DEPTH_TEST);
//設置點和線的寬度
gl.glPointSize(4.0f);
gl.glLineWidth(2.0f);
//僅僅啟用頂點數(shù)據(jù)
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);
//渲染關節(jié)點
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);
//重置回默認狀態(tài)
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個以上的紋理貼圖單元。由于紋理數(shù)據(jù)存儲在OpenGL ES服務器端(可以理解為GPU端,即Graphics Process Unit,圖形處理單元),因此需要我們從客戶端(即外部的應用程序端)將像素數(shù)據(jù)傳入,由底層將這些像素轉(zhuǎn)換成更為高效的、對硬件更為友好的紋素格式。OpenGL ES中的每一個紋理都被當作一個紋理對象,它除了包括紋理像素數(shù)據(jù)之外,還包括該紋理的其他屬性,比如名字、過濾模式、混合模式等。開發(fā)者需要首先向底層申請一個紋理名稱,之后上傳紋理像素數(shù)據(jù),以及設置其他屬性。下面的代碼向我們展示了如何在OPhone中創(chuàng)建一個紋理對象:
/**
*?創(chuàng)建一個紋理對象
*?@param?context?-?應用程序環(huán)境
*?@param?gl?-?opengl?es對象
*?@param?resID?-?R.java中的資源ID
*?@param?wrap_s_mode?-?紋理環(huán)繞S模式
*?@param?wrap_t_mode?-?紋理環(huán)繞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);
//設置環(huán)繞模式
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.
}
}
//綁定像素數(shù)據(jù)到紋理對象
GLUtils.texImage2D(GL10.GL_TEXTURE_2D,0,?bitmap,0);
bitmap.recycle();
returntextureID;
}
創(chuàng)建好紋理對象之后,在使用時,需要首先通過調(diào)用gl.glEnable(GL10.GL_TEXTURE_2D)來通知底層開啟紋理貼圖操作,之后綁定相應的紋理ID到當前紋理貼圖單元,同時通過調(diào)用glTexCoordPointer()來設置好相應的紋理坐標信息,最終提交渲染時,底層就會自動進行紋理映射操作。當紋理不再被使用時,可以通過調(diào)用glDeleteTextures()來將其刪除。
輸入事件響應
我們可以重載GLSurfaceView的onTouchEvent()方法,從而監(jiān)測用戶對屏幕的觸摸事件。本例中,我們根據(jù)觸摸位置的改變,來對模型進行繞Y軸和X軸的旋轉(zhuǎn)。如果有需要,開發(fā)者還可以重載鍵盤按鍵onKeyDown()方法。值得注意的是,由于這些事件和渲染線程是分別獨立的線程,因此有些操作如果需要確保在渲染線程內(nèi)部執(zhí)行的話,可以調(diào)用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;
}
總結(jié)
以上是生活随笔為你收集整理的php open gl,Open GL 资料 01的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度之眼课程打卡-python入门05
- 下一篇: 围棋棋盘怎么编程python_围_围是什