当前位置: 首页 > 知识库问答 >
问题:

如何用openGL ES实现AR物体的移动和旋转?

古明煦
2023-03-14

我正在遵循谷歌教程。我想修改一下。我现在想做的是在屏幕上添加另一个AR对象(它将是一个箭头),并且我希望它总是显示在我的屏幕上,而不是像云锚一样附着在表面上。之后,我希望它指向云锚。我现在自己尝试了,但似乎离我想要的很远。有人能给我一些解决方案或我应该尝试哪种方式吗?谢谢

package com.google.ar.core.codelab.cloudanchor;
import android.app.AlertDialog;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.google.ar.core.Anchor;
import com.google.ar.core.ArCoreApk;
import com.google.ar.core.Camera;
import com.google.ar.core.Config;
import com.google.ar.core.Frame;
import com.google.ar.core.HitResult;
import com.google.ar.core.Plane;
import com.google.ar.core.Point;
import com.google.ar.core.Point.OrientationMode;
import com.google.ar.core.PointCloud;
import com.google.ar.core.Pose;
import com.google.ar.core.Session;
import com.google.ar.core.Trackable;
import com.google.ar.core.TrackingState;
import com.google.ar.core.codelab.cloudanchor.helpers.CameraPermissionHelper;
import com.google.ar.core.codelab.cloudanchor.helpers.CloudAnchorManager;
import com.google.ar.core.codelab.cloudanchor.helpers.ResolveDialogFragment;
import com.google.ar.core.codelab.cloudanchor.helpers.SnackbarHelper;
import com.google.ar.core.codelab.cloudanchor.helpers.StorageManager;
import com.google.ar.core.codelab.cloudanchor.helpers.TapHelper;
import com.google.ar.core.codelab.cloudanchor.helpers.TrackingStateHelper;
import com.google.ar.core.codelab.cloudanchor.rendering.BackgroundRenderer;
import com.google.ar.core.codelab.cloudanchor.rendering.ObjectRenderer;
import com.google.ar.core.codelab.cloudanchor.rendering.ObjectRenderer.BlendMode;
import com.google.ar.core.codelab.cloudanchor.rendering.PlaneRenderer;
import com.google.ar.core.codelab.cloudanchor.rendering.PointCloudRenderer;
import com.google.ar.core.codelab.cloudanchor.helpers.DisplayRotationHelper;
import com.google.ar.core.exceptions.CameraNotAvailableException;
import com.google.ar.core.exceptions.UnavailableApkTooOldException;
import com.google.ar.core.exceptions.UnavailableArcoreNotInstalledException;
import com.google.ar.core.exceptions.UnavailableDeviceNotCompatibleException;
import com.google.ar.core.exceptions.UnavailableSdkTooOldException;
import com.google.ar.core.exceptions.UnavailableUserDeclinedInstallationException;



import com.google.ar.sceneform.AnchorNode;
import com.google.ar.sceneform.Node;
import com.google.ar.sceneform.lullmodel.Vec2;
import com.google.ar.sceneform.math.Quaternion;
import com.google.ar.sceneform.math.Vector3;
import com.google.ar.sceneform.rendering.ModelRenderable;
import com.google.ar.sceneform.rendering.Renderable;
import com.google.ar.sceneform.rendering.ViewRenderable;
import com.google.ar.sceneform.ux.TransformableNode;


import java.io.IOException;
import java.io.PipedOutputStream;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * Main Fragment for the Cloud Anchors Codelab.
 *
 * <p>This is where the AR Session and the Cloud Anchors are managed.
 */
public class CloudAnchorFragment extends Fragment implements GLSurfaceView.Renderer {

  private static final String TAG = CloudAnchorFragment.class.getSimpleName();

  // Rendering. The Renderers are created here, and initialized when the GL surface is created.
  private GLSurfaceView surfaceView;

  private boolean installRequested;

  private Session session;
  private final SnackbarHelper messageSnackbarHelper = new SnackbarHelper();

  private final CloudAnchorManager cloudAnchorManager = new CloudAnchorManager();

