kotlin
复制代码
class DrawData {
private var mProgram : Int = -1
private var NO_OFFSET = 0
private val VERTEX_POS_DATA_SIZE = 3
private val TEXTURE_POS_DATA_SIZE = 2
// 纹理ID
private var mTextureID = IntArray(1)
// VBO IDs
private var mVertexVBO = 0
private var mTexCoordVBO = 0
// 最终变化矩阵
private val mMVPMatrix = FloatArray(16)
// 投影矩阵
private val mProjectionMatrix = FloatArray(16)
// 相机矩阵
private val mViewMatrix = FloatArray(16)
private var mViewPortRatio = 1f
// 1. 准备顶点坐标,分配直接内存
// OpenGL ES坐标系:原点在中心,X轴向右为正,Y轴向上为正,Z轴向外为正
val vertex = floatArrayOf(
-1.0f, 1.0f, 0.0f, // 左上
-1.0f, -1.0f, 0.0f, // 左下
1.0f, 1.0f, 0.0f, // 右上
1.0f, -1.0f, 0.0f, // 右下
)
val vertexBuffer = ByteBuffer.allocateDirect(vertex.size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
// 2. 准备纹理坐标,分配直接内存
// 纹理坐标系:原点在左下角,X轴向右为正,Y轴向上为正
val textureCoords = floatArrayOf(
0.0f, 1.0f, // 左上
0.0f, 0.0f, // 左下
1.0f, 1.0f, // 右上
1.0f, 0.0f, // 右下
)
val textureBuffer = ByteBuffer.allocateDirect(textureCoords.size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
// 3. 创建顶点缓冲区对象
fun initVertexBuffer(){
// 初始化顶点坐标缓冲区
vertexBuffer.put(vertex)
vertexBuffer.position(NO_OFFSET)
// 初始化纹理坐标缓冲区
textureBuffer.put(textureCoords)
textureBuffer.position(NO_OFFSET)
// 创建两个VBO,一个用于顶点坐标,一个用于纹理坐标
val vbo = IntArray(2)
GLES30.glGenBuffers(vbo.size, vbo, NO_OFFSET) // 生成一个缓冲区对象ID,并存储在数组 vbo 中,存放位置为0
// 绑定顶点缓冲区
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo[0])
GLES30.glBufferData(
GLES30.GL_ARRAY_BUFFER,
vertex.size * 4, // 数据总字节数 = 顶点数 * Float占4字节
vertexBuffer,
GLES30.GL_STATIC_DRAW
)
// 绑定纹理缓冲区
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo[1])
GLES30.glBufferData(
GLES30.GL_ARRAY_BUFFER,
textureCoords.size * 4, // 数据总字节数 = 顶点数 * Float占4字节
textureBuffer,
GLES30.GL_STATIC_DRAW
)
mVertexVBO = vbo[0]
mTexCoordVBO = vbo[1]
}
// 4. 初始化着色器程序
fun initShader() {
val vertexShaderCode = """#version 300 es
in vec4 aPosition; // 顶点坐标
uniform mat4 uMVPMatrix; // 变换矩阵
in vec2 aTexCoord; // 纹理坐标
out vec2 vTexCoord;
void main() {
// 输出顶点坐标和纹理坐标到片段着色器
gl_Position = uMVPMatrix * aPosition;
vTexCoord = aTexCoord;
}""".trimIndent() // 顶点着色器代码
val fragmentShaderCode = """#version 300 es
precision mediump float; // 定义float 精度为 mediump
out vec4 fragColor; // 输出片段颜色
in vec2 vTexCoord; // 接收顶点着色器传递过来的纹理坐标
uniform sampler2D uTexture; // 纹理取样器
void main() {
// 使用内置函数texture, 根据纹理坐标和取样器sampler2D计算片段颜色
fragColor = texture(uTexture, vTexCoord);
}""".trimIndent()
// 加载顶点着色器和片段着色器, 并创建着色器程序
val vertexShader = LoadShaderUtil.loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
val fragmentShader = LoadShaderUtil.loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
mProgram = GLES30.glCreateProgram()
GLES30.glAttachShader(mProgram, vertexShader)
GLES30.glAttachShader(mProgram, fragmentShader)
GLES30.glLinkProgram(mProgram)
GLES30.glUseProgram(mProgram)
}
// 5. 加载纹理
fun loadTexture(context: Context, resourceId: Int) {
// 生成纹理
GLES30.glGenTextures(mTextureID.size, mTextureID, NO_OFFSET)
// 绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureID[0])
// 设置纹理参数
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR) // 纹理缩小时使用线性插值
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR) // 纹理放大时使用线性插值
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充
// 加载图片
val options = BitmapFactory.Options().apply {
inScaled = false // 不进行缩放
}
val bitmap = BitmapFactory.decodeResource(context.resources, resourceId, options)
// 将图片数据加载到纹理中
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, NO_OFFSET, bitmap, NO_OFFSET)
// 释放资源
bitmap.recycle()
// 解绑纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, NO_OFFSET)
}
// 6. 计算变换矩阵
fun computeMVPMatrix(width: Float, height: Float) {
// 正交投影矩阵
takeIf { width > height }?.let {
mViewPortRatio = width / height
Matrix.orthoM(
mProjectionMatrix, // 正交投影矩阵
NO_OFFSET, // 偏移量
-mViewPortRatio, // 近平面的坐标系左边界
mViewPortRatio, // 近平面的坐标系右边界
-1f, // 近平面的坐标系的下边界
1f, // 近平面坐标系的上边界
0f, // 近平面距离相机距离
1f // 远平面距离相机距离
)
} ?: run {
mViewPortRatio = height / width
Matrix.orthoM(
mProjectionMatrix, // 正交投影矩阵
NO_OFFSET, // 偏移量
-1f, // 近平面坐标系左边界
1f, // 近平面坐标系右边界
-mViewPortRatio, // 近平面坐标系下边界
mViewPortRatio, // 近平面坐标系上边界
0f, // 近平面距离相机距离
1f // 远平面距离相机距离
)
}
// 设置相机矩阵
// 相机位置(0f, 0f, 1f)
// 物体位置(0f, 0f, 0f)
// 相机方向(0f, 1f, 0f)
Matrix.setLookAtM(
mViewMatrix, // 相机矩阵
NO_OFFSET, // 偏移量
0f, // 相机位置x
0f, // 相机位置y
1f, // 相机位置z
0f, // 物体位置x
0f, // 物体位置y
0f, // 物体位置z
0f, // 相机上方向x
1f, // 相机上方向y
0f // 相机上方向z
)
// 最终变化矩阵
Matrix.multiplyMM(
mMVPMatrix, // 最终变化矩阵
NO_OFFSET, // 偏移量
mProjectionMatrix, // 投影矩阵
NO_OFFSET, // 投影矩阵偏移量
mViewMatrix, // 相机矩阵
NO_OFFSET // 相机矩阵偏移量
)
// 纹理坐标系为(0, 0), (1, 0), (1, 1), (0, 1)的正方形逆时针坐标系,从Bitmap生成纹理,即像素拷贝到纹理坐标系
// 变换矩阵需要加上一个y方向的翻转, x方向和z方向不改变
Matrix.scaleM(
mMVPMatrix,
NO_OFFSET,
1f,
-1f,
1f,
)
val matrixHandler = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix")
GLES30.glUniformMatrix4fv(matrixHandler, 1, false, mMVPMatrix, NO_OFFSET)
}
// 7. 使用着色器程序绘制图形
fun drawSomething(){
// 激活纹理编号
GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureID[0])
// 激活纹理取样器
val textureSampleHandle = GLES30.glGetUniformLocation(mProgram, "uTexture")
GLES30.glUniform1i(textureSampleHandle, NO_OFFSET)
// 激活变换矩阵
val matrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix")
GLES30.glUniformMatrix4fv(matrixHandle, 1, false, mMVPMatrix, NO_OFFSET)
// 输入顶点数据
val positionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition")
GLES30.glEnableVertexAttribArray(positionHandle)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVertexVBO)
GLES30.glVertexAttribPointer(positionHandle, VERTEX_POS_DATA_SIZE, GLES30.GL_FLOAT, false, 0, NO_OFFSET)
// 绑定纹理数据
val textureHandle = GLES30.glGetAttribLocation(mProgram, "aTexCoord")
GLES30.glEnableVertexAttribArray(textureHandle)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mTexCoordVBO)
GLES30.glVertexAttribPointer(textureHandle, TEXTURE_POS_DATA_SIZE, GLES30.GL_FLOAT, false, 0, NO_OFFSET)
// 绘制纹理
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, NO_OFFSET, vertex.size / VERTEX_POS_DATA_SIZE)
// 解绑顶点数据
GLES30.glDisableVertexAttribArray(positionHandle)
// 解绑纹理数据
GLES30.glDisableVertexAttribArray(textureHandle)
}
}
object LoadShaderUtil{
// 创建着色器对象
fun loadShader(type: Int, source: String): Int {
val shader = GLES30.glCreateShader(type)
GLES30.glShaderSource(shader, source)
GLES30.glCompileShader(shader)
return shader
}
}