Android OpenGL ES 教程 第一章 -- 设置视图(View)

翻译:OpenGL ES Tutorial for Android – Part I – Setting up the view

原文地址:点击这里
第一次翻译,部分参考网络。对英文欠佳的同学提供一点点方便。废话不多,分割线后皆为译文。
========================================================================

我将要写一些在 Android 手机上使用 OpenGL ES 的教程。在不同设备上 OpenGL 的理论是一样的,所以,当要跨平台移植的时候应该会很容易。

文中一些详细资料我并不一定能够记得到底是在那里找到的,所以原文的引用说明不一定正确。如果你觉得文中一些内容是引用是出自你手,而我又忘了说明,请你E-mail我。

在代码例子中,有两种不同的连接。具体的函数会链接至 Android 官方文档,之后的会链接至 OpenGL 文档。比如:

gl.glClearColor(0.0f,0.0f,0.0f,0.5f); //OpenGl docs.

那么,开始吧。

在这个教程中我会教你如何设置你的OpenGL ES View---这总是一个比较好开始的地方。


设置一个OpenGL ES View

设置一个 OpenGL view 总是很容易的,在Android上也一样。我们就从下面这2个东西开始。


GLSurfaceView

GLSurfaceView 在 Android 1.5中是一个API类,用于帮助你写 OpenGL ES 应用程序。作用如下:


提供了连接 OpenGL ES 和 View 系统的粘合代码。

提供了OpenGL ES 和 Activity 生命周期协调工作的粘合代码。

更容易选择合适的缓冲帧像素格式。

创建并管理一个单独的渲染线程实现平滑动画。

提供简单易容的调试工具来追踪 OpenGL ES API 调用以及检查错误。


如果你想快速的开始你的 OpenGL ES 应用,从这里开始看比较好。

你唯一需要调用的函数是:

  public void setRenderer(GLSurfaceView.Render renderer)

了解更多关于: GLSurfaceView


GLSurfaceView.Renderer

GLSurfaceView.Renderer 是一个通用的渲染接口。你需要实现所有的方法来渲染帧。
有下面3个方法需要实现:

  //当surface被创建或重新创建的时候调用
  public void onSurfaceCreated(GL10 gl, EGLConfig config)

  //画当前帧时调用
  public void onDrawFrame(GL10 gl)

  //当surface改变大小时调用
  public void onSurfaceChanged(GL10 gl, int width, int height)


onSurfaceCreated

这里合适写一些在渲染周期中不会经常改变的代码。比如初始屏幕颜色、打开Z轴缓冲等。

onDrawFrame

这里执行绘图。

onSurfaceChanged

如果你的设备支持横竖屏切换,当切换时会调用这个方法。你需要做的是重新设定比例。

了解更多:GLSurfaceView.Renderer

组合起来

首先我们创建一个 Activity, 简单干净就好。
package se.jayway.opengl.tutorial;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

public class TutorialPartI extends Activity {
    /** Activity 第一次建立时调用 */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    	super.onCreate(savedInstanceState);
 		GLSurfaceView view = new GLSurfaceView(this);
   		view.setRenderer(new OpenGLRenderer());
   		setContentView(view);
    }
}
我们的渲染器需要多一些的设置,看看代码一会我就解释他们。
package se.jayway.opengl.tutorial;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLU;
import android.opengl.GLSurfaceView.Renderer;

public class OpenGLRenderer implements Renderer {
	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * android.opengl.GLSurfaceView.Renderer#onSurfaceCreated(javax.
         * microedition.khronos.opengles.GL10, javax.microedition.khronos.
         * egl.EGLConfig)
	 */
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		// 设置背景颜色 ( rgba ).
		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);  // OpenGL docs.
		// 开启平滑阴影,默认的就是。
		gl.glShadeModel(GL10.GL_SMOOTH);// OpenGL docs.
		// 设置深度缓冲。
		gl.glClearDepthf(1.0f);// OpenGL docs.
		// 设置深度缓冲测试。
		gl.glEnable(GL10.GL_DEPTH_TEST);// OpenGL docs.
		// 设置深度缓冲测试类型。
		gl.glDepthFunc(GL10.GL_LEQUAL);// OpenGL docs.
		// 真实透视计算。
		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, // OpenGL docs.
                          GL10.GL_NICEST);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * android.opengl.GLSurfaceView.Renderer#onDrawFrame(javax.
         * microedition.khronos.opengles.GL10)
	 */
	public void onDrawFrame(GL10 gl) {
		// 清除屏幕和深度缓冲。
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | // OpenGL docs.
                           GL10.GL_DEPTH_BUFFER_BIT);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * android.opengl.GLSurfaceView.Renderer#onSurfaceChanged(javax.
         * microedition.khronos.opengles.GL10, int, int)
	 */
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		// 设置当前视图大小。
		gl.glViewport(0, 0, width, height);// OpenGL docs.
		// 选择投影模型。
		gl.glMatrixMode(GL10.GL_PROJECTION);// OpenGL docs.
		// 重置投影模型。
		gl.glLoadIdentity();// OpenGL docs.
		// 计算窗口纵横比。
		GLU.gluPerspective(gl, 45.0f,
                                   (float) width / (float) height,
                                   0.1f, 100.0f);
		// 选择modelview模型(Select the modelview matrix)。
		gl.glMatrixMode(GL10.GL_MODELVIEW);// OpenGL docs.
		// 重置modelview模型。
		gl.glLoadIdentity();// OpenGL docs.
	}
}

Fullscreen

在 OpenGLDemo 类中增加下面内容,设置为全屏显示。

 public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE); // (新增)
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN); // (新增)
        ... // 以前的代码.
    }
