java - openGL ES 旋转 AR 对象
问题描述
我正在学习教程,当我把它放在我的代码中时。我的对象将使用触摸点作为它的中心点进行旋转。如果我已经有一个 AR 对象并且我希望第二个对象指向它,我应该如何更改?当我四处走动时,它可以一直指向它吗?
编辑:我正在使用
Matrix.setRotateM(How2rotate, 0, angle, 0, -1.0f, 0)
我想要的代码是计算两个对象之间的角度(这可以使一个对象指向另一个对象)。我如何在 openGL ES 中计算这个角度或其他东西可以计算这个?
代码在这里
package com.google.ar.core.codelab.cloudanchor;
import android.app.AlertDialog;
import android.graphics.drawable.Drawable;
import android.opengl.Matrix;
import android.os.SystemClock;
import android.view.MotionEvent;
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.math.Vector3;
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.
*/
//<OnRotationGestureListener> can be deleted(is testing if it worked or not.)
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 PointCloudRenderer pointCloudRenderer2 = new PointCloudRenderer();
private Node myNode = null;
private float turn = 0;
// 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;
Vector3 II;
Vector3 UU;
float testAngle = 0;
//Tutorial
//Using to let arrow point to my anchor.
private float[] How2rotate = new float[16];
private float[] TEST1 = new float[16];
private float[] TEST2 = new float[16];
//Using to let arrow point to my anchor.
private float angle;
@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) {
//設置每顏色 傳入參數為RGBA
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();
float[] how2rotate2 = new float[16];
long time = SystemClock.uptimeMillis() % 4000L;
angle = 0.090f * ((int) time);
Camera camera = frame.getCamera();
// Handle one tap per frame.
handleTap(frame, camera);
handleTap2(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);
float[] viewmtx2 = new float[16];
// 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);
virtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, andyColor);
virtualObjectShadow.draw(viewmtx, projmtx, colorCorrectionRgba, andyColor);
Matrix.setRotateM(How2rotate, 0, angle, 0, -1.0f, 0);
Matrix.multiplyMM(how2rotate2, 0, viewmtx, 0, How2rotate, 0);
pointer.getPose().toMatrix(anchorMatrix, 0);
virtualObject2.updateModelMatrix(anchorMatrix, 0.5f);
virtualObjectShadow2.updateModelMatrix(anchorMatrix, 0.5f);
virtualObject2.draw(how2rotate2, projmtx,colorCorrectionRgba, andyColor);
/*handleTap(frame, camera);
handleTap2(frame,camera);*/
}
//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();
getActivity().runOnUiThread(() -> resolveButton.setEnabled(false));
messageSnackbarHelper.showMessage(getActivity(), "Now hosting anchor...");
cloudAnchorManager.hostCloudAnchor(session, currentAnchor, /* ttl= */ 300, this::onHostedAnchorAvailable);
break;
}
}
}
}
private void handleTap2(Frame frame, Camera camera) {
if (pointer != null) {
return; // Do nothing if there was already an anchor.
}
MotionEvent tap2 = tapHelper.poll();
if (tap2 != null && camera.getTrackingState() == TrackingState.TRACKING) {
for (HitResult hit : frame.hitTest(tap2)) {
// 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.
pointer = hit.createAnchor();
//Matrix.setRotateM(How2rotate, 0, angle, 0, -1.0f, 0);
getActivity().runOnUiThread(() -> resolveButton.setEnabled(false));
//messageSnackbarHelper.showMessage(getActivity(), "Now hosting anchor...");
//cloudAnchorManager.hostCloudAnchor(session, pointer, /* 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);
}
}
}
我在这里举个例子。看到这个。我现在需要的是为箭头设置一个角度,让它指向正方形。我试图找到解决方案,但他们没有完整的教程。有人可以帮忙吗?提前致谢。
解决方案
推荐阅读
- kubernetes - Helm 无法从命令行将双引号传递给 values.yaml?
- domain-driven-design - 事件采购 - 事件存储
- java - 无法加载 ApplicationContext Java 测试
- python - 尽管代码在 Python 中运行无错误,但运行时错误
- node.js - 如何让雪包在包内查找子路径
- c# - 无法在 C# 应用程序中检测到硬盘驱动器
- regex - 匹配与前一组相同的重复次数
- rust - 使用 cc crate,我如何强制使用特定的编译器?
- python - 在没有 conda 的 Windows 上 pip install fbprophet
- python - 用空格分隔的整数字符串对列表进行排序