Координаты экрана OpenGL в мировых координатах

Я пытаюсь получить мировые координаты из экранных координат с помощью OpenGl. Я провел небольшое исследование, но возвращенные значения не соответствуют друг другу. Фактически, в моем мире у меня есть поверхность с Z = 0, и я хочу иметь трехмерные координаты точки на этой поверхности, поэтому Z = 0. Вот мое исследование:

float cursZ = 1;
long posX, posY, posZ = 0;

int viewport[4];
viewport[0] = Originex;
viewport[1] = Originey;
viewport[2] = largeFen;
viewport[3] = hautFen;
CMatrix4 modelView;
CMatrix4 proj;
CMatrix4 A;
CMatrix4 out;

typeCoorDepl proche = 100.;
typeCoorDepl loin = largeFen + hautFen;

Renderer.GetMatrix(MAT_MODELVIEW, modelView);
Renderer.GetMatrix(MAT_PROJECTION, proj);

modelView.LookAt(TVector3T(sendAngleX, angleY, SendAngleZ), TVector3T(0, 0, 0));

switch (modeVue)// get MAT_PROJECTION
{
case 1: // mode isometrique rotation centree sur l'ecran
    proj.OrthoOffCenter(-(largeFen / 2.0f), (hautFen / 2.0f), (largeFen / 2.0f), -(hautFen / 2.0f), proche, loin);
    break;
case 2: // mode conique rotation centree sur l'ecran
    proj.PerspectiveFOV((70.0*pi / 180) / 2, largeFen / hautFen, proche, loin);
    break;
case 3: // mode isometrique rotation centree sur l'ecran
    proj.OrthoOffCenter(-(largeFen / 2.0f), (hautFen / 2.0f), (largeFen / 2.0f), -(hautFen / 2.0f), proche, loin);
    break;
case 4: // mode conique rotation centree sur l'objet et non sur l'ecran
    proj.PerspectiveFOV((70.0*pi / 180), largeFen / hautFen, proche, loin);
    break;
default:
    break;
}

cursY = hautFen - cursY; // windows start from top-left as openGL from bottom-left
double posx, posy, posz;

float m[16];//, A[16];
float in[4];//, out[4];

A = proj*modelView;

A.Inverse();

in[0] = cursX;
in[1] = cursY;
in[2] = cursZ;
in[3] = 1.0;

/* Map x and y from window coordinates */
in[0] = (in[0] - viewport[0]) / viewport[2];
in[1] = (in[1] - viewport[1]) / viewport[3];

/* Map to range -1 to 1 */
in[0] = in[0] * 2 - 1;
in[1] = in[1] * 2 - 1;
in[2] = in[2] * 2 - 1;

CMatrix4 forTestIn;
forTestIn.a11 = in[0];
forTestIn.a12 = in[1];
forTestIn.a13 = in[2];
forTestIn.a14 = in[3];

out = A*forTestIn;
out.a11 /= out.a14;
out.a12 /= out.a14;
out.a13 /= out.a14;

*finPosX = out.a11;
*finPosY = out.a12;
*finPosZ = out.a13;

Вот мои функции OrthoOffCenter и PerspectiveFOV:

inline void CMatrix4::OrthoOffCenter(typeCoorDepl Left, typeCoorDepl Top, typeCoorDepl Right, typeCoorDepl Bottom, typeCoorDepl Near, typeCoorDepl Far)
{
    a11 = 2 / (Right - Left); a12 = 0.0f;               a13 = 0.0f;           a14 = (Left + Right) / (Left - Right);
    a21 = 0.0f;               a22 = 2 / (Top - Bottom); a23 = 0.0f;           a24 = (Bottom + Top) / (Bottom - Top);
    // a31 = 0.0f;               a32 = 0.0f;               a33 = -2/(Far - Near); a34 = (Near + Far) / (Far - Near);
    // substitution Far/Near cause pb depth buffer
    a31 = 0.0f;               a32 = 0.0f;               a33 = -2/(Near - Far); a34 = (Far + Near) / (Near - Far);
    a41 = 0.0f;               a42 = 0.0f;               a43 = 0.0f; a44 = 1.0f;    
}

