• Create Account

Need scary sound effects or creepy audio loops for your next horror-themed game? Check out Highscore Vol.3 - The Horror Edition in our marketplace. 50 sounds and 10 loops for only \$9.99!

# Object distortions on the edges of window

Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

12 replies to this topic

### #1BlackJoker  Members   -  Reputation: 310

Like
0Likes
Like

Posted 19 September 2013 - 06:23 AM

Hello,

I have a really annoying problem with my camera. It distort objects when they close to the window edges (make objects wider than they really are).

I cannot understand why such behavior could be. I want to achieve such behavior when objects will not stretch in any case.

Here is some code from my engine:



//Calculationg correct window size (For D2D)
RECT r = {0, 0, screenWidth, screenHeight};
screenWidth = r.right - r.left;
screenHeight = r.bottom - r.top;




{
m_accumPitchDegrees = 0.0f;
m_rotationSpeed =  0.1f;
m_fovx =  90.0f;
m_aspectRatio = 0.0f;
m_znear = 0.0f;
m_zfar = 1000.0f;
m_CameraSpeedFlight = 10.1f;

m_eye = XMFLOAT3(0.0f, 0.0f, 0.0f);
m_eye_min = XMFLOAT3(0.0f, 0.0f, 0.0f);
m_eye_max = XMFLOAT3(0.0f, 0.0f, 0.0f);
m_xAxis = XMFLOAT3(1.0f, 0.0f, 0.0f);
m_yAxis = XMFLOAT3(0.0f, 1.0f, 0.0f);
m_zAxis = XMFLOAT3(0.0f, 0.0f, 1.0f);
m_viewDir = XMFLOAT3(0.0f, 0.0f, 1.0f);

m_acceleration = XMFLOAT3(0.0f, 0.0f, 0.0f);
m_acceleration_min = XMFLOAT3(0.0f, 0.0f, 0.0f);
m_acceleration_max = XMFLOAT3(0.0f, 0.0f, 0.0f);
m_currentVelocity = XMFLOAT3(0.0f, 0.0f, 0.0f);
m_velocity = XMFLOAT3(0.0f, 0.0f, 0.0f);
m_velocity_max = XMFLOAT3(0.0f, 0.0f, 0.0f);

m_orientation = XMQuaternionIdentity();

m_viewMatrix = XMMatrixIdentity();
m_projMatrix = XMMatrixIdentity();

}



//Updating View Matrix
{
// Reconstruct the view matrix.

m_orientation = XMQuaternionNormalize(m_orientation);
m_viewMatrix=XMMatrixRotationQuaternion(m_orientation);

XMFLOAT4X4 temp;
XMStoreFloat4x4(&temp,m_viewMatrix);

m_xAxis = XMFLOAT3(temp(0,0), temp(1,0), temp(2,0));
m_yAxis = XMFLOAT3(temp(0,1), temp(1,1), temp(2,1));
m_zAxis = XMFLOAT3(temp(0,2), temp(1,2), temp(2,2));
m_viewDir = m_zAxis;

}



//Camera look at
void CameraDX_Free::lookAt(const XMFLOAT3 &eye, const XMFLOAT3 &target, const XMFLOAT3 &up)
{
m_eye = eye;
m_zAxis = target - eye;

m_viewDir = m_zAxis;

m_viewMatrix = XMMatrixIdentity();

XMFLOAT4X4 temp;
XMStoreFloat4x4(&temp,m_viewMatrix);

temp(0,0) = m_xAxis.x;
temp(1,0) = m_xAxis.y;
temp(2,0) = m_xAxis.z;

temp(0,1) = m_yAxis.x;
temp(1,1) = m_yAxis.y;
temp(2,1) = m_yAxis.z;

temp(0,2) = m_zAxis.x;
temp(1,2) = m_zAxis.y;
temp(2,2) = m_zAxis.z;

// Extract the pitch angle from the view matrix.

m_accumPitchDegrees = XMConvertToDegrees(asinf(temp.m[1][2]) );

m_orientation = XMQuaternionRotationMatrix(m_viewMatrix);
}

Camera perspective

