You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修复OpenGL视频动态壁纸滚动问题及添加随动背景

当前视频滚动逻辑问题与背景实现方案

一、当前视频滚动逻辑的问题分析

  1. maxXOffset/maxYOffset计算逻辑错误
    Canvas实现中,偏移范围基于「缩放后视频与屏幕的宽度差」,而OpenGL代码里的maxXOffset仅通过比例差值的半值计算,未对应到归一化坐标(-1到1)下的实际可滚动范围。当视频比例大于屏幕比例时,该计算无法准确映射视频超出屏幕的部分在归一化坐标中的占比,导致滚动时无法触达视频边缘。

  2. 矩阵变换顺序错误
    OpenGL矩阵变换为右乘逻辑,变换顺序直接影响最终结果。当前代码先缩放、再旋转、最后平移,但视频旋转后宽高比例会变化,后续的缩放和平移会基于错误的宽高计算偏移,导致滚动范围失效。正确顺序应为先旋转,再缩放,最后平移——先将视频转到正确方向,再根据屏幕比例适配缩放,最后应用偏移,确保旋转后的宽高比例被正确纳入计算。

  3. 偏移值的映射逻辑不匹配
    Canvas中偏移是currentxOffset * (屏幕宽度 - 缩放后视频宽度),直接对应像素级滚动范围;而OpenGL中直接将0.5f - xOffset作为偏移量,未将壁纸的偏移比例(0到1)转换为归一化坐标下的实际可滚动距离,导致滚动范围不足。

二、滚动问题的修复建议

  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()
    }
}
  1. 调整矩阵变换顺序
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)
}
  1. 修正偏移值传递逻辑
    将壁纸的偏移比例(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,1]的归一化坐标)。
  • 在渲染视频前,绑定背景着色器,传入与视频相同的MVP矩阵,绘制全屏四边形。
  1. 图片背景实现
  • 将背景图片加载为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 02:34:59