学习OpenGL ES for Android(二)

本章将学习OpenGL ES中点和线的绘制,在绘制之前需要先了解这些:坐标系统;着色器;GLSL(OpenGL ES Shading Language),OpenGL ES着色语言;

坐标系统

和android布局的坐标不同,OpenGL是一个右手坐标系。简单来说,就是正x轴在你的右手边,正y轴朝上,而正z轴是朝向后方的。想象你的屏幕处于三个轴的中心,则正z轴穿过你的屏幕朝向你。坐标系画起来如下:

如果你想要绘制一个在屏幕中心的点时,那么这个点的坐标就是:0,0,0,当你绘制的是不是三维图像时可以无视z轴(你设置0或者1都不会影响显示效果)。坐标的最大值是1,如果你的坐标值大于1的话,这个坐标是无法显示的。

可以参考https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/来学习坐标系统,当然还有一些概念,例如正射投影,透视投影,我们暂时用不到用到的时候我们再来学习。

着色器

从我们输入数据到显示图像,这一流程可以查看下图,

其中需要我们来写是顶点着色器和片段着色器的代码。

  • 顶点着色程序 - 用于渲染形状的顶点的 OpenGL ES 图形代码。
  • 片段着色程序 - 用于使用颜色或纹理渲染形状面的 OpenGL ES 代码。

GLSL

GLSL是一种类C语言,用来编写顶点着色程序和片段着色程序。GLSL语言还是比较复杂的,OpenGL ES2.0的GLSL官方文档地址是https://www.khronos.org/registry/OpenGL/specs/es/2.0/GLSL_ES_Specification_1.00.pdf,如果英文水平比较高可阅读参考,否则可用参考http://colin1994.github.io/2017/11/11/OpenGLES-Lesson04/http://colin1994.github.io/2017/11/12/OpenGLES-Lesson05这两篇文章,写的还是不错的。

变量类型

变量类别变量类型描述
void用于无返回值的函数或空的参数列表
标量float, int, bool浮点型,整型,布尔型的标量数据类型
浮点型向量float, vec2, vec3, vec4包含1,2,3,4个元素的浮点型向量
整数型向量int, ivec2, ivec3, ivec4包含1,2,3,4个元素的整型向量
布尔型向量bool, bvec2, bvec3, bvec4包含1,2,3,4个元素的布尔型向量
矩阵mat2, mat3, mat4尺寸为2x2,3x3,4x4的浮点型矩阵
纹理句柄sampler2D, samplerCube表示2D,立方体纹理的句柄

限定符

限定符描述
< none: default >局部可读写变量,或者函数的参数
const编译时常量,或只读的函数参数
attribute由应用程序传输给顶点着色器的逐顶点的数据
uniform在图元处理过程中其值保持不变,由应用程序传输给着色器
varying由顶点着色器传输给片段着色器中的插值数据

先说几个会用到内置的变量,

gl_Position:只能用在顶点着色程序中,用来定义需要写入的顶点。
gl_PointSize:定义顶点的大小,单位是像素。设置的越大,点就越大,当然显示的点时正方形的。
gl_FragColor :用于片段着色程序中,用来设置图像的颜色。

下面先写顶点着色程序和片段着色程序,

    String vertexShaderCode =
            "attribute vec4 aPosition;" +
                    "void main() {" +
                    "  gl_Position = aPosition;" +
                    "  gl_PointSize = 19.0;" +
                    "}";
   String fragmentShaderCode =
                "precision mediump float;" +
                        "uniform vec4 vColor;" +
                        "void main() {" +
                        "  gl_FragColor = vColor;" +
                        "}";

然后我们定义一个顶点的数组,

    private final float[] triangleCoords = {
            -0.9f, 0.9f, 0.0f,
            -0.9f, 0.8f, 0.0f,

            -0.8f, -0.1f, 0.0f,
            -0.6f, -0.5f, 0.0f,
            -0.5f, -0.8f, 0.0f,

            -0.4f, 0.4f, 0.0f,
            -0.2f, 0.1f, 0.0f,
            -0.0f, 0.5f, 0.0f,
            0.1f, 0f, 0.0f,
            0.3f, -0.5f, 0.0f,

            0.4f, -0.2f, 0.0f,
            0.6f, -0.5f, 0.0f,
            0.9f, -0.6f, 0.0f,
            0.9f, -0.9f, 0.0f,
    };

