Advertisement

World position from depth

Started by September 05, 2019 08:12 PM
5 comments, last by Noxil 5 years ago

Hello

What i would like to do is to calculate the world space position of a pixel in a pixel shader used on a screen quad.

I understand that this is a "common" topic and i have read several posts about this, but they never show the whole picture and most of them are based on OpenGL when im using Directx 11. Im clearly missing something because its just not working for me.

The first pass in my render loop is to render a simple GBuffer.


struct PixelInputType {
	float4 position 			: SV_POSITION;
	float depth 				: TEXCOORD0;
	float2 uv 					: TEXCOORD1;
	float3 normal 				: TEXCOORD2;
};

PixelInputType vs_main( VertexInputType p_Input ) {
	PixelInputType output;
	output.position = mul( float4(p_Input.position, 1.0f), PM_MODEL_VIEW_PROJECTION );
	output.depth = output.position.z;
	// code removed for clairity
	return output;
};

struct PixelOutputType {
	float4 diffuse;
	float4 normal;
	float depth;
};

PixelOutputType ps_main( PixelInputType p_Input ) : SV_TARGET {
	PixelOutputType output;
	// code removed for clairity
	output.depth = p_Input.depth / PM_FAR_CLIP;
	return output;
}

Then i draw the screen quad where i try to recreate the world space position for each pixel.


Texture2D<float> 	g_Depth : register( t2 );

float3 world_position_from_depth( float2 uv, float depth ) {
	float4 ndc = float4( uv * 2.0f - 1.0f, depth * 2.0f - 1.0f, 1.0f );
	float4 clip = mul( ndc, PM_INVERSE_PROJECTION );
	float4 view = mul( clip / clip.w, PM_INVERSE_VIEW );
	return view.xyz;
}

float4 ps_main( PixelInputType p_Input ) : SV_TARGET {
	float depth = g_Depth.Sample( g_ClampSampler, p_Input.uv );
  	float3 wp = world_position_from_depth( p_Input.uv, depth );
  	return visualize_position( wp );
}

I then visualize the position based on this thread: https://discourse.threejs.org/t/reconstruct-world-position-in-screen-space-from-depth-buffer/5532
From the result i can clearly tell that something is wrong. My lines are not "stationary" they move around and bend instead of sticking to their world space position when i move my camera in the world.

Shader constants used in the shaders are applied like this


PM_FAR_CLIP = p_Camera->getFarClip(); // 1000.0f
PM_INVERSE_PROJECTION = p_Camera->getProjection().inverse().transpose();
PM_INVERSE_VIEW = p_Camera->getView().inverse().transpose();
PM_MODEL_VIEW_PROJECTION = ( p_Transform * p_Camera->getView() * p_Camera->getProjection() ).transpose();

And finally, the depth target is created using DXGI_FORMAT_R16_FLOAT.

It became quite a long post but i wanted to ensure that i gave as much detail as i could since i cant figure out what im doing wrong.
If you have any suggestion or ideas they more than welcome! :)

Have you verified that your visualization lines work as intended? Have you tried adding a few on constant vectors, and making sure they show up as you expect them to?

Are you certain you're casting the lines once, saving them, and then viewing those same lines later, and not accidentally re-calculating some part of them based on the constantly-changing depth buffer you're using to view them?

RIP GameDev.net: launched 2 unusably-broken forum engines in as many years, and now has ceased operating as a forum at all, happy to remain naught but an advertising platform with an attached social media presense, headed by a staff who by their own admission have no idea what their userbase wants or expects.Here's to the good times; shame they exist in the past.
Advertisement

Thanks for the suggestion, its not something i have done, but something i will do once i get back from work! :)
I do however believe that it is my calculation of the world space position that is wrong, but thats just my naive thinking.

Assuming the visualization is correct, is the way i recreate the world position done correctly?

Okey so i have now tested with constant values for the visualization, i lerp using screen uv coordinates to move between two world positions. The result looks correct, it gives me a red and blue grid (y-coordinate is 0.0f).

Can anyone confirm that my calculation to restor world space is correct?

Okey so i have made some changes, witch makes it look almost correct!
There are some problems, but im in the ballpark now atleast :) 

The first change was to the depth target of the GBuffer, i changed from R16 to R32 format.
Then i changed how i filled the depth value.


PixelInputType vs_main( VertexInputType p_Input ) {
	PixelInputType output;
	// code removed for clarity
	output.position = mul( float4(p_Input.position, 1.0f), PM_MODEL_VIEW_PROJECTION );
	output.depth.x = output.position.z;
	output.depth.y = output.position.w;
	return output;
};

PixelOutputType ps_main( PixelInputType p_Input ) : SV_TARGET {
	PixelOutputType output;
	// code removed for clarity
	output.depth = p_Input.depth.x / p_Input.depth.y;
  	return output;
}

I then changed the world position calculation in the shader for the screen quad.


float3 world_position_from_depth( float2 uv, float depth ) {
	float4 ndc = float4( uv * 2.0f - 1.0f, depth, 1.0f );
	float4 wp = mul( PM_INVERSE_VIEW_PROJECTION, ndc );
	return ( wp / wp.w ).xyz;
}

The ndc value is not correct i belive, but this gives me a result thats almost correct.

As you can see in the attached image, the red and blue lines on the ground plane is almost correct, they have like a rotational offset that slowly increases as the camera rotates.

image.png

What! I fixed it! :) 

Thanks to everyone who took the time to look over the thread and ofcourse thanks Wyrframe for the suggestion!

The change i did, based on the previous changes was an update to the function that calculates world space coordinates.


float3 get_world_position_from_depth( float2 uv, float depth ) {
	float4 ndc = float4( uv * 2.0f - 1.0f, depth, 1.0f );
	ndc.y *= -1.0f;
	float4 wp = mul( ndc, PM_INVERSE_VIEW_PROJECTION );
	return ( wp / wp.w ).xyz;
}

Notice the ndc.y *= -1.0f

And here is how it looks when its working (ignore the background, its not being taken into account properly).

 

image.png

This topic is closed to new replies.

Advertisement