Как обновить буфер OpenGL? - PullRequest
0 голосов
/ 02 июня 2019

Цель - мне нужно получать новый буфер каждые 32 миллисекунды.

Я взял в качестве базового проекта этот пример:

https://github.com/google-ar/arcore-android-sdk/tree/master/samples/hello_ar_java

Я внес изменения в проектпотому что мне нужно обновлять AR object data каждые 32 миллисекунды.

, поэтому у меня есть такой метод в моем представлении

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
    GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);

    // Prepare the rendering objects. This involves reading shaders, so may throw an IOException.
    try
    {
        // Create the texture and pass it to ARCore session to be filled during update().
        backgroundRenderer.createOnGlThread(/*context=*/ this);
        planeRenderer.createOnGlThread(/*context=*/ this, "models/trigrid.png");
        pointCloudRenderer.createOnGlThread(/*context=*/ this);

        virtualObject.setObjAssetName("models/andy.obj");
        virtualObject.setDiffuseTextureAssetName("models/andy.png");
        virtualObject.setContext(this);
        virtualObject.setMaterialProperties(0.0f, 2.0f, 0.5f, 6.0f);
        virtualObject.configure();
    }
    catch (IOException e)
    {
        Log.e(TAG, "Failed to read an asset file", e);
    }
}

И этот

@Override
public void onDrawFrame(GL10 gl)
{
    // Clear screen to notify driver it should not load any pixels from previous frame.
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

    if (session == null)
    {
        return;
    }
    // Notify ARCore session that the view size changed so that the perspective matrix and
    // the video background can be properly adjusted.
    displayRotationHelper.updateSessionIfNeeded(session);

    try
    {
        session.setCameraTextureName(backgroundRenderer.getTextureId());

        // Obtain the current frame from ARSession. When the configuration is set to
        // UpdateMode.BLOCKING (it is by default), this will throttle the rendering to the
        // camera framerate.
        Frame frame = session.update();
        Camera camera = frame.getCamera();

        // Handle one tap per frame.
        handleTap(frame, camera);

        // If frame is ready, render camera preview image to the GL surface.
        backgroundRenderer.draw(frame);

        // If not tracking, don't draw 3D objects, show tracking failure reason instead.
        if (camera.getTrackingState() == TrackingState.PAUSED)
        {
            messageSnackbarHelper.showMessage(this, TrackingStateHelper.getTrackingFailureReasonString(camera));
            return;
        }

        // Get projection matrix.
        float[] projmtx = new float[16];
        camera.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f);

        // Get camera matrix and draw.
        float[] viewmtx = new float[16];
        camera.getViewMatrix(viewmtx, 0);

        // Compute lighting from average intensity of the image.
        // The first three components are color scaling factors.
        // The last one is the average pixel intensity in gamma space.
        final float[] colorCorrectionRgba = new float[4];
        frame.getLightEstimate().getColorCorrection(colorCorrectionRgba, 0);

        // Visualize tracked points.
        // Use try-with-resources to automatically release the point cloud.
        try (PointCloud pointCloud = frame.acquirePointCloud())
        {
            pointCloudRenderer.update(pointCloud);
            pointCloudRenderer.draw(viewmtx, projmtx);
        }

        // No tracking error at this point. If we detected any plane, then hide the
        // message UI, otherwise show searchingPlane message.
        if (hasTrackingPlane())
        {
            messageSnackbarHelper.hide(this);
        }
        else
        {
            messageSnackbarHelper.showMessage(this, SEARCHING_PLANE_MESSAGE);
        }

        // Visualize planes.
        planeRenderer.drawPlanes(session.getAllTrackables(Plane.class), camera.getDisplayOrientedPose(), projmtx);

        // Visualize anchors created by touch.
        float scaleFactor = 1.0f;
        for (ColoredAnchor coloredAnchor : anchors)
        {
            if (coloredAnchor.anchor.getTrackingState() != TrackingState.TRACKING)
            {
                continue;
            }
            // Get the current pose of an Anchor in world space. The Anchor pose is updated
            // during calls to session.update() as ARCore refines its estimate of the world.
            coloredAnchor.anchor.getPose().toMatrix(anchorMatrix, 0);

            // Update and draw the model and its shadow.
            virtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
            virtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, coloredAnchor.color);
        }

    }
    catch (Throwable t)
    {
        // Avoid crashing the application due to unhandled exceptions.
        Log.e(TAG, "Exception on the OpenGL thread", t);
    }
}

