Search code examples
javascriptopengl-eshtml5-canvaswebglwebgl-earth

WebGL- add label and pick object


I tried picking using color index technique but i want to do using raycasting technique and also to add 3d lable on the picked 3d point.

I am new to webgl and i want a sample code on how to do pick object in webgl using raycasting.

how to do project and unproject in webgl.

I am using pure WebGl to do this(no three.js or other frameworks),so help me with the webgl code examples.


Solution

  • If you are interested in a CPU based solution, here is how:

    First to convert world space into screen space coordinates, all you need to do is transform the world pos by the view-projection matrix. Then you need to do the perspective divide by dividing x,y,z by w. Thats it. This form makes the most sense to me: (Assuming you have the appropriate matrix functions):

    vec3.toScreenSpace = function(out, v3, viewProjectionMatrix, width, height){
        vec3.transformByMat(out, v3, viewProjectionMatrix);
        var m = viewProjectionMatrix;
        var w = m[3] * v3[0] + m[7] * v3[1] + m[11] * v3[2] + m[15]; // required for perspective divide
        if (w !== 0){
            var invW = 1.0/w;
            out[0] = (out[0]*invW+1)/2 * width;
            out[1] = (1-out[1]*invW)/2 * height; // note: Y axis oriented top -> down in screen space
        }
        return out;
    };
    

    Converting screen space to world space is basically the reverse operation with a inverse view projection matrix:

    vec3.toWorldSpace = function(out, v3, viewProjectionMatrix, width, height){
        // expects v3[2] (z value) to be -1 if want position at zNear and +1 at zFar
        var invViewProj = mat4.inverse(vec3.SHARED_MAT4, viewProjectionMatrix);
        var x = 2*v3[0]/width - 1.0;
        var y = 1.0 - (2*v3[1]/height); // note: Y axis oriented top -> down in screen space
        var z = v3[2];
        out[0] = x;
        out[1] = y;
        out[2] = z;
        var m = invViewProj;
        vec3.transformByMat(out, out, m);
        var w = m[3] * x + m[7] * y + m[11] * z + m[15]; // required for perspective divide
        if (w !== 0){
            var invW = 1.0/w;
            out[0] *= invW;
            out[1] *= invW;
            out[2] *= invW;
        }
    
        return out;
    };
    

    Then when you select a x,y coordinate on screen, you can calculate a ray in world space like so:

    function screenPickTest(x, y){
        var pos = SHARED_V;
        var posFar = SHARED_V2;
        pos[0] = x;
        pos[1] = y;
        pos[2] = -1;
    
        posFar[0] = x;
        posFar[1] = y;
        posFar[2] = +1;
    
        pos = vec3.toWorldSpace(pos, pos, viewProjection, canvas.width, canvas.height);
        posFar = vec3.toWorldSpace(posFar, posFar, viewProjection, canvas.width, canvas.height);
    
        var dir = vec3.minus(SHARED_V2, posFar, pos);
    
        return raySweepTestAgainst____(pos, dir, COLLISION_GEOMETRYS);
    }
    

    You have to implement the collision detection function that detects the first object the ray hits and returns the point of collision (along with other data if required).

    With that, you can create a label at the point of collision, etc.