├── .gitignore
├── Assets
├── Resources
│ └── Shaders
│ │ └── Tunnelling.shader
└── Scripts
│ └── ImageEffects
│ └── Tunnelling.cs
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # =============== #
2 | # Unity generated #
3 | # =============== #
4 | /[Ll]ibrary/
5 | /[Tt]emp/
6 | /[Oo]bj/
7 | /[Uu]nity[Gg]enerated/
8 | /[Pp]roject[Ss]ettings/
9 | *.meta
10 |
11 | # ===================================== #
12 | # Visual Studio / MonoDevelop generated #
13 | # ===================================== #
14 | ExportedObj/
15 | *.svd
16 | *.userprefs
17 | *.csproj
18 | *.pidb
19 | *.suo
20 | *.sln
21 | *.user
22 | *.unityproj
23 | *.booproj
24 | .vscode/
25 |
26 | # ============ #
27 | # OS generated #
28 | # ============ #
29 | .DS_Store
30 | .DS_Store?
31 | ._*
32 | .Spotlight-V100
33 | .Trashes
34 | Icon
35 | ehthumbs.db
36 | Thumbs.db
37 | Thumbs.db.meta
38 |
39 | # ===== #
40 | # Other #
41 | # ===== #
42 | *.unitypackage
43 | /[Bb]in/
44 | /Assets/StreamingAssets/
45 | /Assets/[Tt][Ee][Ss][Tt]/
--------------------------------------------------------------------------------
/Assets/Resources/Shaders/Tunnelling.shader:
--------------------------------------------------------------------------------
1 | Shader "Hidden/Tunnelling" {
2 | Properties {
3 | _MainTex ("Texture", 2D) = "white" {}
4 | _AV ("Angular Velocity", Float) = 0
5 | _Feather ("Feather", Float) = 0.1
6 | }
7 | SubShader {
8 | // No culling or depth
9 | Cull Off ZWrite Off ZTest Always
10 |
11 | Pass {
12 | CGPROGRAM
13 | #pragma vertex vert
14 | #pragma fragment frag
15 | #pragma multi_compile __ TUNNEL_SKYBOX
16 |
17 | #include "UnityCG.cginc"
18 |
19 | struct appdata {
20 | float4 vertex : POSITION;
21 | float2 uv : TEXCOORD0;
22 | };
23 |
24 | struct v2f {
25 | float2 uv : TEXCOORD0;
26 | float4 vertex : SV_POSITION;
27 | };
28 |
29 | v2f vert (appdata v) {
30 | v2f o;
31 | o.vertex = UnityObjectToClipPos(v.vertex);
32 | o.uv = v.uv;
33 | return o;
34 | }
35 |
36 | sampler2D _MainTex;
37 | float4 _MainTex_ST;
38 | float _AV;
39 | float _Feather;
40 |
41 | float4x4 _EyeProjection[2];
42 | float4x4 _EyeToWorld[2];
43 |
44 | #if TUNNEL_SKYBOX
45 | samplerCUBE _Skybox;
46 |
47 | inline fixed3 sampleSkybox(float4 vPos){
48 | float3 dir = normalize(mul(_EyeToWorld[unity_StereoEyeIndex], vPos).xyz);
49 | return texCUBE(_Skybox, dir).rgb;
50 | }
51 | #endif
52 |
53 | inline float4 screenCoords(float2 uv){
54 | float2 c = (uv - 0.5) * 2;
55 | float4 vPos = mul(_EyeProjection[unity_StereoEyeIndex], float4(c, 0, 1));
56 | vPos.xyz /= vPos.w;
57 | return vPos;
58 | }
59 |
60 | fixed4 frag (v2f i) : SV_Target {
61 | float2 uv = UnityStereoScreenSpaceUVAdjust(i.uv, _MainTex_ST);
62 | fixed4 col = tex2D(_MainTex, uv);
63 |
64 | float4 coords = screenCoords(i.uv);
65 | float radius = length(coords.xy / (_ScreenParams.xy / 2)) / 2;
66 | float avMin = (1 - _AV) - _Feather;
67 | float avMax = (1 - _AV) + _Feather;
68 | float t = saturate((radius - avMin) / (avMax - avMin));
69 |
70 | #if TUNNEL_SKYBOX
71 | fixed4 effect = fixed4(sampleSkybox(coords),0);
72 | #else
73 | fixed4 effect = fixed4(1,0,0,0);
74 | #endif
75 |
76 | return lerp(col, effect, t);
77 | }
78 | ENDCG
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Assets/Scripts/ImageEffects/Tunnelling.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 |
4 | namespace Sigtrap.ImageEffects {
5 | [RequireComponent(typeof(Camera))]
6 | public class Tunnelling : MonoBehaviour {
7 | #region Public Fields
8 | [Tooltip("Remove for plain black effect.")]
9 | public Cubemap skybox;
10 |
11 | [Header("Angular Velocity")]
12 | ///
13 | /// Angular velocity calculated for this Transform. DO NOT USE HMD!
14 | ///
15 | [Tooltip("Angular velocity calculated for this Transform.\nDO NOT USE HMD!")]
16 | public Transform refTransform;
17 |
18 | ///
19 | /// Below this angular velocity, effect will not kick in. Degrees per second
20 | ///
21 | [Tooltip("Below this angular velocity, effect will not kick in.\nDegrees per second")]
22 | public float minAngVel = 0f;
23 |
24 | ///
25 | /// At/above this angular velocity, effect will be maxed out. Degrees per second
26 | ///
27 | [Tooltip("At/above this angular velocity, effect will be maxed out.\nDegrees per second")]
28 | public float maxAngVel = 180f;
29 |
30 | ///
31 | /// Below this speed, effect will not kick in.
32 | ///
33 | [Tooltip("Below this speed, effect will not kick in.")]
34 | public float minSpeed = 0f;
35 |
36 | ///
37 | /// At/above this speed, effect will be maxed out.
38 | ///
39 | [Tooltip("At/above this speed, effect will be maxed out.\nSet negative for no effect.")]
40 | public float maxSpeed = -1f;
41 |
42 | [Header("Effect Settings")]
43 | ///
44 | /// Screen coverage at max angular velocity.
45 | ///
46 | [Range(0f,1f)][Tooltip("Screen coverage at max angular velocity.\n(1-this) is radius of visible area at max effect (screen space).")]
47 | public float maxEffect = 0.75f;
48 |
49 | ///
50 | /// Feather around cut-off as fraction of screen.
51 | ///
52 | [Range(0f, 0.5f)][Tooltip("Feather around cut-off as fraction of screen.")]
53 | public float feather = 0.1f;
54 |
55 | ///
56 | /// Smooth out radius over time. 0 for no smoothing.
57 | ///
58 | [Tooltip("Smooth out radius over time. 0 for no smoothing.")]
59 | public float smoothTime = 0.15f;
60 | #endregion
61 |
62 | #region Smoothing
63 | private float _avSlew;
64 | private float _av;
65 | #endregion
66 |
67 | #region Shader property IDs
68 | private int _propAV;
69 | private int _propFeather;
70 | #endregion
71 |
72 | #region Eye matrices
73 | Matrix4x4[] _eyeToWorld = new Matrix4x4[2];
74 | Matrix4x4[] _eyeProjection = new Matrix4x4[2];
75 | #endregion
76 |
77 | #region Misc Fields
78 | private Vector3 _lastFwd;
79 | private Vector3 _lastPos;
80 | private Material _m;
81 | private Camera _cam;
82 | #endregion
83 |
84 | #region Messages
85 | void Awake () {
86 | _m = new Material(Shader.Find("Hidden/Tunnelling"));
87 |
88 | if (refTransform == null){
89 | refTransform = transform;
90 | }
91 |
92 | _propAV = Shader.PropertyToID("_AV");
93 | _propFeather = Shader.PropertyToID("_Feather");
94 |
95 | _cam = GetComponent();
96 | }
97 |
98 | void Update(){
99 | Vector3 fwd = refTransform.forward;
100 | float av = Vector3.Angle(_lastFwd, fwd) / Time.deltaTime;
101 | av = (av - minAngVel) / (maxAngVel - minAngVel);
102 |
103 | Vector3 pos = refTransform.position;
104 |
105 | if (maxSpeed > 0) {
106 | float speed = (pos - _lastPos).magnitude / Time.deltaTime;
107 | speed = (speed - minSpeed) / (maxSpeed - minSpeed);
108 |
109 | if (speed > av) {
110 | av = speed;
111 | }
112 | }
113 |
114 | av = Mathf.Clamp01(av) * maxEffect;
115 |
116 | _av = Mathf.SmoothDamp(_av, av, ref _avSlew, smoothTime);
117 |
118 | _m.SetFloat(_propAV, _av);
119 | _m.SetFloat(_propFeather, feather);
120 |
121 | _lastFwd = fwd;
122 | _lastPos = pos;
123 | }
124 |
125 | void OnPreRender(){
126 | // Update eye matrices
127 | Matrix4x4 local;
128 | #if UNITY_2017_2_OR_NEWER
129 | if (UnityEngine.XR.XRSettings.enabled) {
130 | #else
131 | if (UnityEngine.VR.VRSettings.enabled) {
132 | #endif
133 | local = _cam.transform.parent.worldToLocalMatrix;
134 | } else {
135 | local = Matrix4x4.identity;
136 | }
137 |
138 | _eyeProjection[0] = _cam.GetStereoProjectionMatrix(Camera.StereoscopicEye.Left);
139 | _eyeProjection[1] = _cam.GetStereoProjectionMatrix(Camera.StereoscopicEye.Right);
140 | _eyeProjection[0] = GL.GetGPUProjectionMatrix(_eyeProjection[0], true).inverse;
141 | _eyeProjection[1] = GL.GetGPUProjectionMatrix(_eyeProjection[1], true).inverse;
142 |
143 | _eyeProjection[0][1, 1] *= -1f;
144 | _eyeProjection[1][1, 1] *= -1f;
145 |
146 | // Hard-code far clip
147 | _eyeProjection[0][3, 3] = 0.001f;
148 | _eyeProjection[1][3, 3] = 0.001f;
149 |
150 | _eyeToWorld[0] = _cam.GetStereoViewMatrix(Camera.StereoscopicEye.Left);
151 | _eyeToWorld[1] = _cam.GetStereoViewMatrix(Camera.StereoscopicEye.Right);
152 |
153 | _eyeToWorld[0] = local * _eyeToWorld[0].inverse;
154 | _eyeToWorld[1] = local * _eyeToWorld[1].inverse;
155 |
156 | _m.SetMatrixArray("_EyeProjection", _eyeProjection);
157 | _m.SetMatrixArray("_EyeToWorld", _eyeToWorld);
158 |
159 | // Update skybox
160 | if (skybox){
161 | _m.SetTexture("_Skybox", skybox);
162 | _m.EnableKeyword("TUNNEL_SKYBOX");
163 | } else {
164 | _m.DisableKeyword("TUNNEL_SKYBOX");
165 | }
166 | }
167 |
168 | void OnRenderImage(RenderTexture src, RenderTexture dest){
169 | Graphics.Blit(src, dest, _m);
170 | }
171 |
172 | void OnDestroy(){
173 | Destroy(_m);
174 | }
175 | #endregion
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 SixWays
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # UnityVrTunnelling
2 | Simple Unity implementation of *Tunnelling* for VR - dynamic reduction of FOV to combat sim-sickness
3 |
4 | ## DEPRECATED
5 | This package is now deprecated - our advanced plugin [VR Tunnelling Pro is now open source](https://github.com/sigtrapgames/VrTunnellingPro-Unity) and is strongly recommended over this repo.
6 |
7 | ## Tunnelling
8 |
9 | *Tunnelling*, as seen in Ubisoft's Eagle Flight, is dynamic vignetting to reduce FOV in VR while rotating. This reduces perceived motion for players and can be highly effective in reducing sim-sickness.
10 |
11 | ## Use
12 | Just attach to a camera and link the Ref Transform field to the player's vehicle or body (i.e. something which determines their gross rotation in the world - NOT the HMD). The default settings should work ok for most situations.
13 |
14 | Note that Tunnelling.shader must be in the Resources folder, or the shader will work in editor but not in builds.
15 |
16 | ## Cubemaps
17 | Supports drawing a cubemap in the vignette for a "cage" effect.
18 |
19 | ## VR Tunnelling Pro
20 | For many more features, including masking, 3D cage, full cross-platform support, presets and a mobile-friendly version, see our upcoming Unity Asset Store plugin VR Tunnelling Pro: [www.sigtrapgames.com/vrtp](http://www.sigtrapgames.com/vrtp)
21 |
22 | ## Example
23 | 
24 |
--------------------------------------------------------------------------------