inline void CMatrix4::PerspectiveFOV(typeCoorDepl Fov, typeCoorDepl Ratio, typeCoorDepl Near, typeCoorDepl Far)
{
    typeCoorDepl YScale = 1.0f / std::tan(Fov / 2);
    typeCoorDepl XScale = YScale / Ratio;
    typeCoorDepl Coeff  = Far / (Far - Near);

    a11 = XScale; a12 = 0.0f;   a13 = 0.0f;  a14 = 0.0f;
    a21 = 0.0f;   a22 = YScale; a23 = 0.0f;  a24 = 0.0f;
    a31 = 0.0f;   a32 = 0.0f;   a33 = Coeff; a34 = Near * -Coeff;
    a41 = 0.0f;   a42 = 0.0f;   a43 = 1.0f;  a44 = 0.0f;
    // nb : agir sur a14, a24 déplace le centre "de rotation"
}

person Asterakin    schedule 07.07.2017    source источник
comment
покажите свои реализации для OrthoOffCenter и PerspectiveFOV   -  person meowgoesthedog    schedule 07.07.2017
comment
предполагая, что все ваши вычисления вида модели и проекции верны, вы написали A = proj modelView; вместо A = modelView proj ;, а после вам нужно применить инверсию (как вы выразились).   -  person esmitt    schedule 07.07.2017
comment
Если это не домашнее задание и / или вас не интересует математика, вы можете использовать gluUnproject.   -  person Aeluned    schedule 08.07.2017


Ответы (1)


Если вы рисуете что-либо в области просмотра, выполняются следующие преобразования:

  1. Преобразование с помощью матрицы просмотра модели (которая может состоять из отдельной матрицы модели и матрицы просмотра).
  2. Преобразование по матрице проекции (независимо от того, есть ли у вас перспективная или ортогональная проекция).

После этого преобразования координаты находятся в пространстве нормализованных координат устройства (NDC). NDC находится в диапазоне от (-1, -1, -1) до (1,1,1).

Если вы хотите преобразовать обратно точку из нормализованных координат устройства (NDC) в мировое пространство, вам необходимо выполнить точные обратные преобразования:

  1. Преобразуйте обратно с помощью матрицы обратной проекции.
  2. Обратное преобразование с помощью обратной матрицы просмотра модели.

См. Ответы на вопрос о переполнении стека инвертирование матрицы 4x4, чтобы узнать, как вычислить обратную матрицу. :

