Android平台OpenGL ES如何实现触摸事件响应式相机旋转?
实现OpenGL ES基础相机触摸旋转控制
原Android官方示例的触摸旋转仅支持绕Z轴,拆分X/Y轴旋转时出现异常,核心问题是两次调用setRotateM会覆盖之前的矩阵,正确做法是通过矩阵乘法合并两次旋转,同时要注意触摸坐标与旋转轴的对应关系、旋转顺序。
错误原因分析
你当前的代码中:
Matrix.setRotateM(rotationMatrix, 0, angleX, 0f, 1f, 0f) Matrix.setRotateM(rotationMatrix, 0, angleY, 1f, 0f, 0f)
第二次setRotateM会完全替换第一次生成的旋转矩阵,导致只有Y轴旋转生效,X轴旋转被覆盖,所以拖动X轴时效果异常。
正确实现步骤
1. 定义旋转变量
在Renderer中维护两个旋转角度及临时计算矩阵:
class MyGLRenderer : GLSurfaceView.Renderer { var angleX = 0f // 绕X轴旋转角度(对应触摸Y轴拖动) var angleY = 0f // 绕Y轴旋转角度(对应触摸X轴拖动) private val rotationMatrix = FloatArray(16) private val tempMatrix = FloatArray(16) // 临时矩阵用于合并旋转 // 其他变量... }
2. 触摸事件处理
保持触摸坐标到旋转角度的映射,注意触摸Y轴移动对应绕X轴旋转,X轴移动对应绕Y轴旋转:
private const val TOUCH_SCALE_FACTOR = 0.1f override fun onTouchEvent(e: MotionEvent): Boolean { if (e.pointerCount == 1) { when (e.action) { MotionEvent.ACTION_MOVE -> { val dx = e.x - e.rawX val dy = e.y - e.rawY renderer.angleY += dx * TOUCH_SCALE_FACTOR renderer.angleX += dy * TOUCH_SCALE_FACTOR // 限制旋转角度,避免过度翻转(可选) renderer.angleX = renderer.angleX.coerceIn(-90f, 90f) } } } return true }
3. 绘制时合并旋转矩阵
使用Matrix.multiplyMM合并X轴和Y轴的旋转矩阵,而非覆盖:
override fun onDrawFrame(gl: GL10) { // 清除画布 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT or GLES20.GL_DEPTH_BUFFER_BIT) // 初始化单位矩阵 Matrix.setIdentityM(rotationMatrix, 0) // 先绕X轴旋转 Matrix.setRotateM(tempMatrix, 0, angleX, 1f, 0f, 0f) Matrix.multiplyMM(rotationMatrix, 0, rotationMatrix, 0, tempMatrix, 0) // 再绕Y轴旋转 Matrix.setRotateM(tempMatrix, 0, angleY, 0f, 1f, 0f) Matrix.multiplyMM(rotationMatrix, 0, rotationMatrix, 0, tempMatrix, 0) // 将旋转矩阵应用到视图/模型矩阵(根据你的相机设置调整) Matrix.multiplyMM(mVPMatrix, 0, mProjectionMatrix, 0, rotationMatrix, 0) // 绘制物体... }
4. 额外优化(可选)
- 用
ScaleGestureDetector添加双指缩放功能,模拟相机变焦 - 限制旋转角度范围,避免相机翻转到不合理视角
替代方案:使用高层框架
如果不想手动处理矩阵,Android上可以选择:
- Sceneform:类似iOS SceneKit的3D框架,支持一行代码开启相机控制
- Jetpack Compose 3D:最新的Compose 3D库,提供简洁的3D交互API
内容的提问来源于stack exchange,提问作者goofy4224
相关产品推荐
相关产品推荐

