Tìm hiểu cách sử dụng API Vị trí tức thì trong ứng dụng của chính bạn.
Điều kiện tiên quyết
Đảm bảo rằng bạn hiểu rõ các khái niệm cơ bản về AR và cách định cấu hình phiên ARCore trước khi tiếp tục.
Định cấu hình một phiên mới với Vị trí tức thì
Trong phiên ARCore mới, hãy bật Chế độ vị trí tức thì.
Java
// Create the ARCore session.
public void createSession() {
session = new Session(applicationContext);
Config config = new Config(session);
// Set the Instant Placement mode.
config.setInstantPlacementMode(InstantPlacementMode.LOCAL_Y_UP);
session.configure(config);
}
Kotlin
// Create the ARCore session.
fun createSession() {
session = Session(applicationContext);
val config = Config(session)
// Set the Instant Placement mode.
config.instantPlacementMode = Config.InstantPlacementMode.LOCAL_Y_UP
session.configure(config)
}
Đặt một đối tượng
Sử dụng Frame.hitTestInstantPlacement()
để tạo một Điểm Vị trí tức thì có thể theo dõi dựa vào vị trí nhấn vào màn hình.
Truy xuất tư thế hiện tại bằng phương thức getPose()
.
Java
private placementIsDone = false;
public void onDrawFrame(GL10 gl) {
Frame frame = session.update();
// Place an object on tap.
if (!placementIsDone && didUserTap()) {
// Use estimated distance from the user's device to the real world, based
// on expected user interaction and behavior.
float approximateDistanceMeters = 2.0f;
// Performs a ray cast given a screen tap position.
List<HitResult> results =
frame.hitTestInstantPlacement(tapX, tapY, approximateDistanceMeters);
if (!results.isEmpty()) {
InstantPlacementPoint point = (InstantPlacementPoint) results.get(0).getTrackable();
// Create an Anchor from the point's pose.
Anchor anchor = point.createAnchor(point.getPose());
placementIsDone = true;
disableInstantPlacement();
}
}
}
Kotlin
var placementIsDone = false;
fun onDrawFrame(gl: GL10) {
val frame = session.update();
// Place an object on tap.
if (!placementIsDone && didUserTap()) {
// Use estimated distance from the user's device to the real world, based
// on expected user interaction and behavior.
val approximateDistanceMeters = 2.0f;
// Performs a ray cast given a screen tap position.
val results = frame.hitTestInstantPlacement(tapX, tapY, approximateDistanceMeters)
if (results.isNotEmpty()) {
val point = results[0].trackable as InstantPlacementPoint
// Create an Anchor from the point's pose.
val anchor = point.createAnchor(point.pose)
placementIsDone = true
disableInstantPlacement()
}
}
}
Tính năng Đặt ngay hỗ trợ theo dõi không gian màn hình với khoảng cách ước chừng, tự động chuyển sang chế độ theo dõi đầy đủ sau khi điểm Đặt ngay được neo trong thế giới thực. Truy xuất tư thế hiện tại bằng
getPose()
.
Tải phương pháp theo dõi hiện tại bằng
getTrackingMethod()
.
Mặc dù ARCore có thể thực hiện các thử nghiệm truy cập Vị trí tức thì trên các nền tảng của bất kỳ hướng, kết quả đánh sẽ luôn trả về tư thế với +Y lên, ngược với hướng trọng lực. Trên giao diện ngang, các thử nghiệm nhấn cho kết quả chính xác vị trí nhanh hơn nhiều.
Dễ dàng chuyển đổi phương pháp theo dõi
Khi có độ sâu thực, ARCore sẽ thay đổi phương thức theo dõi của InstantPlacementPoint
từ SCREENSPACE_WITH_APPROXIMATE_DISTANCE
thành FULL_TRACKING
.
Tư thế của điểm cũng sẽ thay đổi để phản ánh chiều sâu thực sự.
Điều này có thể khiến đối tượng xuất hiện đột nhiên to lên hoặc nhỏ lại.
Để tránh sự thay đổi đột ngột này, hãy thêm một trình bao bọc InstantPlacementPoint
.
Java
// Wrapper class to track state to reduce sudden visual changes in object size
class WrappedInstantPlacement {
public InstantPlacementPoint point;
public InstantPlacementPoint.TrackingMethod previousTrackingMethod;
public float previousDistanceToCamera;
public float scaleFactor = 1.0f;
public WrappedInstantPlacement(
InstantPlacementPoint point,
InstantPlacementPoint.TrackingMethod previousTrackingMethod,
float previousDistanceToCamera) {
this.point = point;
this.previousTrackingMethod = previousTrackingMethod;
this.previousDistanceToCamera = previousDistanceToCamera;
}
}
Kotlin
// Wrapper class to track state to reduce sudden visual changes in object size
class WrappedInstantPlacement(
var point: InstantPlacementPoint,
var previousTrackingMethod: InstantPlacementPoint.TrackingMethod,
var previousDistanceToCamera: Float,
var scaleFactor: Float = 1.0f
)
Sau đó, hãy thêm nội dung sau vào hoạt động của bạn.
Java
List<WrappedInstantPlacement> wrappedPoints = new ArrayList<>();
public void onDrawFrame(GL10 gl) {
Frame frame = session.update();
Camera camera = frame.getCamera();
// Place an object on tap.
if (didUserTap()) {
// Instant Placement should only be applied if no results are available through hitTest.
List<HitResult> results = frame.hitTest(tapX, tapY);
if (results.isEmpty()) {
// Use the estimated distance from the user's device to the closest
// available surface, based on expected user interaction and behavior.
float approximateDistanceMeters = 2.0f;
// Returns a single result if the hit test was successful.
results =
frame.hitTestInstantPlacement(tapX, tapY, approximateDistanceMeters);
if (!results.isEmpty()) {
// Instant placement was successful.
InstantPlacementPoint point = (InstantPlacementPoint) results.get(0).getTrackable();
wrappedPoints.add(new WrappedInstantPlacement(point, point.getTrackingMethod(),
distance(camera.getPose(), point.getPose())));
}
} else {
// results contain valid hit tests which can be used directly, so instant placement is not required.
}
}
for (WrappedInstantPlacement wrappedPoint : wrappedPoints) {
InstantPlacementPoint point = wrappedPoint.point;
if (point.getTrackingState() == TrackingState.STOPPED) {
wrappedPoints.remove(wrappedPoint);
continue;
}
if (point.getTrackingState() == TrackingState.PAUSED) {
continue;
}
if (point.getTrackingMethod() == TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE) {
// Continue to use the estimated depth and pose. Record the distance to
// the camera for use in the next frame if the transition to full
// tracking happens.
wrappedPoint.previousDistanceToCamera = distance(point.getPose(), camera.getPose());
}
else if (point.getTrackingMethod() == TrackingMethod.FULL_TRACKING) {
if (wrappedPoint.previousTrackingMethod == TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE) {
// Change from the estimated pose to the accurate pose. Adjust the
// object scale to counteract the apparent change due to pose jump.
wrappedPoint.scaleFactor = distance(point.getPose(), camera.getPose()) /
wrappedPoint.previousDistanceToCamera;
// Apply the scale factor to the model.
// ...
wrappedPoint.previousTrackingMethod = TrackingMethod.FULL_TRACKING;
}
}
}
}
float distance(Pose p, Pose q) {
return Math.sqrt(Math.pow(p.tx() - q.tx(), 2) + Math.pow(p.ty() - q.ty(), 2) + Math.pow(p.tz() - q.tz(), 2));
}
Kotlin
var wrappedPoints = mutableListOf<WrappedInstantPlacement>()
fun onDrawFrame(gl: GL10?) {
val frame = session.update()
val camera = frame.camera
// Place an object on tap.
if (didUserTap()) {
// Instant Placement should only be applied if no results are available through hitTest.
var results = frame.hitTest(tapX, tapY);
if (results.isEmpty()) {
// Use the estimated distance from the user's device to the closest
// available surface, based on expected user interaction and behavior.
val approximateDistanceMeters = 2.0f;
// Returns a single result if the hit test was successful.
results = frame.hitTestInstantPlacement(tapX, tapY, approximateDistanceMeters);
if (results.isNotEmpty()) {
// Instant placement was successful.
val point = results[0].trackable as InstantPlacementPoint
val wrapped = WrappedInstantPlacement(point, point.trackingMethod, point.pose.distance(camera.pose))
wrappedPoints.add(wrapped)
}
} else {
// Results contain valid hit tests which can be used directly, so Instant Placement
// is not required.
}
}
loop@ for (wrappedPoint in wrappedPoints) {
val point = wrappedPoint.point
when {
point.trackingState == TrackingState.STOPPED -> {
wrappedPoints.remove(wrappedPoint)
continue@loop
}
point.trackingState == TrackingState.PAUSED -> continue@loop
point.trackingMethod == TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE -> {
// Continue to use the estimated depth and pose. Record the distance to
// the camera for use in the next frame if the transition to full
// tracking happens.
wrappedPoint.previousDistanceToCamera = point.pose.distance(camera.pose)
}
wrappedPoint.previousTrackingMethod == TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE &&
point.trackingMethod == TrackingMethod.FULL_TRACKING -> {
// Change from the estimated pose to the accurate pose. Adjust the
// object scale to counteract the apparent change due to pose jump.
wrappedPoint.scaleFactor =
point.pose.distance(camera.pose) / wrappedPoint.previousDistanceToCamera
// Apply the scale factor to the model.
// ...
wrappedPoint.previousTrackingMethod = TrackingMethod.FULL_TRACKING
}
}
}
}
fun Pose.distance(other: Pose) = sqrt(
(tx() - other.tx()).pow(2.0f) + (ty() - other.ty()).pow(2.0f) + (tz() - other.tz()).pow(2.0f)
)
Tăng hiệu quả sau khi đặt vật thể
Tắt Vị trí tức thì khi đối tượng được đặt đúng để lưu chu kỳ của CPU và sức mạnh.
Java
void disableInstantPlacement() {
Config config = new Config(session);
config.setInstantPlacementMode(Config.InstantPlacementMode.DISABLED);
session.configure(config);
}
Kotlin
fun disableInstantPlacement() {
val config = Config(session)
// Set the Instant Placement mode.
config.instantPlacementMode = Config.InstantPlacementMode.DISABLED
session.configure(config)
}