forked from beduExpert/Android-Avanzado-MASIVO-2021
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBackgroundRenderer.kt
250 lines (234 loc) · 10.4 KB
/
BackgroundRenderer.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
package com.bedu.ar.common.rendering
import android.content.Context
import android.opengl.GLES11Ext
import android.opengl.GLES20
import com.google.ar.core.Coordinates2d
import com.google.ar.core.Frame
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
/**
* This class renders the AR background from camera feed. It creates and hosts the texture given to
* ARCore to be filled with the camera image.
*/
class BackgroundRenderer {
private lateinit var quadCoords: FloatBuffer
private var quadTexCoords: FloatBuffer? = null
private var cameraProgram = 0
private var depthProgram = 0
private var cameraPositionAttrib = 0
private var cameraTexCoordAttrib = 0
private var cameraTextureUniform = 0
var textureId = -1
private set
private var suppressTimestampZeroRendering = true
private var depthPositionAttrib = 0
private var depthTexCoordAttrib = 0
private var depthTextureUniform = 0
private var depthTextureId = -1
/**
* Allocates and initializes OpenGL resources needed by the background renderer. Must be called on
* the OpenGL thread, typically in [GLSurfaceView.Renderer.onSurfaceCreated].
*
* @param context Needed to access shader source.
*/
@JvmOverloads
@Throws(IOException::class)
fun createOnGlThread(context: Context?, depthTextureId: Int = /*depthTextureId=*/-1) {
// Generate the background texture.
val textures = IntArray(1)
GLES20.glGenTextures(1, textures, 0)
textureId = textures[0]
val textureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES
GLES20.glBindTexture(textureTarget, textureId)
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE)
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE)
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR)
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR)
val numVertices = 4
if (numVertices != QUAD_COORDS.size / COORDS_PER_VERTEX) {
throw RuntimeException("Unexpected number of vertices in BackgroundRenderer.")
}
val bbCoords = ByteBuffer.allocateDirect(QUAD_COORDS.size * FLOAT_SIZE)
bbCoords.order(ByteOrder.nativeOrder())
quadCoords = bbCoords.asFloatBuffer()
quadCoords.put(QUAD_COORDS)
quadCoords.position(0)
val bbTexCoordsTransformed =
ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE)
bbTexCoordsTransformed.order(ByteOrder.nativeOrder())
quadTexCoords = bbTexCoordsTransformed.asFloatBuffer()
// Load render camera feed shader.
run {
val vertexShader = ShaderUtil.loadGLShader(
TAG,
context,
GLES20.GL_VERTEX_SHADER,
CAMERA_VERTEX_SHADER_NAME
)
val fragmentShader = ShaderUtil.loadGLShader(
TAG,
context,
GLES20.GL_FRAGMENT_SHADER,
CAMERA_FRAGMENT_SHADER_NAME
)
cameraProgram = GLES20.glCreateProgram()
GLES20.glAttachShader(cameraProgram, vertexShader)
GLES20.glAttachShader(cameraProgram, fragmentShader)
GLES20.glLinkProgram(cameraProgram)
GLES20.glUseProgram(cameraProgram)
cameraPositionAttrib = GLES20.glGetAttribLocation(cameraProgram, "a_Position")
cameraTexCoordAttrib = GLES20.glGetAttribLocation(cameraProgram, "a_TexCoord")
ShaderUtil.checkGLError(
TAG,
"Program creation"
)
cameraTextureUniform = GLES20.glGetUniformLocation(cameraProgram, "sTexture")
ShaderUtil.checkGLError(
TAG,
"Program parameters"
)
}
// Load render depth map shader.
run {
val vertexShader = ShaderUtil.loadGLShader(
TAG,
context,
GLES20.GL_VERTEX_SHADER,
DEPTH_VISUALIZER_VERTEX_SHADER_NAME
)
val fragmentShader = ShaderUtil.loadGLShader(
TAG,
context,
GLES20.GL_FRAGMENT_SHADER,
DEPTH_VISUALIZER_FRAGMENT_SHADER_NAME
)
depthProgram = GLES20.glCreateProgram()
GLES20.glAttachShader(depthProgram, vertexShader)
GLES20.glAttachShader(depthProgram, fragmentShader)
GLES20.glLinkProgram(depthProgram)
GLES20.glUseProgram(depthProgram)
depthPositionAttrib = GLES20.glGetAttribLocation(depthProgram, "a_Position")
depthTexCoordAttrib = GLES20.glGetAttribLocation(depthProgram, "a_TexCoord")
ShaderUtil.checkGLError(
TAG,
"Program creation"
)
depthTextureUniform = GLES20.glGetUniformLocation(depthProgram, "u_DepthTexture")
ShaderUtil.checkGLError(
TAG,
"Program parameters"
)
}
this.depthTextureId = depthTextureId
}
/**
* Draws the AR background image. The image will be drawn such that virtual content rendered with
* the matrices provided by [com.google.ar.core.Camera.getViewMatrix] and
* [com.google.ar.core.Camera.getProjectionMatrix] will
* accurately follow static physical objects. This must be called **before** drawing virtual
* content.
*
* @param frame The current `Frame` as returned by [Session.update].
* @param debugShowDepthMap Toggles whether to show the live camera feed or latest depth image.
*/
@JvmOverloads
fun draw(frame: Frame, debugShowDepthMap: Boolean = /*debugShowDepthMap=*/false) {
// If display rotation changed (also includes view size change), we need to re-query the uv
// coordinates for the screen rect, as they may have changed as well.
if (frame.hasDisplayGeometryChanged()) {
frame.transformCoordinates2d(
Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
quadCoords,
Coordinates2d.TEXTURE_NORMALIZED,
quadTexCoords
)
}
if (frame.timestamp == 0L && suppressTimestampZeroRendering) {
// Suppress rendering if the camera did not produce the first frame yet. This is to avoid
// drawing possible leftover data from previous sessions if the texture is reused.
return
}
draw(debugShowDepthMap)
}
/**
* Draws the camera background image using the currently configured [ ][BackgroundRenderer.quadTexCoords] image texture coordinates.
*/
private fun draw(debugShowDepthMap: Boolean) {
// Ensure position is rewound before use.
quadTexCoords!!.position(0)
// No need to test or write depth, the screen quad has arbitrary depth, and is expected
// to be drawn first.
GLES20.glDisable(GLES20.GL_DEPTH_TEST)
GLES20.glDepthMask(false)
GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
if (debugShowDepthMap) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, depthTextureId)
GLES20.glUseProgram(depthProgram)
GLES20.glUniform1i(depthTextureUniform, 0)
// Set the vertex positions and texture coordinates.
GLES20.glVertexAttribPointer(
depthPositionAttrib, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadCoords
)
GLES20.glVertexAttribPointer(
depthTexCoordAttrib, TEXCOORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadTexCoords
)
GLES20.glEnableVertexAttribArray(depthPositionAttrib)
GLES20.glEnableVertexAttribArray(depthTexCoordAttrib)
} else {
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId)
GLES20.glUseProgram(cameraProgram)
GLES20.glUniform1i(cameraTextureUniform, 0)
// Set the vertex positions and texture coordinates.
GLES20.glVertexAttribPointer(
cameraPositionAttrib, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadCoords
)
GLES20.glVertexAttribPointer(
cameraTexCoordAttrib, TEXCOORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadTexCoords
)
GLES20.glEnableVertexAttribArray(cameraPositionAttrib)
GLES20.glEnableVertexAttribArray(cameraTexCoordAttrib)
}
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
// Disable vertex arrays
if (debugShowDepthMap) {
GLES20.glDisableVertexAttribArray(depthPositionAttrib)
GLES20.glDisableVertexAttribArray(depthTexCoordAttrib)
} else {
GLES20.glDisableVertexAttribArray(cameraPositionAttrib)
GLES20.glDisableVertexAttribArray(cameraTexCoordAttrib)
}
// Restore the depth state for further drawing.
GLES20.glDepthMask(true)
GLES20.glEnable(GLES20.GL_DEPTH_TEST)
ShaderUtil.checkGLError(TAG, "BackgroundRendererDraw")
}
companion object {
private val TAG = BackgroundRenderer::class.java.simpleName
// Shader names.
private const val CAMERA_VERTEX_SHADER_NAME = "shaders/screenquad.vert"
private const val CAMERA_FRAGMENT_SHADER_NAME = "shaders/screenquad.frag"
private const val DEPTH_VISUALIZER_VERTEX_SHADER_NAME =
"shaders/background_show_depth_color_visualization.vert"
private const val DEPTH_VISUALIZER_FRAGMENT_SHADER_NAME =
"shaders/background_show_depth_color_visualization.frag"
private const val COORDS_PER_VERTEX = 2
private const val TEXCOORDS_PER_VERTEX = 2
private const val FLOAT_SIZE = 4
/**
* (-1, 1) ------- (1, 1)
* | \ |
* | \ |
* | \ |
* | \ |
* (-1, -1) ------ (1, -1)
* Ensure triangles are front-facing, to support glCullFace().
* This quad will be drawn using GL_TRIANGLE_STRIP which draws two
* triangles: v0->v1->v2, then v2->v1->v3.
*/
private val QUAD_COORDS = floatArrayOf(
-1.0f, -1.0f, +1.0f, -1.0f, -1.0f, +1.0f, +1.0f, +1.0f
)
}
}