使用Unity实现VR中在黑板上写字(升级篇)(一)-----解决画笔穿透画板的问题

摘要:
一、概述:在使用Unity实现VR中在黑板上写字(初级篇)中的最后留下了一些有待完善的地方,首先完善画笔穿透画板的问题;在之前使用画笔会出现这种情况:可以看到画笔是穿透了画板,这样在VR中会给用户很差的体验,而且因为代码的原因会造成画的过程中中断,所以这个问题必须解决;解决后的使用情况:可以看到现在不会穿透了,而且画起来不会有中断,其实我的手的位置已经穿到画板后面了;实现这个功能,其实有很多种方法

一、概述:

使用Unity实现VR中在黑板上写字(初级篇)中的最后留下了一些有待完善的地方,首先完善画笔穿透画板的问题;

在之前使用画笔会出现这种情况:

使用Unity实现VR中在黑板上写字(升级篇)(一)-----解决画笔穿透画板的问题第1张

可以看到画笔是穿透了画板,这样在VR中会给用户很差的体验,而且因为代码的原因会造成画的过程中中断,所以这个问题必须解决;

解决后的使用情况:

使用Unity实现VR中在黑板上写字(升级篇)(一)-----解决画笔穿透画板的问题第2张

可以看到现在不会穿透了,而且画起来不会有中断,其实我的手的位置已经穿到画板后面了;

实现这个功能,其实有很多种方法,但是最终觉得参照The Lab里实现的方法比较好————用一个Plane类型解决;

二、知识点

Plane这个类中有两个方法:

1.public boolGetSide(Vector3inPt);判断一个点在Plane的哪一侧

Plane把一个空间分成了两部分,当一个点在Plane的normal(法线)指向的一侧的时候,这个值返回True,否则返回False;

2.public floatGetDistanceToPoint(Vector3inPt);判断一个点距离平面的距离,这个值是带符号的,当点在平面的正面的时候,返回正值,否则返回负值;

利用这两个方法可以判断出笔尖是不是穿透了画板;

三、代码原理

笔尖穿透了多少距离,我们就补多少回去,以俯视的视角,想像这个情况

使用Unity实现VR中在黑板上写字(升级篇)(一)-----解决画笔穿透画板的问题第3张

根据上图,可以等到笔尖穿透的距离,从笔尖的位置减去这个距离,则刚好可以让笔尖处于平面上;

因为用的是VRTK插件,这个插件集成了很好的物理交互功能,比如抓起东西;VRTK中写了不少抓起物体的机制:

1.VRTK_ChildOfControllerGrabAttach

2.VRTK_ClimbableGrabAttach

3.VRTK_CustomJointGrabAttach

4.VRTK_FixedJointGrabAttach

5.VRTK_RotatorTrackGrabAttach

6.VRTK_SpringJointGrabAttach

7.VRTK_TrackObjectGrabAttach

对于穿透这种情况,没有一种是比较合适的,因此需要自己扩展一种抓附机制;上面的抓附机制都是直接或者间接继承自VRTK_BaseGrabAttach的,因为定义一个VRTK_InteractableObject的抓起方式的字段就是VRTK_BaseGrabAttach类型;

使用Unity实现VR中在黑板上写字(升级篇)(一)-----解决画笔穿透画板的问题第4张

所以综上,需要扩展一个VRTK_BaseGrabAttach类型的抓附机制,并且Board类有了一些改变,需要增加一个UnityEngine.Plane类型的字段,以及封装一些函数;

四、代码实现

首先Board类增加一个Plane类型的字段

internal Plane boardPlane;

然后初始化这个字段

   private voidStart()
    {
        //初始化Plane,让它的法线是这个画板的forward向量,并且法线通过画板的中心位置,由此确定一个平面
        boardPlane = newPlane(transform.forward, transform.position);
......
}

最后增加核心的方法

