GLSurfaceView
绘制图形拉伸问题
- 假如在
XML
文件中声明GLSurfaceView
的宽高为
android:layout_width="match_parent"
android:layout_height="match_parent
GLSurfaceView
绘制的图形在Open GL ES
坐标系中,而Open GL ES
坐标系会根据GLSurfaceView
的宽高将绘制的图形拉伸,比如绘制一个正方形,有可能绘制成矩形,解决方案:Matrix.frustumM
透视投影解决Matrix.orthoM
正交投影解决
public static void frustumM(float[] m, int offset,
float left, float right, float bottom, float top,
float near, float far) {}
public static void orthoM(float[] m, int mOffset,
float left, float right, float bottom, float top,
float near, float far) {}
OpenGL ES
坐标系

XML文件
<?xml version="1.0" encoding="utf-8"?>
<com.example.myapplication.MyGLSurfaceView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" />
绘制拉伸正方形
自定义GLSurfaceView
代码
class MyGLSurfaceView(context: Context, attrs: AttributeSet) : GLSurfaceView(context, attrs) {
private var mRenderer = MyGLRenderer()
init {
setEGLContextClientVersion(3)
setRenderer(mRenderer)
renderMode = RENDERMODE_WHEN_DIRTY
}
}
自定义GLSurfaceView.Renderer
代码
class MyGLRenderer : GLSurfaceView.Renderer {
private var mDrawData: DrawData? = null
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)
mDrawData = DrawData().apply {
initVertexBuffer()
initShader()
}
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
GLES30.glViewport(0, 0, width, height)
}
override fun onDrawFrame(gl: GL10?) {
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
mDrawData?.drawSomething()
}
}
GLSurfaceView
需要的绘制数据
class DrawData{
var mProgram : Int = -1
var NO_OFFSET = 0
var VERTEX_POS_DATA_SIZE = 3
val vertex = floatArrayOf(
-0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
)
val vertexBuffer = ByteBuffer.allocateDirect(vertex.size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
fun initVertexBuffer(){
vertexBuffer.put(vertex)
vertexBuffer.position(0)
val vbo = IntArray(1)
GLES30.glGenBuffers(1, vbo, 0)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo[0])
GLES30.glBufferData(
GLES30.GL_ARRAY_BUFFER,
vertex.size * 4,
vertexBuffer,
GLES30.GL_STATIC_DRAW
)
}
fun initShader() {
val vertexShaderCode = """#version 300 es
layout (location = 0) in vec4 aPosition;
void main() {
gl_Position = aPosition;
}""".trimIndent()
val fragmentShaderCode = """#version 300 es
precision mediump float;
uniform vec4 vColor;
out vec4 fragColor;
void main() {
fragColor = vColor;
}""".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)
}
fun drawSomething(){
val positionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition")
GLES30.glEnableVertexAttribArray(positionHandle)
GLES30.glVertexAttribPointer(positionHandle, VERTEX_POS_DATA_SIZE, GLES30.GL_FLOAT, false, 0, 0)
val colorHandle = GLES30.glGetUniformLocation(mProgram, "vColor")
GLES30.glUniform4f(colorHandle, 1.0f, 0.5f, 0.5f, 1.0f)
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, NO_OFFSET, vertex.size / VERTEX_POS_DATA_SIZE)
GLES30.glDisableVertexAttribArray(positionHandle)
}
}
object LoadShaderUtil{
fun loadShader(type: Int, source: String): Int {
val shader = GLES30.glCreateShader(type)
GLES30.glShaderSource(shader, source)
GLES30.glCompileShader(shader)
return shader
}
}
效果图