template < typename T_MD, typename T_MS = T_MD, typename T_SCALAR = float >
void mat44_inverse( T_MD &res, const T_MS &m )
{
    T_SCALAR Coef00 = (T_SCALAR)(m[2][2] * m[3][3] - m[3][2] * m[2][3]);
    T_SCALAR Coef02 = (T_SCALAR)(m[1][2] * m[3][3] - m[3][2] * m[1][3]);
    T_SCALAR Coef03 = (T_SCALAR)(m[1][2] * m[2][3] - m[2][2] * m[1][3]);

    T_SCALAR Coef04 = (T_SCALAR)(m[2][1] * m[3][3] - m[3][1] * m[2][3]);
    T_SCALAR Coef06 = (T_SCALAR)(m[1][1] * m[3][3] - m[3][1] * m[1][3]);
    T_SCALAR Coef07 = (T_SCALAR)(m[1][1] * m[2][3] - m[2][1] * m[1][3]);

    T_SCALAR Coef08 = (T_SCALAR)(m[2][1] * m[3][2] - m[3][1] * m[2][2]);
    T_SCALAR Coef10 = (T_SCALAR)(m[1][1] * m[3][2] - m[3][1] * m[1][2]);
    T_SCALAR Coef11 = (T_SCALAR)(m[1][1] * m[2][2] - m[2][1] * m[1][2]);

    T_SCALAR Coef12 = (T_SCALAR)(m[2][0] * m[3][3] - m[3][0] * m[2][3]);
    T_SCALAR Coef14 = (T_SCALAR)(m[1][0] * m[3][3] - m[3][0] * m[1][3]);
    T_SCALAR Coef15 = (T_SCALAR)(m[1][0] * m[2][3] - m[2][0] * m[1][3]);

    T_SCALAR Coef16 = (T_SCALAR)(m[2][0] * m[3][2] - m[3][0] * m[2][2]);
    T_SCALAR Coef18 = (T_SCALAR)(m[1][0] * m[3][2] - m[3][0] * m[1][2]);
    T_SCALAR Coef19 = (T_SCALAR)(m[1][0] * m[2][2] - m[2][0] * m[1][2]);

    T_SCALAR Coef20 = (T_SCALAR)(m[2][0] * m[3][1] - m[3][0] * m[2][1]);
    T_SCALAR Coef22 = (T_SCALAR)(m[1][0] * m[3][1] - m[3][0] * m[1][1]);
    T_SCALAR Coef23 = (T_SCALAR)(m[1][0] * m[2][1] - m[2][0] * m[1][1]);

    _TMat44Helper<T_SCALAR> Fac0(Coef00, Coef00, Coef02, Coef03);
    _TMat44Helper<T_SCALAR> Fac1(Coef04, Coef04, Coef06, Coef07);
    _TMat44Helper<T_SCALAR> Fac2(Coef08, Coef08, Coef10, Coef11);
    _TMat44Helper<T_SCALAR> Fac3(Coef12, Coef12, Coef14, Coef15);
    _TMat44Helper<T_SCALAR> Fac4(Coef16, Coef16, Coef18, Coef19);
    _TMat44Helper<T_SCALAR> Fac5(Coef20, Coef20, Coef22, Coef23);

    _TMat44Helper<T_SCALAR> Vec0((T_SCALAR)m[1][0], (T_SCALAR)m[0][0], (T_SCALAR)m[0][0], (T_SCALAR)m[0][0]);
    _TMat44Helper<T_SCALAR> Vec1((T_SCALAR)m[1][1], (T_SCALAR)m[0][1], (T_SCALAR)m[0][1], (T_SCALAR)m[0][1]);
    _TMat44Helper<T_SCALAR> Vec2((T_SCALAR)m[1][2], (T_SCALAR)m[0][2], (T_SCALAR)m[0][2], (T_SCALAR)m[0][2]);
    _TMat44Helper<T_SCALAR> Vec3((T_SCALAR)m[1][3], (T_SCALAR)m[0][3], (T_SCALAR)m[0][3], (T_SCALAR)m[0][3]);

    _TMat44Helper<T_SCALAR> Inv0(Vec1 * Fac0 - Vec2 * Fac1 + Vec3 * Fac2);
    _TMat44Helper<T_SCALAR> Inv1(Vec0 * Fac0 - Vec2 * Fac3 + Vec3 * Fac4);
    _TMat44Helper<T_SCALAR> Inv2(Vec0 * Fac1 - Vec1 * Fac3 + Vec3 * Fac5);
    _TMat44Helper<T_SCALAR> Inv3(Vec0 * Fac2 - Vec1 * Fac4 + Vec2 * Fac5);

    _TMat44Helper<T_SCALAR> SignA((T_SCALAR)+1.0, (T_SCALAR)-1.0, (T_SCALAR)+1.0, (T_SCALAR)-1.0);
    _TMat44Helper<T_SCALAR> SignB((T_SCALAR)-1.0, (T_SCALAR)+1.0, (T_SCALAR)-1.0, (T_SCALAR)+1.0);
    std::array< _TMat44Helper<T_SCALAR>, 4 > Inverse{ Inv0 * SignA, Inv1 * SignB, Inv2 * SignA, Inv3 * SignB };

    _TMat44Helper<T_SCALAR> Row0(Inverse[0][0], Inverse[1][0], Inverse[2][0], Inverse[3][0]);

    _TMat44Helper<T_SCALAR> Dot0( Row0 );
    Dot0 = Dot0 * _TMat44Helper<T_SCALAR>( (T_SCALAR)m[0][0], (T_SCALAR)m[0][1], (T_SCALAR)m[0][2], (T_SCALAR)m[0][3] );
    T_SCALAR Dot1 = (Dot0[0] + Dot0[1]) + (Dot0[2] + Dot0[3]);

    T_SCALAR OneOverDeterminant = static_cast<T_SCALAR>(1.0) / Dot1;

    for ( int inx1 = 0; inx1 < 4; inx1 ++ )
    {
        for ( int inx2 = 0; inx2 < 4; inx2 ++ )
            res[inx1][inx2] = Inverse[inx1][inx2] * OneOverDeterminant;
    }
}

template< typename SCALAR_TYPE = float >
struct _TMat44Helper
{
    _TMat44Helper( void ) {}
    _TMat44Helper( SCALAR_TYPE x, SCALAR_TYPE y, SCALAR_TYPE z, SCALAR_TYPE w ) : m_v( { x, y, z, w } ){}
    _TMat44Helper( const std::array< SCALAR_TYPE, 4 > & v ) : m_v( v ) {}
    _TMat44Helper( const SCALAR_TYPE v[4] ) { std::memcpy( m_v.data(), v, 4 * sizeof( SCALAR_TYPE ) ); }
    _TMat44Helper( const _TMat44Helper &src ) : m_v( src.m_v ) {}