定义一个颜色的数组,用来绘制颜色,四个数值分别表示rbga。 

 // 0 到1 代表 0- 256 如 181 用0.70703125f
private float[] color = {0.70703125f, 0.10546875f, 0.84375f, 1.0f};

在上篇文章中我们显示了一个窗口,现在我们绘制点和线,在onDrawFrame中进行绘制

首先我们加载着色器,加载着色器的代码如下

public static int loadShader(int shaderType, String source) {
        int shader = GLES20.glCreateShader(shaderType);
        checkGlError("glCreateShader type=" + shaderType);
        GLES20.glShaderSource(shader, source);
        GLES20.glCompileShader(shader);
        int[] compiled = new int[1];
        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == 0) {
            LogUtil.e("Could not compile shader " + shaderType + ":" + GLES20.glGetShaderInfoLog(shader));
            GLES20.glDeleteShader(shader);
            shader = 0;
        }
        return shader;
    }

主要的几个方法
glCreateShader:创建shader,参数值:GLES20.GL_VERTEX_SHADER(顶点着色器)或GLES20.GL_FRAGMENT_SHADER(片段着色器),返回创建的shader值。
glShaderSource:加载着色器代码,参数为create的shader值和GLSL代码。
glCompileShader:加载着色器完成。
glGetShaderiv:获取加载完成的状态,主要用来检测是否加载完成,如果加载失败了则使用glDeleteShader来删除错误的shader。

checkGlError是封装好的检测是否有错误的方法,内容如下

    public static void checkGlError(String op) {
        int error = GLES20.glGetError();
        if (error != GLES20.GL_NO_ERROR) {
            String msg = op + ": glError 0x" + Integer.toHexString(error);
            LogUtil.e(msg);
            throw new RuntimeException(msg);
        }
    }

使用glCreateProgram创建一个空的程序,返回一个非0值。使用glAttachShader来连接当前的程序和创建好的shader值,最后使用glLinkProgram来完成链接program,如果不需要Program后可以使用glDeleteProgram删除program。封装好之后的代码如下

public static int createProgram(String vertexSource, String fragmentSource) {
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
        if (vertexShader == 0) {
            return 0;
        }
        int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
        if (pixelShader == 0) {
            return 0;
        }

        int program = GLES20.glCreateProgram();
        checkGlError("glCreateProgram");
        if (program == 0) {
            LogUtil.e("Could not create program");
        }
        GLES20.glAttachShader(program, vertexShader);
        checkGlError("glAttachShader");
        GLES20.glAttachShader(program, pixelShader);
        checkGlError("glAttachShader");
        GLES20.glLinkProgram(program);
        int[] linkStatus = new int[1];
        GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
        if (linkStatus[0] != GLES20.GL_TRUE) {
            LogUtil.e("Could not link program: ");
            LogUtil.e(GLES20.glGetProgramInfoLog(program));
            GLES20.glDeleteProgram(program);
            program = 0;
        }
        return program;
    }

封装了一个OpenGLUtil工具类来进行shader的加载,便于使用。

加载完成后进行顶点的绘制,完整的onDrawFrame代码如下

    @Override
    public void onDrawFrame(GL10 gl) {
        super.onDrawFrame(gl);
        int shaderProgram = OpenGLUtil.createProgram(vertexShaderCode, fragmentShaderCode);
        GLES20.glUseProgram(shaderProgram);
        int positionHandle = GLES20.glGetAttribLocation(shaderProgram, "aPosition");
        GLES20.glEnableVertexAttribArray(positionHandle);
        FloatBuffer vertexBuffer = OpenGLUtil.createFloatBuffer(triangleCoords);
        GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT,
                false, 3 * 4, vertexBuffer);
        int colorHandle = GLES20.glGetUniformLocation(shaderProgram, "vColor");
        GLES20.glUniform4fv(colorHandle, 1, color, 0); // 设置颜色
        // 画点
        GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 13); // 画点
        // 设置线宽
        GLES20.glLineWidth(18);
        // 画线,不连续的线,例如:有1,2,3,4四个点,1和2是一条线,3,4是一条线
        GLES20.glDrawArrays(GLES20.GL_LINES, 2, 4);
        // 画线,封闭的线,例如:有1,2,3,4四个点,1,2,3,4,1会连接2,2连接3,3连接4,4连接1
        GLES20.glDrawArrays(GLES20.GL_LINE_LOOP, 6, 4);
        // 画线,不封闭的线
        GLES20.glDrawArrays(GLES20.GL_LINE_STRIP, 10, 4);
        GLES20.glDisableVertexAttribArray(positionHandle);
    }

