Object distortions on the edges of window

Started by
11 comments, last by BlackJoker 10 years, 7 months ago

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};
	AdjustWindowRect(&r, WS_TILEDWINDOW, false);
	screenWidth = r.right - r.left;
	screenHeight = r.bottom - r.top;







CameraDX_Free::CameraDX_Free()
{    
	   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
void CameraDX_Free::updateViewMatrix()
{
    // 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;

	
    temp(3,0) = -XMVectorGetX(XMVector3Dot(XMLoadFloat3(&m_xAxis), XMLoadFloat3(&m_eye)));
    temp(3,1) = -XMVectorGetX(XMVector3Dot(XMLoadFloat3(&m_yAxis), XMLoadFloat3(&m_eye)));
    temp(3,2) = -XMVectorGetX(XMVector3Dot(XMLoadFloat3(&m_zAxis), XMLoadFloat3(&m_eye)));

	m_viewMatrix = XMLoadFloat4x4(&temp);
}





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

    XMStoreFloat3(&m_zAxis, XMVector3Normalize (XMLoadFloat3(&m_zAxis)));

    m_viewDir = m_zAxis;

    XMStoreFloat3(&m_xAxis, XMVector3Cross( XMLoadFloat3(&up), XMLoadFloat3(&m_zAxis)));
    XMStoreFloat3(&m_xAxis, XMVector3Normalize (XMLoadFloat3(&m_xAxis)));
	
	XMStoreFloat3(&m_yAxis, XMVector3Cross( XMLoadFloat3(&m_zAxis), XMLoadFloat3(&m_xAxis)));    
    XMStoreFloat3(&m_yAxis, XMVector3Normalize (XMLoadFloat3(&m_yAxis)));
    XMStoreFloat3(&m_xAxis, XMVector3Normalize (XMLoadFloat3(&m_xAxis)));

    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(3,0) = -XMVectorGetX(XMVector3Dot(XMLoadFloat3(&m_xAxis), XMLoadFloat3(&m_eye)));

    temp(0,1) = m_yAxis.x;
    temp(1,1) = m_yAxis.y;
    temp(2,1) = m_yAxis.z;
    temp(3,1) = -XMVectorGetX(XMVector3Dot(XMLoadFloat3(&m_yAxis), XMLoadFloat3(&m_eye)));

    temp(0,2) = m_zAxis.x;
    temp(1,2) = m_zAxis.y;
    temp(2,2) = m_zAxis.z;
    temp(3,2) = -XMVectorGetX(XMVector3Dot(XMLoadFloat3(&m_zAxis), XMLoadFloat3(&m_eye)));

    // Extract the pitch angle from the view matrix.

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

	m_viewMatrix = XMLoadFloat4x4(&temp);
    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_projMatrix = XMLoadFloat4x4(&temp);

    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;
};




////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType LightVertexShader(VertexInputType input)
{
	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.

Advertisement

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

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

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.

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

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

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

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?

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

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Shouldn't this line...

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

...be:

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

?

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.

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 wink.png

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.

This topic is closed to new replies.

Advertisement