  private DisplayRotationHelper displayRotationHelper;
  private TrackingStateHelper trackingStateHelper;
  private TapHelper tapHelper;

  private final StorageManager storageManager = new StorageManager();

  private final BackgroundRenderer backgroundRenderer = new BackgroundRenderer();
  private final ObjectRenderer virtualObject = new ObjectRenderer();
  private final ObjectRenderer virtualObjectShadow = new ObjectRenderer();
  private final PlaneRenderer planeRenderer = new PlaneRenderer();
  private final PointCloudRenderer pointCloudRenderer = new PointCloudRenderer();





  //testing for the arrow

  //Draw the AR object
  private final ObjectRenderer virtualObject2 = new ObjectRenderer();
  //Draw the Ar object shadow
  private final ObjectRenderer virtualObjectShadow2 = new ObjectRenderer();
  private final PlaneRenderer planeRenderer2 = new PlaneRenderer();
  private final PointCloudRenderer pointCloudRenderer2 = new PointCloudRenderer();


  // Temporary matrix allocated here to reduce number of allocations for each frame.
  private final float[] anchorMatrix = new float[16];
  private static final String SEARCHING_PLANE_MESSAGE = "Searching for surfaces...";
  private final float[] andyColor = {139.0f, 195.0f, 74.0f, 255.0f};


  private Button resolveButton;

  private Anchor pointer = null;
  Pose firstNODE;

  @Nullable
  private Anchor currentAnchor = null;

  @Override
  public void onAttach(@NonNull Context context) {
    super.onAttach(context);
    tapHelper = new TapHelper(context);
    trackingStateHelper = new TrackingStateHelper(requireActivity());
  }

  @Override
  public View onCreateView(
      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    // Inflate from the Layout XML file.
    View rootView = inflater.inflate(R.layout.cloud_anchor_fragment, container, false);
    GLSurfaceView surfaceView = rootView.findViewById(R.id.surfaceView);
    this.surfaceView = surfaceView;
    displayRotationHelper = new DisplayRotationHelper(requireContext());
    surfaceView.setOnTouchListener(tapHelper);

    surfaceView.setPreserveEGLContextOnPause(true);
    surfaceView.setEGLContextClientVersion(2);
    surfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending.
    surfaceView.setRenderer(this);
    surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
    surfaceView.setWillNotDraw(false);

    Button clearButton = rootView.findViewById(R.id.clear_button);
    clearButton.setOnClickListener(v -> onClearButtonPressed());

    resolveButton = rootView.findViewById(R.id.resolve_button);
    resolveButton.setOnClickListener(v -> onResolveButtonPressed());
    return rootView;

  }

  @Override
  public void onResume() {
    super.onResume();

    if (session == null) {
      Exception exception = null;
      String message = null;
      try {
        switch (ArCoreApk.getInstance().requestInstall(requireActivity(), !installRequested)) {
          case INSTALL_REQUESTED:
            installRequested = true;
            return;
          case INSTALLED:
            break;
        }

        // ARCore requires camera permissions to operate. If we did not yet obtain runtime
        // permission on Android M and above, now is a good time to ask the user for it.
        if (!CameraPermissionHelper.hasCameraPermission(requireActivity())) {
          CameraPermissionHelper.requestCameraPermission(requireActivity());
          return;
        }

        // Create the session.
        session = new Session(requireActivity());

        Config config = new Config(session);
        config.setCloudAnchorMode(Config.CloudAnchorMode.ENABLED);
        session.configure(config);

      } catch (UnavailableArcoreNotInstalledException
          | UnavailableUserDeclinedInstallationException e) {
        message = "Please install ARCore";
        exception = e;
      } catch (UnavailableApkTooOldException e) {
        message = "Please update ARCore";
        exception = e;
      } catch (UnavailableSdkTooOldException e) {
        message = "Please update this app";
        exception = e;
      } catch (UnavailableDeviceNotCompatibleException e) {
        message = "This device does not support AR";
        exception = e;
      } catch (Exception e) {
        message = "Failed to create AR session";
        exception = e;
      }

      if (message != null) {
        messageSnackbarHelper.showError(requireActivity(), message);
        Log.e(TAG, "Exception creating session", exception);
        return;
      }
    }

    // Note that order matters - see the note in onPause(), the reverse applies here.
    try {
      session.resume();
    } catch (CameraNotAvailableException e) {
      messageSnackbarHelper
          .showError(requireActivity(), "Camera not available. Try restarting the app.");
      session = null;
      return;
    }

    surfaceView.onResume();
    displayRotationHelper.onResume();
  }

