How shaders can be used in Android View and how Android View can use shaders

The translation of the article was prepared in anticipation of the start of the courses " Android Developer. Basic " and " Android Developer. Professional " .






, , , , Canvas. , - . RenderScript, ?





GLSL OpenGL view, Android View (android.view.View). , - :





  • .





  • .





  • .





  • - , (blurring), (distortion), . .





  • .





, . , !





, , , Android View (android.view.View), c OpenGL .





- ShaderViews. .





, , , . :





  • β€” GLSL OpenGL.





  • β€” , view.





  • β€” , Render. , , , .





  • β€” view-, / .





  • β€” Activity Fragment android.





  1. view view- (, view- ShaderView



    ). : SurfaceView



    TextureView



    . .





  2. Render



    , view



    .





  3. 3D- (quadrangle), view (3D, OpenGL 3D-). ; , .





OpenGL quadrilateral inside TextureView.
OpenGL TextureView.

SurfaceView TextureView

SurfaceView



TextureView



Android View, .





SurfaceView



, OpenGL . GLSurfaceView



. , GLSurfaceView



. , , , view



.





TextureView



android.view.View



, , , . , SurfaceView



, ( 1–3 ).





, , view view Android, TextureView



.





, , OpenGL



render TextureView



. β€” GLSurfaceView



, , SurfaceView



, , GLTextureView



.





GLTextureView

, GLSurfaceView



, OpenGL. .





  1. GLTextureView.kt



    , TextureView



    TextureView.SurfaceTextureListener



    View.OnLayoutChangeListener



    . .





open class GLTextureView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) :
    TextureView(context, attrs, defStyleAttr),
    TextureView.SurfaceTextureListener,
    View.OnLayoutChangeListener {
}
      
      



GLTextureView.kt GitHub





5.   finalize()



Kotlin. ( , ).





6. SurfaceHolder



SurfaceTexture



.





7. GLSurfaceView



GLTextureView



.





8. , GLSurfaceView



. , GLSurfaceView



.





9. Java Kotlin. , NULL (, egl: EGL10



egl: EGL10?



).





10. - .





11. .





12. SurfaceTextureListener



.





 override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
        surfaceCreated(surface)
        surfaceChanged(surface, 0, width, height)
    }

    override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
        surfaceChanged(surface, 0, width, height)
    }

    override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
        surfaceDestroyed(surface)
        return true
    }

    override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
    }
      
      



GLTextureView.kt GitHub





13. createSurface()



, view.holder



view.surfaceTexture



.





14. onLayoutChange



.





 override fun onLayoutChange(
        v: View?, left: Int, top: Int,
        right: Int,
        bottom: Int,
        oldLeft: Int,
        oldTop: Int,
        oldRight: Int,
        oldBottom: Int
    ) {
        surfaceChanged(surfaceTexture, 0, right - left, bottom - top)
    }
      
      



GLTextureView.kt GitHub





- .





, , .





fun Resources.getRawTextFile(@RawRes resource: Int): String =
   openRawResource(resource).bufferedReader().use { it.readText() }
      
      



extensions.kt GitHub





, . , .





(Vertex Shader)

( ).





#version 300 es

uniform mat4 uMVPMatrix;
uniform mat4 uSTMatrix;

in vec3 inPosition;
in vec2 inTextureCoord;

out vec2 textureCoord;

void main() {
   gl_Position = uMVPMatrix * vec4(inPosition.xyz, 1);
   textureCoord = (uSTMatrix * vec4(inTextureCoord.xy, 0, 0)).xy;
}
      
      



vertex.vsh GitHub





/ (Fragment Shader)

, , .





, GLSL.





#version 300 es
      
      



, .





uniform vec4 uMyUniform;
      
      



. In β€” ( ), out β€” ( ).





in vec2 textureCoord;
out vec4 fragColor;
      
      



, Android View .





void main() {
   fragColor = vec4(textureCoord.x, textureCoord.y, 1.0, 1.0) * uMyUniform;
}
      
      



:





#version 300 es

precision mediump float;

uniform vec4 uMyUniform;

in vec2 textureCoord;
out vec4 fragColor;

void main() {
    fragColor = vec4(textureCoord.x, textureCoord.y, 1.0, 1.0) * uMyUniform;
}
      
      



fragment_shader.fsh GitHub





QuadRender