    _TMat44Helper & operator = ( const _TMat44Helper<SCALAR_TYPE> &src ) { m_v = src.m_v; return *this; }

    SCALAR_TYPE & operator[](int inx) { return m_v[inx]; }

    template< typename SCALAR_TYPE_OP >
    _TMat44Helper<SCALAR_TYPE> operator *( SCALAR_TYPE_OP s )
    {
        _TMat44Helper res;
        for ( int inx = 0; inx < 4; inx ++ )
            res.m_v[inx] = m_v[inx] * (SCALAR_TYPE)s;
        return res;
    }

    template< typename SCALAR_TYPE_OP >
    _TMat44Helper<SCALAR_TYPE> operator +( const _TMat44Helper<SCALAR_TYPE_OP> &b )
    {
        _TMat44Helper<SCALAR_TYPE> res;
        for ( int inx = 0; inx < 4; inx ++ )
            res.m_v[inx] = m_v[inx] + (SCALAR_TYPE)b.m_v[inx];
        return res;
    }

    template< typename SCALAR_TYPE_OP >
    _TMat44Helper<SCALAR_TYPE> operator -( const _TMat44Helper<SCALAR_TYPE_OP> &b )
    {
        _TMat44Helper<SCALAR_TYPE> res;
        for ( int inx = 0; inx < 4; inx ++ )
            res.m_v[inx] = m_v[inx] - (SCALAR_TYPE)b.m_v[inx];
        return res;
    }

    template< typename SCALAR_TYPE_OP >
    _TMat44Helper<SCALAR_TYPE> operator *( const _TMat44Helper<SCALAR_TYPE_OP> &b )
    {
        _TMat44Helper<SCALAR_TYPE> res;
        for ( int inx = 0; inx < 4; inx ++ )
            res.m_v[inx] = m_v[inx] * (SCALAR_TYPE)b.m_v[inx];
        return res;
    }

    std::array< SCALAR_TYPE, 4 > m_v;
};

Чтобы отобразить точку из координат окна просмотра (экрана) в NDC, вы должны сопоставить координату X и координату Y с диапазоном (-1.0, 1.0). Координата Z более сложна, для этого вам нужно получить доступ к буферу глубины (буфер глубины может быть преобразован в текстуру). Считайте глубину в позиции XY и преобразуйте ее в NDC:

X_ndc = X_screen * 2.0 / VP_sizeX - 1.0;
Y_ndc = Y_screen * 2.0 / VP_sizeY - 1.0;
Z_ndc = 2.0 * depth - 1.0; 

В вашем случае у вас есть только 2D-геометрия и ортогональная проекция, поэтому вам не нужно заботиться о глубине, потому что координата Z всегда 0.0.

Если вы преобразовываете точку с помощью матрицы проекции (или матрицы обратной проекции), вы получите точку в однородной системе координат. Чтобы преобразовать точку из однородной системы координат в декартову систему координат, вы должны разделить ее координаты X, Y и Z на ее вес. Общая функция преобразования точки в декартовой системе координат с однородной матрицей 4 * 4 может выглядеть так:

using TVec3 = std::array<float, 3>;
using TVec4 = std::array<float, 4>;
using TMat4 = std::array<TVec4, 4>;

TVec3 Transform( const TVec3 &vec, const TMat4 &mat )
{
    h {
        vec[0] * mat[0][0] + vec[1] * mat[1][0] + vec[2] * mat[2][0] + mat[3][0],
        vec[0] * mat[0][1] + vec[1] * mat[1][1] + vec[2] * mat[2][1] + mat[3][1],
        vec[0] * mat[0][2] + vec[1] * mat[1][2] + vec[2] * mat[2][2] + mat[3][2],
        vec[0] * mat[0][3] + vec[1] * mat[1][3] + vec[2] * mat[2][3] + mat[3][3]
    };
    if ( h[3] == 0.0 )
        return TVec3{ 1.0e99 }; // division by zero
    return TVec3{ h[0]/h[3], h[1]/h[3], h[2]/h[3] };
}

См. Следующий пример WebGL, где цвет объекта, на который наведен указатель мыши, определяется путем обратного вычисления координат экрана в координаты модели.

<script type="text/javascript">

draw_vert =
"precision mediump float; \n" +
"attribute vec2 inPos; \n" +
"uniform   mat4 u_projectionMat44;" +
"uniform   mat4 u_modelViewMat44;" +
"void main()" +
"{" +
"    vec4 viewPos  = u_modelViewMat44 * vec4( inPos.xy, 0.0, 1.0 );" +
"    gl_Position   = u_projectionMat44 * viewPos;" +
"}";