  @Override
  public void onPause() {
    super.onPause();
    if (session != null) {
      // Note that the order matters - GLSurfaceView is paused first so that it does not try
      // to query the session. If Session is paused before GLSurfaceView, GLSurfaceView may
      // still call session.update() and get a SessionPausedException.
      displayRotationHelper.onPause();
      surfaceView.onPause();
      session.pause();
    }
  }

  @Override
  public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] results) {
    if (!CameraPermissionHelper.hasCameraPermission(requireActivity())) {
      Toast.makeText(requireActivity(), "Camera permission is needed to run this application",
          Toast.LENGTH_LONG)
          .show();
      if (!CameraPermissionHelper.shouldShowRequestPermissionRationale(requireActivity())) {
        // Permission denied with checking "Do not ask again".
        CameraPermissionHelper.launchPermissionSettings(requireActivity());
      }
      requireActivity().finish();
    }
  }

  @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(getContext());
      planeRenderer.createOnGlThread(getContext(), "models/trigrid.png");
      pointCloudRenderer.createOnGlThread(getContext());

      virtualObject.createOnGlThread(getContext(), "models/andy.obj", "models/andy.png");
      virtualObject.setMaterialProperties(0.0f, 2.0f, 0.5f, 6.0f);

      virtualObjectShadow
          .createOnGlThread(getContext(), "models/andy_shadow.obj", "models/andy_shadow.png");
      virtualObjectShadow.setBlendMode(BlendMode.Shadow);
      virtualObjectShadow.setMaterialProperties(1.0f, 0.0f, 0.0f, 1.0f);





      //Tutorial


      //The arrow I want to test
      //planeRenderer2.createOnGlThread(getContext(), "models/trigrid.png");
      virtualObject2.createOnGlThread(getContext(), "models/arrow.obj", "models/color.png");
      virtualObject2.setMaterialProperties(0.0f, 2.0f, 0.5f, 6.0f);

      virtualObjectShadow2
              .createOnGlThread(getContext(), "models/andy_shadow.obj", "models/andy_shadow.png");
      virtualObjectShadow2.setBlendMode(BlendMode.Shadow);
      virtualObjectShadow2.setMaterialProperties(1.0f, 0.0f, 0.0f, 1.0f);




      //modify part

    } catch (IOException e) {
      Log.e(TAG, "Failed to read an asset file", e);
    }
  }



  @Override
  public void onSurfaceChanged(GL10 gl, int width, int height) {
    displayRotationHelper.onSurfaceChanged(width, height);
    GLES20.glViewport(0, 0, width, height);
  }

  @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();

      cloudAnchorManager.onUpdate();




      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);



      // Keep the screen unlocked while tracking, but allow it to lock when tracking stops.
      trackingStateHelper.updateKeepScreenOnFlag(camera.getTrackingState());

      // If not tracking, don't draw 3D objects, show tracking failure reason instead.
      if (camera.getTrackingState() == TrackingState.PAUSED) {
        messageSnackbarHelper.showMessage(
            getActivity(), 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 didn't detect any plane, show searchingPlane message.
      if (!hasTrackingPlane()) {
        messageSnackbarHelper.showMessage(getActivity(), SEARCHING_PLANE_MESSAGE);
      }

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




      //Drawing
      if (currentAnchor != null && currentAnchor.getTrackingState() == TrackingState.TRACKING) {
        currentAnchor.getPose().toMatrix(anchorMatrix, 0);



        // Update and draw the model and its shadow.
        virtualObject.updateModelMatrix(anchorMatrix, 1f);
        virtualObjectShadow.updateModelMatrix(anchorMatrix, 1f);

        virtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, andyColor);
        virtualObjectShadow.draw(viewmtx, projmtx, colorCorrectionRgba, andyColor);



        pointer.getPose().toMatrix(anchorMatrix, 0);
        virtualObject2.updateModelMatrix(anchorMatrix, 1f);
        virtualObjectShadow2.updateModelMatrix(anchorMatrix, 1f);

        virtualObject2.draw(viewmtx, projmtx, colorCorrectionRgba, andyColor);
      }

      //Trying draw arrow


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

  // Handle only one tap per frame, as taps are usually low frequency compared to frame rate.
  private void handleTap(Frame frame, Camera camera) {
    if (currentAnchor != null) {
      return; // Do nothing if there was already an anchor.
    }

    MotionEvent tap = tapHelper.poll();
    MotionEvent tap2 = tapHelper.poll();

    if (tap != null && camera.getTrackingState() == TrackingState.TRACKING) {
      for (HitResult hit : frame.hitTest(tap)) {
        // Check if any plane was hit, and if it was hit inside the plane polygon
        Trackable trackable = hit.getTrackable();
        // Creates an anchor if a plane or an oriented point was hit.
        if ((trackable instanceof Plane
            && ((Plane) trackable).isPoseInPolygon(hit.getHitPose())
            && (PlaneRenderer.calculateDistanceToPlane(hit.getHitPose(), camera.getPose()) > 0))
            || (trackable instanceof Point
            && ((Point) trackable).getOrientationMode()
            == OrientationMode.ESTIMATED_SURFACE_NORMAL)) {
          // Hits are sorted by depth. Consider only closest hit on a plane or oriented point.

          // Adding an Anchor tells ARCore that it should track this position in
          // space. This anchor is created on the Plane to place the 3D model
          // in the correct position relative both to the world and to the plane.
          currentAnchor = hit.createAnchor();


          pointer = hit.createAnchor();


          getActivity().runOnUiThread(() -> resolveButton.setEnabled(false));

          messageSnackbarHelper.showMessage(getActivity(), "Now hosting anchor...");
          cloudAnchorManager.hostCloudAnchor(session, currentAnchor, /* ttl= */ 300, this::onHostedAnchorAvailable);

          break;
        }
      }
    }
  }

  /**
   * Checks if we detected at least one plane.
   */
  private boolean hasTrackingPlane() {
    for (Plane plane : session.getAllTrackables(Plane.class)) {
      if (plane.getTrackingState() == TrackingState.TRACKING) {
        return true;
      }
    }
    return false;
  }

  private synchronized void onClearButtonPressed() {
    // Clear the anchor from the scene.
    cloudAnchorManager.clearListeners();

    resolveButton.setEnabled(true);

    currentAnchor = null;
  }

  private synchronized void onHostedAnchorAvailable(Anchor anchor) {
    Anchor.CloudAnchorState cloudState = anchor.getCloudAnchorState();

    if (cloudState == Anchor.CloudAnchorState.SUCCESS) {
      int shortCode = storageManager.nextShortCode(getActivity());
      storageManager.storeUsingShortCode(getActivity(), shortCode, anchor.getCloudAnchorId());
      messageSnackbarHelper.showMessage(
              getActivity(), "Cloud Anchor Hosted. Short code: " + shortCode);
      currentAnchor = anchor;

    } else {
      messageSnackbarHelper.showMessage(getActivity(), "Error while hosting: " + cloudState.toString());
    }
    /*if (cloudState == Anchor.CloudAnchorState.SUCCESS) {
      messageSnackbarHelper.showMessage(
              getActivity(), "Cloud Anchor Hosted. ID: " + anchor.getCloudAnchorId());
      currentAnchor = anchor;
    } else {
      messageSnackbarHelper.showMessage(getActivity(), "Error while hosting: " + cloudState.toString());
    }*/
  }

  private synchronized void onResolveButtonPressed() {
    ResolveDialogFragment dialog = ResolveDialogFragment.createWithOkListener(
            this::onShortCodeEntered);
    dialog.show(getActivity().getSupportFragmentManager(), "Resolve");


  }

  private synchronized void onShortCodeEntered(int shortCode) {
    String cloudAnchorId = storageManager.getCloudAnchorId(getActivity(), shortCode);
    if (cloudAnchorId == null || cloudAnchorId.isEmpty()) {
      messageSnackbarHelper.showMessage(
              getActivity(),
              "A Cloud Anchor ID for the short code " + shortCode + " was not found.");
      return;
    }
    resolveButton.setEnabled(false);
    cloudAnchorManager.resolveCloudAnchor(
            session,
            cloudAnchorId,
            anchor -> onResolvedAnchorAvailable(anchor, shortCode));
  }

  //Create Anchor
  private synchronized void onResolvedAnchorAvailable(Anchor anchor, int shortCode) {
    Anchor.CloudAnchorState cloudState = anchor.getCloudAnchorState();
    if (cloudState == Anchor.CloudAnchorState.SUCCESS) {
      messageSnackbarHelper.showMessage(getActivity(), "Cloud Anchor Resolved. Short code: " + shortCode);
      currentAnchor = anchor;



    } else {
      messageSnackbarHelper.showMessage(
              getActivity(),
              "Error while resolving anchor with short code " + shortCode + ". Error: "
                      + cloudState.toString());
      resolveButton.setEnabled(true);
    }
  }
}

