0

I would like to produce a projection matrix that will render an object from an arbitrary camera. I've managed to setup a viewMatrix that will look at the object from an arbitrary eye position, but I'm having difficulty setting up the projection matrix.

Given that an object centred at (x,y,z) who's furthest point is r from the centre can for any arbitrary orientation can be entirely enclosed within a sphere of radius r with origin (x,y,z), I calculated my perspective matrix as follows:

float dist2Object = (float) Math.sqrt(vec.lengthSquared());

float objectRadius = (float) Math.sqrt(3 * Math.pow(0.5, 2));
float far = dist2Object + objectRadius;
float near = dist2Object - objectRadius;

// fov_2 = FOV / 2
float fov_2 = (float) (Math.asin(objectRadius / dist2Object));

float r = (float) (Math.tan(fov_2));
float frustum_length = far - near;

depthProjectionMatrix.m00 = 1 / r;
depthProjectionMatrix.m11 = depthProjectionMatrix.m00;
depthProjectionMatrix.m22 = -((far + near) / frustum_length);
depthProjectionMatrix.m23 = -1;
depthProjectionMatrix.m32 = -((2 * near * far) / frustum_length);
depthProjectionMatrix.m33 = 0;

In my example:

  • vec is a vector from the camera to the object
  • the object is a cube who's furthest vertex is (0.5, 0.5, 0.5), giving a r of sqrt(0.75)

As far as I can tell, the geometry and trigonometry should be correct, but rendering the coordinates using the following fragment shader:

#version 150 core

in vec3 pCoord;

out vec4 out_Color;

void main(void) {
    out_Color = vec4(0,1,0,1);
    if(pCoord.x <= -1){
        out_Color = vec4(1,0,0,1);
    }
    if(pCoord.x >= 1){
        out_Color = vec4(0,0,1,1);
    }
    if(pCoord.z <= -1){
        out_Color = vec4(1,0,1,1);
    }
    if(pCoord.z >= 1){
        out_Color = vec4(1,0,1,1);
    }
}

shown at image shows that the FOV is too narrow and that the near and far planes are also too narrow.

How can I fix this?

  • If you want near and far to be just touching the object, then the camera to object distance would have to enter your formulas. I don't see this in the code. – Dawnkeeper Jul 04 '14 at 10:34
  • @Dawnkeeper The vector from the camera to the object is stored in vec and the distance to between camera and object is stored in dist2Object. – AdvancedGarde89 Jul 04 '14 at 10:54

1 Answers1

0

Haven't done this in a while, but it seem you are missing the viewport size in your calculation. Here is what I used for a project. Mind that the matrix here starts with _11 where you use m00:

double aspectRatio = (double)this.viewPort.Width /(double)this.viewPort.Height;
double fov = Math.toRadians(fieldOfView/2.0);
double size = nearClip * Math.tan(fov);

double left = -size* aspectRatio, right = size* aspectRatio, bottom = -size , top = size ;

projectionMatrix.resetToZero();

// the values in comments are for non symetrical frustrum 

// First Column
projectionMatrix._11 = (float) (nearClip/right);//(float) ((2 * nearClip )/ (double)(right - left));

// Second Column
projectionMatrix._22 = (float)(nearClip/top);//(float) (2 * nearClip / (double)(top - bottom));

// Third Column
projectionMatrix._31 = 0;//(float) ((right + left) / (right - left));
projectionMatrix._32 = 0;//(float) ((top + bottom) / (top - bottom));
projectionMatrix._33 = -1*(farClip + nearClip) / (float)(farClip - nearClip);
projectionMatrix._34 = -1;

// Fourth Column
projectionMatrix._43 = -(2 * farClip * nearClip) / (float)(farClip - nearClip);
Dawnkeeper
  • 2,844
  • 1
  • 25
  • 41
  • Thank you for your response. If the aspect ratio is 1 (square), then top == right == size. Since size = near * tan(FOV/2), the assignment to m.11 and m.22 becomes: near / (near * size). This is the same as 1/size which would make our two projection matrices equivalent. The only difference would be on how FOV is calculated (not shown in your code sample). I'll check if my FOV is correct. – AdvancedGarde89 Jul 04 '14 at 12:12
  • The FOV in my example was set by hand/code and not calculated. And your FOV seems to be ... wait shoudn't that be `Math.sin(objectRadius / dist2Object)`? – Dawnkeeper Jul 04 '14 at 12:23
  • I'm using arcsine (inverse sin, Math.asin in java) to calculate the angle from the hypotenuse (distance) and opposite (radius). However, it isn't quite correct .. I should be using arcsecant. I'll check that now. – AdvancedGarde89 Jul 04 '14 at 12:34
  • As you have a right-angled triangle sine should be ok. see [Wiki](https://en.wikipedia.org/wiki/Trigonometric_functions#Right-angled_triangle_definitions) – Dawnkeeper Jul 04 '14 at 12:46
  • So, correcting the FOV calculation to asin(objectRadius / dist2Object) [the trigonometric equivalent of arcsec(dist2Object / objectRadius)] has fixed the issue regarding the FOV being too small. However, there is still a problem with my near and far plane calculation as shown in [image](http://imgbin.org/index.php?page=image&id=18222) From experimentation, it seems to be some sort of non-linear scale ... but I can't figure out much more than that. – AdvancedGarde89 Jul 04 '14 at 12:47
  • So, I've discovered that a large part of the problem was that my vectors were not being normalised (I wasn't dividing xyz by w). The FOV calculation works now and the objects are nicely bound by z is still giving some un expected number. – AdvancedGarde89 Jul 04 '14 at 18:29