W is the radius of the points in normalized device coordinates so W = 2.0*0.5*glPointSize/rendererWidthInPixels Use the following vars to simplify the calc. mat is the ViewToDeviceMatrix A = mat[0][0] B = mat[0][2] C = mat[2][2] D = mat[2][3] E = mat[0][3] ============================================================== ============================================================== Ortho case Xdc1 = A * X1 + E Xdc1 + W = A * X2 + E X1 = (Xdc1 - E) / A X2 = (Xdc1 + W - E) / A X2 - X1 = (Xdc1 + W - E) / A - (Xdc1 - E) / A X2 - X1 = W / A Xdelta = W / A Zdc1 = C * Z1 + D Zdc2 = C * Z2 + D Z1 = (Zdc1 - D) / C Z2 = Z1 + Xdelta Z2 = (Zdc1 - D) / C + W / A Zdc2 = C * ((Zdc1 - D) / C + W/A) + D Zdc2 = (Zdc1 - D) + C*W/A + D Zdc2 = Zdc1 + C*W/A Zdc1 = Zdb1*2.0 - 1.0 Zdb2 = 0.5*(Zdc1 + C*W/A) + 0.5 Zdb2 = 0.5*(Zdb1*2.0 - 1.0 + C*W/A) + 0.5 Zdb2 = 0.5*Zdb1*2.0 - 0.5 + 0.5*C*W/A) + 0.5 Zdb2 = Zdb1 + 0.5*C*W/A S = C R = W/A For far surface use R = -W/A ============================================================== ============================================================== Perspective Case (X1,?,Z1) and (X2,?,Z2) are two points in View Coordinates (Xdc1,?,Zdc1) and (Xdc2,?,Zdc2) are two corresponding points in device coordinates Xdc2 is W pixels right of Xdc1 so Xdc2 = Xdc1 + W Xdc1 and Xdc2 are in the same Zplane so both can use Z1 Applying the VCDC matrix equation Xdc1 = (A*X1 + B*Z1)/-Z1 Xdc1 + W = (A*X2 + B*Z1)/-Z1 Xdc1 = -A*X1/Z1 - B Xdc1 + W = -A*X2/Z1 - B A*X1/Z1 + B = -Xdc1 A*X1/Z1 = -Xdc1 - B This gives us the formulas for X1 and X2 X1 = Z1*(-Xdc1 - B)/A X2 = Z1*(-Xdc1 - W - B)/A The difference is their width, we solve to get Xwidth = Z1*(-Xdc1 - W - B)/A - Z1*(-Xdc1 - B)/A Which nicely simplifies to Xwidth = -W*Z1/A Now compute Z1 from Zdc1 and the mat equations Zdc1 = (C*Z1 + D)/-Z1 Zdc1 = -C - D/Z1 C + Zdc1 = - D/Z1 Z1*(C + Zdc1) = -D Yielding Z1 = -D/(C + Zdc1) We want Z2 to be Xwidth in front of Z1 so Z2 = Z1 + Xwidth Resulting in Z1 = -D/(C + Zdc1) Z2 = Z1 -W*Z1/A Zdc2 = -C - D/Z2; Which can be expanded out and simplied as follows Z1 = -D/(C + Zdc1) Substituting Z1 into the formula for Z2 yields Z2 = -D/(C + Zdc1) + W*D/(A*(C + Zdc1)) Z2 = W*D/(A*(C + Zdc1)) - D/(C + Zdc1) Z2 = D * (W/(A*(C + Zdc1)) - 1.0/(C + Zdc1) ) Z2 = D * (W/A - 1.0)/(C + Zdc1) Substituting Z2 into the formula for Zdc2 yields Zdc2 = -C - D/Z2; Zdc2 = -C - D/(D * (W/A - 1.0)/(C + Zdc1)); Zdc2 = -C - (C + Zdc1)/(W/A - 1.0); Note that the B and D parameters have all dropped out. Including the OpenGL z -1,1 to 0,1 bias and scale we get gl_FragDepth = 0.5*(-1.0*C - (C + 2.0*gl_FragCoord.z - 1.0)/(normalVCVSOutput.z*PointSize/A - 1.0)) + 0.5 Let Q = (normalVCVSOutput.z*PointSize/A - 1.0) gl_FragDepth = 0.5*(-1.0*C - (C + 2.0*gl_FragCoord.z - 1.0)/Q) + 0.5 gl_FragDepth = 0.5*(-C - (C + 2.0*gl_FragCoord.z - 1.0)/Q) + 0.5 gl_FragDepth = 0.5*(-C - (C - 1.0 + 2.0*gl_FragCoord.z)/Q) + 0.5 gl_FragDepth = 0.5*(-C - C/Q + 1.0/Q - 2.0*gl_FragCoord.z/Q) + 0.5 gl_FragDepth = -0.5*C - 0.5*C/Q + 0.5/Q - gl_FragCoord.z/Q + 0.5 can be rewritten in what I think is the most compact form as Q = (normalVCVSOutput.z*W - 1.0) gl_FragDepth = (S - gl_FragCoord.z) / Q + S; where R = W/A S = -0.5*C + 0.5 This requires only two uniforms to be set, S and W. In the fragment shader it requires 1 multiplication two subtractions one addition and one divide.