void CameraDX_Free::perspective(float fovx, float aspect, float znear, float zfar)
{
// Construct a projection matrix based on the horizontal field of view
// 'fovx' rather than the more traditional vertical field of view 'fovy'.

float e = 1.0f / tanf(XMConvertToRadians(fovx) / 2.0f);
float aspectInv = 1.0f / aspect;
float fovy = 2.0f * atanf(aspectInv / e);
float xScale = 1.0f / tanf(0.5f * fovy);
float yScale = xScale / aspectInv;

XMFLOAT4X4 temp ;
XMStoreFloat4x4(&temp,m_projMatrix);

temp(0,0) = xScale;
temp(1,0) = 0.0f;
temp(2,0) = 0.0f;
temp(3,0) = 0.0f;

temp(0,1) = 0.0f;
temp(1,1) = yScale;
temp(2,1) = 0.0f;
temp(3,1) = 0.0f;

temp(0,2) = 0.0f;
temp(1,2) = 0.0f;
temp(2,2) = zfar / (zfar - znear);
temp(3,2) = -znear * zfar / (zfar - znear);

temp(0,3) = 0.0f;
temp(1,3) = 0.0f;
temp(2,3) = 1.0f;
temp(3,3) = 0.0f;

m_fovx = fovx;
m_aspectRatio = aspect;
m_znear = znear;
m_zfar = zfar;
}


Does anyone knows what could cause this issue and how to fix it and make rendered image near to this:

http://en.wikipedia.org/wiki/File:Panotools5618.jpg (second image).

P.S. maybe something wrong with my Vertex shader?



cbuffer MatrixBuffer
{
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
};

//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
float4 position : POSITION;
float2 tex : TEXCOORD0;
float3 normal : NORMAL;

};

struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
float3 normal : NORMAL;
};

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
{
const float C = 0.1f;
float Far = 100000000.0f;
PixelInputType output;

// Change the position vector to be 4 units for proper matrix calculations.
input.position.w = 1.0f;

// Calculate the position of the vertex against the world, view, and projection matrices.
output.position = mul(input.position, worldMatrix); //перемещение, вращение м скейлинг вертексов
output.position = mul(output.position, viewMatrix);
output.position = mul(output.position, projectionMatrix);

output.position.z = log(C*output.position.z + 1) / log(C*Far + 1) * output.position.w;

// Store the texture coordinates for the pixel shader.
output.tex = input.tex;

// Calculate the normal vector against the world matrix only.
output.normal = mul(input.normal, (float3x3)worldMatrix);

// Normalize the normal vector.
output.normal = normalize(output.normal);

return output;
}


I attached some screen shots from my engine to show the problem.

#### Attached Thumbnails

Edited by BlackJoker, 19 September 2013 - 07:40 AM.

### #2L. Spiro  Crossbones+   -  Reputation: 7244

Like
2Likes
Like

Posted 19 September 2013 - 09:37 AM

You’ve just discovered a problem common to every single 3D game.

There is nothing you can reasonably do about it.  Except post-process the final image into the top image of the Panatools link you provided, since without stretching away from the center that is how it should look.

As you can see, their bottom has stretching as well—the doors towards the edges are way too wide.

Unrelated, but you should be passing only a single world-view-projection matrix and perform 1 multiply instead of 3.

And normals should be multiplied by the inverse transpose of the world-view (if lighting in view-space as you will eventually be doing) or the world (if lighting in world space as you are now (at least once you get lighting done)) matrix.  In other words, whether it is the world matrix or the world-view matrix, normals are multiplied by the inverse transpose of it, not the world or world-view matrix directly.

L. Spiro

Edited by L. Spiro, 19 September 2013 - 09:41 AM.

It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013

I went to my local Subway once to find some guy yelling at the staff.  When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

### #3radioteeth  Prime Members   -  Reputation: 643

Like
2Likes
Like

Posted 19 September 2013 - 02:20 PM

The stretching can be helped by reducing your FOV, but then it might start feeling like you are looking at everything through a telescope if you make it too small.

You've probably noticed racing games increase the FOV with the speed of the player's vehicle, giving the illusion that you are going at extreme speeds.