这些已经足够让你的 OpenGL ES view 运行起来了。如果你编译并运行它,你会看见一个不错的黑屏。

引用

文中所引用的内容来自以下地方:

你可以下载这个教程的源代码: Tutorial_Part_I.zip
也可以从这里获取:code.google.com


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的Android OpenGL ES实现3D抛骰子的示例代码: 首先在build.gradle中添加OpenGL ES依赖: ```groovy implementation 'com.android.support:support-v4:28.0.0' implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.google.android.gms:play-services-gcm:16.0.0' implementation 'com.google.android.gms:play-services-ads:17.1.1' implementation 'com.google.android.gms:play-services-auth:16.0.1' implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support:design:28.0.0' implementation 'com.android.support:cardview-v7:28.0.0' implementation 'com.android.support:recyclerview-v7:28.0.0' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.squareup.okhttp3:okhttp:3.11.0' implementation 'com.squareup.okio:okio:1.14.0' implementation 'com.github.bumptech.glide:glide:4.9.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0' implementation 'com.squareup.retrofit2:retrofit:2.5.0' implementation 'com.squareup.retrofit2:converter-gson:2.5.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation 'io.reactivex.rxjava2:rxjava:2.2.8' ``` 接着创建一个继承自GLSurfaceView的自定义视图: ```java public class DiceView extends GLSurfaceView implements Renderer { private final static int DICE_NUM = 3; // 骰子数量 private final static float DICE_SIZE = 0.5f; // 骰子大小 private final static float DICE_SPEED = 0.1f; // 骰子旋转速度 private Dice[] dices = new Dice[DICE_NUM]; // 骰子数组 private float xAngle = 0; // x轴旋转角度 private float yAngle = 0; // y轴旋转角度 private float zAngle = 0; // z轴旋转角度 public DiceView(Context context) { super(context); setRenderer(this); setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { for (int i = 0; i < DICE_NUM; i++) { dices[i] = new Dice(getContext(), DICE_SIZE, DICE_SPEED); } gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); gl.glEnable(GL10.GL_DEPTH_TEST); gl.glEnable(GL10.GL_TEXTURE_2D); gl.glShadeModel(GL10.GL_SMOOTH); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { gl.glViewport(0, 0, width, height); gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); float ratio = (float) width / height; gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); } @Override public void onDrawFrame(GL10 gl) { 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.glRotatef(xAngle, 1, 0, 0); gl.glRotatef(yAngle, 0, 1, 0); gl.glRotatef(zAngle, 0, 0, 1); for (int i = 0; i < DICE_NUM; i++) { dices[i].draw(gl); } } public void setAngles(float xAngle, float yAngle, float zAngle) { this.xAngle = xAngle; this.yAngle = yAngle; this.zAngle = zAngle; } } ``` 然后创建一个Dice类来表示骰子: ```java public class Dice { private FloatBuffer vertexBuffer; private FloatBuffer textureBuffer; private ByteBuffer indexBuffer; private int[] textures = new int[1]; private int[] diceValues = {1, 2, 3, 4, 5, 6}; private float diceSize; private float diceSpeed; private float angleX = 0; private float angleY = 0; private float angleZ = 0; private float[] vertices = { -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f }; private float[] textureCoords = { 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0 }; private byte[] indices = { 0, 1, 2, 0, 2, 3, 0, 3, 7, 0, 7, 4, 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 3, 2, 6, 3, 6, 7, 4, 7, 6, 4, 6, 5 }; public Dice(Context context, float diceSize, float diceSpeed) { this.diceSize = diceSize; this.diceSpeed = diceSpeed; ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4); vbb.order(ByteOrder.nativeOrder()); vertexBuffer = vbb.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); ByteBuffer tbb = ByteBuffer.allocateDirect(textureCoords.length * 4); tbb.order(ByteOrder.nativeOrder()); textureBuffer = tbb.asFloatBuffer(); textureBuffer.put(textureCoords); textureBuffer.position(0); indexBuffer = ByteBuffer.allocateDirect(indices.length); indexBuffer.put(indices); indexBuffer.position(0); Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.dice); GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); bitmap.recycle(); Random random = new Random(); setDiceValue(diceValues[random.nextInt(6)]); } public void draw(GL10 gl) { gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer); gl.glPushMatrix(); gl.glRotatef(angleX, 1, 0, 0); gl.glRotatef(angleY, 0, 1, 0); gl.glRotatef(angleZ, 0, 0, 1); gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_BYTE, indexBuffer); gl.glPopMatrix(); gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); } public void setDiceValue(int value) { angleX = 0; angleY = 0; angleZ = 0; switch (value) { case 1: angleX = 90; break; case 2: angleX = -90; break; case 3: angleY = 90; break; case 4: angleY = -90; break; case 5: angleZ = 90; break; case 6: angleZ = -90; break; } } public void update() { angleX += diceSpeed; angleY += diceSpeed; angleZ += diceSpeed; } } ``` 最后在Activity中使用DiceView即可: ```java public class MainActivity extends AppCompatActivity { private DiceView diceView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diceView = new DiceView(this); setContentView(diceView); } @Override protected void onResume() { super.onResume(); diceView.onResume(); } @Override protected void onPause() { super.onPause(); diceView.onPause(); } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_MOVE: float dx = x - lastX; float dy = y - lastY; diceView.setAngles(dy * 0.5f, dx * 0.5f, 0); diceView.requestRender(); break; } lastX = x; lastY = y; return true; } } ``` 这样就实现了一个简单的Android OpenGL ES实现3D抛骰子的程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值