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

求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. 关键注意事项

  1. GL线程安全:所有OpenGL相关操作必须在GL线程执行,通过GLSurfaceView.queueEvent()切换线程。
  2. 资源释放:在Flutter页面销毁时,必须调用原生的disposeRenderer方法释放SurfaceTexture、GLSurfaceView等资源,避免内存泄漏。
  3. 模型替换:把示例中的立方体数据替换成你自己的3D模型数据,如果你之前用C渲染,可以通过JNI调用C代码,只要确保渲染内容输出到绑定的SurfaceTexture即可。
  4. 缓冲区尺寸:根据你的需求调整setDefaultBufferSize的参数,确保渲染内容的比例和Flutter端Texture Widget的比例一致。

内容的提问来源于stack exchange,提问作者German Saprykin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:28:17