[转]SteamVR 1.x☀️二、实现凝视效果——基于[CameraRig]

目录

🟥 本节目标

🟧 实现原理

🟨 实现步骤

1️⃣ Canvas

2️⃣ 拖入[CameraRig]

3️⃣ 要交互的UI和3D物体设置

4️⃣ 添加准星

5️⃣ GazeController

6️⃣ VRGazeItem

7️⃣ 为准星添加shader


🟥 本节目标

通过跟踪头部运动,设置一个代表光标的准星,碰触物体后改变位置和朝向,贴近被凝视物体。物体a被凝视一定时间后消失,物体b被凝视一定时间后被击中

🟧 实现原理

1、基于射线原理,Update—>Raycast

2、准星或十字线设置为相机子物体,等待操作的动画为圆环逐渐填满

3、被凝视的可是UI,也可是3D物体

4、击中物体一段时间后,可完成相关操作,如消失、缩放、材质变换等

5、元素一般分三种状态:准星进入、准星停留、准星退出(Collider)

🟨 实现步骤

1️⃣ Canvas

建立Canvas,Render Mode设置为World Space,缩放合适比例0.003

2️⃣ 拖入[CameraRig]

3️⃣ 要交互的UI和3D物体设置

a、Cube添加Rigidbody

b、UI添加BoxCollider,并调整合适大小

c、记得给要交互的物体添加Tag——gazeUI、gazeObj。在VRGazeItem代码我们要使用到他们。

4️⃣ 添加准星

添加准星,将准星Canvas放在[CameraRig]——Camera(head)——Camera(eye)下,Render Mode为World Space,实现如下效果

原理:两个Image,sourceimage都用小圆圈图片,显示方式为旋转显示

5️⃣ GazeController

代码GazeController,实现击中物体时UI的变化,挂载到Camera(eye)上(因为从眼睛这发出射线)

using UnityEngine;
using UnityEngine.UI;
 
public class GazeController : MonoBehaviour {
 
    //设置canvas位置、朝向
    public Canvas reticleCanvas;
    //设置准星填充效果等
    public Image reticleImage;
    //被击中的物体
    private GameObject target;
    //准星初始位置
    private Vector3 originPos;
    //准星初始缩放:当看远处时变大,看近处时变小
    private Vector3 originScale;
    //倒计时时间
    private float countDownTime = 3;
    //逝去时间(当前时间)
    private float nowTime = 0;
 
	void Start () {
        reticleImage.fillAmount = 0;
        originPos = reticleCanvas.transform.localPosition;
        originScale = reticleCanvas.transform.localScale;
    }
 
	void Update () {
        Ray ray = new Ray(transform.position, transform.forward);
        RaycastHit hit;
        if(Physics.Raycast(ray,out hit, 100))
        {
            //将准星放到碰撞点上
            reticleCanvas.transform.position = hit.point;
            reticleCanvas.transform.localScale = originScale * hit.distance;
            //让准星的法线方向和被击中的物体法线方向一致
            reticleCanvas.transform.forward = hit.normal;
 
            //视线初次进入处理
            if (hit.transform.gameObject != target)
            {
                //视线凝视的上一个物体,完成退出操作
                if (target != null)
                {
                    VRGazeItem oldItem = target.GetComponent<VRGazeItem>();
                    if (oldItem)
                    {
                        oldItem.OnGazeOut();
                    }
                }
 
                //视线正处于的物体
                target = hit.transform.gameObject;
                VRGazeItem newItem = target.GetComponent<VRGazeItem>();
                if (newItem)
                {
                    newItem.OnGazeIn();
                }
            }
            //视线长久停留处理
            else
            {
                nowTime += Time.deltaTime;
 
                //正在读秒时间
                if (countDownTime > nowTime)
                {
                    reticleImage.fillAmount = nowTime / countDownTime;
                }
                //达到激活条件
                else
                {
                    VRGazeItem gazeFireItem = target.GetComponent<VRGazeItem>();
                    if (gazeFireItem)
                    {
                        gazeFireItem.OnGazeFire(hit);
                    }
                    nowTime = 0;
                }
            }
        }
        else
        {
            reticleCanvas.transform.localPosition = originPos;
            reticleCanvas.transform.localScale = originScale;
            //在未击中时,准星法线方向和摄像机视线方向一致
            reticleCanvas.transform.forward = Camera.main.transform.forward;
            reticleImage.fillAmount = 0;
        }
	}
}

