美文网首页
openGL ES2.0 如何绘制一个三角形

openGL ES2.0 如何绘制一个三角形

作者: 笑而不语ii | 来源:发表于2019-05-05 17:24 被阅读0次

    一、Android OpenGL ES2.0简介

    1、什么是OpenGL

    OpenGL(英语:Open Graphics Library,译名:开放图形库或者“开放式图形库”)是用于渲染2D3D矢量图形的跨语言跨平台应用程序编程接口(API)。这个接口由近350个不同的函数调用组成,用来从简单的图形比特绘制复杂的三维景象。而另一种程序接口系统是仅用于Microsoft Windows上的Direct3D。OpenGL常用于CAD虚拟实境、科学可视化程序和电子游戏开发。 ————百度百科

    说简单就是:

    OpenGL是指定义了一个跨编程语言、跨平台的编程接口规格的专业的图形程序接口。它用于三维图像(二维的亦可),是一个功能强大,调用方便的底层图形库。

    OpenGL在不同的平台上有不同的实现,但是它定义好了专业的程序接口,不同的平台都是遵照该接口来进行实现的,思想完全相同,方法名也是一致的,所以使用时也基本一致,只需要根据不同的语言环境稍有不同而已。

    2、什么是OpenGL ES

    OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。

    OpenGL ES相对于OpenGL来说,减少了许多不是必须的方法和数据类型,去掉了不必须的功能,对代价大的功能做了限制,比OpenGL更为轻量。在OpenGL ES的世界里,没有四边形、多边形,无论多复杂的图形都是由点、线和三角形组成的,也去除了glBegin/glEnd等方法

    3、OpenGL ES可以做什么

    OpenGL ES是手机、PDA和游戏主机等嵌入式设备三维(二维也包括)图形处理的API,当然是用来在嵌入式设备上的图形处理了,OpenGL ES 强大的渲染能力使其成为我们在嵌入式设备上进行图形处理的优良选择。我们经常使用的场景有:
    1.图片处理。比如图片色调转换、美颜等。
    2.摄像头预览效果处理。比如美颜相机、恶搞相机等。
    3.视频处理。摄像头预览效果处理可以,这个自然也不在话下了。
    4.3D游戏。比如神庙逃亡、都市赛车等。

    4.OpenGL ES 2.0中基本概念
    4.1着色器

    OpenGL ES 2.0中最重要的一个概念就是关于着色器:顶点着色器和片元着色器。

    着色器(Shader)是在GPU上运行的小程序。从名称可以看出,可通过处理它们来处理顶点。此程序使用OpenGL ES SL语言来编写。它是一个描述顶点或像素特性的简单程序。

    .顶点着色器

    其功能是把每个顶点在虚拟空间中的三维坐标变换为可以在屏幕上显示的二维坐标,并带有用于z-buffer的深度信息。顶点着色器可以操作的属性有:位置、颜色、纹理坐标,但是不能创建新的顶点。

    .片元着色器

    片元着色器计算每个像素的颜色和其它属性。它通过应用光照值、凹凸贴图,阴影,镜面高光,半透明等处理来计算像素的颜色并输出。它也可改变像素的深度(z-buffering)或在多个渲染目标被激活的状态下输出多种颜色。

    着色器语言(Shading Language)是一种高级的图形编程语言,仅适合于GPU编程,其源自应用广泛的C语言。对于顶点着色器和片元着色器的开发都需要用到着色器语言进行开发。它是面向过程的而非面向对象。具体可参照相关资料。因此,对于绘制不同的图形时,所编写的着色器语言不同,这个需要大家参照相关例子学习。

    4.2坐标

    OpenGL ES 采用的是右手坐标,即向右为X正轴方向,向左为X负轴方向,向上为Y轴正轴方向,向下为Y轴负轴方向,屏幕面垂直向上为Z轴正轴方向,垂直向下为Z轴负轴方向。下图是对比右手和左手坐标系。



    由于实际的绘制是3D图形,投影到屏幕中显示的是2D效果,因此这里需要转换就是3D到2D图形的转换。如下图所示,实际运行是采用选取屏幕中心为原点,原点到屏幕边缘的长度为单位1,由于屏幕中心点到边缘的长度不同(屏幕一般都是长方形,而不是正方形),即:屏幕到宽的距离相对较小,因此从原点到(1,0,0)的距离和到(0,1,0)的距离在屏幕上展示的并不相同。



    在实际运行设置坐标时,就会出现一定的图形变换,例如绘制一个等边三角形,在实际投射到屏幕中时,显示的却是等边三角形,这个就是上面提到的屏幕中心点到屏幕边缘的长度是单位长度,在实际投射时,屏幕到宽和高的比例不同,出现如下图所示的展示效果。
    4.3 其他

    除了上面提到的着色器和坐标外,还包括投影、光照和纹理映射等。

    1.投影:OpenGL ES中有两种投影方式:正交投影和透视投影。正交投影,物体不会随距离观测点的位置而大小发生变化。而透视投影,距离观测点越远,物体越小,距离观测点越近,物体越大。
    2.光照:在屏幕中很难直接的显示3D场景的效果,因为曲面相对平面更具有光照效果,因此在实际的绘制中,需要加入光照元素:环境光、镜面光和散射光。
    3.纹理映射:现实世界中的物体往往是绚丽多彩的,要模拟现实世界的绚丽多彩,绘制出更加真实、酷炫的3D物体,就需要用到纹理映射了。纹理映射是将2D的纹理映射到3D场景中的立体物体上。

    4.4 OpenGL ES 2.0运行过程

    读取顶点数据——执行顶点着色器——组装图元——光栅化图元——执行片元着色器——写入帧缓冲区——显示到屏幕上。

    1.OpenGL作为本地库直接运行在硬件上,没有虚拟机,也没有垃圾回收或者内存压缩。在Java层定义图像的数据需要能被OpenGL存取,因此,需要把内存从Java堆复制到本地堆。

    2.顶点着色器是针对每个顶点都会执行的程序,是确定每个顶点的位置。同理,片元着色器是针对每个片元都会执行的程序,确定每个片元的颜色。
    3.着色器需要进行编译,然后链接到OpenGL程序中。一个OpenGL的程序就是把一个顶点着色器和一个片段着色器链接在一起变成单个对象。

    二、绘制一个简单三角形的步骤

    1.manifest声明OpenGL ES2.0
    <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
    <!--0x00020000表示版本号-->
    
    2.构造GLSufaceView.Render对象

    Renderer类提供三个回调方法供Android系统调用,用来计算在GLSurfaceView中绘制什么以及如何绘制。
    onSurfaceCreated():仅调用一次,用于设置view的OpenGL ES环境(初始化)
    onDrawFrame():每次重绘view时调用,我们自定义的绘制主要是在该方法中实现
    onSurfaceChanged():当view的几何形状发生变化时调用,比如设备屏幕方向改变时

    public class TriggerRender implements GLSurfaceView.Renderer {
        float triangleCoords[] = {
                0.5f, 0.5f, 0.0f, // top
                -0.5f, -0.5f, 0.0f, // bottom left
                0.5f, -0.5f, 0.0f  // bottom right
        };
        float color[] = {1.0f, 0f, 0f, 1.0f}; //red
        private FloatBuffer vertexBuffer;
        private final String vertexShaderCode =
                "attribute vec4 vPosition;" +
                        "void main() {" +
                        "  gl_Position = vPosition;" +
                        "}";
    
        private final String fragmentShaderCode =
                "precision mediump float;" +
                        "uniform vec4 vColor;" +
                        "void main() {" +
                        "  gl_FragColor = vColor;" +
                        "}";
        private int mProgram;
        private int mPositionHandle;
        private int mColorHandle;
        static final int COORDS_PER_VERTEX = 3;
        //顶点个数
        private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
        //顶点之间的偏移量
        private final int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节
    
        @Override
        public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
            //将背景设置为灰色
            GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
            //将坐标数据转换为FloatBuffer,用以传入给OpenGL ES程序
            vertexBuffer = BufferUtil.fBuffer(triangleCoords);
    
            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
                    vertexShaderCode);
            int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
                    fragmentShaderCode);
    
            //创建一个空的OpenGLES程序
            mProgram = GLES20.glCreateProgram();
            //将顶点着色器加入到程序
            GLES20.glAttachShader(mProgram, vertexShader);
            //将片元着色器加入到程序中
            GLES20.glAttachShader(mProgram, fragmentShader);
            //连接到着色器程序
            GLES20.glLinkProgram(mProgram);
        }
    
        @Override
        public void onSurfaceChanged(GL10 gl10, int width, int height) {
            GLES20.glViewport(0, 0, width, height);
        }
    
        @Override
        public void onDrawFrame(GL10 gl10) {
            //将程序加入到OpenGLES2.0环境(加载
            // )
            GLES20.glUseProgram(mProgram);
    
            //获取顶点着色器的vPosition成员句柄
            mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
            //启用三角形顶点的句柄
            GLES20.glEnableVertexAttribArray(mPositionHandle);
            //准备三角形的坐标数据
            GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                    GLES20.GL_FLOAT, false,
                    vertexStride, vertexBuffer);
            //获取片元着色器的vColor成员的句柄
            mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
            //设置绘制三角形的颜色
            GLES20.glUniform4fv(mColorHandle, 1, color, 0);
            //绘制三角形
            GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
            //禁止顶点数组的句柄
            GLES20.glDisableVertexAttribArray(mPositionHandle);
        }
        public int loadShader(int type, String shaderCode) {
            //根据type创建顶点着色器或者片元着色器
            int shader = GLES20.glCreateShader(type);
            //将资源加入到着色器中,并编译
            GLES20.glShaderSource(shader, shaderCode);
            GLES20.glCompileShader(shader);
            return shader;
        }
    }
    
    3.构造GLSurfaceView对象
    public class MyGLSurfaceView extends GLSurfaceView {
        private final  TriggerRender mRender;
        public MyGLSurfaceView(Context context) {
            super(context);
            setEGLContextClientVersion(2);
            mRender = new TriggerRender();
            setRenderer(mRender);
            setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
        }
    }
    
    4、将Activity的ContentView设为GLSurfaceView
    public class MainActivity extends AppCompatActivity {
        private MyGLSurfaceView mGLSurfaceView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mGLSurfaceView = new MyGLSurfaceView(this);
            setContentView(mGLSurfaceView);
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            mGLSurfaceView.onResume();
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            mGLSurfaceView.onPause();
        }
    }
    
    5.运行结果
    结果
    6.总结

    1.看上图就知道了,本来是画等边直角三角形,结果两直角边一场一短;
    2.在使用 GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f)设置背景色的时候没成功;


    本文内容转自 原文,感谢大佬的轮子!
    下一篇文章说下怎么画等边多边形,圆,着色等……

    相关文章

      网友评论

          本文标题:openGL ES2.0 如何绘制一个三角形

          本文链接:https://www.haomeiwen.com/subject/dnuhoqtx.html