ARFoundation之路-AR阴影生成之四

版权声明:Davidwang原创文章,严禁用于任何商业途径,授权后方可转载。

  在前述章节中,我们实现的阴影都是实时阴影,阴影会根据虚拟物体的形状、移动、灯光而产生变化,实时阴影在带来更好适应性的同时也会消耗大量计算资源,特别在移动设备上,这会挤占其他功能的正常运行资源,造成应用卡顿。在VR中,我们可以将光照效果烘焙进场景中以达到提高性能的目的,然而由于AR场景就是真实环境,无法预先烘焙场景(在复杂AR场景中,包括物体自身阴影,也可以采用灯光烘焙方法提高性能),但如果AR中的虚拟物体是刚体,不发生形变,且不能脱离AR平面时,我们可以采用预先制作阴影的方法实现阴影效果。

(一)预先制作阴影

  所谓预先制作阴影,就是在虚拟物体下预先放置一个平面,平面渲染纹理是与虚拟物体匹配的阴影纹理,以此来模拟阴影。因为这个阴影是使用图像的方法来实现的,所以最大的优势是不浪费计算资源,其次是阴影的可以根据需要自由处理成硬阴影、软阴影、超软阴影、斑点阴影等类型,自主性强。缺点是这个阴影一旦设定后在运行时不能依据环境变化而产生变化,不能对环境进行适配。

  为防止Unity自带阴影产生干扰,我们先关闭场景中Directional Light阴影产生,如下图所示。

在这里插入图片描述
  然后制作一张与虚拟物体相匹配的阴影效果图,通常这张图应该是带Alpha通道的png格式纹理图,如下图所示。
在这里插入图片描述
  新建一个材质,命名为ARShadow,材质使用Unlit->Transparent着色器以实现透明效果,纹理使用刚才的阴影纹理,如下图所示。

在这里插入图片描述

  在Hierarchy窗口中新建一个空对象,命名为ARPlane,并将虚拟物体模型作为其子物体放置在其下。在ARPlane下建一个Quad对象,将其X轴旋转90度以使其平铺,将上文制作的ARShadow材质赋给它,并调整Quad对象与模型的相对关系,如下图所示。

在这里插入图片描述
  通过预先制作阴影的方法,我们避免了在运行时实时计算阴影,并且可以预先设置阴影的类型,在真实场景中的效果如下图所示,可以看到,这个阴影效果在模拟一般普通环境下的阴影时非常用效。

在这里插入图片描述

(二)一种精确放置物体的方法

  在之前的操作中,我们加载虚拟物体的做法通常是先识别显示平面,然后通过手势在平面上点击加载虚拟物体,这种方式的问题是过程不直接(先识别显示平面,然后通过点击加载),另外放置位置不精准。本节我们讲述一种规避这两个问题的加载虚拟物体的方法:直接在可放置虚拟物体的地方显示一个放置指示图标,点击屏幕任何地方都会在指示图标所在位置放置虚拟物体。

  虽然现在我们不再需要显示已检测识别的平面,但我们还是平面检测功能(为了真实感,虚拟物体需要放置在平面上而不是悬空浮在空中),因此,我们依然需要在场景中的AR Session Origin对象上挂载AR Plane Manager组件,但由于不需要渲染显示已检测识别的平面,所以其Plane Prefab要置空,如下图所示。

在这里插入图片描述

  新建一个C#脚本,命名为AppController,编写如下代码:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

[RequireComponent(typeof(ARRaycastManager))]
public class AppControler : MonoBehaviour
{    
    public GameObject mObjectPrefab;
    public GameObject mPlacementIndicator;

    private ARRaycastManager mRaycastManager;
    private Pose placementPose;
    private GameObject placementIndicatorObject;
    private bool placementPoseIsValid = false;

    void Start()
    {
        mRaycastManager = GetComponent<ARRaycastManager>();
        ARSessionOrigin mSession = GetComponent<ARSessionOrigin>();
        placementIndicatorObject = Instantiate(mPlacementIndicator, mSession.trackablesParent);
    }

    void Update()
    {
        UpdatePlacementPose();
        UpdatePlacementIndicator();

        if (placementPoseIsValid && Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
        {
            Instantiate(mObjectPrefab, placementPose.position, Quaternion.identity);
        }
    }

    private void UpdatePlacementIndicator()
    {
        if (placementPoseIsValid)
        {
            placementIndicatorObject.SetActive(true);
            placementIndicatorObject.transform.SetPositionAndRotation(placementPose.position, placementPose.rotation);
        }
        else
        {
            placementIndicatorObject.SetActive(false);
        }
    }

    private void UpdatePlacementPose()
    {
        var screenCenter = Camera.current.ViewportToScreenPoint(new Vector3(0.5f, 0.5f));
        var hits = new List<ARRaycastHit>();
        mRaycastManager.Raycast(screenCenter, hits, TrackableType.PlaneWithinPolygon | TrackableType.PlaneWithinBounds);

        placementPoseIsValid = hits.Count > 0;
        if (placementPoseIsValid)
        {
            placementPose = hits[0].pose;

            var cameraForward = Camera.current.transform.forward;
            var cameraBearing = new Vector3(cameraForward.x, 0, cameraForward.z).normalized;
            placementPose.rotation = Quaternion.LookRotation(cameraBearing);
        }
    }
}


  该脚本首先实例化一个指示图标,依然使用射线检测的方式检测与识别平面的相交情况,只是这个射线一直从屏幕的正中间位置发出,不再需要用手去点击,在检测到与平面相交后显示并实时更新指示图标姿态,当放置位置有效时(指示图标显示)点击屏幕后会在指示图标所在位置实例化虚拟物体。

  将该脚本挂载在场景中的AR Session Origin对象上,并为其赋上指示图标与虚拟物体Prefab,编译运行,效果如下图所示。

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_DavidWang_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值