### #4BlackJoker  Members   -  Reputation: 310

Like
0Likes
Like

Posted 19 September 2013 - 02:38 PM

I tried different FOV, but it not reduce the stretching. Is there really no other way to avoid such behavior with perspective camera?

### #5L. Spiro  Crossbones+   -  Reputation: 7244

Like
2Likes
Like

Posted 19 September 2013 - 03:46 PM

Changing the field-of-view doesn’t “avoid” the problem.  It only makes it less noticeable.

Going back to the same link you presented.

http://en.wikipedia.org/wiki/File:Panotools5618.jpg

The top image is how reality works.

Depth to an eye is based on distance from the eye.

If you stare straight at a wall, the part of the wall at eye level is closest to you and the parts above and below your eye are gradually farther away, thus have a greater “z depth,” thus appear farther away and curve in towards the center the higher and lower you go along the wall.

In a 3D game depth is based on a flat plane, not a single point.

Thus if you stare at a wall head-on, all parts have the same z depth and thus no curvature takes place.

Since things are not curving in towards the center, it means they are implicitly being stretched the further away from the center they get.

This is common to all 3D games, and as I said the only way to fix it is to use a post-processing effect to get the top image in that link.  Which I doubt is the better trade-off.

L. Spiro

It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013

I went to my local Subway once to find some guy yelling at the staff.  When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

### #6BlackJoker  Members   -  Reputation: 310

Like
0Likes
Like

Posted 19 September 2013 - 03:56 PM

Thanks for clear explanation, but could you write more detail about that post-processing effect? What it is exactly and how it works and is it possible to achieve the second (bottom) image from the link above using this post-processing effect?

Edited by BlackJoker, 19 September 2013 - 03:59 PM.

### #7L. Spiro  Crossbones+   -  Reputation: 7244

Like
0Likes
Like

Posted 19 September 2013 - 04:28 PM

That is not what I meant.

You already have the bottom image.  Yes, the doors to the sides of the images are stretched as well.

You have to use post-processing to get the top (the correct) image.

L. Spiro

It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013

I went to my local Subway once to find some guy yelling at the staff.  When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

### #8allingm  Members   -  Reputation: 362

Like
0Likes
Like

Posted 19 September 2013 - 06:05 PM

Shouldn't this line...

float xScale = 1.0f / tanf(0.5f * fovy);

...be:

float xScale = 1.0f / tanf(0.5f * fovx);

?

### #9Álvaro  Crossbones+   -  Reputation: 7963

Like
1Likes
Like

Posted 19 September 2013 - 06:49 PM

Changing the field-of-view doesn’t “avoid” the problem.  It only makes it less noticeable.

I just want to clarify one thing: If you use an FOV such that the angle covered by the camera is the same angle that the monitor covers from where you are sitting, the stretching effect does disappear. But we sit kind of far from the monitor, so that angle is quite small and it is impractical to have say a FPS with that setting.

### #10Hodgman  Moderators   -  Reputation: 19226

Like
1Likes
Like

Posted 19 September 2013 - 08:43 PM

There's no 'correct' or 'wrong' version of those two pictures.

In the example pictures, the top one has been taken with a fish-eye lens (curvilinear perspective), and the bottom one has been "corrected" to be a rectilinear perspective (3 point perspective), which is what we get with a "standard" projection matrix. Note how the doors on the left of the images also distort in size as they approach the edge.

A standard rectillinear perspective with a 90º FOV like you're using will have a lot of distortion at the edges. Most games use around 45-80º (many FPS games default to around 60, with the user option to turn it up to 80). Note that when sitting at your desk, your monitor probably takes up about 20º of your human-binocular 120º field-of-view, which is going to ensure that even a small 45º FOV will not look like a convincing window into another world

If you want to produce an image like the top example, then you can use a fish-eye projection instead of a "standard" projection in your vertex shader. This will produce curves instead of straight lines though, so you'd also need to ensure that all of your objects were tessellated enough so they can actually be bent appropriately.

### #11BlackJoker  Members   -  Reputation: 310

Like
0Likes
Like

Posted 20 September 2013 - 01:12 AM