/// <summary>
    ///判断笔尖是在画板的正面还是背面
    /// </summary>
    /// <param name="point">笔尖的位置</param>
    /// <returns>true 在正面;false 在背面</returns>
    public boolGetSideOfBoardPlane(Vector3 point)
    {
        returnboardPlane.GetSide(point);
    }
    /// <summary>
    ///笔尖与平面的距离
    /// </summary>
    /// <param name="point">笔尖的位置</param>
    /// <returns>当在正面的时候返回正值,当在背面的时候返回负值</returns>
    public floatGetDistanceFromBoardPlane(Vector3 point)
    {
        returnboardPlane.GetDistanceToPoint(point);
    }
    /// <summary>
    ///矫正后的笔尖应该在的位置
    /// </summary>
    /// <param name="point">笔尖的位置</param>
    /// <returns>矫正后的笔尖位置</returns>
    publicVector3 ProjectPointOnBoardPlane(Vector3 point)
    {
        float d = -Vector3.Dot(boardPlane.normal, point -transform.position);
        return point + boardPlane.normal *d;
    }

然后扩展一个抓附机制

usingVRTK.GrabAttachMechanics;
usingUnityEngine;
public classPainterGrabAttach : VRTK_BaseGrabAttach
{
    [Header("Painter Options")]
    [SerializeField]
    private Transform tips;//笔尖
    private static Board board;//画板
    #region 重写的父类方法
    protected override voidInitialise()
    {
        //初始化父类的一些字段,这些字段只是标识这个抓附机制的作用
        tracked = false;
        kinematic = false;
        climbable = false;
        //初始化自定义的属性
        if (precisionGrab)//最好不要用精确抓取,因为这样很有可能会让笔处于一个不合理的位置,这样使用的时候,会很变扭(比如必须手腕旋转一个角度,笔才是正的) 
{
            Debug.LogError("PrecisionGrab cant't be true in case of PainterGrabAttach Mechanic");
        }
        board = FindObjectOfType<Board>();
    }
    public override boolStartGrab(GameObject grabbingObject, GameObject givenGrabbedObject, Rigidbody givenControllerAttachPoint)
    {
        if (base.StartGrab(grabbingObject, givenGrabbedObject, givenControllerAttachPoint))
        {
            SnapObjectToGrabToController(givenGrabbedObject);
            grabbedObjectScript.IsKinematic = true;
            return true;
        }
        return false;
    }
    public override void StopGrab(boolapplyGrabbingObjectVelocity)
    {
        ReleaseObject(applyGrabbingObjectVelocity);
        base.StopGrab(applyGrabbingObjectVelocity);
    }
    public override voidProcessFixedUpdate()
    {
        if (grabbedObject)//只有抓住物体后,grabbedObject才不会
{
            grabbedObject.transform.rotation = controllerAttachPoint.transform.rotation *Quaternion.Euler(grabbedSnapHandle.transform.localEulerAngles);
            grabbedObject.transform.position = controllerAttachPoint.transform.position - (grabbedSnapHandle.transform.position -grabbedObject.transform.position);
            float distance = board.GetDistanceFromBoardPlane(tips.position);//笔尖距离平面的距离
            bool isPositiveOfBoardPlane = board.GetSideOfBoardPlane(tips.position);//笔尖是不是在笔尖的正面
            Vector3 direction = grabbedObject.transform.position - tips.position;//笔尖位置指向笔的位置的差向量
            //当笔尖穿透的时候,需要矫正笔的位置 
            if (isPositiveOfBoardPlane || distance > 0.0001f)
            {
                Vector3 pos =board.ProjectPointOnBoardPlane(tips.position);
                grabbedObject.transform.position = pos - board.boardPlane.normal * 0.001f + direction;//pos是笔尖的位置,而不是笔的位置,加上direction后才是笔的位置 
}
        }
    }
    #endregion
    //让手柄抓住物体
    private voidSnapObjectToGrabToController(GameObject obj)
    {
        if (!precisionGrab)
        {
            SetSnappedObjectPosition(obj);
        }
    }
    //设置物体和手柄连接的位置 
    private voidSetSnappedObjectPosition(GameObject obj)
    {
        if (grabbedSnapHandle == null)
        {
            obj.transform.position =controllerAttachPoint.transform.position;
        }
        else
        {
            //设置旋转,controllerAttachPoint是手柄上的一个与物体的连接点
            obj.transform.rotation = controllerAttachPoint.transform.rotation *Quaternion.Euler(grabbedSnapHandle.transform.localEulerAngles);
            //因为grabbedSnapHandle和obj.transform之间可能不是同一个点,所以为了让手柄抓的位置是grabbedSnapHandle,需要减去括号中代表的向量
            obj.transform.position = controllerAttachPoint.transform.position - (grabbedSnapHandle.transform.position -obj.transform.position);
        }
    }
}

