轉自 https://www.helplib.com/yingyongkaifa/article_5701
本文介紹了多操作系統引擎中的OpenGLBox示例。 這是一個真正的跨平臺示例。 在下面的屏幕截圖中,你可以看到基於iOS和Android的基於java的應用程序和行爲相同。 不同平臺的應用程序共享數據和源代碼的一部分。 這是如何實現的向下滾動? !
每個OpenGL程序員至少有一次做了這個小程序: 旋轉 3D 多立方體如果你不熟悉 3D 圖形編程,請不要擔心。 本示例將幫助你從頭開始。 如果你已經很好地處理 OpenGL,本文將幫助你專注於Android和iOS開發的特定特性。 開始吧。
我們的幾何包含 6面,適用於立方體和 24頂點。 爲什麼有這麼多頂點? 每個面都有一個多操作系統引擎 logo的紋理。 因此,我們必須將 8個實頂點分割成具有不同紋理座標的3新頂點。
我們的場景中沒有燈光效果。 因此,我們可以使用相當簡單的着色器。 頂點着色器只轉換幾何體,並將紋理座標傳遞給 fragment 明暗器。 然後 fragment 明暗器製作簡單的紋理。
平臺特定代碼
多操作系統引擎不需要隱藏平臺特定代碼。 這對於移植和調試應用程序來說。 每個平臺的類都可以有自己的功能:
iOS
複製代碼 package com.intel.inde.moe.samples.openglbox.ios;classMain extends NSObject implements UIApplicationDelegateclassOpenGLBoxController extends GLKViewController implements GLKViewDelegateclassShaderProgram extends ShaderProgramBase |
基於 GLKit的框架提供了功能和類,以減少創建基於shader的應用程序所需的工作:
GLKViewController
GLKViewController類提供所有標準視圖控制器功能,但另外還實現了 OpenGL ES呈現循環。 在視圖中,GLKViewController 對象與 GLKView對象一起工作,以顯示動畫的幀。
GLKView
通過提供 OpenGL ES感知視圖的默認實現,GLKView 類簡化了創建 OpenGL ES應用程序所需的工作。 一個直接代表應用程序管理framebuffer對象;當需要更新內容時,應用程序就會直接進入緩衝區。
GLKViewDelegate
可以將實現 GLKViewDelegate 協議的對象設置爲一個 GLKView對象委託。 委託允許應用程序向 GLKView 對象提供draw方法,而無需子類化 GLKView類。
Android
複製代碼 package com.intel.inde.moe.samples.openglbox.android;classOpenGLBoxActivityextends ActivityclassSurfaceViewextends GLSurfaceViewclassBoxRendererimplements GLSurfaceView.RendererclassShaderProgramextends ShaderProgramBase |
Android框架中有兩個基本類,用於使用 OpenGL ai API創建和操作圖形: GLSurfaceView 和 GLSurfaceView.Renderer。 要在Android應用程序中使用 OpenGL,需要了解如何在 Activity 中實現這些類。
GLSurfaceView
這個類是一個視圖可以使用 OpenGL API調用繪製和操作對象,在函數與 SurfaceView類似。 你可以通過創建一個實例並將你的呈現器插件添加到它來使用這個類。 但是,如果要捕獲觸摸屏事件,應該擴展 GLSurfaceView 類來實現觸摸偵聽器 ,以響應觸摸事件的響應。
GLSurfaceView.Renderer
這裏接口定義了在 GLSurfaceView中繪製圖形所需的方法。 你必須提供這裏接口的實現作爲單獨的類,並使用 GLSurfaceView.setRenderer() 將它的附加到你的 GLSurfaceView實例。
公共代碼
在兩個平臺上,opengl代碼相似。 它允許我們將接口甚至抽象類提取到項目的公共部分。 例如使用着色器的類必須繼承抽象基類 <a id="ShaderProgramBase" name="ShaderProgramBase">ShaderProgramBase</a>
:
複製代碼
package com.intel.inde.moe.samples.openglbox.common;publicabstractclass ShaderProgramBase { protectedstaticint INVALID_VALUE = -1; protectedint programHandle = INVALID_VALUE; protected HashMap<String, Integer> attributes = new HashMap<String, Integer>(); publicabstractvoid create(String vertexCode, String fragmentCode); publicabstractvoid use(); publicabstractvoid unUse(); publicabstractint getAttributeLocation(String attribute); publicabstractint getUniformLocation(String uniform); protectedabstractint loadShader(int type, String shaderCode); publicint getProgramHandle() { return programHandle; } }
現在讓我們深入討論 ShaderProgramBase.create()
方法的具體實現。
iOS
複製代碼
package com.intel.inde.moe.samples.openglbox.ios; ...publicclass ShaderProgram extends ShaderProgramBase { ... @Overridepublicvoid create(String vertexCode, String fragmentCode) { . . . programHandle = OpenGLES.glCreateProgram(); OpenGLES.glAttachShader(programHandle, vertexShader); OpenGLES.glAttachShader(programHandle, fragmentShader); OpenGLES.glLinkProgram(programHandle); } }
Android
複製代碼
package com.intel.inde.moe.samples.openglbox.android; ...publicclass ShaderProgram extends ShaderProgramBase { ... @Overridepublicvoid create(String vertexCode, String fragmentCode) { . . . programHandle = GLES20.glCreateProgram(); GLES20.glAttachShader(programHandle, vertexShader); GLES20.glAttachShader(programHandle, fragmentShader); GLES20.glLinkProgram(programHandle); } }
就像你所看到的,這些代碼Fragment看起來非常相似。 看起來,代碼似乎只有前綴- 英鎊和英鎊。 不太正確,但不遠。 請仔細查看 loadShader()
方法:
iOS
複製代碼
package com.intel.inde.moe.samples.openglbox.ios; ...publicclass ShaderProgram extends ShaderProgramBase { ... @Overrideprotectedint loadShader(int type, String shaderCode) { . . . IntPtr status = PtrFactory.newIntReference(); OpenGLES.glGetShaderiv(shader, ES2.GL_COMPILE_STATUS, status); if (status.getValue() == 0) { BytePtr info = PtrFactory.newWeakByteArray(256); OpenGLES.glGetShaderInfoLog(shader, 256, PtrFactory.newWeakIntReference(0), info); OpenGLES.glDeleteShader(shader); thrownew IllegalArgumentException("Shader compilation failed with:" + info.toASCIIString()); } . . . } }
Android
複製代碼
package com.intel.inde.moe.samples.openglbox.android; ...publicclass ShaderProgram extends ShaderProgramBase { ... @Overrideprotectedint loadShader(int type, String shaderCode) { . . . int[] status = newint[1]; GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, status, 0); if (status[0] == 0) { String info = GLES20.glGetShaderInfoLog(shader); GLES20.glDeleteShader(shader); thrownew IllegalArgumentException("Shader compilation failed with:" + info); } . . . } }
這個代碼與我們傳遞指向OpenGL的指針的方式不同。 你將在其他地方看到類似的區別。
公用數據
現在是時候描述其他公共部分了。 我們的應用程序使用以下常規數據:
複製代碼
publicclass Geometry { publicstaticfinalfloat[] VERTICES = newfloat[] {.. . }; publicstaticfinalbyte[] INDICES = {.. . }; }
複製代碼
publicclass Shaders { publicstaticfinalString VERTEXT_SHADER = ".. ."; publicstaticfinalString FRAGMENT_SHADER = ".. ."; }
- 參數( 背景顏色,旋轉速度,不同相機相關值)
複製代碼
publicclass Parameters { . . . publicstaticfinalfloat DEGREES_PER_SECOND = 90.0f; . . . }
所有這些常量都是自解釋的。 它們代表通用 3D 數據模型和操作邏輯。 在較大的項目中,公共數據將更大。
三維數學
OpenGL 2.0和更高版本不提供創建或者指定轉換矩陣的內置函數。 可以編程着色程序提供頂點轉換,並使用通用統一變量指定明暗器輸入。
iOS
GLKit框架包括一個綜合的向量和矩陣類型和函數庫,在iOS硬件上優化。
複製代碼
viewMatrix = GLKit.GLKMatrix4MakeLookAt( 0.0f, 0.0f, Parameters.EYE_Z, // eye0.0f, 0.0f, 0.0f, // center0.0f, 1.0f, 0.0f // up-vector); viewMatrix = GLKit.GLKMatrix4Rotate(viewMatrix, GLKit.GLKMathDegreesToRadians(Parameters.PITCH), 1, 0, 0);
Android
類 android.opengl.Matrix 提供數學實用程序。 這些方法對OpenGL格式矩陣和存儲在float數組中的向量進行操作。
複製代碼
Matrix.setLookAtM(viewMatrix, 0, 0.0f, 0.0f, Parameters.EYE_Z, // eye0.0f, 0.0f, 0.0f, // center0.0f, 1.0f, 0.0f // up-vector); Matrix.rotateM(viewMatrix, 0, Parameters.PITCH, 1, 0, 0);
但是你可以自己實現基本數學類,並增加公共代碼的重用。
正在加載紋理
iOS
GLKTextureLoader 類簡化了加載紋理數據所需的工作。 在圖像 I/O 框架支持的大多數圖像格式中,GLKTextureLoader 類可以加載二維或者cubemap紋理。 在iOS上,它還可以加載在 PVRTC 格式中壓縮的紋理。 它可以同步或者異步加載數據。
複製代碼
publicstaticint loadGLTexture(String name, String extension) { NSMutableDictionary options = NSMutableDictionary.alloc().init(); options.put(NSNumber.numberWithBool(true), GLKit.GLKTextureLoaderOriginBottomLeft()); String path = NSBundle.mainBundle().pathForResourceOfType(name, extension); GLKTextureInfo info = GLKTextureLoader.textureWithContentsOfFileOptionsError(path, options, null); if (info == null) { System.out.println("Error loading file:" + name + "." + extension); return -1; } return info.name(); }
GLKTextureLoader 和 GLKTextureInfo類不管理你的OpenGL紋理。 一旦紋理恢復到你的應用,你就會對它負責。 在使用OpenGL紋理完成應用之後,它必須通過調用 glDeleteTextures() 函數來顯式釋放它。
Android
爲了好或者壞,但我們是手工實現的:
複製代碼
publicstaticint loadGLTexture(Bitmap bitmap) { int[] textureIDs = newint[1]; GLES20.glGenTextures(1, textureIDs, 0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIDs[0]); // Create Nearest Filtered Texture GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT); // Use the Android GLUtils to specify a two-dimensional texture image from our bitmap GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); return textureIDs[0]; }
當然,你總是可以編寫你自己的包裝器的代碼。 我們沒有設定這樣一個任務。
計時
iOS
方法 timeSinceLastUpdate()
返回自上次視圖控制器調用委託方法 update()
之後所經過的時間量:
複製代碼
publicvoid update() { rotation += Parameters.DEGREES_PER_SECOND * timeSinceLastUpdate(); modelMatrix = GLKit.GLKMatrix4Identity(); modelMatrix = GLKit.GLKMatrix4Rotate(modelMatrix, GLKit.GLKMathDegreesToRadians(rotation), 0, 1, 0); }
Android
在Android上沒有這種方法。 所以我們必須手工重新創建它:
複製代碼
privatelong lastTime;publicvoid onSurfaceCreated(GL10 unused, EGLConfig config) { . . . lastTime = System.nanoTime(); }publicvoid onDrawFrame(GL10 unused) { update(); drawBox(); }double timeSinceLastUpdate() { long time = System.nanoTime(); double delta = (double) (time - lastTime)/1000000000; lastTime = time; return delta; }privatevoid update() { if (isPaused) return; rotation += Parameters.DEGREES_PER_SECOND * timeSinceLastUpdate(); Matrix.setIdentityM(modelMatrix, 0); Matrix.rotateM(modelMatrix, 0, rotation, 0, 1, 0); }
摘要
在iOS上使用Java開發非常類似於Android上的許多優點:
- OpenGL ES是一個基於和Android的API,具有良好的可移植性
- 熟悉的Java語法
- 使用強大的iOS模擬器和 Android 模擬器 來調試兩個目標