Andriod OpenGL 教程 07 - 光照和输入控制

来源:互联网 发布:js美化工具 编辑:程序博客网 时间:2024/06/11 04:23

关键字: android OpenGL 移动开发 教程

 

在这一课,我们将添加光照和输入控制,它可以是我们的应用看起来更美观,在这一课我们将演示三种不同的纹理滤波方式。演示如何通过键盘和触屏操作来移动场景中的对象,同时将演示在OpenGL场景中应用简单的光照。

为了演示用户输入,捕获用户输入事件,我们需要从GLSurfaceView导出一个子类来重载事件处理方法(比如重载onKeyUp(), onTouchEvent())。该子类的命名为MyGLSurfaceView,我们重载了onKeyUponTouchEvent方法,分别用来处理键盘和触屏事件。

在第六课的基础上,TextureCube处理更换了纹理,其他都没做改变。在MyGLRenderer中加了几个控制变量:

   floatangleX = 0.0f;

    floatangleY = 0.0f;

    floatspeedX = 0.0f;

    floatspeedY = 0.0f;

   floatz = -6.0f;

用于控制立方体的在Z方向的位置,在xy方向旋转的角度和速度。对其中onDrawFrame方法中的内容做了相应调整,方便我们演示用户输入的效果。

public void onDrawFrame(GL10 gl){// 清除屏幕和深度缓存gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);// 启动和关闭光源    if (lightingEnabled) {       gl.glEnable(GL10.GL_LIGHTING);    } else {         gl.glDisable(GL10.GL_LIGHTING);    }// -------- 渲染彩色立方体 ------// 重置当前的模型观察矩阵gl.glLoadIdentity();// 纵深向里移动zgl.glTranslatef(0.0f, 0.0f, z);// 绕X轴旋转立方体 gl.glRotatef(angleX, 1.0f, 0.0f, 0.0f);// 绕Y轴旋转立方体gl.glRotatef(angleY, 0.0f, 1.0f, 0.0f);// 画立方体cube.draw(gl, currentTextureFilter);// 每次刷新之后更新旋转角度angleX += speedX;angleY += speedY;}


 

MyGLSurfaceView实现了用户输入控制。

 

相应的,GLLesson07Activity.javaonCreate方法的代码调整为:

publicvoid onCreate(Bundle savedInstanceState) {   

        super.onCreate(savedInstanceState);

        glView =new MyGLSurfaceView(this);//创建一个定制的GLSurfaceView

        setContentView(glView);                   

}

编译运行,我们就可以通过输入一些控制来看立方体空间位置的变化。

 

现在我们演示不同的纹理映射滤波方式,首先对TextureCube中的代码进行调整textureIDs现在数组的的长度为3

int[]textureIDs =newint[3];//纹理-ID数组

draw方法添加了一个新的输入变量inttextureFilter用于选择滤波方式。

public class TextureCube {

     …………

             //通过纹理坐标ID选择纹理的滤波方式

   gl.glBindTexture(GL10.GL_TEXTURE_2D,textureIDs[textureFilter]);

   …………

    gl.glGenTextures(3, textureIDs, 0);//3个纹理生成纹理ID数组

    //生成 Nearest方式滤波的纹理并绑定到纹理0

    gl.glBindTexture(GL10.GL_TEXTURE_2D,textureIDs[0]);

    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);

    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);

    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);

    //生成Linear方式滤波的纹理并绑定到纹理1

    gl.glBindTexture(GL10.GL_TEXTURE_2D,textureIDs[1]);

    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);

         

    //生成mipmapped方式滤波的纹理并绑定到纹理2

    gl.glBindTexture(GL10.GL_TEXTURE_2D,textureIDs[2]);

    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR_MIPMAP_NEAREST);

    if(glinstanceof GL11) {

       gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_GENERATE_MIPMAP, GL11.GL_TRUE);

    }

    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);

   bmp.recycle();

}

MyGLRenderer中的代码修还为:

…………

intcurrentTextureFilter = 0;  //当前纹理滤波方式

…………

cube.draw(gl,currentTextureFilter);

…………

 

MyGLSurfaceViewonKeyUp方法,添加

case KeyEvent.KEYCODE_DPAD_CENTER//选择纹理滤波方式

         renderer.currentTextureFilter = (renderer.currentTextureFilter + 1) % 3;

     break;

然后编译运行。选择DPAD_CENTER按钮点击,观察纹理的效果,就会发现NEAEST效果差一些,其他两种方式的效果要好些。

最后一步,我们演示光照效果。在MyGLRenderer中加入灯光控制的代码