draw_frag =
"precision mediump float; \n" +
"uniform vec3 u_color;" +
"void main()" +
"{" +
"    gl_FragColor = vec4( u_color.xyz, 1.0 );" +
"}";

glArrayType = typeof Float32Array !="undefined" ? Float32Array : ( typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array );

function IdentityMat44() {
    var m = new glArrayType(16);
    m[0]  = 1; m[1]  = 0; m[2]  = 0; m[3]  = 0;
    m[4]  = 0; m[5]  = 1; m[6]  = 0; m[7]  = 0;
    m[8]  = 0; m[9]  = 0; m[10] = 1; m[11] = 0;
    m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1;
    return m;
};

function RotateAxis(matA, angRad, axis) {
    var aMap = [ [1, 2], [2, 0], [0, 1] ];
    var a0 = aMap[axis][0], a1 = aMap[axis][1]; 
    var sinAng = Math.sin(angRad), cosAng = Math.cos(angRad);
    var matB = new glArrayType(16);
    for ( var i = 0; i < 16; ++ i ) matB[i] = matA[i];
    for ( var i = 0; i < 3; ++ i ) {
        matB[a0*4+i] = matA[a0*4+i] * cosAng + matA[a1*4+i] * sinAng;
        matB[a1*4+i] = matA[a0*4+i] * -sinAng + matA[a1*4+i] * cosAng;
    }
    return matB;
}

function Translate( matA, trans ) {
    var matB = new glArrayType(16);
    for ( var i = 0; i < 16; ++ i ) matB[i] = matA[i];
    for ( var i = 0; i < 3; ++ i )
        matB[12+i] = matA[i] * trans[0] + matA[4+i] * trans[1] + matA[8+i] * trans[2] + matA[12+i];
    return matB;
}

function Scale( matA, scale ) {
    var matB = new glArrayType(16);
    for ( var i = 0; i < 16; ++ i ) matB[i] = matA[i];
    for ( var a = 0; a < 4; ++ a )
        for ( var i = 0; i < 3; ++ i )
            matB[a*4+i] = matA[a*4+i] * scale[0];
    return matB;
}

Ortho = function( l, r, t, b, n, f ) {
    var fn  = f + n;
    var f_n = f - n;
    var m = IdentityMat44();
    m[0]  = 2/(r-l); m[1]  = 0;       m[2]  =  0;       m[3]  = 0;
    m[4]  = 0;       m[5]  = 2/(t-b); m[6]  =  0;       m[7]  = 0;
    m[8]  = 0;       m[9]  = 0;       m[10] = -2 / f_n; m[11] = -fn / f_n;
    m[12] = 0;       m[13] = 0;       m[14] = 0;        m[15] = 1;
    return m;
}

vec4_add = function( a, b ) { return [ a[0]+b[0], a[1]+b[1], a[2]+b[2], a[3]+b[3] ]; }
vec4_sub = function( a, b ) { return [ a[0]-b[0], a[1]-b[1], a[2]-b[2], a[3]-b[3] ]; }
vec4_mul = function( a, b ) { return [ a[0]*b[0], a[1]*b[1], a[2]*b[2], a[3]*b[3] ]; }
vec4_scale = function( a, s ) { return [ a[0]*s, a[1]*s, a[2]*s, a[3]*s ]; }