Ноэто не очень интересная, интересная часть, расположенная в ObjectRendered

Там у нас есть метод configure(), который мы вызываем один раз из View из onSurfaceCreated, и здесь мы просто настраиваем объект

public void configure() throws IOException
{
    vertexShader = ShaderUtil.loadGLShader(TAG, mContext, GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_NAME);
    fragmentShader = ShaderUtil.loadGLShader(TAG, mContext, GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_NAME);

    program = GLES20.glCreateProgram();
    GLES20.glAttachShader(program, vertexShader);
    GLES20.glAttachShader(program, fragmentShader);
    GLES20.glLinkProgram(program);
    GLES20.glUseProgram(program);

    ShaderUtil.checkGLError(TAG, "Program creation");

    modelViewUniform = GLES20.glGetUniformLocation(program, "u_ModelView");
    modelViewProjectionUniform = GLES20.glGetUniformLocation(program, "u_ModelViewProjection");

    positionAttribute = GLES20.glGetAttribLocation(program, "a_Position");
    normalAttribute = GLES20.glGetAttribLocation(program, "a_Normal");
    texCoordAttribute = GLES20.glGetAttribLocation(program, "a_TexCoord");

    textureUniform = GLES20.glGetUniformLocation(program, "u_Texture");

    lightingParametersUniform = GLES20.glGetUniformLocation(program, "u_LightingParameters");
    materialParametersUniform = GLES20.glGetUniformLocation(program, "u_MaterialParameters");
    colorCorrectionParameterUniform = GLES20.glGetUniformLocation(program, "u_ColorCorrectionParameters");
    colorUniform = GLES20.glGetUniformLocation(program, "u_ObjColor");

    ShaderUtil.checkGLError(TAG, "Program parameters");

    // Read the texture.
    Bitmap textureBitmap = BitmapFactory.decodeStream(mContext.getAssets().open(mDiffuseTextureAssetName));

    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glGenTextures(textures.length, textures, 0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);

    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, textureBitmap, 0);
    GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);

    textureBitmap.recycle();

    ShaderUtil.checkGLError(TAG, "Texture loading");

    // Read the obj file.
    InputStream objInputStream = mContext.getAssets().open(mObjAssetName);
    obj = ObjReader.read(objInputStream);

    // Prepare the Obj so that its structure is suitable for
    // rendering with OpenGL:
    // 1. Triangulate it
    // 2. Make sure that texture coordinates are not ambiguous
    // 3. Make sure that normals are not ambiguous
    // 4. Convert it to single-indexed data
    obj = ObjUtils.convertToRenderable(obj);
}

Также здесь у нас есть метод obtainARObjectData

public void obtainARObjectData()
{
    // OpenGL does not use Java arrays. ByteBuffers are used instead to provide data in a format
    // that OpenGL understands.
    // Obtain the data from the OBJ, as direct buffers:
    IntBuffer wideIndices = ObjData.getFaceVertexIndices(obj, 3);
    FloatBuffer vertices = ObjData.getVertices(obj);
    FloatBuffer texCoords = ObjData.getTexCoords(obj, 2);
    //      FloatBuffer normals = ObjData.getNormals(obj);

    // Convert int indices to shorts for GL ES 2.0 compatibility
    ShortBuffer indices = ByteBuffer.allocateDirect(2 * wideIndices.limit()).order(ByteOrder.nativeOrder()).asShortBuffer();
    while (wideIndices.hasRemaining())
    {
        indices.put((short) wideIndices.get());
    }
    indices.rewind();

    int[] buffers = new int[2];
    GLES20.glGenBuffers(2, buffers, 0);
    vertexBufferId = buffers[0];
    indexBufferId = buffers[1];

    // Load vertex buffer
    verticesBaseAddress = 0;
    texCoordsBaseAddress = verticesBaseAddress + 4 * vertices.limit();
    normalsBaseAddress = texCoordsBaseAddress + 4 * texCoords.limit();
    final int totalBytes = normalsBaseAddress; //* normals.limit();

    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId);
    GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, totalBytes, null, GLES20.GL_STATIC_DRAW);
    GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, verticesBaseAddress, 4 * vertices.limit(), vertices);
    GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, texCoordsBaseAddress, 4 * texCoords.limit(), texCoords);
    //      GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, normalsBaseAddress, 4 * normals.limit(), normals);
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

    // Load index buffer
    GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBufferId);
    indexCount = indices.limit();
    GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, 2 * indexCount, indices, GLES20.GL_STATIC_DRAW);
    GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);

    ShaderUtil.checkGLError(TAG, "OBJ buffer load");

    Matrix.setIdentityM(modelMatrix, 0);
}

