修复OpenGL视频动态壁纸滚动问题及添加随动背景
当前视频滚动逻辑问题与背景实现方案
一、当前视频滚动逻辑的问题分析
maxXOffset/maxYOffset计算逻辑错误
Canvas实现中,偏移范围基于「缩放后视频与屏幕的宽度差」,而OpenGL代码里的maxXOffset仅通过比例差值的半值计算,未对应到归一化坐标(-1到1)下的实际可滚动范围。当视频比例大于屏幕比例时,该计算无法准确映射视频超出屏幕的部分在归一化坐标中的占比,导致滚动时无法触达视频边缘。矩阵变换顺序错误
OpenGL矩阵变换为右乘逻辑,变换顺序直接影响最终结果。当前代码先缩放、再旋转、最后平移,但视频旋转后宽高比例会变化,后续的缩放和平移会基于错误的宽高计算偏移,导致滚动范围失效。正确顺序应为先旋转,再缩放,最后平移——先将视频转到正确方向,再根据屏幕比例适配缩放,最后应用偏移,确保旋转后的宽高比例被正确纳入计算。偏移值的映射逻辑不匹配
Canvas中偏移是currentxOffset * (屏幕宽度 - 缩放后视频宽度),直接对应像素级滚动范围;而OpenGL中直接将0.5f - xOffset作为偏移量,未将壁纸的偏移比例(0到1)转换为归一化坐标下的实际可滚动距离,导致滚动范围不足。
二、滚动问题的修复建议
- 修正
maxXOffset/maxYOffset计算
先处理视频旋转后的实际宽高,再计算归一化坐标下的可滚动范围:
override fun setScreenSize(width: Int, height: Int) { if (screenWidth != width || screenHeight != height) { screenWidth = width screenHeight = height // 计算旋转后的视频宽高 val rotatedVideoWidth = if (videoRotation % 180 == 0) videoWidth else videoHeight val rotatedVideoHeight = if (videoRotation % 180 == 0) videoHeight else videoWidth val videoRatio = rotatedVideoWidth.toFloat() / rotatedVideoHeight val screenRatio = screenWidth.toFloat() / screenHeight if (videoRatio > screenRatio) { // 视频比屏幕宽,计算X轴可滚动范围 val scale = screenHeight.toFloat() / rotatedVideoHeight val scaledVideoWidth = rotatedVideoWidth * scale maxXOffset = (scaledVideoWidth - screenWidth) / (2 * screenWidth) maxYOffset = 0f } else { // 视频比屏幕高,计算Y轴可滚动范围(如果需要) val scale = screenWidth.toFloat() / rotatedVideoWidth val scaledVideoHeight = rotatedVideoHeight * scale maxYOffset = (scaledVideoHeight - screenHeight) / (2 * screenHeight) maxXOffset = 0f } updateMatrix() } }
- 调整矩阵变换顺序
private fun updateMatrix() { Matrix.setIdentityM(mvp, 0) val rotatedVideoWidth = if (videoRotation % 180 == 0) videoWidth else videoHeight val rotatedVideoHeight = if (videoRotation % 180 == 0) videoHeight else videoWidth val videoRatio = rotatedVideoWidth.toFloat() / rotatedVideoHeight val screenRatio = screenWidth.toFloat() / screenHeight // 先旋转视频到正确方向 if (videoRotation % 360 != 0) { Matrix.rotateM(mvp, 0, -videoRotation.toFloat(), 0f, 0f, 1f) } // 再根据屏幕比例缩放 if (videoRatio >= screenRatio) { Matrix.scaleM(mvp, 0, screenHeight / rotatedVideoHeight / screenRatio, screenHeight / rotatedVideoHeight, 1f) } else { Matrix.scaleM(mvp, 0, screenWidth / rotatedVideoWidth, screenWidth / rotatedVideoWidth * screenRatio, 1f) } // 最后应用偏移 Matrix.translateM(mvp, 0, xOffset, yOffset, 0f) }
- 修正偏移值传递逻辑
将壁纸的偏移比例(0到1)转换为归一化坐标下的实际偏移量:
override fun onOffsetsChanged(xOffset: Float, yOffset: Float, xOffsetStep: Float, yOffsetStep: Float, xPixelOffset: Int, yPixelOffset: Int) { super.onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixelOffset, yPixelOffset) if (allowSlide && !isPreview) { // 把0→1的偏移比例转换为归一化坐标下的[-maxXOffset, maxXOffset] val normalizedXOffset = (xOffset - 0.5f) * 2 * maxXOffset renderer!!.setOffset(normalizedXOffset, 0f) } }
三、添加随滚动移动的背景色/图片
在OpenGL渲染流程中,先绘制背景,再绘制视频,确保背景在视频下方且变换同步:
- 纯色背景实现
- 编写简单的颜色着色器:顶点着色器传递全屏坐标,片段着色器输出固定颜色。
- 创建全屏四边形的顶点数据(覆盖[-1,-1]到[1,1]的归一化坐标)。
- 在渲染视频前,绑定背景着色器,传入与视频相同的MVP矩阵,绘制全屏四边形。
- 图片背景实现
- 将背景图片加载为OpenGL纹理,处理纹理坐标与屏幕比例的适配。
- 编写纹理着色器:顶点着色器传递位置和纹理坐标,片段着色器采样纹理。
- 绘制全屏四边形时使用与视频相同的MVP矩阵,保证背景随视频同步滚动;若需要不同滚动速度,可调整偏移量的比例。
核心渲染流程示例:
override fun onDrawFrame(gl: GL10?) { // 绘制背景 backgroundShader.use() backgroundShader.setMatrix(mvp) backgroundQuad.draw() // 绘制视频 videoShader.use() videoShader.setMatrix(mvp) videoTexture.bind() videoQuad.draw() }
内容的提问来源于stack exchange,提问作者android developer
相关产品推荐
相关产品推荐

