Android OpenGL(六) 纹理
纹理
纹理是一个2D图片(甚至也有1D和3D的纹理),
它可以用来添加物体的细节;你可以想象纹理是一张绘有砖块的纸,无缝折叠贴合到你的3D的
房子上,这样你的房子看起来就像有砖墙外表了
纹理环绕方式
纹理坐标的范围通常是从(0, 0)到(1, 1),那如果我们把纹理坐标设置在范围之外会发生什么?
OpenGL默认的行为是重复这个纹理图像(我们基本上忽略浮点纹理坐标的整数部分),但
OpenGL提供了更多的选择:
使用方式类似:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
纹理过滤
纹理过滤:GL_NEAREST 、GL_LINEAR和多级渐远纹理
GL_NEAREST(也叫邻近过滤,Nearest Neighbor Filtering)是OpenGL默认的纹理过滤方
式。当设置为GL_NEAREST的时候,OpenGL会选择中心点最接近纹理坐标的那个像素。
GL_LINEAR(也叫线性过滤,(Bi)linear Filtering)它会基于纹理坐标附近的纹理像素,计算出
一个插值,近似出这些纹理像素之间的颜色。一个纹理像素的中心距离纹理坐标越近,那么这个
纹理像素的颜色对最终的样本颜色的贡献越大。
当进行放大(Magnify)和缩小(Minify)操作的时候可以设置纹理过滤的选项,比如你可以在纹理
被缩小的时候使用邻近过滤,被放大时使用线性过滤。我们需要使用glTexParameter*函数为
放大和缩小指定过滤方式。这段代码看起来会和纹理环绕方式的设置很相似:
多级
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
多级渐远纹理
过滤方式
GL_NEAREST_MIPMAP_NEAREST :使用最邻近的多级渐远纹理来匹配像素大小,并使用邻近插值进行纹理采样
GL_LINEAR_MIPMAP_NEAREST: 使用最邻近的多级渐远纹理级别,并使用线性插值进行采样
GL_NEAREST_MIPMAP_LINEAR 在两个最匹配像素大小的多级渐远纹理之间进行线性插值,使用邻近插值进行采样
GL_LINEAR_MIPMAP_LINEAR 在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
纹理的使用
-
生成纹理
GLES30.glGenTextures(1, textureIds, 0)
-
绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[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)
-
载入图片数据
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D,
0,
GLES30.GL_RGBA,
bitmap,
0)
- 取消绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
纹理加载
private fun loadTexture() {
GLES30.glGenTextures(1, textureIds, 0)
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[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)
// 加载bitmap到纹理中
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D,
0,
GLES30.GL_RGBA,
bitmap,
0)
GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D)
bitmap.recycle()
// 取消绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
}
纹理应用
展示图片
布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.opengl.GLSurfaceView
android:id="@+id/glSurfaceView_img"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Activity
class OpenGlImgActivity : AppCompatActivity() {
private var glSurfaceView: GLSurfaceView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_opengl_img_demo)
glSurfaceView = findViewById(R.id.glSurfaceView_img)
glSurfaceView?.setEGLContextClientVersion(3)
glSurfaceView?.setRenderer(GLImageViewRender(BitmapFactory.decodeResource(resources, R.drawable.flower)))
glSurfaceView?.renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
}
}
GLSurfaceView.Renderer
import android.graphics.Bitmap
import android.opengl.GLES30
import android.opengl.GLSurfaceView
import android.opengl.GLUtils
import android.opengl.Matrix
import android.util.Log
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10
class GLImageViewRender(val bitmap:Bitmap) : GLSurfaceView.Renderer {
/*
* 顶点位置程序
*/
private val vertexShaderCode =
"uniform mat4 uTMatrix;" +
"attribute vec4 aPosition;" +
"attribute vec2 aTexCoord;" +
"varying vec2 vTexCoord;" +
"void main() { " +
" gl_Position = uTMatrix * aPosition;" +
" vTexCoord = aTexCoord;" +
"}"
/**
* 片元颜色程序
*/
private val fragmentShaderCode =
"precision mediump float;" +
"uniform sampler2D uSampler;" +
"varying vec2 vTexCoord;" +
"void main() { " +
" gl_FragColor = texture2D(uSampler,vTexCoord);" +
"}"
/**
* 三角形顶点位置
*/
private val coodData = floatArrayOf(
// 顶点坐标 纹理坐标
-1f, 1f, 0.0f, 0f, 0f, // 左上角
-1f, -1f, 0.0f, 0f, 1f, //左下角
1f, 1.0f, 0.0f, 1f, 0f, //右上角
1f, -1f, 0.0f, 1f, 1f //右下角
)
private var translateMatrix = FloatArray(16)
private var program: Int = 0
private var positionHandle: Int = -1
private var texCoordHandle: Int = -1
private var samplerHandle: Int = -1
private var uMatrixHandle: Int = -1
// vbo
private var vboId = IntArray(1)
// 纹理id array
private var textureIds= IntArray(1)
private lateinit var coordBuffer: FloatBuffer
private lateinit var byteBuffer: ByteBuffer
override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
// 设置背景颜色
GLES30.glClearColor(1f, 0f, 0f, 1.0f)
// 清理缓存
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
// 顶点坐标内存申请
createFloatBuffer()
// 创建定点着色程序
val vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
// 创建片元着色程序
val fragmentShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
if (vertexShader != 0 && fragmentShader != 0) {
linkProgram(vertexShader, fragmentShader)
GLES30.glDeleteShader(vertexShader)
GLES30.glDeleteShader(fragmentShader)
// 生成VBO
GLES30.glGenBuffers(1, vboId, 0)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboId[0])
GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, byteBuffer.capacity(), byteBuffer, GLES30.GL_STATIC_DRAW)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
//
Matrix.setIdentityM(translateMatrix, 0)
// 将数据传递shader
positionHandle = GLES30.glGetAttribLocation(program, "aPosition")
GLES30.glEnableVertexAttribArray(positionHandle)
texCoordHandle = GLES30.glGetAttribLocation(program, "aTexCoord")
GLES30.glEnableVertexAttribArray(texCoordHandle)
uMatrixHandle = GLES30.glGetUniformLocation(program, "uTMatrix")
samplerHandle = GLES30.glGetUniformLocation(program, "uSampler")
loadTexture()
}
}
private fun linkProgram(vertexShader: Int, fragmentShader: Int) {
// 创建空的opengl es 程序
program = GLES30.glCreateProgram()
program.let {
// 将顶点着色器加入程序
GLES30.glAttachShader(it, vertexShader)
// 将片元着色器加入程序
GLES30.glAttachShader(it, fragmentShader)
// 链接到着色器程序
GLES30.glLinkProgram(it)
// 将程序加入到opengl30环境中
GLES30.glUseProgram(it)
val info = GLES30.glGetProgramInfoLog(it)
// 打印链接程序日志
Log.e("wdf", "info==" + info)
}
}
private fun createFloatBuffer() {
// 申请物理层空间
byteBuffer = ByteBuffer.allocateDirect(coodData.size * 4).apply {
this.order(ByteOrder.nativeOrder())
}
// 坐标数据转换
coordBuffer = byteBuffer.asFloatBuffer()
coordBuffer.put(coodData, 0, coodData.size)
coordBuffer.position(0)
}
override fun onSurfaceChanged(p0: GL10?, width: Int, height: Int) {
// 设置绘制窗口
GLES30.glViewport(0, 0, width, height)
}
override fun onDrawFrame(p0: GL10?) {
if (program <= 0) {
return
}
GLES30.glUseProgram(program)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboId[0])
program?.let {
GLES30.glVertexAttribPointer(positionHandle, 3, GLES30.GL_FLOAT, false, 5 * Float.SIZE_BYTES, 0)
GLES30.glVertexAttribPointer(texCoordHandle, 2, GLES30.GL_FLOAT, false, 5 * Float.SIZE_BYTES, 3 * Float.SIZE_BYTES)
GLES30.glUniformMatrix4fv(uMatrixHandle, 1, false, translateMatrix, 0)
// 激活纹理单元
GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
// 绑定纹理单元
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0])
// 第二个参数传递激活的纹理单元,因为激活的是GLES30.GL_TEXTURE0,因此传递0
GLES30.glUniform1i(samplerHandle, 0)
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
}
}
private fun loadTexture() {
GLES30.glGenTextures(1, textureIds, 0)
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[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)
// 加载bitmap到纹理中
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D,
0,
GLES30.GL_RGBA,
bitmap,
0)
GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D)
bitmap.recycle()
// 取消绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
}
/**
* 创建shader,加载shader程序
*/
private fun loadShader(type: Int, shaderCode: String): Int {
val shader = GLES30.glCreateShader(type)
GLES30.glShaderSource(shader, shaderCode)
GLES30.glCompileShader(shader)
return shader
}
}