Shouldn't this line...

float xScale = 1.0f / tanf(0.5f * fovy);

...be:

float xScale = 1.0f / tanf(0.5f * fovx);

?

No, because in that case image will be twisted.

To All

I think the real problem is in projection matrix. I used standard projection matrix by mistake and receive such distortions, when i switched to my custom projection matrix, I receive now more than twice less distortions (70px vs 30px now), but my question is: is it possible to improve projection matrix more to have less distortions leaving FOV 90 degrees?

For now I build my projection matrix like this:



void CameraDX_Free::perspective(float fovx, float aspect, float znear, float zfar)
{
// Construct a projection matrix based on the horizontal field of view
// 'fovx' rather than the more traditional vertical field of view 'fovy'.

float e = 1.0f / tanf(XMConvertToRadians(fovx) / 2.0f);
float aspectInv = 1.0f / aspect;
float fovy = 2.0f * atanf(aspectInv / e);
float xScale = 1.0f / tanf(0.5f * fovy);
float yScale = xScale / aspectInv;

XMFLOAT4X4 temp ;
XMStoreFloat4x4(&temp,m_projMatrix);

temp(0,0) = xScale;
temp(1,0) = 0.0f;
temp(2,0) = 0.0f;
temp(3,0) = 0.0f;

temp(0,1) = 0.0f;
temp(1,1) = yScale;
temp(2,1) = 0.0f;
temp(3,1) = 0.0f;

temp(0,2) = 0.0f;
temp(1,2) = 0.0f;
temp(2,2) = zfar / (zfar - znear);
temp(3,2) = -znear * zfar / (zfar - znear);

temp(0,3) = 0.0f;
temp(1,3) = 0.0f;
temp(2,3) = 1.0f;
temp(3,3) = 0.0f;

m_fovx = fovx;
m_aspectRatio = aspect;
m_znear = znear;
m_zfar = zfar;
}


Edited by BlackJoker, 20 September 2013 - 01:43 AM.

### #12L. Spiro  Crossbones+   -  Reputation: 7244

Like
1Likes
Like

Posted 20 September 2013 - 05:04 AM

So, let me clarify things.

What I described as “correct” was described by Hodgman as being fish-eye, which indeed it often is.

What Álvaro says disappears does disappear, but only as much as the human mind can or typically perceives.  Mathematically, in physics, the curves are still there, just reduced so much that we do not notice them.

When I said the “fish-eye” effect was “correct” I meant physically.  All eyes and cameras have the fish-eye effect, but usually reduced to such a point we simply cannot detect it.  We only start to call it “fish-eye” when it is exaggerated to the point we start to easily notice it.

All humans have the fish-eye effect to some degree, depending on your field-of-view, which depends on how far outside of your sockets your eyes are.

However we are all also trained to focus on what is ahead rather than to the sides.  What happens in our peripheral vision is low-frequency and only meant to drag our eyesight over to put the event into the high-frequency zone the focal-point area of our eyes are designed to digest.

If you can put the high-frequency area on hold for as long as you can and put the line that marks the corner of your room as far into your peripheral as you can and try to focus carefully on that area of your sight, you will be able to see the line marking the corner of your wall curving just as in the first image in the link does.

It’s “correct” based on being physically…correct.

As Hodgman also mention, it is based on a curvilinear perspective, but you are still trying to get non-curved but also non-stretched results in a rectilinear perspective transform.

It’s simply not possible.  The only possible way things can appear to always be the correct widths and heights (not stretched) is with a curvilinear perspective and a huge amount of tessellation.  Or post-processing.

L. Spiro

Edited by L. Spiro, 20 September 2013 - 05:55 AM.

It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013

I went to my local Subway once to find some guy yelling at the staff.  When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

### #13BlackJoker  Members   -  Reputation: 310

Like
0Likes
Like

Posted 20 September 2013 - 07:03 AM

L. Spiro

Thanks for explanation. It is much more clear now.

Foe this moment I use FOV 75 degrees and there is no (or almost no) visible stretching of objects, so I think on this stage I have almost perfect image.

Thanks to all who participated in this discussion.

Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

PARTNERS