« AndroidでSystem.out.println | トップページ | AndroidのOpenGL ESで高レベルオブジェクト »

AndroidのOpenGLで透視投影

とりあえずOpenGLをいろいろいじっているわけですが、 やっとこさ透視投影と陰面消去ができたのでお知らせします。

package net.chephes.graphics;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLU;

import android.app.Activity;
import android.os.Bundle;
import android.view.*;
import android.graphics.*;
import android.content.Context;
import android.content.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.Log;



public class Android3D extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        View view = new View3D(this);
        setContentView(view);
    }
   
    public static void trace(String message){
    Log.i("Android3D", message);
    }
}

class TexturePlane{
    private ByteBuffer  mIndexBuffer;
    private FloatBuffer   mVertexBuffer;
    private FloatBuffer mTextureBuffer;
    private int texid;
    private float x, y, z;

    float one = 1;
    float vertices[] = {
           -one, -one, 0,
            one, -one, 0,
            one,  one, 0,
           -one,  one, 0,
        };
       
    byte indices[] = {
            0, 1, 2,    2, 3, 0
    };
float textures[] = {
1, 1,
0, 1,
0, 0,
1, 0
};
   
    public TexturePlane(int texid){
    mVertexBuffer = createFloatBuffer(vertices);
    mIndexBuffer = createByteBuffer(indices);
    mTextureBuffer = createFloatBuffer(textures);
    this.texid = texid;
    }
   
    public void setX(float x){
    this.x = x;
    for(int i = 0; i < vertices.length; i+=3){
        mVertexBuffer.put(i, vertices[i] + x);
    }
    }
   
    public void setY(float y){
    this.y = y;
    for(int i = 1; i < vertices.length; i+=3){
        mVertexBuffer.put(i, vertices[i] + y);
    }
    }
   
    public void setZ(float z){
    this.z = z;
    for(int i = 2; i < vertices.length; i+=3){
        mVertexBuffer.put(i, vertices[i] + z);
    }
    }
   
    public float getX(){return x;}
    public float getY(){return y;}
    public float getZ(){return z;}
   
    public void render(GL10 gl){
        gl.glColor4f(1f, 1f, 1f, 0f);

gl.glBindTexture(gl.GL_TEXTURE_2D, texid);

        gl.glTexCoordPointer(2, gl.GL_FLOAT, 0, mTextureBuffer);
        gl.glVertexPointer(3, gl.GL_FLOAT, 0, mVertexBuffer);
        gl.glDrawElements(gl.GL_TRIANGLES, 6, gl.GL_UNSIGNED_BYTE, mIndexBuffer);
    }
   
    public static IntBuffer createIntBuffer(int[] data){
    ByteBuffer buffer = ByteBuffer.allocateDirect(data.length * 4);
    buffer.order(ByteOrder.nativeOrder());
    IntBuffer ret = buffer.asIntBuffer();
    ret.put(data);
    ret.position(0);
   
    return ret;
    }

    public static FloatBuffer createFloatBuffer(float[] data){
    ByteBuffer buffer = ByteBuffer.allocateDirect(data.length * 4);
    buffer.order(ByteOrder.nativeOrder());
    FloatBuffer ret = buffer.asFloatBuffer();
    ret.put(data);
    ret.position(0);
   
    return ret;
    }
   
    public static ByteBuffer createByteBuffer(byte[] data){
    ByteBuffer buffer = ByteBuffer.allocateDirect(data.length);
    buffer.order(ByteOrder.nativeOrder());
    buffer.put(data);
    buffer.position(0);
    return buffer;
    }
}

class View3D extends View{
private OpenGLContext glContext;
   
    private boolean isDraw = false;
    private TexturePlane plane, plane2;
   
