VR / OpenVR sample code

Does anyone have a working sample using OpenVR and Monogame?

I’ve been trying to get this working, with no luck so far.

Web searches have not yielded much… almost all samples / demos are using Unity.


I think demonaxis has a working demo on github.
Dunno if this is what your looking for i don’t know anything about vr.

Thanks… I did look at his code, but it’s pretty tightly coupled with his engine, and it’s hard to unravel all the rendering stuff.

I did manage to get the initialization and status stuff working, but getting something to display is proving to be quite difficult.

Did you see this post its got a lot of info and looks like quite a few of the guys here have gotten into it.

Maybe they can point you in the right direction when they see this.

I’m working on an OpenVR project and am in the process of writing a nice OOP binding layer on top of the flat bindings that OpenVR generates. The layer on top is completely platform agnostic and a standalone library. I won’t make them public yet, but here’s a rundown of what I do to get stuff working:


  • OpenVR.IsRuntimeInstalled
  • OpenVR.IsHmdPresent (this isn’t actually reliable in my experience)
  • OpenVR.Init (error code 108 means no HMD connected, so you can still detect this even if the previous method does not work)
  • for i in (0 .. OpenVR.k_unMaxTrackedDeviceCount) check CVRSystem.GetTrackedDeviceClass and CVRSystem.GetControllerRoleForTrackedDeviceIndex to find the left and right controllers.
  • CVRSystem.GetRecommendedRenderTargetSize to get the width and height of RTs used for left and right eye.


Complete transformation should be Model * View * Eye^-1 * Projection where Eye is the result of CVRSystem.GetEyeToHeadTransform and Projection is the result of CVRSystem.GetProjectionMatrix. For non-VR rendering (or to make sure you render anything to the HMD at all) just use the identity matrix for eye and a typical perspective projection. I.e.

var aspect = GraphicsDevice.Viewport.AspectRatio;
projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView, aspect, NearPlane, FarPlane);

Probably a good idea to run without VR first, to make sure the issue is really with the VR setup. View should include the HMD transform, see below. Obviously this should also be the identity matrix without VR.

Matrices returned by OpenVR are left-handed. Make sure you transpose them when converting to a MonoGame Matrix.

public static Matrix ToMg(this HmdMatrix34_t mat)
    var m = new Matrix(
        mat.m0, mat.m4, mat.m8, 0.0f,
        mat.m1, mat.m5, mat.m9, 0.0f,
        mat.m2, mat.m6, mat.m10, 0.0f,
        mat.m3, mat.m7, mat.m11, 1.0f);  
    return m;
public static Matrix ToMg(this HmdMatrix44_t mat)
    var m = new Matrix(
        mat.m0, mat.m4, mat.m8, mat.m12,
        mat.m1, mat.m5, mat.m9, mat.m13,
        mat.m2, mat.m6, mat.m10, mat.m14,
        mat.m3, mat.m7, mat.m11, mat.m15);  
    return m;

You must call OpenVR.Compositor.WaitGetPoses every frame. This updates poses for tracked devices like the controllers and HMD. I tried testing my set up before implementing this part and OpenVR did not render anything if I didn’t call this method. The index of the HMD is always 0, so the first pose in the returned arrays can be used for the HMD view.


When you’re done rendering to your eye RenderTargets like you normally would, but with their respective transformations you’ll need to submit them to OpenVR. It’s easiest to let OpenVR handle the distortion and simply submit a handle to your RTs. You can use reflection to get the native handle to the Texture2D. For DesktopGL you can use the following snippet:

var fieldInfo = typeof(Texture2D).GetField("glTexture", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
var handle = new IntPtr((int) fieldInfo.GetValue(myEyeRenderTarget));

Then submit

var tex = new Texture_t();
tex.handle = handle;
tex.eType = ETextureType.OpenGL;
tex.eColorSpace = EColorSpace.Auto;
var texBounds = new VRTextureBounds_t();
texBounds.uMin = 0;
texBounds.uMax = 1;
texBounds.vMin = 0;
texBounds.vMax = 1;
OpenVR.Compositor.Submit(eye, ref tex, ref texBounds, EVRSubmitFlags.Submit_Default);


  • Just call OpenVR.Shutdown

Hope that helps :slight_smile: If you want to see some of the code, feel free to DM me.

@Jjagg thanks for the pointers! I’ll try this out today and will ping you if I have questions.

Fingers crossed that it works this time… :slight_smile:

1 Like