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

Flutter集成Vuforia SDK:跟踪第二个ImageTarget时关联对象消失问题

问题分析与解决方案

你的核心问题是:当前代码仅渲染当前帧跟踪到的单个ImageTarget,没有维护已跟踪目标的状态,导致切换跟踪目标时,之前的AR对象停止渲染。要实现多目标持续显示,需要保存所有已成功跟踪过的目标的位置矩阵,并在每一帧中遍历渲染所有目标。

关键问题点

  1. getImageTargetResult仅返回单个跟踪目标,没有收集所有已跟踪目标的信息
  2. renderFrame中只调用一次renderImageTarget,仅渲染当前最新跟踪的目标
  3. renderRectangle每次调用都会重新创建着色器,造成性能浪费

解决方案步骤

1. 新增已跟踪目标的状态存储结构

在Controller类中添加数据结构,保存所有已跟踪目标的名称和矩阵信息:

// 定义跟踪目标结构体
struct TrackedTarget {
    std::string name;
    VuMatrix44F projectionMatrix;
    VuMatrix44F modelViewMatrix;
    VuMatrix44F scaledModelViewMatrix;
};

// 维护已跟踪目标列表
std::vector<TrackedTarget> mTrackedTargets;

2. 更新目标跟踪逻辑,收集所有已跟踪目标

替换原getImageTargetResult方法,改为批量更新已跟踪目标列表:

void Controller::updateTrackedTargets() {
    VuTrackableList trackableList;
    vuStateGetTrackableList(getVuState(), &trackableList);

    for (int i = 0; i < vuTrackableListGetSize(&trackableList); ++i) {
        VuTrackable* trackable = vuTrackableListGetElement(&trackableList, i);
        VuTrackableType type;
        vuTrackableGetType(trackable, &type);

        if (type == VU_TRACKABLE_IMAGE_TARGET) {
            VuImageTarget* imageTarget = static_cast<VuImageTarget*>(trackable);
            const char* targetName = vuImageTargetGetName(imageTarget);
            
            VuTrackableStatus status;
            vuTrackableGetStatus(trackable, &status);

            // 仅处理正在跟踪的目标
            if (status == VU_TRACKABLE_STATUS_TRACKED) {
                VuMatrix44F projectionMatrix, modelViewMatrix, scaledModelViewMatrix;
                vuTrackableGetProjectionMatrix(trackable, &projectionMatrix);
                vuTrackableGetModelViewMatrix(trackable, &modelViewMatrix);

                // 复用原缩放逻辑
                VuVector3F scaleVec{0.3f, 0.3f, 1.0f};
                scaledModelViewMatrix = vuMatrix44FScale(scaleVec, modelViewMatrix);
                VuVector3F positionVec{0.0f, 0.0f, 0.0f};
                scaledModelViewMatrix = vuMatrix44FTranslate(positionVec, scaledModelViewMatrix);

                // 更新或添加目标到列表
                bool targetExists = false;
                for (auto& existingTarget : mTrackedTargets) {
                    if (existingTarget.name == targetName) {
                        existingTarget.projectionMatrix = projectionMatrix;
                        existingTarget.modelViewMatrix = modelViewMatrix;
                        existingTarget.scaledModelViewMatrix = scaledModelViewMatrix;
                        targetExists = true;
                        break;
                    }
                }
                if (!targetExists) {
                    mTrackedTargets.push_back({targetName, projectionMatrix, modelViewMatrix, scaledModelViewMatrix});
                }
            }
        }
    }
    vuTrackableListDestroy(&trackableList);
}

// 提供获取目标列表的方法
const std::vector<TrackedTarget>& Controller::getTrackedTargets() const {
    return mTrackedTargets;
}

3. 修改渲染逻辑,遍历渲染所有已跟踪目标

更新renderFrame方法,批量渲染所有保存的目标:

JNIEXPORT jboolean JNICALL
Java_com_smartboom_arengine_flutter_1smartboom_1ar_1engine_engine_vuforia_VuforiaARView_renderFrame(JNIEnv* /* env */, jobject /* this */)
{
    if (!controller.isARStarted())
    {
        return JNI_FALSE;
    }

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    int vbTextureUnit = 0;
    VuRenderVideoBackgroundData renderVideoBackgroundData;
    renderVideoBackgroundData.renderData = nullptr;
    renderVideoBackgroundData.textureData = nullptr;
    renderVideoBackgroundData.textureUnitData = &vbTextureUnit;
    double viewport[6];
    
    if (controller.prepareToRender(viewport, &renderVideoBackgroundData))
    {
        glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
        auto renderState = controller.getRenderState();

        gWrapperData.renderer.renderVideoBackground(
                renderState.vbProjectionMatrix, renderState.vbMesh->pos, renderState.vbMesh->tex,
                renderState.vbMesh->numFaces, renderState.vbMesh->faceIndices, vbTextureUnit);

        // 更新已跟踪目标列表
        controller.updateTrackedTargets();
        
        // 遍历渲染所有已跟踪目标
        const auto& trackedTargets = controller.getTrackedTargets();
        for (const auto& target : trackedTargets) {
            gWrapperData.renderer.renderImageTarget(target.projectionMatrix, target.modelViewMatrix, target.scaledModelViewMatrix, target.name.c_str());
        }

        if (gWrapperData.usingARCore)
        {
            accessFusionProviderPointers();
        }
    }
    controller.finishRender();
    return JNI_TRUE;
}

4. 优化着色器创建逻辑,避免重复创建

修改GLESRenderer类,缓存矩形着色器程序,避免每次渲染都重新编译:

class GLESRenderer {
private:
    GLuint mRectangleShaderProgramID = 0;
    // ...其他成员变量
};

GLuint GLESRenderer::getRectangleShader() {
    if (mRectangleShaderProgramID == 0) {
        // 原createRectangleShader的完整逻辑
        const char* vertexShaderSource =
                "attribute vec4 a_Position;\n"
                "attribute vec2 a_TexCoord;\n"
                "varying vec2 v_TexCoord;\n"
                "uniform mat4 u_MVPMatrix;\n"
                "void main() {\n"
                "  gl_Position = u_MVPMatrix * a_Position;\n"
                "  v_TexCoord = a_TexCoord;\n"
                "}\n";

        const char* fragmentShaderSource =
                "precision mediump float;\n"
                "varying vec2 v_TexCoord;\n"
                "uniform sampler2D u_Texture;\n"
                "void main() {\n"
                "  gl_FragColor = texture2D(u_Texture, v_TexCoord);\n"
                "}\n";

        GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
        GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

        glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
        glCompileShader(vertexShader);

        glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
        glCompileShader(fragmentShader);

        mRectangleShaderProgramID = glCreateProgram();
        glAttachShader(mRectangleShaderProgramID, vertexShader);
        glAttachShader(mRectangleShaderProgramID, fragmentShader);
        glLinkProgram(mRectangleShaderProgramID);

        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);
    }
    return mRectangleShaderProgramID;
}

// 修改renderRectangle中的着色器获取方式
void GLESRenderer::renderRectangle(const VuMatrix44F& projectionMatrix, const VuMatrix44F& modelViewMatrix, GLuint textureId) {
    // ...其他代码
    GLuint shaderProgramID = getRectangleShader();
    // ...其他代码
}

效果说明

修改后,每帧会自动收集所有正在跟踪的ImageTarget,已成功跟踪过的目标会被保留在列表中持续渲染,即使切换跟踪其他目标,之前的AR对象也会保持在原位置显示。

内容的提问来源于stack exchange,提问作者Ali Sheykhi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 16:09:49