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
)
}
}
效果图