声明几个控制光照的变量

    booleanlightingEnabled =false;  //是否打开光照

    privatefloat[]lightAmbient = {0.5f, 0.5f, 0.5f, 1.0f};

    privatefloat[]lightDiffuse = {1.0f, 1.0f, 1.0f, 1.0f};

privatefloat[]lightPosition = {0.0f, 0.0f, 2.0f, 1.0f};

onSurfaceCreated中添加设置光源的代码:

// 设置光源 GL_LIGHT1的环境光(ambient)漫射光(diffuse)

        gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_AMBIENT,lightAmbient, 0);

        gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_DIFFUSE,lightDiffuse, 0);

        gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_POSITION,lightPosition, 0);

        gl.glEnable(GL10.GL_LIGHT1);  //使能光源1

    gl.glEnable(GL10.GL_LIGHT0);  //使能缺省光源0

onDramFrame中加入启动和关闭光源的代码

// 启动和关闭光源

        if (lightingEnabled) {

           gl.glEnable(GL10.GL_LIGHTING);

        } else {

             gl.glDisable(GL10.GL_LIGHTING);

    }

最后在MyGLSurfece.java中的onKey方法中加入用户控制代码,用于打开个关闭光源。

case KeyEvent.KEYCODE_L//光源开关

             renderer.lightingEnabled = !renderer.lightingEnabled;

         break;

代码:

GLLesson07Activity.java