五、场景中的设置

1.笔尖的位置还是要设置好

使用Unity实现VR中在黑板上写字(升级篇)(一)-----解决画笔穿透画板的问题第5张

笔尖的位置要设置在笔芯的尖端,snapPoint的位置和旋转,决定了手柄抓住笔时的位置和旋转;

2.其它需要注意的一些设置

使用Unity实现VR中在黑板上写字(升级篇)(一)-----解决画笔穿透画板的问题第6张

免责声明:文章转载自《使用Unity实现VR中在黑板上写字(升级篇)(一)-----解决画笔穿透画板的问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇linux 系统中awk命令实现统计每行数据的最大值、最小值使用dd工具对磁盘RAID5和10进行I/O性能测试下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

HTML 最简单的tips 怎么支持指定DIV显示提示信息

<body> <style type="text/css"> a.link{position:relative;} a.link div.tips{ border:1px solid #333; padding:10px; background-color:#ff0; position:absolute; top:16px; le...

div固定显示的几种方法

很多时候我们会受到一些需求: 1、div一直置顶 2、div一直置底 3、超过一定的位置之后div置顶 4、超过一定位置之后div置底 那么下面针对上面的几个问题写几个案例: 一、div一直在屏幕的上方,这个倒是容易咱们直接使用position:fixed;然后设置他的top值和left就可以了,别忘了设置宽度哦 <div class="top"&g...

使用Unity实现VR中在黑板上写字(升级篇)(二)----- 加入黑板擦

黑板擦的功能其实和画笔是一样的,只是黑板擦设置的颜色是画板最原始的颜色,而笔设置的是其他的颜色。 所以最大的不同时,当手柄握住黑板擦时和握住笔时的函数实现是不一样的;实现这个功能之后,黑板擦的擦掉功能将在后续的篇章中和画笔一起完成; 可以看到不管黑板擦以什么角度开始靠近画板,最终这个黑板擦一定是和画板平行的; 先看看画板的坐标系: 再看看黑板擦的坐标系...

CESIUM内置shader变量和函数[转]

cesium中内置了一些常量、变量和函数,在vs和fs中可直接使用。 内置uniform 内置uniform主要置于AutomaticUniforms类里面,该类私有未开放文档。 czm_backgroundColor An automatic GLSL uniform representing the current scene backgrou...

[转]VR原理讲解及开发入门

本文转自:http://www.52vr.com/article-661-1.html 本文是作者obuil根据多年心得专门为想要入门的VR开发者所写,由52VR网站提供支持。   1. VR沉浸感和交互作用产生的原理:   在之前,我们观看一个虚拟的创造内容是通过平面显示器的,52VR上次发布过一篇文章《一张图让你认识VR》,在其中,你会看到很多10年...

iOS 中的frame,bounds,center,transform关联

这里有一篇好文章 http://www.winddisk.com/2012/06/07/transform/ 先看几个知识点,UIView 的frame,bounds,center,transform属性,CAlayer的position,anchorPoint,transform. 1.当一个view的frame被更改时 a.当更改size时,它的bou...