我目前正在使用Android上的GL ES 2.0渲染相机预览到SurfaceTexture,使用opengl渲染它,然后将其传输到媒体编解码器的输入表面以进行录制。它以表面视图显示给用户,通过设置表面视图的纵横比,相机预览不会因屏幕尺寸而失真。
录制是纵向的,但在某些时候,传入的纹理将开始以横向方式显示,此时我想缩小并将其显示为拉伸宽度的“电影”以适应屏幕边缘在顶部和底部使用黑条进行横向分析,以保持纹理的纵横比。
drawing code in onDrawFrame is pretty simple。该链接具有着色器等的其余设置代码,但它只是设置一个三角形条带来绘制。
private final float[] mTriangleVerticesData = {
// X, Y, Z, U, V
-1.f, -1.f, 0, 0.f, 0.f,
1.f, -1.f, 0, 1.f, 0.f,
-1.f, 1.f, 0, 0.f, 1.f,
1.f, 1.f, 0, 1.f, 1.f,
};
public static final String VERTEX_SHADER =
"uniform mat4 uMVPMatrix;\n" +
"uniform mat4 uSTMatrix;\n" +
"attribute vec4 aPosition;\n" +
"attribute vec4 aTextureCoord;\n" +
"varying vec2 vTextureCoord;\n" +
"void main() {\n" +
" gl_Position = uMVPMatrix * aPosition;\n" +
" vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
"}\n";
private float[] mMVPMatrix = new float[16];
private float[] mSTMatrix = new float[16];
public TextureManager() {
mTriangleVertices = ByteBuffer.allocateDirect(
mTriangleVerticesData.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTriangleVertices.put(mTriangleVerticesData).position(0);
mTriangleHalfVertices = ByteBuffer.allocateDirect(
mTriangleVerticesHalfData.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTriangleHalfVertices.put(mTriangleVerticesHalfData).position(0);
Matrix.setIdentityM(mSTMatrix, 0);
}
onDrawFrame(){
mSurfaceTexture.getTransformMatrix(mSTMatrix);
GLES20.glUseProgram(mProgram);
checkGlError("glUseProgram");
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
checkGlError("glVertexAttribPointer maPosition");
GLES20.glEnableVertexAttribArray(maPositionHandle);
checkGlError("glEnableVertexAttribArray maPositionHandle");
mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
checkGlError("glVertexAttribPointer maTextureHandle");
GLES20.glEnableVertexAttribArray(maTextureHandle);
checkGlError("glEnableVertexAttribArray maTextureHandle");
Matrix.setIdentityM(mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
checkGlError("glDrawArrays");
GLES20.glFinish();`
}
我尝试过的东西还没有完全奏效:缩放mMVPMatrix或mSTMatrix进行放大。我可以放大,这样我就可以让景观视频的“中心切片”无失真地显示,但这会中断占视频的40%,这不是一个很好的解决方案。通过缩放这些基质来缩小,导致纹理重复边缘上的像素,因为夹具到边缘的行为。
将mTriangleVerticesData的x,y,z部分减半会给出所需行为的一些,如下面的屏幕截图所示,除了精确的宽高比。正如预期的那样,图片的中心部分减半并居中。然而,纹理重复到左,右和底部,并且左上方有变形。我想要的就是它的中心,黑色/没有任何东西围绕着它。
我可以缩小然后转换mMVPMatrix或mSTMatrix,然后更改我的着色器以显示黑色(0,1)以外的任何内容但最终我想要将多个纹理叠加在一起,如全尺寸背景和部分尺寸前景纹理。要做到这一点,我必须最终弄清楚如何只在可用空间的一部分中显示纹理,而不仅仅是操纵纹理,使其看起来就像正在发生的那样。
感谢您阅读所有内容。任何帮助,建议或猜测都是值得赞赏的。
答案 0 :(得分:1)
看起来你正在寻找的是一个“适合”的系统来获得元素的正确框架。这意味着您有一个100x200
图片,并且您希望将其显示在50x50
的框架中。结果应该是在矩形(25, 0, 25, 50)
中看到整个图像。因此,生成的帧必须遵守图像比率(25/50 = 100/200
),并且必须遵守原始帧边界。
为实现此目的,您需要比较图像比率和目标帧率:imageRatio = imageWidth/imageHeight
和frameRatio = frameWidth/frameHeight
。然后,如果图像比率大于框架比率,则意味着您需要在顶部和底部设置黑色边框,而如果框架比率较大,则您将在左侧和右侧看到黑色边框。
所以要计算目标框架:
imageRatio = imageWidth/imageHeight
frameRatio = frameWidth/frameHeight
if(imageRatio > frameRatio) {
targetFrame = {0, (frameHeight-frameWidth/imageRatio)*.5, frameWidth, frameWidth/imageRatio} // frame as: {x, y, width, height}
}
else {
targetFrame = {(frameWidth-frameHeight*imageRatio)*.5, 0, frameHeight*imageRatio, frameHeight} // frame as: {x, y, width, height}
}
在您的情况下,图像宽度和高度是从流中接收的图像;框架宽度和高度来自您的目标框架,这似乎是矩阵的结果,但对于全屏幕情况,如果您使用它,它只是来自glOrtho的值。然后应该使用目标框架来构造顶点位置,以便获得完全正确的顶点数据以显示完整的纹理。
我看到你使用矩阵来完成你的所有计算,并且可以使用相同的算法转换为矩阵,但我不鼓励你这样做。您似乎过度滥用矩阵,这使得您的代码完全无法维护。我建议在你的情况下你保持“ortho”投影矩阵,使用框架绘制纹理,只使用矩阵比例和翻译,这是有意义的。
答案 1 :(得分:1)
重复的图像块看起来像GPU平铺伪像,而不是纹理重复。添加glClear()
电话以清除背景。