mat44_inverse = function( m ) {

    var Coef00 = m[2*4+2] * m[3*4+3] - m[3*4+2] * m[2*4+3];
    var Coef02 = m[1*4+2] * m[3*4+3] - m[3*4+2] * m[1*4+3];
    var Coef03 = m[1*4+2] * m[2*4+3] - m[2*4+2] * m[1*4+3];    
    var Coef04 = m[2*4+1] * m[3*4+3] - m[3*4+1] * m[2*4+3];
    var Coef06 = m[1*4+1] * m[3*4+3] - m[3*4+1] * m[1*4+3];
    var Coef07 = m[1*4+1] * m[2*4+3] - m[2*4+1] * m[1*4+3];   
    var Coef08 = m[2*4+1] * m[3*4+2] - m[3*4+1] * m[2*4+2];
    var Coef10 = m[1*4+1] * m[3*4+2] - m[3*4+1] * m[1*4+2];
    var Coef11 = m[1*4+1] * m[2*4+2] - m[2*4+1] * m[1*4+2];   
    var Coef12 = m[2*4+0] * m[3*4+3] - m[3*4+0] * m[2*4+3];
    var Coef14 = m[1*4+0] * m[3*4+3] - m[3*4+0] * m[1*4+3];
    var Coef15 = m[1*4+0] * m[2*4+3] - m[2*4+0] * m[1*4+3];   
    var Coef16 = m[2*4+0] * m[3*4+2] - m[3*4+0] * m[2*4+2];
    var Coef18 = m[1*4+0] * m[3*4+2] - m[3*4+0] * m[1*4+2];
    var Coef19 = m[1*4+0] * m[2*4+2] - m[2*4+0] * m[1*4+2];   
    var Coef20 = m[2*4+0] * m[3*4+1] - m[3*4+0] * m[2*4+1];
    var Coef22 = m[1*4+0] * m[3*4+1] - m[3*4+0] * m[1*4+1];
    var Coef23 = m[1*4+0] * m[2*4+1] - m[2*4+0] * m[1*4+1];
      
    var Fac0 = [Coef00, Coef00, Coef02, Coef03];
    var Fac1 = [Coef04, Coef04, Coef06, Coef07];
    var Fac2 = [Coef08, Coef08, Coef10, Coef11];
    var Fac3 = [Coef12, Coef12, Coef14, Coef15];
    var Fac4 = [Coef16, Coef16, Coef18, Coef19];
    var Fac5 = [Coef20, Coef20, Coef22, Coef23];
      
    var Vec0 = [ m[1*4+0], m[0*4+0], m[0*4+0], m[0*4+0] ];
    var Vec1 = [ m[1*4+1], m[0*4+1], m[0*4+1], m[0*4+1] ];
    var Vec2 = [ m[1*4+2], m[0*4+2], m[0*4+2], m[0*4+2] ];
    var Vec3 = [ m[1*4+3], m[0*4+3], m[0*4+3], m[0*4+3] ];
      
    var Inv0 = vec4_add( vec4_sub( vec4_mul(Vec1, Fac0), vec4_mul(Vec2, Fac1) ), vec4_mul( Vec3, Fac2 ) );
    var Inv1 = vec4_add( vec4_sub( vec4_mul(Vec0, Fac0), vec4_mul(Vec2, Fac3) ), vec4_mul( Vec3, Fac4 ) );
    var Inv2 = vec4_add( vec4_sub( vec4_mul(Vec0, Fac1), vec4_mul(Vec1, Fac3) ), vec4_mul( Vec3, Fac5 ) );
    var Inv3 = vec4_add( vec4_sub( vec4_mul(Vec0, Fac2), vec4_mul(Vec1, Fac4) ), vec4_mul( Vec2, Fac5 ) );
      
    var SignA = [+1.0, -1.0, +1.0, -1.0];
    var SignB = [-1.0, +1.0, -1.0, +1.0];
    var Inverse = [ vec4_mul(Inv0, SignA), vec4_mul(Inv1, SignB), vec4_mul(Inv2, SignA), vec4_mul(Inv3, SignB) ];
      
    var Row0 = [Inverse[0][0], Inverse[1][0], Inverse[2][0], Inverse[3][0] ];
      
    var Dot0 = [Row0[0], Row0[1], Row0[2], Row0[3] ];
    Dot0 = vec4_mul( Dot0, [ m[0], m[1], m[2], m[3] ] );
    var Dot1 = (Dot0[0] + Dot0[1]) + (Dot0[2] + Dot0[3]);
      
    var OneOverDeterminant = 1 / Dot1;

    var res = IdentityMat44();  
    for ( var inx1 = 0; inx1 < 4; inx1 ++ ) {
        for ( var inx2 = 0; inx2 < 4; inx2 ++ )
            res[inx1*4+inx2] = Inverse[inx1][inx2] * OneOverDeterminant;
    }
    return res;
}

Transform = function(vec, mat) {
    var h = [
        vec[0] * mat[0*4+0] + vec[1] * mat[1*4+0] + vec[2] * mat[2*4+0] + mat[3*4+0],
        vec[0] * mat[0*4+1] + vec[1] * mat[1*4+1] + vec[2] * mat[2*4+1] + mat[3*4+1],
        vec[0] * mat[0*4+2] + vec[1] * mat[1*4+2] + vec[2] * mat[2*4+2] + mat[3*4+2],
        vec[0] * mat[0*4+3] + vec[1] * mat[1*4+3] + vec[2] * mat[2*4+3] + mat[3*4+3] ]
    if ( h[3] == 0.0 )
        return [0, 0, 0]
    return [ h[0]/h[3], h[1]/h[3], h[2]/h[3] ];
}