一些主要方法
glUseProgram:使用创建好的program
glGetAttribLocation:查询由程序指定的先前链接的程序对象,返回aPosition变量顶点属性的索引
glEnableVertexAttribArray:启用由索引指定的顶点属性数组。 
createFloatBuffer:封装的生成FloatBuffer代码。
glVertexAttribPointer:指定顶点数组的位置和数据格式,几个参数:index,索引;size,每个顶点的数,当前是3;type,类型,float,short等等,当前是float;normalized,设置为false;stride,顶点间的偏移,每个顶点3个float值,每个float是4个字节;pointer,顶点的集合。
glGetUniformLocation:获取片段着色器中属性的索引。
glUniform4fv:修改统一变量或统一变量数组的值。4fv表示 vec4,同样的方法有很多:glUniform3fv,glUniform3iv等等,可以参考官网的glUniform文档。
glDrawArrays:开始绘制。参数mode类型:有GL_POINTS(点),GL_LINES(线),GL_LINE_STRIP,GL_LINE_LOOP,GL_TRIANGLE_STRIP,GL_TRIANGLE_FAN和GL_TRIANGLE;参数first:开始位置;count:总数。
glDisableVertexAttribArray:禁用顶点属性数组,当绘制完成后可以调用。

显示效果如下

源码地址https://github.com/jklwan/OpenGLProject/blob/master/sample/src/main/java/com/chends/opengl/view/window/PointLineView.java

这一章了解了基本的OpenGL ES图像的绘制方式,下一章会来学习面的显示。

In Pro OpenGL ES for Android, you'll find out how to harness the full power of OpenGL ES, and design your own 3D applications by building a fully-functional 3D solar system model using Open GL ES! OpenGL has set the standard for 3D computer graphics, and is an essential aspect of Android development. This book offers everything you need to know, from basic mathematical concepts to advanced coding techniques. You'll learn by building a fascinating 3D solar system simulator! After introducing Open GL ES, Pro OpenGL ES for Android explains the basics of 3D math and then orients you to the native Android 3D libraries you'll be using in your own 3D games and the solar system project you'll build using this book. Through the solar system example project, you'll learn how to incorporate a variety of graphic and animation techniques into your applications. You will also discover how the full spectrum of 3D development that awaits, with topics such as lighting, texture-mapping, modeling, shaders, blending modes, and several more advanced concepts. By the time you finish Pro OpenGL ES for Android, you'll have learned all the skills you'll need to build your own incredible 3D applications, based on one of the most powerful 3D libraries available. What you'll learn * The basics of 3D mathematics, and how they are applied in the OpenGL library * How to design and build your 3D worlds * To create 2D interfaces within the 3D world * To develop animation and 3D movement * How to implement 3D shading, coloring, and texturing * The differences between OpenGL and other 3D toolkits * To build a fully-functional 3D solar system simulator using OpenGL ES Who this book is for Experienced Android programmers who want to enter the 3D world of OpenGL ES programming. Table of Contents * Introduction to OpenGL ES and Our 3D Solar System Project * Generating a Basic OpenGL Program * Getting Past the 3D Math * Shading, Lighting and Colors * Materials and Textures * Animation * Creating a User Interface * Blending Modes, Buffer Objects, and Other Cool Stuff * Latest Features of OpenGL ES * Ray Tracing, Hidden Surfaces, and Other Advanced Topics Appendix A: APIs
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值