# Incorrect Z transform in fragment shader based rasterizer

I have been trying to implement a simple rasterizer in GLSL fragment shader. The goal is to render a quad with texture mapped on it, with correct 3d transforms (perspective and model matrices). I got to the point where it looks ok,except that the Z position of the plane is exactly 2 times closer to the screen than in normal OpenGL rendering pipeline. I double checked all the matrices,the data is correct.Here is my GLSL code:

 uniform mat4 modelMatrix;
uniform mat4 projMatrix;
uniform sampler2D tex;
uniform ivec2 res;
out vec4 oColor;
struct Tri{
vec4 v0;
vec4 v1;
vec4 v2;
vec2 uv0;
vec2 uv1;
vec2 uv2;
};

vec4 vertices[4];
// 2d cross product, aka "wedge product", returns signed area
// of a parallellogram defined by the 2 vectors.
float cross2d(in vec2 v1, in vec2 v2){
return (v1.x*v2.y) - (v1.y*v2.x);
}
bool drawTriangleBary(in Tri tri,in vec2 uv,inout vec4 color){
vec2 p1 = tri.v0.xy;
vec2 p2 = tri.v1.xy;
vec2 p3 = tri.v2.xy;

float triangleArea =  -(cross2d(p2 - p1, p3 - p1)) * 0.5;
float w1 = (cross2d(p2 - uv, p1 - uv) * 0.5) / triangleArea;
float w2 = (cross2d(p2 - uv, uv - p3) * 0.5) / triangleArea;
float w3 = (cross2d(p3 - uv, uv - p1) * 0.5) / triangleArea;

if(w1 < 0.0 || w2 < 0.0 || w3 < 0.0){
return false;
}
float z1 = 1.0 / tri.v0.w;
float z2 = 1.0 / tri.v1.w;
float z3 = 1.0 / tri.v2.w;

float depth = 1.0 / (w2 * z1 + w3 * z2 + w1 * z3) ;
// Perspective correction for UV coordinates
vec2 puv = w2 * (tri.uv0 * z1) + w3 * (tri.uv1 * z2) + w1 * (tri.uv2 * z3);
puv *= depth;
color = texture(tex, puv);
return true;
}

void main(){
vec2 uv = gl_FragCoord.xy / vec2(res);
uv -= vec2(0.5);
//build default vertices:
vertices[0] = vec4(-1.0,-1.0,   0.0,1.0);
vertices[1] = vec4( 1.0,-1.0,   0.0,1.0);
vertices[2] = vec4( 1.0,1.0,    0.0,1.0);
vertices[3] = vec4(-1.0,1.0,    0.0,1.0);

//transform vertices:
for(int c = 0; c < 4; ++c){
vertices[c] = projMatrix  * modelMatrix * vertices[c];
vertices[c].xyz   /=  vertices[c].w; //to NDC
}
//define triangle geometry:
Tri tri1 = Tri(vertices[0],vertices[1],vertices[2]
,vec2(0.0,0.0),vec2(1.0,0.0),vec2(1.0,1.0));

bool rasterize = false;
vec4 colorRes = vec4(0.0);
rasterize = drawTriangleBary(tri1, uv, colorRes);
if(rasterize){
oColor = colorRes;
}
}


I omit the code of drawTriangleBary() which simply calculates barycentric coords,computes correct UVs and samples texture.The area of interest is the part where I transform the vertices. For example,if I multiple .w by 2 the Z position looks correct:

 for(int c = 0; c < 4; ++c){
vertices[c] = projMatrix  * modelMatrix * vertices[c];
vertices[c].xyz   /=  vertices[c].w * 2.0; //to NDC


}

Same effect is when I set .z position in modelMatrix which is 2 times larger than the desired position. Is it expected behavior due to transition from clip to NDC space? Or I am missing something here?

• $uv = 2.0(uv-1.0)$ As it is, it's in $[-0.5,0.5]^2$ rather than $[-1,1]^2$. – lightxbulb Mar 18 at 19:25
• How uv is related here? I have no issues with texture mapping.I guess I need to remap NDC from [0,1] to [-1,1] – Michael IV Mar 18 at 19:38
• You use it here: $drawTriangleBary(tri1, uv, colorRes);$. And yes $ndc$ should be in $[-1,1]^3$ (in some APIs the $z$ coord is in $[0,1]$). – lightxbulb Mar 18 at 19:46
• Again,my texture mapping is fine.Yes,I use initial screen space UVs to get barycentric etc. But my question is related to wrong .z position which is 2 times closer to the view port than expected. So I don't understand what you're trying to say. – Michael IV Mar 18 at 19:52
• Ah ok. Sure $uv+= uv - vec2(1)$ is the same as $uv = 2uv - vec2(1)$, guess I just missed that part (it's less obvious written like this too). – lightxbulb Mar 18 at 20:43