// shader program object
var ShaderProgram = {};
ShaderProgram.Create = function( shaderList, uniformNames ) {
    var shaderObjs = [];
    for ( var i_sh = 0; i_sh < shaderList.length; ++ i_sh ) {
        var shderObj = this.CompileShader( shaderList[i_sh].source, shaderList[i_sh].stage );
        if ( shderObj == 0 )
          return 0;
        shaderObjs.push( shderObj );
    }
    var progObj = this.LinkProgram( shaderObjs )
    if ( progObj != 0 ) {
        progObj.unifomLocation = {};
        for ( var i_n = 0; i_n < uniformNames.length; ++ i_n ) {
            var name = uniformNames[i_n];
            progObj.unifomLocation[name] = gl.getUniformLocation( progObj, name );
        }
    }
    return progObj;
}
ShaderProgram.Use = function( progObj ) { gl.useProgram( progObj ); } 
ShaderProgram.SetUniformInt = function( progObj, name, val ) { gl.uniform1i( progObj.unifomLocation[name], val ); }
ShaderProgram.SetUniform2f = function( progObj, name, arr ) { gl.uniform2fv( progObj.unifomLocation[name], arr ); }
ShaderProgram.SetUniform3f = function( progObj, name, arr ) { gl.uniform3fv( progObj.unifomLocation[name], arr ); }
ShaderProgram.SetUniformMat44 = function( progObj, name, mat ) { gl.uniformMatrix4fv( progObj.unifomLocation[name], false, mat ); }
ShaderProgram.CompileShader = function( source, shaderStage ) {
    var shaderObj = gl.createShader( shaderStage );
    gl.shaderSource( shaderObj, source );
    gl.compileShader( shaderObj );
    var status = gl.getShaderParameter( shaderObj, gl.COMPILE_STATUS );
    if ( !status ) alert(gl.getShaderInfoLog(shaderObj));
    return status ? shaderObj : 0;
} 
ShaderProgram.LinkProgram = function( shaderObjs ) {
    var prog = gl.createProgram();
    for ( var i_sh = 0; i_sh < shaderObjs.length; ++ i_sh )
        gl.attachShader( prog, shaderObjs[i_sh] );
    gl.linkProgram( prog );
    status = gl.getProgramParameter( prog, gl.LINK_STATUS );
    if ( !status ) alert("Could not initialise shaders");
    gl.useProgram( null );
    return status ? prog : 0;
}
        

function drawScene(){

    var canvas = document.getElementById( "camera-canvas" );
    var currentTime = Date.now();   
    var deltaMS = currentTime - startTime;
    var aspect =  canvas.width / canvas.height;
    var matOrtho = Ortho( -aspect, aspect, 1, -1, -1, 1 );
    var matOrthoInv = mat44_inverse( matOrtho )
        
    gl.viewport( 0, 0, canvas.width, canvas.height );
    gl.enable( gl.DEPTH_TEST );
    gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
    gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
    ShaderProgram.Use( progDraw );
    gl.enableVertexAttribArray( progDraw.inPos );
    gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
    gl.vertexAttribPointer( progDraw.inPos, 2, gl.FLOAT, false, 0, 0 ); 
    gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
    ShaderProgram.SetUniformMat44( progDraw, "u_projectionMat44",  matOrtho );
        
    var col = [ [1.0,0.0,0.0], [1.0,1.0,0.0], [0.0,0.0,1.0] ];
    var invMat = []
    for ( var i = 0; i < 3; ++ i ) {    
        var modelMat = Scale( IdentityMat44(), [ 0.3, 0.3, 0.3] );
        var angRad = CalcAng( currentTime, 20.0 ) + i * Math.PI * 2 / 3;
        var sinAng = Math.sin(angRad), cosAng = Math.cos(angRad);
        modelMat[12] = cosAng * 0.6;
        modelMat[13] = sinAng * 0.6;
        invMat.push( mat44_inverse( modelMat ) );
        
        ShaderProgram.SetUniformMat44( progDraw, "u_modelViewMat44", modelMat );
        ShaderProgram.SetUniform3f( progDraw, "u_color", col[i] );
        gl.drawElements( gl.TRIANGLES, bufObj.inx.len, gl.UNSIGNED_SHORT, 0 );
    }
    gl.disableVertexAttribArray( progDraw.pos );

    var newColor = "#000000";
    var colorMap = ["#ff0000", "#ffff00", "#0000ff" ];
    var pos = [-1, -1];
    if (mousePos[0] > 0 && mousePos[1] > 0 ) {
        var pos = [2.0 * mousePos[0] / canvas.width - 1.0, 1.0 - 2.0 * mousePos[1] / canvas.height];
        for ( var i = 0; i < 3; ++ i ) {
            var testVec = [ pos[0], pos[1], 0 ];
            testVec = Transform(testVec, matOrthoInv);
            testVec = Transform(testVec, invMat[i]);
            if (testVec[0] > -1.0 && testVec[0] < 1.0 && testVec[1] > -1.0 && testVec[1] < 1.0 ) {
                newColor = colorMap[i];
            }
        }
    }
    document.getElementById( "color" ).value = newColor;
    document.getElementById( "mouseX" ).innerHTML = pos[0];
    document.getElementById( "mouseY" ).innerHTML = pos[1];    
}

