天天看点

实战OpenGl绘制三角形

片段着色器操作

glClearColor(1.0f, 1.0f, 1.0f, 1.0f);指定将颜色缓冲清空为什么颜色,参数为对应的RGBA值,该步骤只是设置属性

glClear(GL_COLOR_BUFFER_BIT);真正将颜色缓冲设置为glClearColor指定的值,该步骤才是真正的改变颜色缓冲为设置属性的值

glClear方法的参数为指定要设置的缓冲区,可以传入的数值为:GL_COLOR_BUFFER_BIT、GL_DEPTH_BUFFER_BIT 和 GL_STENCIL_BUFFER_BIT,分别表示颜色缓冲、深度缓冲、模板缓冲。

**关于这三个缓冲:

**前面说过光栅化后形成的片段包含绘制一个像素的所有信息,包含颜色、深度、模板等,所有这些信息会缓存在3个缓冲区中,分别就是颜色缓冲(color buffer)、深度缓冲(depth buffer)、模板缓冲(stencil buffer)。

uniform代表着色器程序的常量,类似于Final,着色器程序会执行很多次,一个片段执行一次片段着色器,有多少个片段就会执行多少次片段着色器

和顶点着色器不同,顶点数据比如坐标每次执行顶点着色器都是变化的

uniform常量的赋值:

glUniform4fv是OpenGL中对uniform变量赋值全家桶的其中一个方法,最后的v表示uniform变量类型为向量vec,对于全家桶中修改向量的方法系列,其基本格式为:

glUniform + n维向量 + 向量元素数据类型 + v

代码实战

package com.example.glsurfacedemo

import android.opengl.GLES20
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import java.util.*
import javax.microedition.khronos.opengles.GL10.GL_COLOR_BUFFER_BIT

class Triangle {
    //VAO数组中存放顶点,VBO数组中存放顶点的数据。


    // 顶点数组对象【用于存放顶点数组】Vertex Array Object,VAO
    // 三角形三个点的坐标值(逆时针方向,在3D坐标系中,方向决定了哪面是正面)
    //VAO用于存放各个顶点的坐标(X,Y,Z)
    private var triangleCoords = floatArrayOf(
        0.0f, 1.0f, 0.0f,      // top
        1.0f, -1.0f, 0.0f,    // bottom left
        -1f,-1f, 0.0f   ,     // bottom right
    )


    // 设置颜色(分别代表red, green, blue and alpha)
    private val color = floatArrayOf(1f, 0.4f, 0.3f, 0.1f)

    //存放顶点缓冲的数据  Vertex Buffer Object,VBO
    private var vertexBuffer: FloatBuffer =
    // 坐标点的数目 * float所占字节
    ByteBuffer.allocateDirect(triangleCoords.size * 4)
        .order(ByteOrder.nativeOrder()).asFloatBuffer().apply {
                // 把坐标添加到FloatBuffer
                put(triangleCoords)
                // 设置buffer的位置为起始点0
                position(0)
            }

    // 每个顶点的坐标数
    val COORDS_PER_VERTEX = 3

    /**
* 顶点着色器代码;
*/
    private val vertexShaderCode =
    "attribute vec4 vPosition;" +
    "void main() {" +
    "  gl_Position = vPosition;" +
    "}"

    /**
* 片段着色器代码
*/
    private val fragmentShaderCode =
    "precision highp float;" +
    "uniform vec4 vColor;" +
    "void main() {" +
    "  gl_FragColor = vColor;" +
    "}"

    /**
* 着色器程序ID引用
*/
    private var mProgram: Int

    init {
        // 编译顶点着色器和片段着色器
        val vertexShader: Int = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
        val fragmentShader: Int = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)
        // glCreateProgram函数创建一个着色器程序,并返回新创建程序对象的ID引用
        mProgram = GLES20.glCreateProgram().also {
            // 把顶点着色器添加到程序对象
            GLES20.glAttachShader(it, vertexShader)
            // 把片段着色器添加到程序对象
            GLES20.glAttachShader(it, fragmentShader)
            // 连接并创建一个可执行的OpenGL ES程序对象
            GLES20.glLinkProgram(it)
        }
    }

    private fun loadShader(type: Int, shaderCode: String): Int {
        // glCreateShader函数创建一个顶点着色器或者片段着色器,并返回新创建着色器的ID引用
        val shader = GLES20.glCreateShader(type)
        // 把着色器和代码关联,然后编译着色器
        GLES20.glShaderSource(shader, shaderCode)
        GLES20.glCompileShader(shader)
        return shader
    }

    private val vertexCount: Int = triangleCoords.size / COORDS_PER_VERTEX
    private val vertexStride: Int = COORDS_PER_VERTEX * 4 // 4 bytes per vertex

    /**
* 实际绘制时执行的方法
**/
    fun draw() {
        // 激活着色器程序,把程序添加到OpenGL ES环境
        GLES20.glUseProgram(mProgram)
        // 获取顶点着色器中的vPosition变量(因为之前已经编译过着色器代码,所以可以从着色器程序中获取);用唯一ID表示
        val position = GLES20.glGetAttribLocation(mProgram, "vPosition")
        // 允许操作顶点对象position
        GLES20.glEnableVertexAttribArray(position)
        // 将顶点数据传递给position指向的vPosition变量;将顶点属性与顶点缓冲对象关联
        //第四个参数是VBO代表的是数组缓冲对象如FloatBuffer,而VAO数组是数组数据,是FloatArray、区别是VBO会被缓冲在GPU内部,使用的时候CPU告知如何去取GPU中的缓冲数据,而VAO每次都需要和GPU通信传递数据,性能太差
        //因此VBO和VAO的差别就是顶点属性数组信息是否缓存在GPU内部缓冲区中还是CPU每次都携带顶点信息和GPU通信
        GLES20.glVertexAttribPointer(
            position, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
            false, 0, vertexBuffer
        )
        // 获取片段着色器中的vColor变量
        val colorHandle = GLES20.glGetUniformLocation(mProgram, "vColor")
        // 通过colorHandle设置绘制的颜色值
        GLES20.glUniform4fv(colorHandle, 1, color, 0)
        // 绘制顶点数组;
        //        GLES20.glClear(GL_COLOR_BUFFER_BIT)
        //        GLES20.glClearColor(1.0f, 1.0f, 0.2f, 0.2f);
        //使用的图元链接图形,从第几个顶点元素绘制,需要绘制多少个顶点
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 3)
        // 操作完后,取消允许操作顶点对象position
        GLES20.glDisableVertexAttribArray(position)
    }
    //首先获取顶点着色器中的glposition属性,接着从0开始取,步长为12,区三个顶点。将这三个顶点信息绘制成指定图形
}