求Flutter Android端OpenGL结合SurfaceTexture与Texture Widget实现示例
我刚好完成过类似的Android端实现,把OpenGL渲染的3D内容通过SurfaceTexture对接Flutter的Texture Widget,完全替代原生View的方案。下面给你一个完整的可运行示例,包含Flutter端和Android原生端的代码,还有关键要点的解释:
Android端OpenGL + SurfaceTexture 对接Flutter Texture Widget 实现指南
核心原理
Flutter的Texture Widget需要一个由Flutter Engine分配的TextureId,Android端通过SurfaceTexture将OpenGL的渲染内容输出到该纹理ID对应的目标上,最终由Flutter的Texture Widget展示。整个流程通过MethodChannel完成Flutter与原生的通信,包括初始化渲染器、更新旋转参数、释放资源等操作。
1. Flutter端实现
Flutter侧主要负责创建Texture Widget、通过MethodChannel与原生交互,以及控制3D模型的旋转:
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class OpenGL3DPage extends StatefulWidget { const OpenGL3DPage({super.key}); @override State<OpenGL3DPage> createState() => _OpenGL3DPageState(); } class _OpenGL3DPageState extends State<OpenGL3DPage> { int? _textureId; final MethodChannel _channel = const MethodChannel('opengl_3d_renderer'); double _rotationY = 0.0; @override void initState() { super.initState(); _initRenderer(); } Future<void> _initRenderer() async { // 调用原生方法初始化渲染器,获取TextureId final int textureId = await _channel.invokeMethod('initRenderer'); setState(() { _textureId = textureId; }); } void _updateRotation() { setState(() { _rotationY += 10; if (_rotationY >= 360) _rotationY = 0; }); // 把旋转参数传递给原生渲染器 _channel.invokeMethod('updateRotation', {'rotationY': _rotationY}); } @override void dispose() { // 释放原生渲染资源 _channel.invokeMethod('disposeRenderer'); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('OpenGL 3D Model')), body: Center( child: _textureId != null ? Texture(textureId: _textureId!) : const CircularProgressIndicator(), ), floatingActionButton: FloatingActionButton( onPressed: _updateRotation, child: const Icon(Icons.refresh), ), ); } }
2. Android端原生实现
2.1 OpenGL渲染器类
负责OpenGL的初始化、3D模型绘制和旋转参数更新(示例用立方体演示,你可以替换成自己的3D模型数据):
import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.opengl.Matrix; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; public class OpenGLRenderer implements GLSurfaceView.Renderer { private float[] mModelMatrix = new float[16]; private float[] mViewMatrix = new float[16]; private float[] mProjectionMatrix = new float[16]; private float[] mMVPMatrix = new float[16]; private int mProgram; private int mPositionHandle; private int mColorHandle; private int mMVPMatrixHandle; private float mRotationY = 0f; // 立方体顶点数据(替换为你的3D模型数据) private final float[] mCubeVertices = { // 前 -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, // 后、左、右、上、下顶点省略,完整代码见示例 }; private final float[] mCubeColors = { // 前表面红色 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, // 其他面颜色省略 }; private final short[] mCubeIndices = { 0, 1, 2, 0, 2, 3, // 前表面三角面 // 其他面索引省略 }; @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f); // 编译并链接着色器程序 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); mProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(mProgram, vertexShader); GLES20.glAttachShader(mProgram, fragmentShader); GLES20.glLinkProgram(mProgram); // 获取着色器句柄 mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); mColorHandle = GLES20.glGetAttribLocation(mProgram, "vColor"); mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); // 初始化视图矩阵 Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glViewport(0, 0, width, height); float ratio = (float) width / height; Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7); } @Override public void onDrawFrame(GL10 gl) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); GLES20.glEnable(GLES20.GL_DEPTH_TEST); GLES20.glUseProgram(mProgram); // 更新模型旋转矩阵 Matrix.setIdentityM(mModelMatrix, 0); Matrix.rotateM(mModelMatrix, 0, mRotationY, 0, 1, 0); // 计算MVP矩阵 Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0); Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0); // 传递顶点、颜色、MVP矩阵数据到着色器 GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, java.nio.FloatBuffer.wrap(mCubeVertices)); GLES20.glEnableVertexAttribArray(mPositionHandle); GLES20.glVertexAttribPointer(mColorHandle, 4, GLES20.GL_FLOAT, false, 0, java.nio.FloatBuffer.wrap(mCubeColors)); GLES20.glEnableVertexAttribArray(mColorHandle); GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0); // 绘制立方体 GLES20.glDrawElements(GLES20.GL_TRIANGLES, mCubeIndices.length, GLES20.GL_UNSIGNED_SHORT, java.nio.ShortBuffer.wrap(mCubeIndices)); GLES20.glDisableVertexAttribArray(mPositionHandle); GLES20.glDisableVertexAttribArray(mColorHandle); } public void setRotationY(float rotationY) { mRotationY = rotationY; } private int loadShader(int type, String shaderCode) { int shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; } // 顶点着色器代码 private final String vertexShaderCode = "attribute vec4 vPosition;" + "attribute vec4 vColor;" + "uniform mat4 uMVPMatrix;" + "varying vec4 _vColor;" + "void main() {" + " _vColor = vColor;" + " gl_Position = uMVPMatrix * vPosition;" + "}"; // 片段着色器代码 private final String fragmentShaderCode = "precision mediump float;" + "varying vec4 _vColor;" + "void main() {" + " gl_FragColor = _vColor;" + "}"; }
2.2 对接Flutter Texture的核心类
负责创建Flutter的SurfaceTextureEntry、管理GLSurfaceView、处理MethodChannel通信:
import android.content.Context; import android.opengl.GLSurfaceView; import android.view.SurfaceTexture; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.view.TextureRegistry; public class OpenGLTextureRenderer implements MethodChannel.MethodCallHandler, SurfaceTexture.OnFrameAvailableListener { private final Context mContext; private final TextureRegistry mTextureRegistry; private TextureRegistry.SurfaceTextureEntry mTextureEntry; private SurfaceTexture mSurfaceTexture; private GLSurfaceView mGLSurfaceView; private OpenGLRenderer mOpenGLRenderer; public OpenGLTextureRenderer(Context context, FlutterEngine flutterEngine) { mContext = context; mTextureRegistry = flutterEngine.getRenderer().getTextureRegistry(); // 注册MethodChannel,与Flutter通信 new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "opengl_3d_renderer").setMethodCallHandler(this); } @Override public void onMethodCall(MethodCall call, MethodChannel.Result result) { switch (call.method) { case "initRenderer": initRenderer(); result.success(mTextureEntry.id()); break; case "updateRotation": float rotationY = call.argument("rotationY"); if (mOpenGLRenderer != null) { mOpenGLRenderer.setRotationY(rotationY); } result.success(null); break; case "disposeRenderer": disposeRenderer(); result.success(null); break; default: result.notImplemented(); break; } } private void initRenderer() { // 创建Flutter的SurfaceTextureEntry,获取TextureId mTextureEntry = mTextureRegistry.createSurfaceTexture(); mSurfaceTexture = mTextureEntry.surfaceTexture(); mSurfaceTexture.setOnFrameAvailableListener(this); // 初始化GLSurfaceView mGLSurfaceView = new GLSurfaceView(mContext); mGLSurfaceView.setEGLContextClientVersion(2); mOpenGLRenderer = new OpenGLRenderer(); mGLSurfaceView.setRenderer(mOpenGLRenderer); mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); // 设置缓冲区尺寸,绑定SurfaceTexture到GL上下文 mSurfaceTexture.setDefaultBufferSize(1080, 1920); mGLSurfaceView.queueEvent(() -> { // OpenGL操作必须在GL线程执行 mSurfaceTexture.attachToGLContext(mTextureEntry.id()); }); } @Override public void onFrameAvailable(SurfaceTexture surfaceTexture) { // 通知Flutter有新帧可以渲染 mTextureEntry.markFrameAvailable(); } private void disposeRenderer() { // 释放资源,避免内存泄漏 if (mGLSurfaceView != null) { mGLSurfaceView.queueEvent(() -> { mSurfaceTexture.detachFromGLContext(); mOpenGLRenderer = null; }); mGLSurfaceView.onPause(); mGLSurfaceView = null; } if (mTextureEntry != null) { mTextureEntry.release(); mTextureEntry = null; } if (mSurfaceTexture != null) { mSurfaceTexture.release(); mSurfaceTexture = null; } } }
2.3 在MainActivity中初始化
import io.flutter.embedding.android.FlutterActivity; import io.flutter.embedding.engine.FlutterEngine; public class MainActivity extends FlutterActivity { @Override public void configureFlutterEngine(FlutterEngine flutterEngine) { super.configureFlutterEngine(flutterEngine); // 初始化OpenGL纹理渲染器 new OpenGLTextureRenderer(this, flutterEngine); } }
3. 关键注意事项
- GL线程安全:所有OpenGL相关操作必须在GL线程执行,通过
GLSurfaceView.queueEvent()切换线程。 - 资源释放:在Flutter页面销毁时,必须调用原生的
disposeRenderer方法释放SurfaceTexture、GLSurfaceView等资源,避免内存泄漏。 - 模型替换:把示例中的立方体数据替换成你自己的3D模型数据,如果你之前用C渲染,可以通过JNI调用C代码,只要确保渲染内容输出到绑定的SurfaceTexture即可。
- 缓冲区尺寸:根据你的需求调整
setDefaultBufferSize的参数,确保渲染内容的比例和Flutter端Texture Widget的比例一致。
内容的提问来源于stack exchange,提问作者German Saprykin
相关产品推荐
相关产品推荐