var startTime;
function Fract( val ) { 
    return val - Math.trunc( val );
}
function CalcAng( currentTime, intervall ) {
    return Fract( (currentTime - startTime) / (1000*intervall) ) * 2.0 * Math.PI;
}
function CalcMove( currentTime, intervall, range ) {
    var pos = self.Fract( (currentTime - startTime) / (1000*intervall) ) * 2.0
    var pos = pos < 1.0 ? pos : (2.0-pos)
    return range[0] + (range[1] - range[0]) * pos;
}    

var mousePos = [-1, -1];
var gl;
var prog;
var bufObj = {};
function cameraStart() {

    var canvas = document.getElementById( "camera-canvas");
    gl = canvas.getContext( "experimental-webgl" );
    if ( !gl )
      return;

    progDraw = ShaderProgram.Create( 
      [ { source : draw_vert, stage : gl.VERTEX_SHADER },
        { source : draw_frag, stage : gl.FRAGMENT_SHADER }
      ],
      [ "u_projectionMat44", "u_modelViewMat44", "u_color"] );
    progDraw.inPos = gl.getAttribLocation( progDraw, "inPos" );
    if ( prog == 0 )
        return;

    var pos = [ -1, -1, 1, -1, 1, 1, -1, 1 ];
    var inx = [ 0, 1, 2, 0, 2, 3 ];
    bufObj.pos = gl.createBuffer();
    gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
    gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( pos ), gl.STATIC_DRAW );
    bufObj.inx = gl.createBuffer();
    bufObj.inx.len = inx.length;
    gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
    gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( inx ), gl.STATIC_DRAW );

    startTime = Date.now();
    setInterval(drawScene, 50);
}

(function() {
    document.onmousemove = handleMouseMove;
    function handleMouseMove(event) {
        var dot, eventDoc, doc, body, pageX, pageY;

        event = event || window.event; // IE-ism

        if (event.pageX == null && event.clientX != null) {
            eventDoc = (event.target && event.target.ownerDocument) || document;
            doc = eventDoc.documentElement;
            body = eventDoc.body;

            event.pageX = event.clientX +
              (doc && doc.scrollLeft || body && body.scrollLeft || 0) -
              (doc && doc.clientLeft || body && body.clientLeft || 0);
            event.pageY = event.clientY +
              (doc && doc.scrollTop  || body && body.scrollTop  || 0) -
              (doc && doc.clientTop  || body && body.clientTop  || 0 );
        }

        var canvas = document.getElementById( "camera-canvas");
        var x = event.pageX - canvas.offsetLeft;
        var y = event.pageY - canvas.offsetTop;
        mousePos = [-1, -1];
        if ( x >= 0 && x < canvas.width && y >= 0 && y < canvas.height ) {
            mousePos = [x, y]; 
        }
    }
})();

</script>

<body onload="cameraStart();">
    <div style="margin-left: 260px;">
        <div style="float: right; width: 100%; background-color: #CCF;">
            <form name="inputs">
                <table>
                    <tr> <td> <input type="color" value="#000000" id="color" disabled></td> </tr> 
                    <tr> <td> <span id="mouseX">0</span> </td> </tr>
                    <tr> <td> <span id="mouseY">0</span> </td> </tr>
                </table>
            </form>
        </div>
        <div style="float: right; width: 260px; margin-left: -260px;">
            <canvas id="camera-canvas" style="border: none;" width="256" height="256"></canvas>
        </div>
        <div style="clear: both;"></div>
    </div>
</body>

person Rabbid76    schedule 09.07.2017