Android OpenGl Es learning (four): add color

Overview

This is a new series. Learning OpengGl Es is actually the study notes of the "OpenGl Es Application Development Practice Guide Android Volume". If you are interested, you can directly read this book. Of course, this will record your own understanding. The following are only notes. In case you forget later

Later, the first nine chapters of the book will be analyzed and recorded in turn

Android OpenGl Es learning (1): Create an OpenGl es program

Android OpenGl Es learning (two): define vertices and shaders

Android OpenGl Es learning (3): Compile shaders

Android OpenGl Es learning (four): add color

Android OpenGl Es learning (5): adjust the aspect ratio

Android OpenGl Es learning (6): enter the three-dimensional

Android OpenGl Es learning (7): use texture

Android OpenGl Es learning (eight): build simple objects

Android OpenGl Es learning (9): add touch feedback

The final goal is to realize a simple game of hockey, like this

Add color

In the real world, objects have various colors that change color. I can also add colors to our rectangles in the code.

Smooth color finding

In the previous article, we learned how to use uniform to draw objects with a single color, so how do we use many different colors and color finding to express a complex scene?

OpenGL has a method to mix different colors in the same triangle. If each vertex of a triangle has a different color, and these colors are mixed on the surface of these triangles, we end up with a smooth color-finding triangle

Smooth color finding is done between vertices

OpenGL gave us a way to smoothly blend the color of each vertex on the surface of a straight line and a triangle. Next, we need to make our rectangle appear bright in the middle and dark at the edges, so we need to know the center of the rectangle. Point, let us know where to start mixing colors, so we have to modify the structure of this rectangle to get the center point

Triangle fan

The vertex defined in the previous article

 / //逆时针绘制三角形
    float[] tableVertices = {
    
    
            //第一个三角
            -0.5f, -0.5f,
            0.5f, 0.5f,
            -0.5f, 0.5f,
            //第二个三角
            -0.5f,-0.5f,
            0.5f, -0.5f,
            0.5f, 0.5f,
            //线
            -0.5f, 0f,
            0.5f, 0f,
            //点
            0f, -0.25f,
            0f, 0.25f
    };

We need to change this vertex to the following form

 //逆时针绘制三角形
    float[] tableVertices = {
    
    
            //顶点
            0f, 0f,
            -0.5f, -0.5f,
            0.5f, -0.5f,
            0.5f, 0.5f,
            -0.5f, 0.5f,
            -0.5f, -0.5f,
            //线
            -0.5f, 0f,
            0.5f, 0f,
            //点
            0f, -0.25f,
            0f, 0.25f
    };

How to understand this?

With the addition of the center point (0, 0) of this rectangle, we finally see four triangles instead of two, as shown in the figure:

Each triangle needs 3 vertices, but sometimes the same vertex is used for multiple triangles. Let’s look at the picture above. The vertices on each side are used by two triangles, and the center vertex is used by 4 triangles. In order not to write these vertices repeatedly, we can define the vertices in the above way, and then tell opengl to reuse these vertices, and draw these vertices as triangle fans.

A triangle fan, starting from the middle vertex, using two adjacent vertices to create the first triangle, and then each vertex will create a triangle, and fan out around the starting center point. In order to close the fan, we Need to draw the second point repeatedly at the end

At the same time, we also need to update the opengl function to let him know that we are drawing a triangular fan

GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);
//更新为
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 6);

Then run the project, the same as the previous one, but the drawing method has changed

Add new color attributes

We have updated the structure of the rectangle above, and now we add color attributes to each vertex, we update the array as follows

  float[] tableVertices = {
    
    
            //顶点
            0f, 0f,
            //顶点颜色值
            1f, 1f, 1f,

            -0.5f, -0.5f,
            0.7f, 0.7f, 0.7f,

            0.5f, -0.5f,
            0.7f, 0.7f, 0.7f,

            0.5f, 0.5f,
            0.7f, 0.7f, 0.7f,

            -0.5f, 0.5f,
            0.7f, 0.7f, 0.7f,

            -0.5f, -0.5f,
            0.7f, 0.7f, 0.7f,

            //线
            -0.5f, 0f,
            1f, 0f, 0f,

            0.5f, 0f,
            1f, 0f, 0f,

            //点
            0f, -0.25f,
            1f, 0f, 0f,

            0f, 0.25f,
            0f, 0f, 1f
    };