我的应用程序正在运行(我希望绿色箭头指向andy,但似乎它们彼此相连。)

共有1个答案

徐欣德
2023-03-14

按照本教程进行对象交互,如旋转、缩放和移动。

https://blog.yudiz.com/object-interaction-in-arcore-for-android/

 类似资料:
  • 本文向大家介绍three.js实现围绕某物体旋转,包括了three.js实现围绕某物体旋转的使用技巧和注意事项,需要的朋友参考一下 话不多说,请看代码: 可以拖动右上角观察变化 以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持呐喊教程!

  • 我试着让两个立方体以不同的方式旋转。 为了设置旋转,我这样做。 在多维数据集类内部,我没有使用GL11.glLoadId相()来重置旋转,而是做了这样的事情。 这将重置每个轴的旋转。 数组“rot”保存x、y和z旋转,并通过多维数据集类中的这3种方法进行更新。 单独地,每个GL11.glRotatef(etc, etc, etc)和GL11.glRotatef(etc*-1.0f, etc, et

  • 使用此代码执行asynctask之前,在progressdialog中显示旋转动画 ProgressDialog pDialog=ProgressDialog.show(getActivity(),null,null,true,false);pDialog.SetContentView(r.Layout.Loading); 这是xml

  • 本文向大家介绍Javascript 多物体运动的实现,包括了Javascript 多物体运动的实现的使用技巧和注意事项,需要的朋友参考一下 我们先来看下之前的运动的代码,是否支持多物体运动,会出现怎么样的问题。 以下是Javascript 代码: 此时当鼠标移入到第一个div 时,他是正常运行的。但是如果现在又移动到第二个或者第三个div时候就会出现bug。 image 这个是什么原因呢? 看图可

  • 本文向大家介绍Unity实现移动物体到鼠标点击位置,包括了Unity实现移动物体到鼠标点击位置的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了Unity实现移动物体到鼠标点击位置的具体代码,供大家参考,具体内容如下 目的: 移动物体到鼠标点击处屏幕所对应的空间位置,并使物体正对着点击的对象,不能倾斜。 首先,需要获取点击屏幕所对应的空间位置,这可以通过先获取屏幕坐标,然后转成空间坐标

  • 本文向大家介绍Unity3D实现人物移动示例,包括了Unity3D实现人物移动示例的使用技巧和注意事项,需要的朋友参考一下 一个是通过W、A、S、D来移动人物(示例一),另个是按屏幕上的按钮来移动人物(示例二)。很简单,只改了几行代码。 下面是“Assets”文件夹里面的资源。 示例一: 示例二 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。