Trying to reproduce WorldToViewportPoint function

Hello. I am trying to reproduce native camera.WorldToViewportPoint function, for projecting mesh vertices to viewport. I write colors to texture for visualisation. So, here is what i’ve tried:

  1. Transform vertex position to worldspace using TransformPoint, then use WorldToViewport
Vector3 v = goTransform.TransformPoint(vertices[i]);
Vector3 pos = mCamera.WorldToViewportPoint(v);
texture.SetPixel((int)(pos.x*textureSizeW), (int)(pos.y*textureSizeH), Color.white);

And as result, everithing is fine:
1831957--117466--1.png
2) Calculate ModelViewProjection matrix and multiply it with vertex position.

Matrix4x4 M = goTransform.localToWorldMatrix;
Matrix4x4 V = mCamera.worldToCameraMatrix;
Matrix4x4 P = mCamera.projectionMatrix;
Matrix4x4 MVP = P*V*M;

Vector3 pos = MVP.MultiplyPoint(vertices[j]);
texture.SetPixel((int)(pos.x*textureSizeW), (int)(pos.y*textureSizeH), Color.white);

But result is wrong:
1831957--117468--2.png
3) After reading this answer, I’ve changed my code to:

Matrix4x4 M = goTransform.localToWorldMatrix;
Matrix4x4 V = mCamera.worldToCameraMatrix;
Matrix4x4 P = mCamera.projectionMatrix;
    if (d3d) {
        // Invert Y for rendering to a render texture
        for (int i = 0; i < 4; i++) {
            P[1,i] = -P[1,i];
        }
        // Scale and bias from OpenGL -> D3D depth range
        for (int i = 0; i < 4; i++) {
            P[2,i] = P[2,i]*0.5f + P[3,i]*0.5f;
    }
}
Matrix4x4 MVP =P*V*M;
Vector3 pos = MVP.MultiplyPoint(vertices[j]);
texture.SetPixel((int)(pos.x*textureSizeW), (int)(pos.y*textureSizeH), Color.white);

But result are still wrong:
1831957--117469--3.png
Could you help me to understand what am I missing? Thanks

Just bumping this thread with clarifying why I do not want to use standard camera’s function. I wrote my own occlusion culling solution and this function is a performance bottleneck. “Manual” matrix operations much faster. The only problem is selection of correct matrix.

I Had the same problem and I solved it. I hope that, even if a year has passed, this still can help someone

Vector3 WorldToScreen(Matrix4x4 camMVP, Vector3 point, float renderWidth, float renderHeight) {
            Vector3 result;
            result.x = camMVP.m00 * point.x + camMVP.m01 * point.y + camMVP.m02 * point.z + camMVP.m03;
            result.y = camMVP.m10 * point.x + camMVP.m11 * point.y + camMVP.m12 * point.z + camMVP.m13;
            result.z = camMVP.m20 * point.x + camMVP.m21 * point.y + camMVP.m22 * point.z + camMVP.m23;
            float num = camMVP.m30 * point.x + camMVP.m31 * point.y + camMVP.m32 * point.z + camMVP.m33;
            num = 1f / num;
            result.x *= num;
            result.y *= num;
            result.z = num; // num contains the 1 / (distance to camera), ideal to linearly interpolate the depth in a rasterizer
            point = result;

            point.x = (point.x * 0.5f + 0.5f) * renderWidth;
            point.y = (point.y * 0.5f + 0.5f) * renderHeight;

            return point;
        }

Be aware of points

2 Likes