, , β€” . ShaderView



.





OpenGL . β€” , .





GLTextureView.Renderer



:





onSurfaceCreated()



β€” , (uniform) .





onDrawFrame()



β€” . .





onSurfaceChanged()



β€” .





, . , OpenGL, . , , .





.





private const val FLOAT_SIZE_BYTES = 4 //  Float
private const val TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES // 5 float’    (3 float’    2   )
private const val TRIANGLE_VERTICES_DATA_POS_OFFSET = 0 //       
private const val TRIANGLE_VERTICES_DATA_UV_OFFSET = 3 //     3- float’ (4-  5- float’)

//   

const val VERTEX_SHADER_IN_POSITION = "inPosition"
const val VERTEX_SHADER_IN_TEXTURE_COORD = "inTextureCoord"
const val VERTEX_SHADER_UNIFORM_MATRIX_MVP = "uMVPMatrix"
const val VERTEX_SHADER_UNIFORM_MATRIX_STM = "uSTMatrix"

const val FRAGMENT_SHADER_UNIFORM_MY_UNIFORM = "uMyUniform"

private const val UNKNOWN_PROGRAM = -1
private const val UNKNOWN_ATTRIBUTE = -1
      
      



QuadRender.kt GitHub





, .





private var vertexShaderSource : String, //    
private var fragmentShaderSource : String, //    
QuadRender.kt  GitHub
      
      



.





private val quadVertices: FloatBuffer

init {
  //    
   val quadVerticesData = floatArrayOf(
       // [x,y,z, U,V]
       -1.0f, -1.0f, 0f, 0f, 1f,
       1.0f, -1.0f, 0f, 1f, 1f,
       -1.0f, 1.0f, 0f, 0f, 0f,
       1.0f, 1.0f, 0f, 1f, 0f
   )

   quadVertices = ByteBuffer
       .allocateDirect(quadVerticesData.size * FLOAT_SIZE_BYTES)
       .order(ByteOrder.nativeOrder())
       .asFloatBuffer()
       .apply {
           put(quadVerticesData).position(0)
       }
}
      
      



QuadRender.kt GitHub





.





private val matrixMVP = FloatArray(16)
private val matrixSTM = FloatArray(16)
      
      



QuadRender.kt GitHub





init{}



.





init {
 // ,    

  Matrix.setIdentityM(matrixSTM, 0)
}
      
      



QuadRender.kt GitHub





, .





private var inPositionHandle = UNKNOWN_ATTRIBUTE
private var inTextureHandle = UNKNOWN_ATTRIBUTE
private var uMVPMatrixHandle = UNKNOWN_ATTRIBUTE
private var uSTMatrixHandle = UNKNOWN_ATTRIBUTE

private var uMyUniform = UNKNOWN_ATTRIBUTE
      
      



QuadRender.kt GitHub





.





private var program = UNKNOWN_PROGRAM
      
      



QuadRender.kt GitHub





, . onSurfaceCreated()



. , uMyUniform



, .





override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
  //     
createProgram(vertexShaderSource, fragmentShaderSource)

  //    
   inPositionHandle = GLES20.glGetAttribLocation(program, VERTEX_SHADER_IN_POSITION)
   checkGlError("glGetAttribLocation $VERTEX_SHADER_IN_POSITION")
   if (inPositionHandle == UNKNOWN_ATTRIBUTE) throw RuntimeException("Could not get attrib location for $VERTEX_SHADER_IN_POSITION")
   inTextureHandle = GLES20.glGetAttribLocation(program, VERTEX_SHADER_IN_TEXTURE_COORD)
   checkGlError("glGetAttribLocation $VERTEX_SHADER_IN_TEXTURE_COORD")
   if (inTextureHandle == UNKNOWN_ATTRIBUTE) throw RuntimeException("Could not get attrib location for $VERTEX_SHADER_IN_TEXTURE_COORD")

   uMVPMatrixHandle = GLES20.glGetUniformLocation(program, VERTEX_SHADER_UNIFORM_MATRIX_MVP)
   checkGlError("glGetUniformLocation $VERTEX_SHADER_UNIFORM_MATRIX_MVP")
   if (uMVPMatrixHandle == UNKNOWN_ATTRIBUTE) throw RuntimeException("Could not get uniform location for $VERTEX_SHADER_UNIFORM_MATRIX_MVP")

   uSTMatrixHandle = GLES20.glGetUniformLocation(program, VERTEX_SHADER_UNIFORM_MATRIX_STM)
   checkGlError("glGetUniformLocation $VERTEX_SHADER_UNIFORM_MATRIX_STM")
   if (uSTMatrixHandle == UNKNOWN_ATTRIBUTE) throw RuntimeException("Could not get uniform location for $VERTEX_SHADER_UNIFORM_MATRIX_STM")

  // (!)    

   uMyUniform = GLES30.glGetUniformLocation(program, FRAGMENT_SHADER_UNIFORM_MY_UNIFORM)
   checkGlError("glGetUniformLocation $FRAGMENT_SHADER_UNIFORM_MY_UNIFORM")
   if (uMyUniform == UNKNOWN_ATTRIBUTE) throw RuntimeException("Could not get uniform location for $FRAGMENT_SHADER_UNIFORM_MY_UNIFORM")
}
      
      



