Ok, just a small update on my progress here. I finally found the bug in my code that was making everything go haywire. I had simply mixed up the row vs column representation of matrixes, I thought they were reversed from what I was doing in my engine but they actually line up.
To whom it may concern, the commented out code in CameraMatrix::set_frustum would work if you swap the indices around and it would be more readable then the current code being executed (though it does exactly the same thing).
Anyway, calculating a standard mono camera matrix can be done as follows:
ymax = p_z_near * tan(p_fovy_degrees * Math_PI / 360.0f);
xmax = ymax * p_aspect;
set_frustum(-xmax, xmax, -ymax, ymax, p_z_near, p_z_far);
CameraMatrix::set_perspective has a slightly different approach but I believe the end result is similar if not identical but I to do the adjustment for stereoscopic I only know the approach calculating a frustum.
I've created a second CameraMatrix::set_perspective that takes 3 extra parameters:
- p_eye which is 0 (mono, same output as a normal camera), 1 (left) and 2 (right)
- p_intraocular_dist which is the distance between the two eyes
- p_convergence_dist which is the depth at which an object would render the same to both eyes. Anything before this distance will pop out of the screen, and anything after will seem to be behind the screen
To create the frustum shift to get a parallel stereo matrix you calculate a shift value as follows based on the intraocular distance and the convergence distance:
frustumshift = (p_intraocular_dist / 2.0) * p_z_near / p_convergence_dist;
Then you either add (left eye) or subtract (right eye) this shift like so:
set_frustum(-xmax + frustumshift, xmax + frustumshift, -ymax, ymax, p_z_near, p_z_far); /* left eye */
At this point in time we have the correct shift of our frustum however our two cameras are still looking outwards from the same point in space. Lamberto made this shift by moving the camera but I'm doing it in code. First it's easy to build in here as we have all our values handy but there are two future reasons I'm doing this:
1) ideally I want to change this logic at some point that we don't have two cameras but one camera that is able to render both viewport
2) when you're using an HMD like an HTC Vive or Oculus Rift the IOD is either adjusted on the headset itself or set through the middleware tools. We're actually not going to calculate our camera matrices at all, we'll be getting them from the HMD
Anyways, I'm getting off topic, we move the cameras by half the IOD. Now this may seem counterintuitive but we're actually add this for the left eye and subtract for the right (we're not moving the camera, it stays at 0,0,0, we're moving the world):
modeltranslation = p_intraocular_dist / 2.0; /* left eye */
// translate matrix by (modeltranslation, 0.0, 0.0)
CameraMatrix cm;
cm.set_identity();
cm.matrix[3][0] = modeltranslation;
*this = *this * cm;
Now that's all there is too it. Well almost. This is where I am at for calculating the correct matrices. There are two more things missing from this part of the code so far:
1) the above would work nicely for a side by side stereoscopic render for an HMD but for a 3DTV the image gets stretched so the aspect ratio is half what it should be. I need to find a nice way to configure that I need to adjust the aspect ratio
2) the above code has the center of the camera in the center of the viewport. Perfect for a 3DTV but for an HMD the center of the camera needs to be aligned with the center of your eye, and your eye is more inwards. We need another translation for adjusting this. This could possibly be done with the H Offset property of the camera.
Ok thats the heart of it which is why I spend a lot of detail about this but other classes needed to be altered.
I had to change the VisualServerRaster class and its parent class so it can deal with the extra properties of the camera.
I also needed to make a number of changes to the Camera class itself.
Both of those I will talk about in more detail tomorrow after I get a good night sleep. For both classes there are a few things that should be improved but where I simply lack experience with GoDot so I'll be interested in feedback. I will also check in the source code so far into GitHub tomorrow.
Finally, I need to adjust Lamberto's example. I've split his example in two so the stereoscopic camera can be used independently of the gyroscope logic he wrote for Android but I need to clean it up some more. All in all its looking pretty good on my 3DTV so far:)
To be continued