3차원 그래픽 프로그래밍 - 안드로이드 opengl, 매트릭스 설정




5년 전, "3D Photomaker" 라는 이름의 앱 출시를 했었습니다.

https://blog.naver.com/rookiego90/220017275808


일반적인 2D사진을 3차원 좌표상에 그려줘서 사용자의 조작을 통해, 입체감을 부여하는 어플이었습니다. 실력이 미천하여 여러가지 부족한 부분이 많았는데, 그 중에 아무리 고민하고 개선해보려 해도 해결이 되지 않았던 부분이 정확한 위치를 피킹(Picking) 하는 것이 었습니다. 스크린 상에서 터치한 위치를 계산하여, 3차원 좌표계에 있는 오브젝트(사진)의 높이를 조절하는게 정확히 되지 않아 애를 먹었습니다.

최근, 예전 소스코드를 찾아서 다시 개선해볼까 하여 한달정도 마이그레이션과 매트릭스와 좌표계를 다시 손을 보면서 해결하게 되었습니다. 화면상에 구현해 낸 3차원 공간과 실제 수학적으로 계산하여 그려놓은 좌표계를 정확히 일치시켰습니다. 당연히 그렇게 하는것이 맞지만, 그렇게 생각하지 않고 그냥 보기 좋게 그림만 그리는 식으로 하는 경우도 많은 것으로 알고 있습니다.

직교 투영과 원근투영 모두 정확한 공간산출이 가능하게 되었으니, 각각의 상세한 내용은 다음번 포스팅에 공유하도록 하겠습니다.



직교 투영 (orthogonal matrix)





정사각형을 x축과 y축으로 회전하여 뒤로 돌아서 뒤로 누운 모양인데, 직교투영이라 원근감이 없어 사다리꼴 모양이 됩니다.

직교투영은 육면체의 정의된 공간을 매트릭스로 투영을 시켜 -1~1 사이의 이차원 정사각형 좌표계로 결과물을이 나오게 됩니다.

따라서, 스크린 좌표계와는 x축, y축 비율만 곱하여 계산하면 되기 때문에 어렵지 않습니다.





원근 투영 (perspective matrix)






위의 직교 투영과 동일하게 물체를 이동, 회전 시켰으나, 원근 투영의 결과물은 다음 그림과 같이 원근감이 있어 실제와 좀 더 같아 보입니다. 앞으로 작업은 원근 투영으로 진행할 예정입니다.

시야각, z축 설정값 등으로 화면에 보이는 물체의 모양이 변하게 되는데 이는 잘 조절하여 적절한 값을 찾아야 합니다.






매트릭스 설정


매트릭스 설정 순서는 아래와 같습니다. 정석이 아닐 수도 있으니 참고하시기 바랍니다.

먼저 화면을 그리기 전, 윈도우 크기가 정해질 때, 제일 먼저 호출하는 함수 입니다.

보통은 여기서 투영 매트릭스를 설정하나, 저는 opengl 제공함수가 아닌 제가 만든 라이브러리를 이용해 매트릭스 설정을 하므로 방식이 약간 다릅니다.


public void onSurfaceChanged(GL10 gl, int width, int height) {
        Pj_include.getInstance().setWindowInfo(width, height);
        gl.glViewport(0, 0, width, height);
        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();
    }


뷰포트 설정과 단위행렬만 설정합니다. onSurfaceChanged 는 화면크기가 바뀌거나 화면이 새로 켜지는 등의 변화가 있어야 호출이 1회 되며, 평상 시 드로잉 할때에는 호출되지 않습니다.




    public void onDrawFrame(GL10 gl)
    {
        m_gl = gl;
        synchronized(m_gl)
        {
            try
            {
                makeMatrix();
                makeGeo();

                gl.glViewport(0, 0, Pj_include.getInstance().m_winWidth, Pj_include.getInstance().m_winHeight);
                gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

                //그리기 ...
        }
     }


매 프레임마다 호출하는 그리기 함수 입니다. 여기서 매트릭스 설정(makematrix), 좌표변환 변수 설정(makeGeo) 를 해준 후, 그림을 그립니다.





    private void makeMatrix()
    {
        //PROJECTION MATRIX
        mMat.LoadIdentity();

        if (Pj_include.getInstance().getSetting_Projection())
        {
            mMat.LoadPerspective(Pj_include.getInstance().m_fFovy, (float)Pj_include.getInstance().m_winWidth/Pj_include.getInstance().m_winHeight, Pj_include.getInstance().m_fZnear, Pj_include.getInstance().m_fZfar);
        }
        else
        {
            mMat.LoadOrthogonal(-Pj_include.getInstance().m_winWidth*0.5f, Pj_include.getInstance().m_winWidth*0.5f, -Pj_include.getInstance().m_winHeight*0.5f, Pj_include.getInstance().m_winHeight*0.5f, Pj_include.getInstance().m_fZnear_ortho, Pj_include.getInstance().m_fZfar_ortho);
        }

        mMat.getCurMatrix(Pj_include.getInstance().m_prMat);

        //MODELVIEW MATRIX
        mMat.LoadIdentity();

        if (Pj_include.getInstance().getSetting_Projection())
        {
            mMat.Translatef(0.0f, 0.0f, -Pj_include.getInstance().m_fZdist);
        }
        else
        {
            mMat.Translatef(0.0f, 0.0f, -Pj_include.getInstance().m_fZdist_ortho);
        }

        mMat.Rotatef(Pj_include.getInstance().m_fAngleX, 1.0f, 0.0f, 0.0f);
        mMat.Rotatef(Pj_include.getInstance().m_fAngleY, 0.0f, 1.0f, 0.0f);

        mMat.Scalef(Pj_include.getInstance().m_fScale, Pj_include.getInstance().m_fScale, Pj_include.getInstance().m_fScale);

        mMat.getCurMatrix(Pj_include.getInstance().m_mvMat);

        //MVP MATRIX
        mMat.GetMatrixMultiplyWithMatrix(Pj_include.getInstance().m_prMat, Pj_include.getInstance().m_mvMat, Pj_include.getInstance().m_mvpMat);
    }


매트릭스 설정 입니다. 직교/원근 투영 설정에 따라, 투영 매트릭스는 해당 설정을 하고, 모델뷰(이동) 매트릭스는 물체의 이동, 회전, 크기 순으로 설정을 합니다. 실제 물체에 적용 되는 순서는 매트릭스 설정 순서의 반대인 크기, 회전, 이동 순으로 계산하면 됩니다. 위 함수를 아래에서 위로 올라가면서 물체에 적용하면 됩니다.

좌표계 상의 실제 계산 결과는 다음 포스팅에 정리해 보겠습니다. 감사합니다.





*글의 원문은 아래 블로그에 게시되어 있습니다.









#안드로이드, #android, #opengl, #그래픽, #라이브러리, #투영, #행렬, #매트릭스, #직교, #원근, #perspective, #orthogonal, #matrix, #피킹, #picking

댓글