    public View3D(Context context){
super(context);

glContext = new OpenGLContext(OpenGLContext.DEPTH_BUFFER);
GL10 gl = (GL10)(glContext.getGL());
       
        plane = new TexturePlane(createTexture(gl, loadImage(R.drawable.kirby_t, 128, 128)));
        plane.setZ(30);
        plane2 = new TexturePlane(createTexture(gl,
        loadImage(R.drawable.texture, 512, 512)));
        plane2.setZ(10);
       
isDraw = true;
Android3D.trace("start...");
}
   
    public int createTexture(GL10 gl, Canvas canvas){
int a[] = new int[1];
gl.glGenTextures(1, a, 0);
gl.glBindTexture(gl.GL_TEXTURE_2D, a[0]);

IntBuffer buffer = TexturePlane.createIntBuffer(canvas.getPixels());

gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA,
canvas.getBitmapWidth(), canvas.getBitmapHeight(), 0,
gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, buffer);

return a[0];
    }

public Canvas loadImage(int fileid, int width, int height) {
Resources r = this.getContext().getResources();
Bitmap bitmap = Bitmap.createBitmap(width, height, true);
Drawable drawable = r.getDrawable(fileid);
Canvas canvas = new Canvas(bitmap);

drawable.setBounds(0, 0, width, height);
drawable.draw(canvas);

return canvas;
}

private void setPerspective(GL10 gl, float fovy, float aspect, float zNear, float zFar){
        gl.glMatrixMode(gl.GL_PROJECTION);
        gl.glLoadIdentity();
        GLU.gluPerspective(gl, fovy, aspect, zNear, zFar);
}

private void setCamera(GL10 gl, float x, float y, float z, float vx, float vy, float vz){
        gl.glMatrixMode(gl.GL_MODELVIEW);
        gl.glLoadIdentity();
        GLU.gluLookAt(gl, x, y, z,
        vx, vy, vz,
        0f, 1f, 0f);

}

    @Override
    protected void onDraw(Canvas canvas) {
    if(!isDraw){ return;}

GL10 gl = (GL10)(glContext.getGL());
glContext.waitNative(canvas, this);
        gl.glViewport(0, 0, getWidth(), getHeight());
       
        setPerspective(gl, 10.0f, (float)getWidth() / getHeight(), 1f, 100f);
        setCamera(gl, 10f, 20f, 70f, 0, 0, 0);
       
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT);

gl.glEnableClientState(gl.GL_VERTEX_ARRAY);
        gl.glEnableClientState(gl.GL_TEXTURE_COORD_ARRAY);
        gl.glEnable(gl.GL_TEXTURE_2D);
        gl.glDisable(gl.GL_CULL_FACE);

        gl.glEnable(gl.GL_DEPTH_TEST);
plane.render(gl);
        plane2.render(gl);
        gl.glDisable(gl.GL_DEPTH_TEST);

glContext.waitGL();
exe();
invalidate();
    }
   
    private void exe(){
plane.setZ(plane.getZ() - 0.1f);
    }
}

注目すべきはonDraw関数の中身。まず

glViewport

で表示箇所を設定し、

GLU.gluPerspective

で透視投影行列を設定しています。 そのとき、

glMatrixMode(GL10.GL_PROJECTION);

を呼び出して 投影用の行列を選択するのを忘れずに。 そして今度はカメラの設定。

glMatrixMode(GL10.GL_MODELVIEW);

で 行列を選んで、

GLU.gluLookAt

でカメラ位置を設定。 始めの3つのfloatはカメラの位置、次の3つは見る場所、最後の3つは どの方向を上とするか。 それができたらとりあえず

glClear

でいろいろ消して、

glEnable(GL10.GL_DEPTH_TEST);
//...処理
glDisable(GL10.GL_DEPTH_TEST);

の間でオブジェクトをいろいろ追加。最後は

waitGLを呼び出してレンダリング
完了。

まぁだいたいこんな流れです。OpenGLがシェルのような対話型の APIであることと、内部的に持ってる行列を選んだり、 どれを描くかを

glEnable

で選んだりするというのに気付くと 大分理解しやすいかと思います。

|

« AndroidでSystem.out.println | トップページ | AndroidのOpenGL ESで高レベルオブジェクト »