We added three extra numbers for each vertex, these numbers represent red, green, and blue respectively, and together they form the color of the corresponding vertex

Add color attributes to the shader

Let's update the vertex shader firstvertex_shader.glsl

 attribute vec4 a_Position;
 attribute vec4 a_Color;
 varying vec4 v_Color;
 
  void main() {
      v_Color=a_Color;
      gl_Position =  a_Position;
      gl_PointSize=10.0;
   }

The addition of a property a_Color, and one varyingofv_Color

When opengl constructs a line and triangle, it will construct the corresponding graphics based on the vertices, and then decompose the image into fragments, and then each fragment will be executed once by the fragment shader

varyingIt is a special variable type. He mixes the values ​​given to him and sends these values ​​to the fragment color finder. If a vertex 0 is red and vertex 1 is green, it is then assigned to v_Color through a_Color to tell The mixed color of opengl close to vertex 0 becomes redder, and the color close to vertex 1 becomes greener

Update the snippet color finder belowfragment_shader1.glsl

 precision mediump float;
 varying vec4 v_Color;
 
   void main() {
        gl_FragColor = v_Color;
    }

We replace the original code with varyingvariables . If the vertex belongs to a straight line, the two vertex colors of the straight line will be used to calculate the mixed color. If it belongs to a triangle, the three vertex colors of the triangle will be used to calculate the mixed color.v_Coloruniformopengl

varying how to generate the mixed color of each fragment

Now we understand that the mixed color of each segment of a line or triangle can be varyinggenerated by one . We can not only mix colors, but also pass any value to varying. OpenGL will choose two values ​​of the vertices of a straight line or three of the three vertices of a triangle. Value, and then blend these values ​​smoothly. Each segment will have a different value. This blending is achieved using linear difference

Linear difference of a straight line

Suppose we have a straight line, it has a red vertex and a green vertex

Straight line from left to right: the far left 100%红色 0%绿色, then the red gradually fades 50%红色50%绿色to the right , until the middle , continues to the right and the green gradually increases until the far right0%红色 100%绿色

Once we add the two colors together, we finally get a mixed straight line. This is the basic explanation of the linear difference. The intensity of each color depends on the distance between each fragment and the vertex containing that color.

We can use the values ​​of vertex 0 and vertex 1 to calculate the distance ratio corresponding to the current fragment. The distance ratio is a percentage between 0 and 100. 0% is the left vertex and 100% is the right vertex. We move left to right, The distance ratio will increase from 0% to 100%

To use the linear difference to calculate the mixed value, you can use the following formula

blended_value=(vertex_0_value*(100%-diatance_ratio))+(vertex_1_value*diatance_ratio)

红色(1,0,0),绿色(010),下图验证这个公式


Blend on the triangular surface

In fact, the triangle color mixture is similar to the straight line, but the calculation method is different this time.

As shown in the figure, for any point given by the triangle, draw a straight line from this point to the three vertices to generate an inner triangle. The area ratio of the three triangles determines the weight of each color on this point.

The sum of these weights is also 100%, you can use the following formula to calculate the color component of each point

blended_value=(vertex_0_value*vertex_0_weight) + (vertex_1_value*vertex_1_weight)+
(vertex_2_value*(100%-vertex_0_weight-vertex_1_weight))

The principle is the same, but this time we have to deal with three points instead of two

Render with new color attributes

We have updated the shader above, added the color attribute, removed the uniform, then my java code should be updated accordingly

