• Android OpenGL教程-第一课【转】


    第一课 快速的开始一个Android OpenGL项目

        首先,读懂我们的教程,需要有android的初步基础,我们这里只是通过android提供的SDK,来进行OpenGL的学习,所以你必须先学习如何建立一个android的项目,同时了解activity的生命周期和android下的屏幕或键盘响应机制。

    好的,开始建立一个android的项目Lesson1,Activity的名字的名字我们叫Lesson。

    Lesson类:

    import android.app.Activity;
    import android.os.Bundle;
    import android.view.Window;
    import android.view.WindowManager;
    
    public class Lesson extends Activity {
        private OpenGLView mOpenGLView;
    
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // 去标题栏
            requestWindowFeature(Window.FEATURE_NO_TITLE);
    
            // 设置全屏
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);
    
            mOpenGLView = new OpenGLView(this);
    
            setContentView(mOpenGLView);
        }
    }

    我们在这里用了一个OpenGLView类,把这个类直接设为contentView。

    OpenGLView类:

    import android.content.Context;
    import android.opengl.GLSurfaceView;
    
    /**
     * OpenGLView类继承自GLSurfaceView,这个类是android提供的用opengl画图的类。
     */
    public class OpenGLView extends GLSurfaceView {
        private OpenGLRenderer mRenderer;
    
        public OpenGLView(Context context) {
    
            super(context);
    
            mRenderer = new OpenGLRenderer();
    
            setRenderer(mRenderer);
    
            // TODO Auto-generated constructor stub
    
        }
    }

    OpenGLView类继承自GLSurfaceView,这个类是android提供的用opengl画图的类。这里用了OpenGLRenderer类,Render是渲染的意思,真正画图的操作在这个类里面。

     

    OpenGLRenderer类:

    import javax.microedition.khronos.egl.EGLConfig;
    import javax.microedition.khronos.opengles.GL10;
    
    import android.opengl.GLSurfaceView.Renderer;
    
    /**
     * 渲染器类,实现了GLSurfaceView.Renderer接口,实现这个接口,需要实现3个方法:OnSurfaceCreated(),
     * OnSurfaceChanged(),OnDrawFrame()。
     */
    public class OpenGLRenderer implements Renderer {
    
        /**
         * 该方法是画图方法,类似于View类的OnDraw(),一般所有的画图操作都在这里实现。
         */
        @Override
        public void onDrawFrame(GL10 gl) {
            // 清除屏幕和深度缓存。
            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    
            // 重置当前的模型观察矩阵。
            gl.glLoadIdentity();
        }
    
        /**
         * 在Surface发生改变的时候调用,例如从竖屏切换到横屏的时候
         */
        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            // 设置输出屏幕大小
            gl.glViewport(0, 0, width, height);
        }
    
        /**
         * 在Surface创建的时候调用,一般在这里做一个初始化openggl的操作
         */
        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            // 启用smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。
            gl.glShadeModel(GL10.GL_SMOOTH);
    
            // 设置清除屏幕时所用的颜色,参数对应(红,绿,蓝,Alpha值)。色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况。
            gl.glClearColor(0f, 0f, 0f, 0f);
    
            // 下面三行是关于depth buffer(深度缓存)的。将深度缓存设想为屏幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。
            gl.glClearDepthf(1.0f);
            gl.glEnable(GL10.GL_DEPTH_TEST);
            gl.glDepthFunc(GL10.GL_LEQUAL);
    
            // 这里告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。
            gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
        }
    
    }

    这个类实现了GLSurfaceView.Renderer接口,实现这个接口,需要实现3个方法:OnSurfaceCreated(),OnSurfaceChanged(),OnDrawFrame()。第一个方法在Surface创建的时候调用,我们一般在这里做一个初始化openggl的操作,第二个方法在Surface发生改变的时候调用,例如从竖屏切换到横屏的时候;第三个方法是画图方法,类似于View类的OnDraw(),一般所有的画图操作都在这里实现。

    注:现在我们大多是在JAVA层操作,后来学习深入后,这里画图的一些操作大多需要很多的计算,我们可以用NDK在C++层进行。

    我们看OnSurfaceCreate里面的代码,这里都是在做对OpengGL的初始化。

     

    gl.glShadeModel(GL10.GL_SMOOTH);

    启用smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑,在以后的课程中会看到他的效果。

     

    gl.glClearColor(0f, 0f, 0f, 0f);

    设置清除屏幕时所用的颜色。如果您对色彩的工作原理不清楚的话,我快速解释一下。色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况。glClearColor 后的第一个参数是Red Intensity(红色分量),第二个是绿色,第三个是蓝色。最大值也是1.0f,代表特定颜色分量的最亮情况。最后一个参数是Alpha值。当它用来清除屏幕的时候,我们不用关心第四个数字。现在让它为0.0f。我会用另一个教程来解释这个参数。

    通过混合三种原色(红、绿、蓝),您可以得到不同的色彩。希望您在学校里学过这些。因此,当您使用glClearColor(0.0f,0.0f,1.0f,0.0f),您将用亮蓝色来清除屏幕。如果您用 glClearColor(0.5f,0.0f,0.0f,0.0f)的话,您将使用中红色来清除屏幕。不是最亮(1.0f),也不是最暗 (0.0f)。要得到白色背景,您应该将所有的颜色设成最亮(1.0f)。要黑色背景的话,您该将所有的颜色设为最暗(0.0f)。我们在这里设置屏幕为黑色。

     

    gl.glClearDepthf(1.0f);

    gl.glEnable(GL10.GL_DEPTH_TEST);

    gl.glDepthFunc(GL10.GL_LEQUAL);

    这三行是关于depth buffer(深度缓存)的。将深度缓存设想为屏幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。我们本节的程序其实没有真正使用深度缓存,但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。这样您就不会将一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分。

    gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);

    这里告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。

     

    OnDrawFrame方法中

    gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);

    清除屏幕和深度缓存。

    gl.glLoadIdentity();

    重置当前的模型观察矩阵。

     

           本课的效果只是生成了一个黑色的屏幕,很多代码并没有发生应有的效果,后面的课程中,我们会使用到这些代码,到时候大家就能看到这些代码所产生的效果。

     

     

    附:

    1.   我们来改变背景的颜色

    gl.glClearColor(1.0f, 0.0f, 0.0f, 0.0f);从SurfaceCreate放到OnDrawFrame里面,这里设置为红色了,运行下看看,是不是背景成红色了。

    2. 加上点击屏幕来改变背景的颜色

    第一步,在render里面加3个float(成员变量)

    private float cr,cg,cb;

    第二步,加个设置r,g,b的函数

    // (这是附加的)设置RGB颜色的方法
    public void setColor(float r, float g, float b) {
        cr = r;
        cg = g;
        cb = b;
    }

    第三步,在glclearColor里面用cr,cg,cb来设置颜色。

    gl.glClearColor(cr, cg, cb, 0.0f);

    第四步,在view (OpenGLView类)里面override onTouchEvent函数,在这里响应点击屏幕的事件。

    @Override
    public boolean onTouchEvent(final MotionEvent event) {
        /*
         * 由于渲染对象是运行在一个独立的渲染线程中,所以需要采用跨线程的机制来进行事件的处理。
         * 但是Android提供了一个简便的方法:
         * 我们只需要在事件处理中使用queueEvent(Runnable)就可以了.
         */
        queueEvent(new Runnable() {
            @Override
            public void run() {
                mRenderer.setColor(event.getX() / getWidth(), event.getY()
                        / getHeight(), 1.0f);
            }
        });
    
        return super.onTouchEvent(event);
    }

    至于这里为什么用final,大家去网上查查,刚好也复习一下内部类调用参数,需要final这个知识点。

     

    运行效果:

    个人资料:http://blog.csdn.net/xiajun07061225/article/details/7455283

    (注:本教程转自其他网页,并加以修改,查看原网页请点击这里

  • 相关阅读:
    Datawhale编程实践(LeetCode 腾讯精选练习50)Task11
    Datawhale编程实践(LeetCode 腾讯精选练习50)Task10
    Datawhale编程实践(LeetCode 腾讯精选练习50)Task9
    Datawhale编程实践(LeetCode 腾讯精选练习50)Task8
    Datawhale编程实践(LeetCode 腾讯精选练习50)Task7
    Java多线程之三volatile与等待通知机制示例
    Java多线程之一
    [WC2021] 括号路径
    [CF1375H] Set Merging
    [CF1342E] Placing Rooks
  • 原文地址:https://www.cnblogs.com/wuqianling/p/6596014.html
Copyright © 2020-2023  润新知