Upload
phungtruc
View
227
Download
0
Embed Size (px)
Citation preview
OpenGL
• OpenGL en Android es la versión ES
• OpenGL es un estándar de rendering
• ES for embedded systems
• La versión actual es 3.0, pero en Android 2.0
OpenGL
•http://developer.android.com/training/graphics/opengl/index.html
•Tutorial básico introductorio
•Sin texturas
OpenGL
• Transformaciones 3D
• Rendering basado en polígonos
• Define un pipeline de rendering en la tarjeta, se descargan programas (shaders)
OpenGL ejemplo
• Antes del <application></application>
• En el Manifest, (sólo si voy a usar texto):
<supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" />
<supports-gl-texture android:name="GL_OES_compressed_paletted_texture" />
• En el Manifest, voy a usar OpenGL 2.0:
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
Activity
•Creo la SurfaceView y la expando
•No hace falta layout (pantalla completa)
•Se puede poner como widget (no el ejemplo)
OpenGL Activity
package org.lsub.paurea.surfaceview;
import android.app.Activity;
import android.os.Bundle;
import android.opengl.GLSurfaceView;
!
!
OpenGL Activitypublic class MainActivity extends Activity {
private GLSurfaceView mGLView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new MyGLSurfaceView(this);
setContentView(mGLView);
}
}
OpenGL Activity @Override
protected void onResume()
{
super.onResume();
mGLView.onResume();
}
@Override
protected void onPause()
{
super.onPause();
mGLView.onPause()
}}
!
GLSurfaceView
•Varios modos de render
•Mejor renderizar cuando está sucio
• Se puede renderizar en demanda o de forma continua
GLSurfaceViewclass MyGLSurfaceView extends GLSurfaceView {
private final MyGLRenderer mRenderer;
public MyGLSurfaceView(Context context){
super(context);
setEGLContextClientVersion(2);
mRenderer = new MyGLRenderer(context);
setRenderer(mRenderer);
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
GLSurfaceView!
private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
private float mPreviousX;
private float mPreviousY;
@Override
public boolean onTouchEvent(MotionEvent e) {
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mPreviousX;
float dy = y - mPreviousY;
// reverse direction of rotation above the mid-line
if (y > getHeight() / 2) {
dx = dx * -1 ;
GLSurfaceView!
}
// reverse direction of rotation to left of the mid-line
if (x < getWidth() / 2) {
dy = dy * -1 ;
}
float angle = mRenderer.getAngle() +
((dx + dy) * TOUCH_SCALE_FACTOR);
mRenderer.setAngle(angle); // = 180.0f / 320
requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}
GLRenderer
package org.lsub.paurea.surfaceview;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.Matrix;
import android.content.Context;
GLRenderer
• Tiene 3 callbacks importantes
• onSurfaceCreated: la primera vez o si se pierde el contexto de openGL y hay que recrearlo
• onSurfaceChanged: si hay un cambio importante (e.g. landscape) y la primera vez
• onDrawFrame: cada vez que hay que dibujar un frame
GLRendererpublic class MyGLRenderer implements GLSurfaceView.Renderer {
private final Context mActivityContext;
//private Triangle triangle;
private Square sprite;
//Matrix Initializations
private final float[] mMVPMatrix = new float[16];
private final float[] mProjMatrix = new float[16];
private final float[] mVMatrix = new float[16];
private float[] mRotationMatrix = new float[16];
OpenGL GLRenderer
//Declare as volatile (race?)
public volatile float mAngle;
public MyGLRenderer(final Context context) { mActivityContext = context }
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
//Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
//Initialize Shapes
sprite = new Square(mActivityContext);
}
GLRenderer
•En onDrawFrame
•Se pasa la imagen por las transformaciones
•Modelo, Vista, Proyección (MVP)
•Multiplicación de matrices
•Luego se dibuja
GLRenderer
• MVP
•Model, cambio de coordenadas de objeto al mundo
• View, cambio de coordenadas de la cámara
• Projection, proyección sobre la cámara
GLRenderer
• Projection matrix
• Un poco especial, aplica la distorsión de la perspectiva (puede ser ortográfica)
Coordenadas Homogéneas
• ¿Por qué algunas matrices tienen 4D (en lugar de 3D)?
• Porque están en coordenadas homogéneas
• Es un tipo de álgebra que se llama álgebra proyectiva
Coordenadas Homogéneas
• Idea del álgebra proyectiva
• Hay puntos en el infinito (como cuando dibujamos con puntos de fuga con perspectiva)
Coordenadas Homogéneas
• ¿Como se representa un vector que apunta al infinito?
• En coordenadas homogéneas
Coordenadas Homogéneas
• ¿Como se representa un punto en el infinito?
• Una coordenada extra P
• (X, Y, Z, P) (X/P, Y/P, Z/P)
• Si P vale 0, el punto está en el infinito
• El punto (0, 0, 0, 0) no vale, el origen es (0, 0, 0, 1)
• Dos coordenadas u, v representan el mismo punto si ku = v
Coordenadas Homogéneas
• (kX, kY, kZ, kP) y (X, Y, Z, P) son el mismo punto
• Es una clase de equivalencia o espacio cociente
• Todos los puntos/vectores en una recta son el mismo (4D - 1D = 3D)
Coordenadas Homogéneas
• Puedo representar transformaciones algebraicas
• Con “perspectiva”, por ejemplo proyectar sobre el viewport
Matrices
• La mayor parte de las veces, no hace falta usar directamente coordenadas homogéneas (ni matrices)
• Las construyo describiéndolas (como en el ejemplo)
• Pero los arrays son de 4x4 = 16 entradas
public void onDrawFrame(GL10 unused) {
float[] scratch = new float[16];
//Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
//Set the camera position (View Matrix)
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
//Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
//Create a rotation transformation for the triangle
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
//Combine the rotation matrix with the projection and camera view
Matrix.multiplyMM(scratch, 0, mRotationMatrix, 0, mMVPMatrix, 0);
//Draw Shape
//triangle.Draw(mMVPMatrix);
sprite.Draw(scratch);
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
//This Projection Matrix is applied to object coordinates in the onDrawFrame() method
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
GLRenderer
•onSurfaceChanged se crea el ViewPort
•Que es la ventana al mundo
•Se obtiene de la transformación inducida por el frustrum (pirámide truncada)
public static int loadShader(int type, String shaderCode) {
int shader = GLES20.glCreateShader(type);
//Add The Source Code and Compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
!
public float getAngle() {
return mAngle;
}
public void setAngle(float angle) {
mAngle = angle;
}
}
Squarepackage org.lsub.paurea.surfaceview;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import android.opengl.GLES20;
import android.content.Context;
import android.opengl.GLUtils;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap;
public class Square {
//Reference to Activity Context
private final Context mActivityContext;
//Added for Textures
private final FloatBuffer mSquareTextureCoordinates;
private int mTextureUniformHandle;
private int mTextureCoordinateHandle;
private final int mTextureCoordinateDataSize = 2;
Shaders
•Código que se compila, enlaza y carga en tiempo de ejecución en la tarjeta de video
•En el ejemplo de vértices y fragmentos
•Son un pipeline de transformaciones
•En el shader de fragmentos se aplican las texturas
private final String vertexShaderCode =
"attribute vec2 a_TexCoordinate;" +
"varying vec2 v_TexCoordinate;" +
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition * uMVPMatrix;" +
" v_TexCoordinate = a_TexCoordinate;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"uniform sampler2D u_Texture;" +
"varying vec2 v_TexCoordinate;" +
"void main() {" +
" gl_FragColor = (vColor * texture2D(u_Texture, v_TexCoordinate));" +
"}";
Square
• Coordenadas
• Se pueden usar con 2D o 3D (ver constante COORDS_PER_VERTEX
• Las otras valen 0
Square
• Hay que serializar las cosas para OpenGL
• Se usa ByteBuffer
• Hay que convertir el endianness de Java al nativo y ponerlo en un formato que entiende OpenGL
private final int shaderProgram;
private final FloatBuffer vertexBuffer;
private final ShortBuffer drawListBuffer;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 2;
static float spriteCoords[] = {
-0.5f, 0.5f, // top left
-0.5f, -0.5f, // bottom left
0.5f, -0.5f, // bottom right
0.5f, 0.5f}; //top right
private short drawOrder[] = {0, 1, 2, 0, 2, 3}; //Order to draw vertices
private final int vertexStride = COORDS_PER_VERTEX * 4; //Bytes per vertex
// Set color with red, green, blue and alpha (opacity) values
float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 1.0f};
public Square(final Context activityContext) {
mActivityContext = activityContext;
! //Initialize Vertex Byte Buffer for Shape Coordinates / # of coordinate values * 4 bytes per float
ByteBuffer bb = ByteBuffer.allocateDirect(spriteCoords.length * 4);
//Use the Device's Native Byte Order
bb.order(ByteOrder.nativeOrder());
//Create a floating point buffer from the ByteBuffer
vertexBuffer = bb.asFloatBuffer();
//Add the coordinates to the FloatBuffer
vertexBuffer.put(spriteCoords);
//Set the Buffer to Read the first coordinate
vertexBuffer.position(0);
// S, T (or X, Y)
// Texture coordinate data.
// Because images have a Y axis pointing downward (values increase as you move down) while
// OpenGL has a Y axis pointing upward, we adjust for that here by flipping the Y axis.
// What's more is that the texture coordinates are the same for every face.
final float[] SquareTextureCoordinateData = {
-0.5f, 0.5f,
-0.5f, -0.5f,
0.5f, -0.5f,
0.5f, 0.5f
};
mSquareTextureCoordinates = ByteBuffer.allocateDirect(SquareTextureCoordinateData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
mSquareTextureCoordinates.put(SquareTextureCoordinateData).position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(spriteCoords.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
shaderProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(shaderProgram, vertexShader);
GLES20.glAttachShader(shaderProgram, fragmentShader);
//Texture Code
GLES20.glBindAttribLocation(shaderProgram, 0, "a_TexCoordinate");
GLES20.glLinkProgram(shaderProgram);
//Load the texture
mTextureDataHandle = loadTexture(mActivityContext, R.drawable.texture);
}
public void Draw(float[] mvpMatrix) {
//Add program to OpenGL ES Environment
GLES20.glUseProgram(shaderProgram);
//Get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(shaderProgram, "vPosition");
//Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
//Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
//Get Handle to Fragment Shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(shaderProgram, "vColor");
//Set the Color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
//Set Texture Handles and bind Texture
mTextureUniformHandle = GLES20.glGetAttribLocation(shaderProgram, "u_Texture");
mTextureCoordinateHandle = GLES20.glGetAttribLocation(shaderProgram, "a_TexCoordinate");
//Set the active texture unit to texture unit 0.
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
! //Bind the texture to this unit.
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle);
//Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
GLES20.glUniform1i(mTextureUniformHandle, 0);
//Pass in the texture coordinate information
mSquareTextureCoordinates.position(0);
GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize, GLES20.GL_FLOAT, false, 0,
mSquareTextureCoordinates);
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
//Get Handle to Shape's Transformation Matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(shaderProgram, "uMVPMatrix");
//Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
//Draw the square
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT,
drawListBuffer);
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
public static int loadTexture(final Context context, final int resourceId) {
final int[] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
if (textureHandle[0] != 0) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false; // No pre-scaling
// Read in the resource
final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);
// Bind to the texture in OpenGL
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
// Set filtering
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
!
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// Recycle the bitmap, since its data has been loaded into OpenGL.
bitmap.recycle();
}
if (textureHandle[0] == 0) {
throw new RuntimeException("Error loading texture.");
}
return textureHandle[0];
}
}
OpenGL• Muchas más cosas
• Iluminación, shaders más complicados, blending etc.
• Más tutoriales: http://insanitydesign.com/wp/projects/nehe-android-ports/ http://www.learnopengles.com/android-lesson-one-getting-started/
• Shading language: https://www.opengl.org/documentation/glsl/
• Recetas shaders: http://www.amazon.com/OpenGL-Shading-Language-Cookbook-Edition/dp/1782167021