package wintop.gllesson07;import android.app.Activity;import android.opengl.GLSurfaceView;import android.os.Bundle;public class GLLesson07Activity extends Activity {private GLSurfaceView glView;// 使用GLSurfaceView 导出类/** Called when the activity is first created. */    @Override    public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);                       glView = new MyGLSurfaceView(this);// 创建一个定制的GLSurfaceView        setContentView(glView);    }        // Call back when the activity is going into the background    @Override    protected void onPause(){    super.onPause();    glView.onPause();    }        // Call back after onPause()    @Override    protected void onResume()    {    super.onResume();    glView.onResume();    }}


MyGLRenderer.java

package wintop.gllesson07;import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10;import android.content.Context;import android.opengl.GLSurfaceView;import android.opengl.GLU;public class MyGLRenderer implements  GLSurfaceView.Renderer {private Context context;// 应用的上下文句柄private TextureCube  cube;// 下面的变量控制立方体的在Z方向的位置,在x和y方向旋转的角度和速度float angleX = 0.0f;float angleY = 0.0f;float speedX = 0.0f;float speedY = 0.0f;float z = -6.0f;int currentTextureFilter = 0;// 当前纹理滤波方式// 光照boolean lightingEnabled = false;   // 是否打开光照private float[] lightAmbient = {0.5f, 0.5f, 0.5f, 1.0f};private float[] lightDiffuse = {1.0f, 1.0f, 1.0f, 1.0f};private float[] lightPosition = {0.0f, 0.0f, 2.0f, 1.0f};// Constructor with global application contextpublic MyGLRenderer(Context context) {this.context = context;// 设置所用图形的数据数组缓冲区cube = new TextureCube();}// Call back when the surface is first created or re-created@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config){// 启用阴影平滑gl.glShadeModel(GL10.GL_SMOOTH);// 设置背景颜色gl.glClearColor(0.2f, 0.4f, 0.52f, 1.0f);// 设置深度缓存gl.glClearDepthf(1.0f);// 启用深度测试gl.glEnable(GL10.GL_DEPTH_TEST);// 所作深度测试的类型gl.glDepthFunc(GL10.GL_LEQUAL);// 告诉系统对透视进行修正gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);// 禁止抖动以取得更好的性能gl.glDisable(GL10.GL_DITHER);// 设置纹理cube.loadTexture(gl, context);// 加载纹理gl.glEnable(GL10.GL_TEXTURE_2D);// 纹理使能// 设置光源 GL_LIGHT1 的环境光(ambient)和 漫射光(diffuse)    gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_AMBIENT, lightAmbient, 0);    gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_DIFFUSE, lightDiffuse, 0);    gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_POSITION, lightPosition, 0);    gl.glEnable(GL10.GL_LIGHT1);   // 使能光源1    gl.glEnable(GL10.GL_LIGHT0);   // 使能缺省光源0}// Call back after onSurfaceCreated() or whenever the window's size changes@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height){if(height == 0)// 防止被零除{height = 1;}// 重置当前的视图区域gl.glViewport(0, 0, width, height);// 选择投影矩阵gl.glMatrixMode(GL10.GL_PROJECTION);// 重置投影矩阵gl.glLoadIdentity();// 设置视图区域的大小GLU.gluPerspective(gl, 45.0f, (float)width/(float)height,0.1f,100.0f);// 选择模型观察矩阵gl.glMatrixMode(GL10.GL_MODELVIEW);// 重置模型观察矩阵gl.glLoadIdentity();}// Call back to draw the current frame.@Overridepublic void onDrawFrame(GL10 gl){// 清除屏幕和深度缓存gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);// 启动和关闭光源    if (lightingEnabled) {       gl.glEnable(GL10.GL_LIGHTING);    } else {         gl.glDisable(GL10.GL_LIGHTING);    }// -------- 渲染彩色立方体 ------// 重置当前的模型观察矩阵gl.glLoadIdentity();// 纵深向里移动zgl.glTranslatef(0.0f, 0.0f, z);// 绕X轴旋转立方体 gl.glRotatef(angleX, 1.0f, 0.0f, 0.0f);// 绕Y轴旋转立方体gl.glRotatef(angleY, 0.0f, 1.0f, 0.0f);// 画立方体cube.draw(gl, currentTextureFilter);// 每次刷新之后更新旋转角度angleX += speedX;angleY += speedY;}}


MyGLSurfaceView.java

package wintop.gllesson07;import android.opengl.GLSurfaceView;import android.content.Context;import android.view.KeyEvent;import android.view.MotionEvent;// 为了捕获用户输入事件,我们需要从GLSurfaceView导出一个子类来重载事件处理// 方法(比如重载onKeyUp(), onTouchEvent())public class MyGLSurfaceView extends GLSurfaceView {MyGLRenderer renderer;    // 定制的 GL 渲染器//  用于触屏事件private final float TOUCH_SCALE_FACTOR = 180.0f / 320.0f;private float previousX;private float previousY;// 构造函数 - 创建和设置渲染器public MyGLSurfaceView(Context context) {super(context);renderer = new MyGLRenderer(context);this.setRenderer(renderer);// 设置焦点,否则键盘/按钮可能无响应this.requestFocus();this.setFocusableInTouchMode(true);}// 处理键盘事件@Overridepublic boolean onKeyUp(int keyCode, KeyEvent evt) {    switch(keyCode) {         case KeyEvent.KEYCODE_DPAD_LEFT:   // 减少Y向旋转速度            renderer.speedY -= 0.1f;            break;         case KeyEvent.KEYCODE_DPAD_RIGHT:  // 增加Y向旋转速度            renderer.speedY += 0.1f;            break;         case KeyEvent.KEYCODE_DPAD_UP:     // 减少X向旋转速度            renderer.speedX -= 0.1f;            break;         case KeyEvent.KEYCODE_DPAD_DOWN:   // 增加X向旋转速度             renderer.speedX += 0.1f;            break;         case KeyEvent.KEYCODE_A:           // 缩小 (减少 z)            renderer.z -= 0.2f;            break;         case KeyEvent.KEYCODE_Z:           // 放大 (增加 z)            renderer.z += 0.2f;            break;         case KeyEvent.KEYCODE_DPAD_CENTER:  // 选择纹理滤波方式             renderer.currentTextureFilter = (renderer.currentTextureFilter + 1) % 3;             break;         case KeyEvent.KEYCODE_L:  // 光源开关              renderer.lightingEnabled = !renderer.lightingEnabled;             break;         default:         return super.onKeyUp(keyCode, evt);      }      return true;  // 事件处理后返回   }// 处理触屏事件@Overridepublic boolean onTouchEvent(final MotionEvent evt) {float currentX = evt.getX();    float currentY = evt.getY();    float deltaX, deltaY;    switch (evt.getAction()) {       case MotionEvent.ACTION_MOVE:          // 根据物体的移动调整合理的旋转角度          deltaX = currentX - previousX;          deltaY = currentY - previousY;          renderer.angleX += deltaY * TOUCH_SCALE_FACTOR;          renderer.angleY += deltaX * TOUCH_SCALE_FACTOR;    }    // Save current x, y    previousX = currentX;    previousY = currentY;    return true;  // 事件处理后返回}}


TextureCube.java

package wintop.gllesson07;import java.io.IOException;import java.io.InputStream;import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.nio.FloatBuffer;import javax.microedition.khronos.opengles.GL10;import javax.microedition.khronos.opengles.GL11;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.opengl.GLUtils;// 生成一个带纹理的立方体// 这里指定义一个面的顶点,立方体的其他面通过平移和旋转这个面来渲染public class TextureCube {private FloatBuffer vertexBuffer;// 顶点数组缓冲区private FloatBuffer texBuffer;// 纹理坐标数据缓冲区private float[] vertices = { // 定义一个面的顶点坐标-1.0f, -1.0f, 0.0f,  // 0. 左-底-前1.0f, -1.0f, 0.0f,   // 1. 右-底-前-1.0f,  1.0f, 0.0f,  // 2. 左-顶-前1.0f,  1.0f, 0.0f    // 3. 右-顶-前};float[] texCoords = { // 定义上面的面的纹理坐标        0.0f, 1.0f,  // A. 左-下        1.0f, 1.0f,  // B. 右-下         0.0f, 0.0f,  // C. 左-上         1.0f, 0.0f   // D. 右-上    };int[] textureIDs = new int[3]; // 纹理-ID数组// 构造函数,设置缓冲区public TextureCube(){// 设置顶点数组,顶点数据为浮点数据类型。一个浮点类型的数据长度为四个字节    ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);    vbb.order(ByteOrder.nativeOrder()); // 使用原生字节顺序    vertexBuffer = vbb.asFloatBuffer(); // 将字节类型缓冲区转换成浮点类型    vertexBuffer.put(vertices);         // 将数据复制进缓冲区    vertexBuffer.position(0);           // 定位到初始位置     // 设置纹理坐标数组缓冲区,起数据类型为浮点数据    ByteBuffer tbb = ByteBuffer.allocateDirect(texCoords.length * 4);    tbb.order(ByteOrder.nativeOrder());    texBuffer = tbb.asFloatBuffer();    texBuffer.put(texCoords);    texBuffer.position(0);}// 绘图public void draw(GL10 gl, int textureFilter){// 选择滤波方式  gl.glFrontFace(GL10.GL_CCW);    // 正前面为逆时针方向    gl.glEnable(GL10.GL_CULL_FACE); // 使能剔除面    gl.glCullFace(GL10.GL_BACK);    // 剔除背面(不显示)        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, texBuffer); // 定义纹理坐标数组缓冲区        // 通过纹理坐标ID选择纹理的滤波方式    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[textureFilter]);        // 前    gl.glPushMatrix();    gl.glTranslatef(0.0f, 0.0f, 1.0f);    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);    gl.glPopMatrix();        // 左    gl.glPushMatrix();    gl.glRotatef(270.0f, 0.0f, 1.0f, 0.0f);    gl.glTranslatef(0.0f, 0.0f, 1.0f);    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);    gl.glPopMatrix();        // 后    gl.glPushMatrix();    gl.glRotatef(180.0f, 0.0f, 1.0f, 0.0f);    gl.glTranslatef(0.0f, 0.0f, 1.0f);    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);    gl.glPopMatrix();    // 右    gl.glPushMatrix();    gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);    gl.glTranslatef(0.0f, 0.0f, 1.0f);    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);    gl.glPopMatrix();    // 顶    gl.glPushMatrix();    gl.glRotatef(270.0f, 1.0f, 0.0f, 0.0f);    gl.glTranslatef(0.0f, 0.0f, 1.0f);    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);    gl.glPopMatrix();    // 底    gl.glPushMatrix();    gl.glRotatef(90.0f, 1.0f, 0.0f, 0.0f);    gl.glTranslatef(0.0f, 0.0f, 1.0f);    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);    gl.glPopMatrix();        // 恢复原来的状态    gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);     gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);    gl.glDisable(GL10.GL_CULL_FACE);}// 加载一个图像到GL纹理public void loadTexture(GL10 gl, Context context) {gl.glGenTextures(1, textureIDs, 0);// 生成纹理ID数组gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[0]);// 绑定到纹理ID// 设置纹理过滤方式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);// 构造一个输入流来加载纹理文件"res/drawable/crate.bmp"InputStream ins = context.getResources().openRawResource(R.drawable.crate);Bitmap bmp;try {// 读取并将输入流解码成位图bmp = BitmapFactory.decodeStream(ins);} finally {try {ins.close();}catch(IOException e) {}}gl.glGenTextures(3, textureIDs, 0); // 为3个纹理生成纹理ID数组// 生成 Nearest方式滤波的纹理并绑定到纹理0    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[0]);    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);    // 生成Linear方式滤波的纹理并绑定到纹理1    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[1]);    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);          // 生成 mipmapped方式滤波的纹理并绑定到纹理2    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[2]);    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR_MIPMAP_NEAREST);    if(gl instanceof GL11) {       gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_GENERATE_MIPMAP, GL11.GL_TRUE);    }GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);bmp.recycle();}}


程序的运行结果如下图所示:

 

 代码下载地址:http://download.csdn.net/detail/seniorwizard/4470542

原创粉丝点击