QuadRender.kt GitHub





, (uMyUniform



) . .





onSurfaceCreated()



.





/**
*          
*/
private fun createProgram(vertexSource: String, fragmentSource: String): Boolean {
   if (program != UNKNOWN_PROGRAM) {
      //  
       GLES30.glDeleteProgram(program)
       program = UNKNOWN_PROGRAM
   }
  //   
   val vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexSource)
   if (vertexShader == UNKNOWN_PROGRAM) {
       return false
   }
   //   
   val pixelShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentSource)
   if (pixelShader == UNKNOWN_PROGRAM) {
       return false
   }
   program = GLES30.glCreateProgram()
   if (program != UNKNOWN_PROGRAM) {
       GLES30.glAttachShader(program, vertexShader)
       checkGlError("glAttachShader: vertex")
       GLES30.glAttachShader(program, pixelShader)
       checkGlError("glAttachShader: pixel")
       return linkProgram()
   }
   return true
}

private fun linkProgram(): Boolean {
   if (program == UNKNOWN_PROGRAM) {
       return false
   }
   GLES30.glLinkProgram(program)
   val linkStatus = IntArray(1)
   GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, linkStatus, 0)
   if (linkStatus[0] != GLES30.GL_TRUE) {
       Log.e(TAG, "Could not link program: ")
       Log.e(TAG, GLES30.glGetProgramInfoLog(program))
       GLES30.glDeleteProgram(program)
       program = UNKNOWN_PROGRAM
       return false
   }
   return true
}

private fun loadShader(shaderType: Int, source: String): Int {
   var shader = GLES30.glCreateShader(shaderType)
   if (shader != UNKNOWN_PROGRAM) {
       GLES30.glShaderSource(shader, source)
       GLES30.glCompileShader(shader)
       val compiled = IntArray(1)
       GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0)
       if (compiled[0] == UNKNOWN_PROGRAM) {
           Log.e(TAG, "Could not compile shader $shaderType:")
           Log.e(TAG, GLES30.glGetShaderInfoLog(shader))
           GLES30.glDeleteShader(shader)
           shader = UNKNOWN_PROGRAM
       }
   }
   return shader
}

private fun checkGlError(op: String) {
   var error: Int
   while (GLES30.glGetError().also { error = it } != GLES30.GL_NO_ERROR) {
       Log.e(TAG, "$op: glError $error")
       throw RuntimeException("$op: glError $error")
   }
      
      



QuadRender.kt GitHub





, , β€” onDrawFrame()



.





override fun onDrawFrame(gl: GL10?) {
   //   ""
   GLES30.glClearColor(0.0f, 0.0f, 0.0f, 0.0f)
   GLES30.glClear(GLES30.GL_DEPTH_BUFFER_BIT or GLES30.GL_COLOR_BUFFER_BIT)

   //  
   GLES30.glUseProgram(program)

   //    ( )
   setAttribute(inPositionHandle, VERTEX_SHADER_IN_POSITION, 3, TRIANGLE_VERTICES_DATA_POS_OFFSET) // 3   3 float’  
   setAttribute(inTextureHandle, VERTEX_SHADER_IN_TEXTURE_COORD, 2, TRIANGLE_VERTICES_DATA_UV_OFFSET) // 2   2 float’   

  //  
   Matrix.setIdentityM(matrixMVP, 0)
   GLES30.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, matrixMVP, 0)
   GLES30.glUniformMatrix4fv(uSTMatrixHandle, 1, false, matrixSTM, 0)

  // (!)     
   val uMyUniformValue = floatArrayOf(1.0f, 0.75f, 0.95f, 1f) //  ,      
   GLES30.glUniform4fv(uMyUniform, 1, uMyUniformValue, 0)

   //    (  )
   GLES30.glBlendFunc(GLES30.GL_SRC_ALPHA, GLES30.GL_ONE_MINUS_SRC_ALPHA)
   GLES30.glEnable(GLES20.GL_BLEND)

  //   
   GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
   checkGlError("glDrawArrays")

   GLES30.glFinish()
}
      
      