First we add two constants

  private final int BYTES_PER_FLOAT = 4;
  private int POSITION_COMPONENT_COUNT = 2;
  //新增
  private final int COLOR_COMPONENT_COUNT = 3;
  private final int STRIDE = (POSITION_COMPONENT_COUNT + COLOR_COMPONENT_COUNT) * BYTES_PER_FLOAT;

The first constant COLOR_COMPONENT_COUNTindicates that the color has three components, rgb,

The second constant STRIDE: We now have both position and color attributes in the same data array. OpenGL cannot assume that the next position is immediately following the previous position. If OpenGL reads the vertex data, then if you want to read the next vertex data, he The color data needs to be skipped, so we need STRIDE to tell opengl how many bytes are between each position, so that he knows how much to skip

Then update the attribute vertex

  		//获取shader属性
        a_position = GLES20.glGetAttribLocation(program, "a_Position");
        a_color = GLES20.glGetAttribLocation(program, "a_Color");

        //绑定a_position和verticeData顶点位置
        /**
         * 第一个参数,这个就是shader属性
         * 第二个参数,每个顶点有多少分量,我们这个只有来个分量
         * 第三个参数,数据类型
         * 第四个参数,只有整形才有意义,忽略
         * 第5个参数,一个数组有多个属性才有意义,我们只有一个属性,传0
         * 第六个参数,opengl从哪里读取数据
         */
        verticeData.position(0);
        GLES20.glVertexAttribPointer(a_position, POSITION_COMPONENT_COUNT, GLES20.GL_FLOAT,
                false, STRIDE, verticeData);
        //开启顶点
        GLES20.glEnableVertexAttribArray(a_position);

		//注释1
        verticeData.position(POSITION_COMPONENT_COUNT);
        GLES20.glVertexAttribPointer(a_color, COLOR_COMPONENT_COUNT, GLES20.GL_FLOAT,
                false, STRIDE, verticeData);
        //开启顶点
        GLES20.glEnableVertexAttribArray(a_color);

Let’s start with note 1

verticeData.position(POSITION_COMPONENT_COUNT);: When OpenGL starts to read the color attributes, we have to start from the first color attribute, not the first position attribute, so we have to skip the first position

Next call GLES20.glVertexAttribPointerto associate the color data with a_color in the colorfinder, and the span tells opengl the distance between the two colors

Run it, you can change the color by yourself and try

Use Android only Color class to convert colors

When we use floating point attributes, we need the value of each color component between 0-1. If we have a color value, how can we get the value of the corresponding color component?

Android provides the Color class, you can easily get the value of each component of the color, such as:

     float red = Color.red(Color.GREEN) / 255f;
     float green = Color.green(Color.GREEN) / 255f;
     float blue = Color.blue(Color.GREEN) / 255f;

     //网络颜色定义

     int parseColor = Color.parseColor("#FFFFFF");
     float red = Color.red(parseColor) / 255f;
     float green = Color.green(parseColor) / 255f;
     float blue = Color.blue(parseColor) / 255f;

Color.red(),Color.green(),Color.blue(),Return 0-255, in order to convert to the opengl color range, just divide by 255

Complete code

public class AirHockKeyRender1 implements GLSurfaceView.Renderer {
    
    

    private final FloatBuffer verticeData;
    private final int BYTES_PER_FLOAT = 4;
    private int POSITION_COMPONENT_COUNT = 2;
    private final int COLOR_COMPONENT_COUNT = 3;
    private final int STRIDE = (POSITION_COMPONENT_COUNT + COLOR_COMPONENT_COUNT) * BYTES_PER_FLOAT;
    private final Context mContext;
    //逆时针绘制三角形
    //逆时针绘制三角形
    float[] tableVertices = {
    
    
            //顶点
            0f, 0f,
            //顶点颜色值
            1f, 1f, 1f,

            -0.5f, -0.5f,
            0.7f, 0.7f, 0.7f,

            0.5f, -0.5f,
            0.7f, 0.7f, 0.7f,

            0.5f, 0.5f,
            0.7f, 0.7f, 0.7f,

            -0.5f, 0.5f,
            0.7f, 0.7f, 0.7f,

            -0.5f, -0.5f,
            0.7f, 0.7f, 0.7f,

            //线
            -0.5f, 0f,
            1f, 0f, 0f,

            0.5f, 0f,
            1f, 0f, 0f,

            //点
            0f, -0.25f,
            1f, 0f, 0f,

            0f, 0.25f,
            0f, 0f, 1f
    };
    private int a_position;
    private int a_color;



