【OPENGL&WEBGL】將OpenGL裏的gluProject和gluUnProject函數web化

前言

最近的需求需要涉及到在web上實現各個矩陣的變換和逆變換,不過功能也僅限於此,沒必要因爲這個功能而引用three.js,所以就想着gluProject和gluUnProject兩個函數在JavaScript上實現一遍,於是對照着這兩個函數的源碼進行改寫。改寫用到了矩陣向量庫glMatrix

gluProject和gluUnProject源碼

GLint GLAPIENTRY
gluProject(GLdouble objx, GLdouble objy, GLdouble objz, 
          const GLdouble modelMatrix[16], 
          const GLdouble projMatrix[16],
              const GLint viewport[4],
          GLdouble *winx, GLdouble *winy, GLdouble *winz)
{
    double in[4];
    double out[4];

    in[0]=objx;
    in[1]=objy;
    in[2]=objz;
    in[3]=1.0;
    __gluMultMatrixVecd(modelMatrix, in, out);
    __gluMultMatrixVecd(projMatrix, out, in);
    if (in[3] == 0.0) return(GL_FALSE);
    in[0] /= in[3];
    in[1] /= in[3];
    in[2] /= in[3];
    /* Map x, y and z to range 0-1 */
    in[0] = in[0] * 0.5 + 0.5;
    in[1] = in[1] * 0.5 + 0.5;
    in[2] = in[2] * 0.5 + 0.5;

    /* Map x,y to viewport */
    in[0] = in[0] * viewport[2] + viewport[0];
    in[1] = in[1] * viewport[3] + viewport[1];

    *winx=in[0];
    *winy=in[1];
    *winz=in[2];
    return(GL_TRUE);
}


GLint GLAPIENTRY
gluUnProject(GLdouble winx, GLdouble winy, GLdouble winz,
        const GLdouble modelMatrix[16],                     //注意:模型視圖矩陣
        const GLdouble projMatrix[16],
                const GLint viewport[4],
            GLdouble *objx, GLdouble *objy, GLdouble *objz)
{
    double finalMatrix[16];
    double in[4];
    double out[4];
    //合併MVP矩陣
    __gluMultMatricesd(modelMatrix, projMatrix, finalMatrix);

    //MVP矩陣求逆矩陣
    if (!__gluInvertMatrixd(finalMatrix, finalMatrix)) return(GL_FALSE);

    in[0]=winx;
    in[1]=winy;
    in[2]=winz;
    in[3]=1.0;

    //從屏幕座標變換爲0到1之間的座標
    /* Map x and y from window coordinates */
    in[0] = (in[0] - viewport[0]) / viewport[2];
    in[1] = (in[1] - viewport[1]) / viewport[3];

    //從0到1之間變換到-1到1.
    /* Map to range -1 to 1 */
    in[0] = in[0] * 2 - 1;
    in[1] = in[1] * 2 - 1;
    in[2] = in[2] * 2 - 1;

    //乘以MVP逆矩陣之後得到世界空間的座標
    __gluMultMatrixVecd(finalMatrix, in, out);

    //從齊次座標變換爲笛卡爾座標
    if (out[3] == 0.0) return(GL_FALSE);
    out[0] /= out[3];
    out[1] /= out[3];
    out[2] /= out[3];

    //將結果賦值。
    *objx = out[0];
    *objy = out[1];
    *objz = out[2];
    return(GL_TRUE);
}

JavaScript改寫版本

其實,如果懂渲染管線的知識,應該這兩個函數的源碼應該非常清晰易懂。如果看不懂,請先搞懂各個座標轉換的知識。
下面是兩個函數的JavaScript版本改寫,要先下載glMatrix.js。

//將世界座標轉換爲屏幕座標。
        function project(objX, objY, objZ, modelviewMatrix, projectMatrix, viewport, winX, winY, winZ) {
            var input = new Array(4);
            var output = new Array(4);

            input[0] = objX;
            input[1] = objY;
            input[2] = objZ;
            input[3] = 1.0;

            vec4.transformMat4(output, input, modelviewMatrix);//模型視圖變換
            vec4.transformMat4(input, output, projectMatrix);//投影變換

            //透視除法,進入標準化設備座標
            if (input[3] == 0.0) return;
            input[0] /= input[3];
            input[1] /= input[3];
            input[2] /= input[3];

            //將座標由-1到1,轉換到0-1
            input[0] = input[0] * 0.5 + 0.5;
            input[1] = input[1] * 0.5 + 0.5;
            input[2] = input[2] * 0.5 + 0.5;

            //將x,y轉換到屏幕座標
            input[0] = input[0] * viewport[2] + viewport[0];
            input[1] = input[1] * viewport[3] + viewport[1];

            winX = input[0];
            winY = input[1];
            winZ = input[2];
        }

//將屏幕座標轉換爲世界座標。
        function Unproject(winX, winY, winZ, modelviewMatrix, projectMatrix, viewport, objX, objY, objZ) {
            var finalMatrix = mat4.create();
            var input = new Array(4);
            var output = new Array(4);


            mat4.invert(modelviewMatrix, modelviewMatrix);
            mat4.invert(projectMatrix, projectMatrix);
            //合併MVP矩陣
            mat4.multiply(finalMatrix, modelviewMatrix, projectMatrix);



            input[0] = winX;
            input[1] = winY;
            input[2] = winZ;
            input[3] = 1.0;

            //從屏幕座標變換爲0到1之間的座標
            input[0] = (input[0] - viewport[0]) / viewport[2];
            input[1] = (input[1] - viewport[1]) / viewport[3];

            //從0到1之間變換到-1到1之間
            input[0] = input[0] * 2 - 1;
            input[1] = input[1] * 2 - 1;
            input[2] = input[2] * 2 - 1;

            //乘以MVP逆矩陣之後得到世界空間的座標
            vec4.transformMat4(output, input, finalMatrix);
            //從齊次座標變換爲笛卡爾座標
            if (output[3] == 0.0) return;
            output[0] /= output[3];
            output[1] /= output[3];
            output[2] /= output[3];

            //將結果賦值。
            objX = output[0];
            objY = output[1];
            objZ = output[2];
        }

我自己寫了個小的測試例子,大家如果感興趣可以下載,自行打斷點進行測試。
下載地址

發佈了147 篇原創文章 · 獲贊 124 · 訪問量 75萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章