Также метод draw()

public void draw(float[] cameraView, float[] cameraPerspective, float[] colorCorrectionRgba, float[] objColor)
{
    requestRefreshARData();

    ShaderUtil.checkGLError(TAG, "Before draw");

    // Build the ModelView and ModelViewProjection matrices
    // for calculating object position and light.
    Matrix.multiplyMM(modelViewMatrix, 0, cameraView, 0, modelMatrix, 0);
    Matrix.multiplyMM(modelViewProjectionMatrix, 0, cameraPerspective, 0, modelViewMatrix, 0);

    GLES20.glUseProgram(program);
    ShaderUtil.checkGLError(TAG, "Before draw");

    // Set the lighting environment properties.
    GLES20.glUniform4fv(colorCorrectionParameterUniform, 1, colorCorrectionRgba, 0);

    // Set the object color property.
    GLES20.glUniform4fv(colorUniform, 1, objColor, 0);

    // Set the object material properties.
    GLES20.glUniform4f(materialParametersUniform, ambient, diffuse, specular, specularPower);

    // Attach the object texture.
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
    GLES20.glUniform1i(textureUniform, 0);

    // Set the vertex attributes.
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId);

    GLES20.glVertexAttribPointer(positionAttribute, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, verticesBaseAddress);
    GLES20.glVertexAttribPointer(normalAttribute, 3, GLES20.GL_FLOAT, false, 0, normalsBaseAddress);
    GLES20.glVertexAttribPointer(texCoordAttribute, 2, GLES20.GL_FLOAT, false, 0, texCoordsBaseAddress);

    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

    // Set the ModelViewProjection matrix in the shader.
    GLES20.glUniformMatrix4fv(modelViewUniform, 1, false, modelViewMatrix, 0);
    GLES20.glUniformMatrix4fv(modelViewProjectionUniform, 1, false, modelViewProjectionMatrix, 0);

    // Enable vertex arrays
    GLES20.glEnableVertexAttribArray(positionAttribute);
    GLES20.glEnableVertexAttribArray(normalAttribute);
    GLES20.glEnableVertexAttribArray(texCoordAttribute);

    GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBufferId);
    GLES20.glDrawElements(GLES20.GL_TRIANGLES, indexCount, GLES20.GL_UNSIGNED_SHORT, 0);
    GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);

    ShaderUtil.checkGLError(TAG, "After draw");
}

Итак, в конце у нас есть самый интересный метод requestRefreshARData() что мы вызываем из предыдущего draw().

, если я напишу этот метод следующим образом

private boolean mFlag = true;

private void requestRefreshARData()
{
    if (mFlag)
    {
        obtainARObjectData();
        mFlag = false;
    }
}

Все работает, но на самом деле мы обновляем данные объекта только один раз при первом вызовеметода рисования, тогда как нам нужно обновлять данные объекта каждые 32 миллисекунды.

Итак, просто для теста я добавил такую ​​реализацию этого метода

private boolean mFlag = true;
private Handler mHandler = new Handler(Looper.getMainLooper());

private void requestRefreshARData()
{
    if (mFlag)
    {
        obtainARObjectData();
        mFlag = false;

        mHandler.postDelayed(new Runnable()
        {
            @Override
            public void run()
            {
                mFlag = true;
            }
        }, 32);
    }
}

и теперь у меня есть такой результат t

https://youtu.be/9uATexWFzNU

Что я ожидаю?Я ожидаю, что каждые 32 миллисекунды мой объект AR будет обновляться (я знаю, что этот объект времени будет обновляться с теми же данными, я изменю набор данных в последнее время), и пользователь сможет видеть его на экране, не щелкая.

Я провел большой поиск в Google и понял, что обновление данных в другом потоке не является хорошим решением из-за контекста OpenGL ... Кроме того, openGL имеет один буфер, и теперь, когда я пытаюсь обновить буфер ирисовать объект на экране У меня есть этот эффект стряхивания (из-за обновления буфера тяжело для OpenGL)

Что я делаю не так?Почему объект щелкает?

...