QuadRender.kt GitHub





, (uMyUniformValue



) (uMyUniform



) .





, surfaceChange()



β€” .





override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
   GLES30.glViewport(0, 0, width, height)
}
      
      



QuadRender.kt GitHub





.





ShaderView

, , ShaderView, . ! ShaderView.





private const val OPENGL_VERSION = 3

class ShaderView @JvmOverloads constructor(
   context: Context,
   attrs: AttributeSet? = null,
   defStyleAttr: Int = 0
) :
   GLTextureView(context, attrs, defStyleAttr) {

   init {
      //   OpenGL
       setEGLContextClientVersion(OPENGL_VERSION)

      //       
       val vsh = context.resources.getRawTextFile(R.raw.vertex_shader)
       val fsh = context.resources.getRawTextFile(R.raw.fragment_shader)

      //  
       setRenderer(QuadRender(vertexShaderSource = vsh, fragmentShaderSource = fsh))

       //    RENDERMODE_WHEN_DIRTY  RENDERMODE_CONTINUOUSLY
       setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY) //  GLSurfaceView.RENDERMODE_CONTINUOUSLY       
   }
}
      
      



ShaderView.kt GitHub





:

, , (, ).





sampler2D



texture()



GLSL.





.





#version 300 es
precision mediump float;

uniform sampler2D uTexture;

in vec2 textureCoord;
out vec4 fragColor;

void main() {
   fragColor = texture(uTexture, textureCoord);
}
      
      



fragment_texture_shader.fsh GitHub





OpenGL.





fun Resources.loadBitmapForTexture(@DrawableRes drawableRes: Int): Bitmap {
   val options = BitmapFactory.Options()
   options.inScaled = false //   true. false,     

  //   
   return BitmapFactory.decodeResource(this, drawableRes, options)
}

/**
*    Bitmap     
* @needToRecycle -       Bitmap,    GPI?
*/
@Throws(RuntimeException::class)
fun Bitmap.toGlTexture(needToRecycle: Boolean = true, textureSlot: Int = GLES30.GL_TEXTURE0): Int {
  //  
   val textureIds = IntArray(1)
   GLES30.glGenTextures(1, textureIds, 0) //  ID  
   if (textureIds[0] == 0) {
       throw java.lang.RuntimeException("It's not possible to generate ID for texture")
   }
   
   GLES30.glActiveTexture(textureSlot) //   #0  
   GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0]) //    ID   

  //  
   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)

  //     GPU
   GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, this, 0)
  //       
   if (needToRecycle) {
       this.recycle()
   }

  //    
   GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)

   return textureIds[0]
}
      
      



extensions.kt GitHub





(bitmap), loadBitmapForTexture()



, QuadRender.onSurfaceCreated()



. OpenGL ( GL_TEXTURE0



GL_TEXTURE31



).





, .





uTextureHandle = GLES30.glGetUniformLocation(program, FRAGMENT_SHADER_UNIFORM_TEXTURE)
uTextureId = textureBitmap.toGlTexture(needToRecycle = true, GLES30.GL_TEXTURE0)
      
      



QuadRender.kt GitHub





, QuadRender.onDrawFrame()



.





.





GLES30.glUniform1i(uTextureHandle, 0) // 0 as far as it's slot number 0// 0    0
GLES30.glActiveTexture(GLES30.GL_TEXTURE0) //    ,     
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, uTextureId)
      
      



QuadRender.kt GitHub





.





ShaderView API .






: "Android Developer. Basic" / "Android Developer. Professional".



:



1)
Android:

-

- Canvas, Path, Paint

-



2)
- β€” Android 2 , β€” Kotlin.








All Articles