    public AirHockKeyRender1(Context context) {
    
    
        this.mContext=context;
        //把float加载到本地内存
        verticeData = ByteBuffer.allocateDirect(tableVertices.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(tableVertices);
        verticeData.position(0);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    
    
        //当surface被创建时,GlsurfaceView会调用这个方法,这个发生在应用程序
        // 第一次运行的时候或者从其他Activity回来的时候也会调用

        //清空屏幕
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        //读取着色器源码
        String fragment_shader_source = ReadResouceText.readResoucetText(mContext, R.raw.fragment_shader1);
        String vertex_shader_source = ReadResouceText.readResoucetText(mContext, R.raw.vertex_shader1);

        //编译着色器源码
        int mVertexshader = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertex_shader_source);
        int mFragmentshader = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragment_shader_source);
        //链接程序
        int program = ShaderHelper.linkProgram(mVertexshader, mFragmentshader);

        //验证opengl对象
        ShaderHelper.volidateProgram(program);
        //使用程序
        GLES20.glUseProgram(program);

        //获取shader属性
        a_position = GLES20.glGetAttribLocation(program, "a_Position");
        a_color = GLES20.glGetAttribLocation(program, "a_Color");

        //绑定a_position和verticeData顶点位置
        /**
         * 第一个参数,这个就是shader属性
         * 第二个参数,每个顶点有多少分量,我们这个只有来个分量
         * 第三个参数,数据类型
         * 第四个参数,只有整形才有意义,忽略
         * 第5个参数,一个数组有多个属性才有意义,我们只有一个属性,传0
         * 第六个参数,opengl从哪里读取数据
         */
        verticeData.position(0);
        GLES20.glVertexAttribPointer(a_position, POSITION_COMPONENT_COUNT, GLES20.GL_FLOAT,
                false, STRIDE, verticeData);
        //开启顶点
        GLES20.glEnableVertexAttribArray(a_position);

        verticeData.position(POSITION_COMPONENT_COUNT);
        GLES20.glVertexAttribPointer(a_color, COLOR_COMPONENT_COUNT, GLES20.GL_FLOAT,
                false, STRIDE, verticeData);
        //开启顶点
        GLES20.glEnableVertexAttribArray(a_color);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
    
    
        //在Surface创建以后,每次surface尺寸大小发生变化,这个方法会被调用到,比如横竖屏切换

        GLES20.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
    
    
        //当绘制每一帧数据的时候,会调用这个放方法,这个方法一定要绘制一些东西,即使只是清空屏幕
        //因为这个方法返回后,渲染区的数据会被交换并显示在屏幕上,如果什么都没有话,会看到闪烁效果

        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

        //绘制长方形
        //指定着色器u_color的颜色为白色
        /**
         * 第一个参数:绘制绘制三角形
         * 第二个参数:从顶点数组0索引开始读
         * 第三个参数:读入6个顶点
         *
         * 最终绘制俩个三角形,组成矩形
         */
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 6);

        //绘制分割线

        GLES20.glDrawArrays(GLES20.GL_LINES, 6, 2);

        //绘制点
        GLES20.glDrawArrays(GLES20.GL_POINTS, 8, 1);

        GLES20.glDrawArrays(GLES20.GL_POINTS, 9, 1);
    }
}

Guess you like

Origin blog.csdn.net/qq_34760508/article/details/108872400