透视投影绘制不拉伸的正方形
透视投影PerspectiveProjection
自定义GLSurfaceView
代码
class MyGLSurfaceView(context: Context, attrs: AttributeSet) : GLSurfaceView(context, attrs) {
private var mRenderer = MyGLRenderer()
init {
setEGLContextClientVersion(3)
setRenderer(mRenderer)
renderMode = RENDERMODE_WHEN_DIRTY
}
}
自定义GLSurfaceView.Renderer
代码
class MyGLRenderer : GLSurfaceView.Renderer {
private var mDrawData: DrawDataWithPerspectiveProjection? = null
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)
mDrawData = DrawDataWithPerspectiveProjection().apply {
initVertexBuffer()
initShader()
}
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
GLES30.glViewport(0, 0, width, height)
mDrawData?.computeMVPMatrix(width.toFloat(), height.toFloat())
}
override fun onDrawFrame(gl: GL10?) {
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
mDrawData?.drawSomething()
}
}
GLSurfaceView
需要的绘制数据
class DrawDataWithPerspectiveProjection {
var mProgram : Int = -1
var NO_OFFSET = 0
var VERTEX_POS_DATA_SIZE = 3
val vertex = floatArrayOf(
-0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
)
val vertexBuffer = ByteBuffer.allocateDirect(vertex.size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
fun initVertexBuffer(){
vertexBuffer.put(vertex)
vertexBuffer.position(0)
val vbo = IntArray(1)
GLES30.glGenBuffers(1, vbo, 0)
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo[0])
GLES30.glBufferData(
GLES30.GL_ARRAY_BUFFER,
vertex.size * 4,
vertexBuffer,
GLES30.GL_STATIC_DRAW
)
}
fun initShader() {
val vertexMapShaderCode = """
#version 300 es
uniform mat4 uMVPMatrix;
layout (location = 0) in vec4 aPosition;
void main() {
gl_Position = uMVPMatrix * aPosition;
}""".trimIndent()
val fragmentShaderCode = """#version 300 es
precision mediump float;
uniform vec4 vColor;
out vec4 fragColor;
void main() {
fragColor = vColor;
}""".trimIndent()
val vertexShader = LoadShaderUtil.loadShader(GLES30.GL_VERTEX_SHADER, vertexMapShaderCode)
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)
}
fun drawSomething(){
val matrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix")
GLES30.glUniformMatrix4fv(matrixHandle, 1, false, mMVPMatrix, 0)
val positionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition")
GLES30.glEnableVertexAttribArray(positionHandle)
GLES30.glVertexAttribPointer(positionHandle, VERTEX_POS_DATA_SIZE, GLES30.GL_FLOAT, false, 0, 0)
val colorHandle = GLES30.glGetUniformLocation(mProgram, "vColor")
GLES30.glUniform4f(colorHandle, 1.0f, 0.5f, 0.5f, 1.0f)
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, NO_OFFSET, vertex.size / VERTEX_POS_DATA_SIZE)
GLES30.glDisableVertexAttribArray(positionHandle)
}
private val mMVPMatrix = FloatArray(16)
private val mProjectionMatrix = FloatArray(16)
private val mViewMatrix = FloatArray(16)
private var mViewPortRatio = 1f
fun computeMVPMatrix(width: Float, height: Float) {
takeIf { width > height }?.let {
mViewPortRatio = width / height
Matrix.frustumM(
mProjectionMatrix,
NO_OFFSET,
-mViewPortRatio,
mViewPortRatio,
-1f,
1f,
1f,
2f
)
} ?: run {
mViewPortRatio = height / width
Matrix.frustumM(
mProjectionMatrix,
NO_OFFSET,
-1f,
1f,
-mViewPortRatio,
mViewPortRatio,
1f,
2f
)
}
Matrix.setLookAtM(
mViewMatrix,
NO_OFFSET,
0f,
0f,
1f,
0f,
0f,
0f,
0f,
1f,
0f
)
Matrix.multiplyMM(
mMVPMatrix,
NO_OFFSET,
mProjectionMatrix,
NO_OFFSET,
mViewMatrix,
NO_OFFSET
)
}
}
效果图
