Just pasting the google cardboard script. The most bottom part of the code is mockFieldOfView is the variable which manipulates the FOV of cardboard camera. Please go through it once
// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if UNITY_ANDROID && !UNITY_EDITOR
#define ANDROID_DEVICE
#endif
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
// A Cardboard object serves as the bridge between the C# and the Java/native
// components of the plugin.
//
// On each frame, it's responsible for
// - Reading the current head orientation and eye transforms from the Java
// vrtoolkit
// - Triggering the native (C++) UnityRenderEvent callback at the end of the
// frame. This sends the texture containing the latest frame that Unity
// rendered into the Java vrtoolkit to be corrected for lens distortion and
// displayed on the device.
public class Cardboard : MonoBehaviour
{
// Distinguish the two stereo eyes.
public enum Eye
{
Left,
Right
}
// Lacking any better knowledge, use this as the inter-pupillary distance.
public const float NOMINAL_IPD = 0.06f;
// The singleton instance of the Cardboard class.
private static Cardboard sdk = null;
public static Cardboard SDK
{
get
{
if (sdk == null)
{
Debug.Log ("Creating SDK object");
var go = new GameObject ("Cardboard SDK", typeof(Cardboard));
go.transform.localPosition = Vector3.zero;
}
return sdk;
}
}
// Whether VR-mode is enabled.
[SerializeField]
[HideInInspector]
private bool
vrModeEnabled = true;
public bool VRModeEnabled
{
get
{
return vrModeEnabled;
}
set
{
vrModeEnabled = value;
}
}
[SerializeField]
[HideInInspector]
private bool
enableAlignmentMarker = true;
// Whether to draw the alignment marker. The marker is a vertical line that
// splits the viewport in half, designed to help users align the screen with the Cardboard.
public bool EnableAlignmentMarker
{
get
{
return enableAlignmentMarker;
}
set
{
enableAlignmentMarker = value;
#if ANDROID_DEVICE
try {
cardboardActivity.Call("setEnableAlignmentMarker", enableAlignmentMarker);
} catch (AndroidJavaException e) {
Debug.LogError("Failed to SetEnableAlignmentMarker: " + e);
}
#endif
}
}
[SerializeField]
[HideInInspector]
private bool
enableSettingsButton = true;
// Whether to draw the settings button. The settings button opens the Google
// Cardboard app to allow the user to configure their individual settings and Cardboard
// headset parameters
public bool EnableSettingsButton
{
get
{
return enableSettingsButton;
}
set
{
enableSettingsButton = value;
#if ANDROID_DEVICE
try {
cardboardActivity.Call("setEnableSettingsButton", enableSettingsButton);
} catch (AndroidJavaException e) {
Debug.LogError("Failed to SetEnableSettingsButton: " + e);
}
#endif
}
}
// Whether the device is in the Cardboard.
public bool InCardboard { get; private set; }
// Defer updating InCardboard till end of frame.
private bool newInCardboard;
#if UNITY_EDITOR
// Helper for the custom editor script.
public void SetInCardboard(bool value)
{
newInCardboard = value; // Takes effect at end of frame.
}
#endif
// Whether the Cardboard trigger (i.e. magnet) was pulled.
// True for exactly one frame (between 2 EndOfFrames) on each pull.
public bool CardboardTriggered
{
get;
private set;
}
// Next frame's value of CardboardTriggered.
private bool newCardboardTriggered = false;
// Whether screen taps are converted to Cardboard trigger events.
public bool TapIsTrigger
{
get
{
return tapIsTrigger;
}
set
{
tapIsTrigger = value;
#if ANDROID_DEVICE
try {
cardboardActivity.Call("setConvertTapIntoTrigger", tapIsTrigger);
} catch (AndroidJavaException e) {
Debug.LogError("Failed to setConvertTapIntoTrigger: " + e);
}
#endif
}
}
[SerializeField]
[HideInInspector]
private bool
tapIsTrigger = false;
// The texture that Unity renders the scene to. This is sent to the plugin,
// which renders it to screen, correcting for lens distortion.
public RenderTexture StereoScreen { get; private set; }
// Transform from body to head.
public Matrix4x4 HeadView { get { return headView; } }
// The transformation from head to eye.
public Matrix4x4 EyeView (Cardboard.Eye eye)
{
return eye == Cardboard.Eye.Left ? leftEyeView : rightEyeView;
}
// The projection matrix for a given eye. This encodes the field of view,
// IPD, and other parameters configured by the SDK.
public Matrix4x4 Projection (Cardboard.Eye eye)
{
return eye == Cardboard.Eye.Left ? leftEyeProj : rightEyeProj;
}
// Local transformation of head.
public Quaternion HeadRotation { get; private set; }
// Local transformations of eyes relative to head.
public Vector3 LeftEyeOffset { get; private set; }
public Vector3 RightEyeOffset { get; private set; }
public Vector3 EyeOffset (Cardboard.Eye eye)
{
return eye == Cardboard.Eye.Left ? LeftEyeOffset : RightEyeOffset;
}
// Minimum distance from the user that an object may be viewed in stereo
// without eye strain, in meters. The stereo eye separation should
// be scaled down if the "center of interest" is closer than this. This
// will set a lower limit on the disparity of the COI between the two eyes.
// See CardboardEye.OnPreCull().
public float MinimumComfortDistance
{
get
{
return 1.0f;
}
}
// Maximum distance from the user that an object may be viewed in
// stereo without eye strain, in meters. The stereo eye separation
// should be scaled up of if the COI is farther than this. This will
// set an upper limit on the disparity of the COI between the two eyes.
// See CardboardEye.OnPreCull().
// Note: For HMDs with optics that focus at infinity there really isn't a
// maximum distance, so this number can be set to "really really big".
public float MaximumComfortDistance
{
get
{
return 100000f; // i.e. really really big.
}
}
public float IPD
{
get
{
return NOMINAL_IPD;
}
}
#if UNITY_IPHONE && !UNITY_EDITOR
[DllImport("__Internal")]
#else
[DllImport("RenderingPlugin")]
#endif
private static extern void InitFromUnity (int textureID);
#if ANDROID_DEVICE
private const string cardboardClass =
"com.google.vrtoolkit.cardboard.plugins.unity.UnityCardboardActivity";
private AndroidJavaObject cardboardActivity;
#endif
// Only call native layer once per frame.
private bool updated = false;
// Head and eye transforms retrieved from native SDK.
private Matrix4x4 headView;
private Matrix4x4 leftEyeView;
private Matrix4x4 leftEyeProj;
private Matrix4x4 rightEyeView;
private Matrix4x4 rightEyeProj;
// Configures which Cardboard features are enabled, depending on
// the Unity features available.
private class Config
{
// Features.
public bool supportsRenderTextures;
public bool isAndroid;
public bool supportsAndroidRenderEvent;
public bool isAtLeastUnity4_5;
// Access to plugin.
public bool canAccessActivity = false;
// Should be called on main thread.
public void initialize ()
{
supportsRenderTextures = SystemInfo.supportsRenderTextures;
isAndroid = Application.platform == RuntimePlatform.Android;
try
{
Regex r = new Regex (@"(\d+\.\d+)\..*");
string version = r.Replace (Application.unityVersion, "$1");
if (new Version (version) >= new Version ("4.5"))
{
isAtLeastUnity4_5 = true;
}
}
catch
{
Debug.LogWarning ("Unable to determine Unity version from: " + Application.unityVersion);
}
supportsAndroidRenderEvent = isAtLeastUnity4_5 && isAndroid;
}
public bool canApplyDistortionCorrection ()
{
return supportsRenderTextures && supportsAndroidRenderEvent && canAccessActivity;
}
public string getDistortionCorrectionDiagnostic ()
{
List<string> causes = new List<string> ();
if (!isAndroid)
{
causes.Add ("Must be running on Android device");
}
else if (!canAccessActivity)
{
causes.Add ("Cannot access UnityCardboardActivity. " + "Verify that the jar is in Assets/Plugins/Android");
}
if (!supportsRenderTextures)
{
causes.Add ("RenderTexture (Unity Pro feature) is unavailable");
}
if (!isAtLeastUnity4_5)
{
causes.Add ("Unity 4.5+ is needed for Android UnityPluginEvent");
}
return String.Join ("; ", causes.ToArray ());
}
}
private Config config = new Config ();
void Awake ()
{
if (sdk == null)
{
sdk = this;
}
else
{
Debug.LogWarning ("Cardboard SDK object should be a singleton.");
enabled = false;
}
config.initialize ();
#if ANDROID_DEVICE
try {
AndroidJavaClass player = new AndroidJavaClass(cardboardClass);
cardboardActivity = player.CallStatic<AndroidJavaObject>("getActivity");
player.Dispose();
cardboardActivity.Call("initFromUnity", gameObject.name);
config.canAccessActivity = true;
} catch (AndroidJavaException e) {
Debug.LogError("Cannot access UnityCardboardActivity. "
+ "Verify that the jar is in Assets/Plugins/Android. " + e);
}
// Force side-effectful initialization using serialized values.
EnableAlignmentMarker = enableAlignmentMarker;
EnableSettingsButton = enableSettingsButton;
TapIsTrigger = tapIsTrigger;
#endif
if (config.canApplyDistortionCorrection ())
{
Debug.Log ("Creating new cardboard screen texture");
StereoScreen = new RenderTexture (Screen.width, Screen.height, 16,
RenderTextureFormat.RGB565);
StereoScreen.Create ();
InitFromUnity (StereoScreen.GetNativeTextureID ());
} else {
if (!Application.isEditor) {
Debug.LogWarning ("Lens distortion-correction disabled. Causes: ["
+ config.getDistortionCorrectionDiagnostic () + "]");
}
}
InCardboard = newInCardboard = false;
#if UNITY_EDITOR
if (VRModeEnabled && Application.isPlaying) {
SetInCardboard(true);
}
#endif
StartCoroutine ("EndOfFrame");
}
#if UNITY_EDITOR
private const float TAP_TIME_LIMIT = 0.2f;
private float touchStartTime = 0;
void Update() {
if (!InCardboard) {
return; // Only simulate trigger pull if there is a trigger to pull.
}
if (Input.GetMouseButtonDown(0)) {
touchStartTime = Time.time;
} else if (Input.GetMouseButtonUp(0)) {
if (Time.time - touchStartTime <= TAP_TIME_LIMIT) {
newCardboardTriggered = true;
}
touchStartTime = 0;
}
}
#endif
#if ANDROID_DEVICE
// Right-handed to left-handed matrix converter.
private static readonly Matrix4x4 flipZ = Matrix4x4.Scale(new Vector3(1, 1, -1));
// Call the SDK (if needed) to get the current transforms for the frame.
// This is public so any game script can do this if they need the values.
public bool UpdateState() {
if (updated) {
return true;
}
float[] frameInfo = null;
try {
// Pass nominal clip distances - will correct later for each camera.
frameInfo = cardboardActivity.Call<float[]>("getFrameParams",
1.0f /* near */,
1000.0f /* far */);
} catch (AndroidJavaException e) {
Debug.LogError("Exception: " + e);
}
// FIXME: Should not call getFrameParams() more than once.
if (frameInfo == null) {
return false;
}
// Extract the matrices (currently that's all we get back).
int j = 0;
for (int i = 0; i < 16; ++i, ++j) {
headView *= frameInfo[j];*
}
for (int i = 0; i < 16; ++i, ++j) {
leftEyeView = frameInfo[j];
}
for (int i = 0; i < 16; ++i, ++j) {
leftEyeProj = frameInfo[j];
}
for (int i = 0; i < 16; ++i, ++j) {
rightEyeView = frameInfo[j];
}
for (int i = 0; i < 16; ++i, ++j) {
rightEyeProj = frameInfo[j];
}
// Convert views to left-handed coordinates because Unity uses them
// for Transforms, which is what we will update from the views.
// Also invert because the incoming matrices go from camera space to
// cardboard space, and we want the opposite.
// Lastly, cancel out the head rotation from the eye views,
// because we are applying that on a parent object.
leftEyeView = flipZ * headView * leftEyeView.inverse * flipZ;
rightEyeView = flipZ * headView * rightEyeView.inverse * flipZ;
headView = flipZ * headView.inverse * flipZ;
HeadRotation = Quaternion.LookRotation(headView.GetColumn(2),
headView.GetColumn(1));
LeftEyeOffset = leftEyeView.GetColumn(3);
RightEyeOffset = rightEyeView.GetColumn(3);
updated = true;
return true;
}
#else
// public float mockFieldOfView = 77.0f; // Vertical degrees.
* private float zeroParallaxDistance = 0.308f; // Meters in real world.*
#if UNITY_EDITOR
[Tooltip(“When playing in the editor, just release Ctrl to untilt the head.”)]
public bool autoUntiltHead = true;
// Use mouse to emulate head in the editor.
private float mouseX = 0;
* private float mouseY = 0;*
private float mouseZ = 0;
#endif
* // Mock implementation for use in the Unity editor.*
* public bool UpdateState ()*
* {*
* if (updated) {*
* return true;*
* }*
* // NOTE: View matrices have to be camera->world and use left-handed*
* // coordinates (+Z is forward). See the flip/invert/flip code*
* // in the Android version of this function.*
* headView = Matrix4x4.identity;*
#if UNITY_EDITOR
bool rolled = false;
if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)) {
mouseX += Input.GetAxis(“Mouse X”) * 5;
if (mouseX <= -180) {
mouseX += 360;
} else if (mouseX > 180)
mouseX -= 360;
mouseY -= Input.GetAxis(“Mouse Y”) * 2.4f;
mouseY = Mathf.Clamp(mouseY, -80, 80);
} else if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) {
rolled = true;
mouseZ += Input.GetAxis(“Mouse X”) * 5;
mouseZ = Mathf.Clamp(mouseZ, -80, 80);
}
if (!rolled && autoUntiltHead) {
// People don’t usually leave their heads tilted to one side for long.
mouseZ = Mathf.Lerp(mouseZ, 0, Time.deltaTime / (Time.deltaTime + 0.1f));
}
var rot = Quaternion.Euler(mouseY, mouseX, mouseZ);
headView = Matrix4x4.TRS(Vector3.zero, rot, Vector3.one);
#endif
* float eyeoffset = -IPD / 2;*
* leftEyeView = Matrix4x4.identity;*
* leftEyeView [0, 3] = eyeoffset;*
* leftEyeProj = Matrix4x4.Perspective (mockFieldOfView,*
0.5f * Screen.width / Screen.height,
1.0f, 1000.0f);
_ leftEyeProj [0, 2] = eyeoffset / zeroParallaxDistance * leftEyeProj [0, 0];_
* // Right matrices same as left ones but for some sign flippage.*
* rightEyeView = leftEyeView;*
_ rightEyeView [0, 3] = -1;
rightEyeProj = leftEyeProj;*
rightEyeProj [0, 2] *= -1;_
* HeadRotation = Quaternion.LookRotation (headView.GetColumn (2),*
headView.GetColumn (1));
* LeftEyeOffset = leftEyeView.GetColumn (3);*
* RightEyeOffset = rightEyeView.GetColumn (3);*
* updated = true;*
* return true;*
* }*
#endif
#if ANDROID_DEVICE
// How long to hold a simulated screen tap.
private const float TAP_INJECTION_TIME = 0.1f;
private const long NO_DOWNTIME = -1; // Sentinel for when down time is not set.
private long downTime = NO_DOWNTIME; // Time the current tap injection started, if any.
// Fakes a screen tap by injecting a pointer-down and pointer-up events
// with a suitable delay between them.
IEnumerator DoAndroidScreenTap(int x, int y) {
if (downTime != NO_DOWNTIME) { // Sanity check.
yield break;
}
try {
downTime = cardboardActivity.Call(“injectTouchDown”, x, y);
} catch (AndroidJavaException e) {
Debug.LogError("Failed to inject touch down: " + e);
yield break;
}
yield return new WaitForSeconds(TAP_INJECTION_TIME);
try {
cardboardActivity.Call(“injectTouchUp”, x, y, downTime);
} catch (AndroidJavaException e) {
Debug.LogError("Failed to inject touch up: " + e);
}
downTime = NO_DOWNTIME;
}
#endif
* // Makes Unity see a mouse click (down + up) at the given pixel.*
* public void InjectMouseClick (int x, int y)*
* {*
#if ANDROID_DEVICE
if (downTime == NO_DOWNTIME) { // If not in the middle of a tap injection.
StartCoroutine(DoAndroidScreenTap(x, y));
}
#endif
* }*
* // Makes Unity see a mouse move to the given pixel.*
* public void InjectMouseMove (int x, int y)*
* {*
* if (x == (int)Input.mousePosition.x && y == (int)Input.mousePosition.y) {*
* return; // Don’t send a 0-pixel move.*
* }*
#if ANDROID_DEVICE
if (downTime == NO_DOWNTIME) { // If not in the middle of a tap injection.
try {
cardboardActivity.Call(“injectMouseMove”, x, y);
} catch (AndroidJavaException e) {
Debug.LogError("Failed to inject mouse move: " + e);
}
}
#endif
* }*
* void OnInsertedIntoCardboardInternal ()*
* {*
* newInCardboard = true;*
* }*
* void OnRemovedFromCardboardInternal ()*
* {*
* newInCardboard = false;*
* }*
* void OnCardboardTriggerInternal ()*
* {*
* newCardboardTriggered = true;*
* }*
* void OnDestroy ()*
* {*
* StopCoroutine (“EndOfFrame”);*
* if (sdk == this) {*
* sdk = null;*
* }*
* }*
* IEnumerator EndOfFrame ()*
* {*
* while (true) {*
* yield return new WaitForEndOfFrame ();*
* if (vrModeEnabled && StereoScreen != null && UpdateState ()) {*
* GL.InvalidateState (); // necessary for Windows, but not Mac.*
* GL.IssuePluginEvent (1);*
* }*
* InCardboard = newInCardboard;*
* CardboardTriggered = newCardboardTriggered;*
* newCardboardTriggered = false;*
* updated = false;*
* }*
* }*
#if !UNITY_EDITOR
* private GUIStyle style;*
* // The amount of time (in seconds) to show error message on GUI.*
* private const float GUI_ERROR_MSG_DURATION = 10;
_ private const string warning =
@"Distortion correction is disabled.*
Requires Unity Android Pro v4.5+.
See log for details.";_
* void OnGUI ()*
* {*
* if (Debug.isDebugBuild && !config.canApplyDistortionCorrection () &&*
* Time.realtimeSinceStartup <= GUI_ERROR_MSG_DURATION) {
_ DisplayDistortionCorrectionDisabledWarning ();
}
}*_
* private void DisplayDistortionCorrectionDisabledWarning ()*
* {*
* if (style == null) {*
* style = new GUIStyle ();*
* style.normal.textColor = Color.red;*
* style.alignment = TextAnchor.LowerCenter;*
* }*
* if (VRModeEnabled) {*
_ style.fontSize = (int)(50 * Screen.width / 1920f / 2);
* GUI.Label (new Rect (0, 0, Screen.width / 2, Screen.height), warning, style);
GUI.Label (new Rect (Screen.width / 2, 0, Screen.width / 2, Screen.height), warning, style);
} else {
style.fontSize = (int)(50 * Screen.width / 1920f);
GUI.Label (new Rect (0, 0, Screen.width, Screen.height), warning, style);
}
}
#endif*
#if UNITY_ANDROID
public float mockFieldOfView = 77.0f;
#endif
}_