コメント

package com.google.android.plane;

import android.app.Activity;
import android.content.Context;
import android.content.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.OpenGLContext;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.view.View;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Random;
import java.util.Vector;

import javax.microedition.khronos.opengles.GL10;


public class plane extends Activity
{
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(new GLView(this));
}

public class GLView extends View {
private OpenGLContext mGLContext;
private Vector m3DObjects;
private float mAngle;
private long mNextTime;
private boolean mAnimate;

public GLView(Context context) {
super(context);

mGLContext = new OpenGLContext(0);

createObjects();

mAnimate = false;
}

private void createObjects() {
m3DObjects = new Vector();
DisplayObject3D object;
int texId = createTexture(loadImage(R.drawable.pig,128,128));
//object = new Plane(0x10000 > 1;
int oneY = mHeight >> 1;
int gridU = segX;
int gridV = segY;
int gridU1 = gridU + 1;
int gridV1 = gridV + 1;
int incU = mWidth / gridU;
int incV = mHeight / gridV;
int depth = 0;

//vertices
int[] vertices = new int[gridU1 * gridV1 * 3];
int cnt = 0;
for( int iu = 0; iu < gridU1; ++ iu ) {
for( int iv = 0; iv < gridV1; ++ iv ) {
vertices[cnt++] = iu * incU - oneX;
vertices[cnt++] = iv * incV - oneY;
vertices[cnt++] = depth;
}
}

//faces
byte[] indices = new byte[gridU * gridV * 2 * 3];
cnt = 0;
for( int iu = 0; iu < gridU; ++ iu ) {
for( int iv = 0; iv < gridV; ++ iv ) {
//Triangle A
indices[cnt++] = (byte)(iu * gridV1 + iv);
indices[cnt++] = (byte)(iu * gridV1 + (iv+1));
indices[cnt++] = (byte)((iu+1) * gridV1 + iv);

//Triangle B
indices[cnt++] = (byte)((iu+1) * gridV1 + (iv+1));
indices[cnt++] = (byte)((iu+1) * gridV1 + iv);
indices[cnt++] = (byte)(iu * gridV1 + (iv+1));
}
}

//colors
int[] colors = new int[gridU1 * gridV1 * 4];
Random rand = new Random();
for( int i = 0; i < colors.length; i += 4 ) {
colors[i] = rand.nextInt();
colors[i+1] = rand.nextInt();
colors[i+2] = rand.nextInt();
colors[i+3] = 0x10000;
}

mVertexBuffer = createIntBuffer(vertices);
mColorBuffer = createIntBuffer(colors);
mIndexBuffer = createByteBuffer(indices);
}

public void setX(int x) {
int d = x - this.x;
this.x = x;

for( int i = 0; i < mVertexBuffer.capacity(); i += 3 ) {
mVertexBuffer.put(i, mVertexBuffer.get(i) + d);
}
}

public void setY(int y) {
int d = y - this.y;
this.y = y;

for( int i = 1; i < mVertexBuffer.capacity(); i += 3 ) {
mVertexBuffer.put(i, mVertexBuffer.get(i) +d );
}
}

public void setZ(int z) {
int d = z - this.z;
this.z = z;

for( int i = 2; i < mVertexBuffer.capacity(); i += 3 ) {
mVertexBuffer.put(i, mVertexBuffer.get(i) + d);
}
}

public void draw(GL10 gl) {
gl.glFrontFace(GL10.GL_CW);
gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer);
gl.glColorPointer(4, GL10.GL_FIXED, 0, mColorBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, mIndexBuffer.capacity(), GL10.GL_UNSIGNED_BYTE, mIndexBuffer);
}
}




