I am trying to get world Coordinates from screen coordinates with OpenGl. I made some research but values returned are uncoherent. In fact in my world I have a surface at Z=0 and I want to have the 3d coordinates of a point in that surface so with Z=0. Here my research :
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;
Here my OrthoOffCenter and PerspectiveFOV function :
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"
}
If you draw something on the viewport the following transformations are performed:
Transformation by the modelview matrix (which may consists of a separated model matrix and view matrix).
Transformation by the projection matrix (regardless of whether you have perspective or orthographic projection).
After this transformations the coordinates are in the Normalized Device Coordinates space (NDC). The NDC is in a range from (-1,-1,-1) to (1,1,1).
If you want to convert back a point from the Normalized Device Coordinates (NDC) to the world space you have to do the exactly reverse transformations:
Transform back by the inverse projection matrix.
Transform back by the inverse modelview matrix.
See the answers to the Stack Overflow Question inverting a 4x4 matrix to see how to calculate a inverse matrix:
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;
};
To map a point from viewport (screen) coordinates to the NDC you have to map the X-coordinate and the Y-coordinate to the range (-1.0, 1.0).
The Z-coordinate is more tricky, for this you have to access the depth buffer (The depth buffer can be rendered to a texture).
Read the depth at the XY position and convert it to the 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;
In your case you have only a 2D geometry and an orthographic projection, so you do not have to care about the depth, because the Z coordinate is always 0.0.
If you transform a point by the projection matrix (or the inverse projection matrix) you will get a point in a homogeneous coordinate system. To transform a point from a homogeneous coordinates system to a cartesian coordinate system you have to devide its X, Y and Z coordinate by it weight.
A general transformation function of a point in a cartesian coordinate system with a homogeneous 4*4 matrix may look like this:
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] };
}
See the following WebGL example, where the color of the object, where the mouse hovers, is found by calculating back from the screen coordinates to the model coordinates.
<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>
I have a terrain with a specific length and width, which take the information from he file bmp. With Shade of grey i can modify my terrain. Fine, it works great.
Here comes the problem.
I need my terrain divided into physically smaller parts, joined together create the original terrain.
How can i do this? I know that its possible. Any sugestions? Where, why there?
Mys file:
HeightField.cpp
#include "HeightField.h"
HeightField::HeightField(Terrain terrain, PxPhysics& sdk, PxMaterial &material, int width, int height)
{
this->nrVertices = terrain.NumVertices;
this->terrain = terrain;
this->width = width;
this->height = height;
this->fillSamples();
this->fillDesc();
this->aHeightField = sdk.createHeightField(hfDesc);
this->hfGeom = new PxHeightFieldGeometry(aHeightField, PxMeshGeometryFlags(), this->terrain.dy / 255.0, this->terrain.dx, this->terrain.dz);
this->terrainPos = new PxTransform(PxVec3(-this->terrain.dx*(this->width - 1) / 2, 0.0f, this->terrain.dz*(this->height - 1) / 2), PxQuat(3.1415 / 2.0, PxVec3(0, 1, 0)));
this->g_pxHeightField = sdk.createRigidDynamic(*this->terrainPos);
this->g_pxHeightField->setRigidDynamicFlag(PxRigidDynamicFlag::eKINEMATIC, true);
PxShape* aHeightFieldShape = this->g_pxHeightField->createShape(*(this->hfGeom), material);
}
HeightField::~HeightField()
{
}
void HeightField::fillSamples()
{
this->samples = (PxHeightFieldSample*)malloc(sizeof(PxHeightFieldSample)*(this->nrVertices));
for (int i = 0; i < this->nrVertices; i++)
{
samples[i].height = this->terrain.hminfo.heightMap[i].y;
samples[i].clearTessFlag();
}
}
void HeightField::fillDesc()
{
this->hfDesc.format = PxHeightFieldFormat::eS16_TM;
this->hfDesc.nbColumns = this->width;
this->hfDesc.nbRows = this->height;
this->hfDesc.samples.data = this->samples;
this->hfDesc.samples.stride = sizeof(PxHeightFieldSample);
}
Terrain.cpp
#include "Terrain.h"
Terrain::Terrain(void)
{
v = NULL;
indices = NULL;
dx = dz = 1000; //odleg³oœæ miêdzy punktami grid'a
dy = 1000; //maksymalna wysokoϾ terenu
}
Terrain::~Terrain(void)
{
if (v != NULL) delete [] v;
if (indices != NULL) delete indices;
if (hminfo.heightMap != NULL) delete [] hminfo.heightMap;
}
bool Terrain::HeightMapLoad(char* filename, float sx, float sz, float maxy)
{
FILE *filePtr; // Point to the current position in the file
BITMAPFILEHEADER bitmapFileHeader; // Structure which stores information about file
BITMAPINFOHEADER bitmapInfoHeader; // Structure which stores information about image
int imageSize, index;
unsigned char height;
// Open the file
filePtr = fopen(filename,"rb");
if (filePtr == NULL)
return 0;
dx = sz;
dz = sz;
dy = maxy;
// Get the width and height (width and length) of the image
hminfo.terrainWidth = bitmapInfoHeader.biWidth;
hminfo.terrainHeight = bitmapInfoHeader.biHeight;
// Initialize the heightMap array (stores the vertices of our terrain)
hminfo.heightMap = new IntV3[hminfo.terrainWidth * hminfo.terrainHeight];
// We use a greyscale image, so all 3 rgb values are the same, but we only need one for the height
// So we use this counter to skip the next two components in the image data (we read R, then skip BG)
int k=0;
// Read the image data into our heightMap array
for(int j=0; j< hminfo.terrainHeight; j++)
{
for(int i=0; i< hminfo.terrainWidth; i++)
{
height = bitmapImage[k];
index = ( hminfo.terrainWidth * (hminfo.terrainHeight - 1 - j)) + i;
hminfo.heightMap[index].x = i - (hminfo.terrainWidth - 1)/2;
hminfo.heightMap[index].y = height;
hminfo.heightMap[index].z = j - (hminfo.terrainHeight - 1)/2;
k+=3;
}
k++;
}
int cols = hminfo.terrainWidth;
int rows = hminfo.terrainHeight;
//Create the grid
NumVertices = 2 * rows * cols;
NumFaces = (rows-1)*(cols-1)*2;
v = new struct HeightFieldVertex[NumVertices];
for(DWORD i = 0; i < rows; ++i)
{
for(DWORD j = 0; j < cols; ++j)
{
v[i*cols+j].pos.x = hminfo.heightMap[i*cols+j].x * dx;
v[i*cols+j].pos.y = (float(hminfo.heightMap[i*cols+j].y)/255.0) * dy;
v[i*cols+j].pos.z = hminfo.heightMap[i*cols+j].z * dz;
v[i*cols+j].texCoord = D3DXVECTOR2(j, i);
}
}
indices = new DWORD[NumFaces * 3];
k = 0;
for(DWORD i = 0; i < rows-1; i++)
{
for(DWORD j = 0; j < cols-1; j++)
{
indices[k] = i*cols+j; // Bottom left of quad
indices[k+1] = i*cols+j+1; // Bottom right of quad
indices[k+2] = (i+1)*cols+j; // Top left of quad
indices[k+3] = (i+1)*cols+j; // Top left of quad
indices[k+4] = i*cols+j+1; // Bottom right of quad
indices[k+5] = (i+1)*cols+j+1; // Top right of quad
k += 6; // next quad
}
}
//normals & tangents
std::vector<D3DXVECTOR3> tempNormal;
//normalized and unnormalized normals
D3DXVECTOR3 unnormalized(0.0f, 0.0f, 0.0f);
//tangent stuff
std::vector<D3DXVECTOR3> tempTangent;
D3DXVECTOR3 tangent(0.0f, 0.0f, 0.0f);
float tcU1, tcV1, tcU2, tcV2;
//Used to get vectors (sides) from the position of the verts
float vecX, vecY, vecZ;
//Two edges of our triangle
D3DXVECTOR3 edge1(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 edge2(0.0f, 0.0f, 0.0f);
//Compute face normals
//And Tangents
for(int i = 0; i < NumFaces; ++i)
{
//Get the vector describing one edge of our triangle (edge 0,2)
vecX = v[indices[(i*3)+1]].pos.x - v[indices[(i*3)]].pos.x;
vecY = v[indices[(i*3)+1]].pos.y - v[indices[(i*3)]].pos.y;
vecZ = v[indices[(i*3)+1]].pos.z - v[indices[(i*3)]].pos.z;
edge1 = D3DXVECTOR3(vecX, vecY, vecZ); //Create our first edge
//Get the vector describing another edge of our triangle (edge 2,1)
vecX = v[indices[(i*3)+2]].pos.x - v[indices[(i*3)]].pos.x;
vecY = v[indices[(i*3)+2]].pos.y - v[indices[(i*3)]].pos.y;
vecZ = v[indices[(i*3)+2]].pos.z - v[indices[(i*3)]].pos.z;
edge2 = D3DXVECTOR3(vecX, vecY, vecZ); //Create our second edge
//Cross multiply the two edge vectors to get the un-normalized face normal
D3DXVec3Cross(&unnormalized, &edge1, &edge2);
tempNormal.push_back(unnormalized);
//Find first texture coordinate edge 2d vector
tcU1 = v[indices[(i*3)+1]].texCoord.x - v[indices[(i*3)]].texCoord.x;
tcV1 = v[indices[(i*3)+1]].texCoord.y - v[indices[(i*3)]].texCoord.y;
//Find second texture coordinate edge 2d vector
tcU2 = v[indices[(i*3)+2]].texCoord.x - v[indices[(i*3)]].texCoord.x;
tcV2 = v[indices[(i*3)+2]].texCoord.y - v[indices[(i*3)]].texCoord.y;
//Find tangent using both tex coord edges and position edges
tangent.x = (tcV2 * edge1.x - tcV1 * edge2.x) / (tcU1 * tcV2 - tcU2 * tcV1);
tangent.y = (tcV2 * edge1.y - tcV1 * edge2.y) / (tcU1 * tcV2 - tcU2 * tcV1);
tangent.z = (tcV2 * edge1.z - tcV1 * edge2.z) / (tcU1 * tcV2 - tcU2 * tcV1);
tempTangent.push_back(tangent);
}
//Compute vertex normals (normal Averaging)
D3DXVECTOR4 normalSum(0.0f, 0.0f, 0.0f, 0.0f);
D3DXVECTOR4 tangentSum(0.0f, 0.0f, 0.0f, 0.0f);
int facesUsing = 0;
float tX, tY, tZ; //temp axis variables
//Go through each vertex
for(int i = 0; i < NumVertices; ++i)
{
//Check which triangles use this vertex
for(int j = 0; j < NumFaces; ++j)
{
if(indices[j*3] == i ||
indices[(j*3)+1] == i ||
indices[(j*3)+2] == i)
{
tX = normalSum.x + tempNormal[j].x;
tY = normalSum.y + tempNormal[j].y;
tZ = normalSum.z + tempNormal[j].z;
normalSum = D3DXVECTOR4(tX, tY, tZ, 0.0f); //If a face is using the vertex, add the unormalized face normal to the normalSum
facesUsing++;
}
}
//Get the actual normal by dividing the normalSum by the number of faces sharing the vertex
normalSum = normalSum / facesUsing;
facesUsing = 0;
//Check which triangles use this vertex
for(int j = 0; j < NumFaces; ++j)
{
if(indices[j*3] == i ||
indices[(j*3)+1] == i ||
indices[(j*3)+2] == i)
{
//We can reuse tX, tY, tZ to sum up tangents
tX = tangentSum.x + tempTangent[j].x;
tY = tangentSum.y + tempTangent[j].y;
tZ = tangentSum.z + tempTangent[j].z;
tangentSum = D3DXVECTOR4(tX, tY, tZ, 0.0f); //sum up face tangents using this vertex
facesUsing++;
}
}
//Get the actual normal by dividing the normalSum by the number of faces sharing the vertex
tangentSum = tangentSum / facesUsing;
//Normalize the normalSum vector and tangent
D3DXVec4Normalize(&normalSum, &normalSum);
D3DXVec4Normalize(&tangentSum, &tangentSum);
//Store the normal and tangent in our current vertex
v[i].normal.x = normalSum.x;
v[i].normal.y = normalSum.y;
v[i].normal.z = normalSum.z;
v[i].tangent.x = tangentSum.x;
v[i].tangent.y = tangentSum.y;
v[i].tangent.z = tangentSum.z;
D3DXVECTOR3 bit;
D3DXVec3Cross(&bit, &v[i].normal, &v[i].tangent);
v[i].bitangent = -1.0 * bit;
//Clear normalSum, tangentSum and facesUsing for next vertex
normalSum = D3DXVECTOR4(0.0f, 0.0f, 0.0f, 0.0f);
tangentSum = D3DXVECTOR4(0.0f, 0.0f, 0.0f, 0.0f);
facesUsing = 0;
}
////terrain AABB
//MinX = -1.0 * dx * (hminfo.terrainWidth - 1)/2;
//MinY = 0.0;
//MinZ = -1.0 * dz * (hminfo.terrainHeight - 1)/2;
//MaxX = dx * (hminfo.terrainWidth - 1)/2;
//MaxY = dy;
//MaxZ = dz * (hminfo.terrainHeight - 1)/2;
return true;
}
Fragment of Base.cpp
HeightField *hf = new HeightField(g_Terrain, *g_PhysicsSDK, *material, g_Terrain.hminfo.terrainWidth, g_Terrain.hminfo.terrainHeight);
g_PxScene->addActor(*(hf->g_pxHeightField));
I want to get something similar, but in order to work correctly
PVD - simulation Terrain
If you are familiar with the website rastertek for working with DirectX 10 or 11 in c++ within his updated DirectX 11 Terrain Series or version 2 a similar construct was created in tutorial 9 in which he calls Terrain Cells. The direct link to that tutorial can be found here: DirectX 11: Terrain Series2: Tutorial 9 - Terrain Cells. This should provide an excellent reference that I think is related toward the topic at hand and your initial question.