Android OpenGLES中保持宽高比将CameraX纹理Upscale至视口的技术问题
在GLSurfaceView着色器中保持几何体尺寸不变,实现相机纹理的宽高比适配Upscale
解决思路:不缩放几何体顶点,而是通过调整纹理UV坐标实现相机帧的宽高比适配,同时让几何体保持全屏尺寸。
1. Java层计算UV变换参数
在渲染循环中,根据纹理和视口的宽高比计算UV的缩放因子与偏移量:
float imgAspect = (float) cameraFrameTexture.getWidth() / cameraFrameTexture.getHeight(); float viewAspect = (float) GLRenderer.rendererWidth / GLRenderer.rendererHeight; float scaleUVX = 1.0f; float scaleUVY = 1.0f; float offsetUVX = 0.0f; float offsetUVY = 0.0f; if (imgAspect > viewAspect) { // 图像宽于视口:垂直方向缩放,上下留黑居中 scaleUVY = viewAspect / imgAspect; offsetUVY = (1.0f - scaleUVY) / 2.0f; } else { // 图像高于视口:水平方向缩放,左右留黑居中 scaleUVX = imgAspect / viewAspect; offsetUVX = (1.0f - scaleUVX) / 2.0f; } // 传递参数到着色器 int scaleLoc = GLES20.glGetUniformLocation(yourProgramId, "uScaleUV"); int offsetLoc = GLES20.glGetUniformLocation(yourProgramId, "uOffsetUV"); GLES20.glUniform2f(scaleLoc, scaleUVX, scaleUVY); GLES20.glUniform2f(offsetLoc, offsetUVX, offsetUVY);
2. 顶点着色器(仅处理UV变换)
保持顶点位置不变,只对UV坐标做适配变换,确保纹理采样区域正确:
uniform mat4 uMVPMatrix; uniform vec2 uScaleUV; uniform vec2 uOffsetUV; attribute vec3 aPosition; attribute vec2 aTexCoords; varying vec2 vTexCoords; void main() { // 几何体顶点位置保持不变,依然占满视口 gl_Position = uMVPMatrix * vec4(aPosition.xyz, 1.0); // 计算适配后的UV坐标 vTexCoords = aTexCoords * uScaleUV + uOffsetUV; }
3. 片段着色器(正常采样)
使用调整后的UV坐标采样纹理即可:
precision mediump float; uniform sampler2D uCameraTexture; varying vec2 vTexCoords; void main() { gl_FragColor = texture2D(uCameraTexture, vTexCoords); }
重要说明
- 原方案问题:你之前的代码是缩放顶点位置,导致几何体缩小来适配纹理比例;新方案保持几何体全屏,通过调整纹理采样范围实现比例适配,完全满足需求。
- 多着色器适配:该UV变换逻辑独立于顶点变换矩阵,其他需要相机纹理的着色器只需传入相同的
uScaleUV和uOffsetUV参数即可,不会干扰原有矩阵功能。 - 性能优势:GPU层面的UV变换是并行处理,完全适配实时相机流的性能要求,避免了Bitmap/Canvas方案的CPU耗时问题。
内容的提问来源于stack exchange,提问作者Ashish11
相关产品推荐
相关产品推荐