class TexturePlane extends Plane
{

private FloatBuffer mTextureBuffer;


private int texId;
//float textures[] = { 1, 1, 0, 1,0, 0,1, 0}; //问题
public TexturePlane(int width, int height, int segX, int segY,int texid)
{
super(width, height, segX, segY);

this.texId = texid;
float textures[] = { 1, 1,
0, 1,
0, 0,
1, 0};

mTextureBuffer = createFloatBuffer(textures);

}
public FloatBuffer createFloatBuffer(float[] data)
{
ByteBuffer buffer = ByteBuffer.allocateDirect(data.length * 4);
buffer.order(ByteOrder.nativeOrder());
FloatBuffer ret = buffer.asFloatBuffer();
ret.put(data);
ret.position(0);

return ret;
}


public void draw(GL10 gl)
{


gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
gl.glColor4f(1f, 1f, 1f, 0f);

gl.glBindTexture(gl.GL_TEXTURE_2D, texId);
gl.glTexCoordPointer(2, gl.GL_FLOAT, 0, mTextureBuffer);
gl.glVertexPointer(3, gl.GL_FLOAT, 0, mVertexBuffer);

gl.glDrawElements(gl.GL_TRIANGLES, 6, gl.GL_UNSIGNED_BYTE, mIndexBuffer);

gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glDisable(GL10.GL_TEXTURE_2D);


}

}


}


hello,I think the codes funtion is similar to your code,but it do work out,i do not know opengl very well,so i can not find out what is the problem,so i will appreciate it very much if you could check my codes and tell me how to handle the problem to make it can work.thanks and best regards

投稿: | 2008年2月21日 (木) 12時42分

thank you for your comment.
is this a whole source of your program?
if it is, i think you forgot to override View.onDraw(Canvas canvas). in the function, you have to call some other gl-controlling method.
in addition, if you are using New version of AndroidSDK, this code doesn't work any more.

投稿: Chephes | 2008年2月21日 (木) 13時37分

/* public TexturePlane(int width, int height,int texid)
{
for(int i = 0; i < vertices.length; i+=3)
{
vertices[i] *= width;
vertices[i+1] *= height;
}
// mVertexBuffer = createIntBuffer(vertices);
// mIndexBuffer = createByteBuffer(indices);
mVertexBuffer = createFloatBuffer(vertices);
mIndexBuffer = createByteBuffer(indices);
mTextureBuffer = createFloatBuffer(textures);
this.texid = texid;
}*/
what i want to do is just make the textureplane's width and height are variable ,and i think wo should make another constructor,but the forwarded code is not work,and i think maybe the texture should also be adjust,but i do not know the method,could you help me ,thanks and best regurds,i am looking forward to your reply!

投稿: | 2008年2月21日 (木) 16時05分

Hello,i am a chinese girl and work in a software corp. in China.
At present ,I am working on the Android platform.
Firstly,i am so sorry for my broken english.
Secondly,i admire you are so smart that can develop some opengl functions in so short time,i found that Japanse is very efficient and also smart.
Thirdly,i hope we can make friends.
My name is Annie.

投稿: | 2008年2月21日 (木) 16時17分

Hi Annie.
i'm very glad to receive the message about my blog from other country. i feel this blog is going to be an international blog :)

btw, are you the same person who has commented before?

投稿: Chephes | 2008年2月21日 (木) 17時07分

yes,you are so clever!
I feel so happy when i see your reply so quickly,i just think you may not reply to me or i will wait for a long time!

投稿: | 2008年2月21日 (木) 17時53分

Do you know you are my first foreign friend,i am so exciting!

投稿: | 2008年2月21日 (木) 17時55分

Thanks.
talking over the comment is not so confortable, so please send me an e-mail.
then talk about android over the mailer!

投稿: Chephes | 2008年2月21日 (木) 17時59分

Hi:i am sorry i don't konw your e-mail address,could you please tell me the address,thanks!My e-mail address is hmmanhui@yahoo.com.cn.

投稿: | 2008年2月21日 (木) 18時33分

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/486213/8992755

この記事へのトラックバック一覧です: AndroidのOpenGLで透視投影:

« AndroidでSystem.out.println | トップページ | AndroidのOpenGL ESで高レベルオブジェクト »