6️⃣ VRGazeItem

该代码挂载到被击中的物体上,实现该物体被击中时的效果

与该代码配套的是两个material:HighlightMat和NormalMat,实现3D物体被凝视时材质的变换

using UnityEngine;
using UnityEngine.EventSystems;
 
public class VRGazeItem : MonoBehaviour {
 
    public Material highlightMat;
    public Material normalMat;
 
    //视线进入
    public void OnGazeIn()
    {
        if (gameObject.tag == "gazeUI")
        {
            //VR场景中UI实现鼠标移入效果
            ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerEnterHandler);
        }else if (gameObject.tag == "gazeObj")
        {
            gameObject.GetComponent<Renderer>().material = highlightMat;
        }
    }
 
    //视线移出
    public void OnGazeOut()
    {
        if (gameObject.tag == "gazeUI")
        {
            //VR场景中UI实现鼠标移出入效果
            ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerExitHandler);
        }
        else if (gameObject.tag == "gazeObj")
        {
            gameObject.GetComponent<Renderer>().material = normalMat;
        }
    }
 
    //凝视处理函数
    public void OnGazeFire(RaycastHit hit)
    {
        if (gameObject.tag == "gazeUI")
        {
            gameObject.SetActive(false);
        }
        else if (gameObject.tag == "gazeObj")
        {
            gameObject.GetComponent<Rigidbody>().AddForceAtPosition(hit.point.normalized * 100, hit.point);
        }
    }
}

7️⃣ 为准星添加shader

该名为UIOverlay的shader解决了准星面片贴近物体表面闪烁问题

将添加了该shader的material赋值到准星和准星的背景板上即可。

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
 
Shader "UI/Overlay"
{
	Properties
	{
		[PerRendererData] _MainTex ("Font Texture", 2D) = "white" {}
 
		_Color("Tint", Color) = (1,1,1,1)
 
		_StencilComp ("Stencil Comparison", Float) = 8
		_Stencil ("Stencil ID", Float) = 0
		_StencilOp ("Stencil Operation", Float) = 0
		_StencilWriteMask ("Stencil Write Mask", Float) = 255
		_StencilReadMask ("Stencil Read Mask", Float) = 255
 
		_ColorMask ("Color Mask", Float) = 15
	}
	
	SubShader
	{
		LOD 100
 
		Tags
		{
			"Queue" = "Transparent"
			"IgnoreProjector" = "True"
			"RenderType" = "Transparent"
			"PreviewType"="Plane"
			"CanUseSpriteAtlas" = "True"
		}
 
		Stencil
		{
			Ref [_Stencil]
			Comp [_StencilComp]
			Pass [_StencilOp] 
			ReadMask [_StencilReadMask]
			WriteMask [_StencilWriteMask]
		}
		
		Cull Off
		Lighting Off
		ZWrite Off
		ZTest Always
		Offset -1, -1
		Blend SrcAlpha OneMinusSrcAlpha
		ColorMask [_ColorMask]
 
		Pass
		{
			CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#include "UnityCG.cginc"
				#include "UnityUI.cginc"
 
				struct appdata_t
				{
					float4 vertex : POSITION;
					float2 texcoord : TEXCOORD0;
					float4 color : COLOR;
				};
	
				struct v2f
				{
					float4 vertex : SV_POSITION;
					half2 texcoord : TEXCOORD0;
					fixed4 color : COLOR;
				};
	
				sampler2D _MainTex;
				float4 _MainTex_ST;
				fixed4 _Color;
				fixed4 _TextureSampleAdd;
				
				v2f vert (appdata_t v)
				{
					v2f o;
					o.vertex = UnityObjectToClipPos(v.vertex);
					o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
					o.color = v.color * _Color;
#ifdef UNITY_HALF_TEXEL_OFFSET
					o.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
#endif
 
					return o;
				}
				
				fixed4 frag (v2f i) : SV_Target
				{
					fixed4 col = (tex2D(_MainTex, i.texcoord) + _TextureSampleAdd) * i.color;
					clip (col.a - 0.01);
					return col;
				}
			ENDCG
		}
	}
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值