# Determine the z coordinate of a deformed plane based on uv (xy) coordinates

I am using a formula to turn the following texture

into what looks like two 3 dimensional planes

The formula I am using for the texture deformation is

float2 texUV = float2(uv.x / abs(uv.y), (1.0 / abs(uv.y)));

Based on the deformation equation above, how can I work out a value for z on the surface of the plane at any given uv (xy) coordinate?

• What's the range of uv? [-1,1] from bottom-left to top-right? – Coburn Oct 25 '16 at 17:32
• In this case the uv coordinates have a range of [0, 1]. [0,0] being bottom left and [1,1] top right. – user1423893 Oct 25 '16 at 17:49
• Are you looking for vertical z or the camera space depth z? – Coburn Oct 25 '16 at 23:04
• I am looking for camera space depth z. I'm not certain what you mean by vertical z in this case. – user1423893 Oct 26 '16 at 11:25

Your image can be interpreted a couple different ways so I will make a few assumptions up front:

• The "geometry" you are emulating is meant to look as if it has already been "projected" (if it was plane geometry and ran through MVP matrices it would produce similar output).
• The planes are flat and not angled.
• The planes are both 0.5 units away from the camera in the vertical z axis.
• Assume the viewport is perpendicular to both of the planes
• No scale, FOV, aspect ratio for the viewport is given. We assume some further down

These assumptions describe a simple version of your scene. We will look at breaking some of these assumptions throughout the answer but keep this in mind for now.

To demonstrate the different depth values, I made a similar image to your question.

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 screenUV = fragCoord.xy / iResolution.xy;
vec2 uv = screenUV.xy * 2.0 - 1.0;

vec2 texUV = vec2(uv.x / abs(uv.y), (1.0 / abs(uv.y)));
vec4 color = texture2D(iChannel0, texUV);
color = vec4(color.r, 0.0, color.ba);
color = mix(vec4(0.0,0.0,0.0,1.0), color, abs(uv.y));

fragColor = color;

/*To render depth values
fragColor = vec4(0.0, depth, 0.0, 1.0); //Just z
fragColor = vec4(fragColor.r, depth, fragColor.ba); //With original textures

will be shown in green!*/
}


You have texUV.y calculated which is over the range [1, +Inf),(+Inf, 1]. This is because 1.0/abs([-1.0,1.0]) == [1, +Inf),(+Inf, 1]. This will be the basis for our depth value as it's what is used to create the planes that have the texture of unit length applied to them. To be a depth value we need to convert it to a [0,1] linear range. To do this we need to

1. Get rid of infinities by clip the original range at a near and far plane.
2. Apply a scale factor such that the planes are properly "projected" (one unit of texture is one unit of world space)

The near plane and far plane are based on your particular use case. I have chosen 0.001 and 100.0 for this example.

const float nearPlane = 0.001;//Depth of 0.0
const float farPlane = 100.0; //Depth of 1.0


The scale factor determines for every unit length of the the plane (which is delineated by one repetition of your texture), how many world space units should there exists. It would seem at first glance we could just choose a simple multiplier but remember we have a scene that's already been "projected". So, actual scale factor needs to reflect the "camera" that's viewing the scene.

If the planes were completely perpendicular to the camera before they were projected, the scale factor is based entirely on the slope of the bottom plane of the camera. We choose a fake FOV and aspect ratio for demonstration but you can use whatever values suit you for these.

const float PI = 3.141592;
const float camFOV = 80.0 * PI/180.0;
const float aspectRatio = 4.0/3.0;

const float camYSlope = tan(atan(aspectRatio * tan(camFOV/2.0))/2.0);


If the planes weren't completely perpendicular, a large value (for planes angled towards the camera) or a smaller value (for planes angled away from the camera couple be substituted for camYSlope. A different vertical z value adjustment would need to be used in the depth calculation as well (instead of the 0.5 assumtion)

The depth can then be calculated like so:

float depth = (clamp(
(texUV.y-1.0)  //Shift by 1 backward ([0, +Inf) (+Inf, 0]
*camYSlope    //Scale by some factor of world units / uv units, using fake camera values gives real output
+0.5           //Add 0.5 to assume that an extra vertical distance for top and bottom plane fragment depth is included
, nearPlane, farPlane) //Clamp to the near and far plane [nearPlane, farPlane]
- nearPlane) //Remove the shift [0.0, farPlane - nearPlane]
/ (farPlane-nearPlane) //Remove the scale [0.0, 1.0]
;


Putting it all together we get

const float nearPlane = 0.001;//Depth of 0.0
const float farPlane = 100.0; //Depth of 1.0

const float PI = 3.141592;
const float camFOV = 80.0 * PI/180.0;
const float aspectRatio = 4.0/3.0;

const float camYSlope = tan(atan(aspectRatio * tan(camFOV/2.0))/2.0);

float depth = (clamp((texUV.y-1.0)*camYSlope+0.5, nearPlane, farPlane) - nearPlane) / (farPlane-nearPlane);


All of the above code can be played with at this shader toy. Changing the nearPlane, farPlane, camFOV, and aspectRatio and hitting ALT + ENTER will update the shader to show the various changes.

Using this final value equation and modulating the near and far plane we can easily change the desired depth values.

nearPlane = 0.001; farPlane = 15.0; //A closer farPlane

• I couldn't get it to work with [0,1] so the uv variable in my answer is [-1,1] and screenUV is [0,1] – Coburn Oct 25 '16 at 20:19
• It looks like you're interpreting "z" to mean the vertical coordinate axis, parallel to the uv.y direction in the image plane. I suspect the question may be about the depth axis (forward along the camera's facing direction/"into" the screen) - as though one were trying to generate a depth buffer consistent with the infinite receding planes illusion. This is usually how z is used in graphics contexts like hlsl shaders, as tagged in the question. – DMGregory Oct 25 '16 at 22:08
• @DMGregory Hmm, perhaps I should have asked. I figured the camera forward z was already being calculated as texUV.y (would just to be transformed a bit) so she/he wasn't asking about it. I asked OP so I'll amend my answer if that is the case – Coburn Oct 25 '16 at 23:05
• Rewrote the answer for depth instead of vertical z. – Coburn Oct 26 '16 at 20:17

For this solution you are gonna need to know what is the normal of the botton and top plane. Lets say we have 2 planes defined by a normal N1 and N2 and a distance from the origen D1 and D2. We also have a XY point, P and a Z vector from this point V(0,0,1).

We will calculate Z1 (coordenate Z respect plane 1) and Z2 (coordenate Z respect plane 2)

t1 = -(P.Dot(N1) + D1) / V.Dot(N1)
Z1 = (P + t1*V).z

t2 = -(P.Dot(N2) + D2) / V.Dot(N2)
Z2 = (P + t2*V).z


You can choose for example the min(Z1,Z2) if it is what you want. If you dont know how to obtein D1 and D2, they are defined by:

D1 = N1.Dot(Any point on plane 1)
D2 = N2.Dot(Any point on plane 2)


If you dont know the normal of this planes but you know 3 points(we will call them P0 P1 and P2) of each plane you can calculate the normal by:

N = (P2 - P0).Cross(P1 - P0).Normalize()


Hope it helps