OpenGL на Java, ручное создание матрицы gluLookAt
Я экспериментировал с использованием матричной манипуляции вместо использования функций на основе эйлера или кватерниона, которые поставляются с Open GL ES (и ulti lib).
Я могу заставить большинство вещей работать, но я не могу воссоздать функцию gluLookAt совершенно правильно, как вы сможете увидеть на скриншотах ниже:
Правильная версия gluLookAt.
Busted, матричная версия.
Обратите внимание, что снимок экрана - это только часть реального экрана. Эти кнопки, которые вы видите в обеих, являются наложениями, выполненными с использованием 2D-орфографии, и они помогают показать разницу, потому что они находятся в одном месте на обоих изображениях.
Я взял мою информацию отсюда: http://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml
Который я воссоздал в своем коде Java ниже (некоторые детали опущены):
class point3D {
public double mX, mY, mZ;
}
double scaler(point3D pt){
return Math.pow(
Math.pow(pt.mX, 2.0) + Math.pow(pt.mY, 2.0) + Math.pow(pt.mZ, 2.0),
1.0/3.0
);
}
void cross(point3D A, point3D B, point3D output){
output.mX = (A.mY*B.mZ) - (A.mZ*B.mY);
output.mY = (A.mZ*B.mX) - (A.mX*B.mZ);
output.mZ = (A.mX*B.mY) - (A.mY*B.mX);
}
void normalise(point3D normMe, point3D output){
double s = scaler(normMe);
output.mX = normMe.mX / s;
output.mY = normMe.mY / s;
output.mZ = normMe.mZ / s;
}
float mx, my, mz, mfx, mfy, mfz;
private FloatBuffer mLookatMx; //- All setup and stuff elsewhere
void myLookAt(){
point3D ptTmpA, ptTmpB, ptTmpC, ptTmpD;
//- 'forward' F part
ptTmpA.mX = mfx - mx;
ptTmpA.mY = mfy - my;
ptTmpA.mZ = mfz - mz;
normalise(ptTmpA, ptTmpA);
//- 'up' part
ptTmpB.mX = 0;
ptTmpB.mY = 1.0;
ptTmpB.mZ = 0;
cross(ptTmpB, ptTmpA, ptTmpC); //- S
normalise(ptTmpC, ptTmpC);
cross(ptTmpA, ptTmpC, ptTmpD); //- U
normalise(ptTmpD, ptTmpD);
//- the 4x4 matrix. Not including the transformation this time for simplicity.
//- m = {
//- s1, s2, s3, -x
//- u1, u2, u3, -y
//- -f1, -f2, -f3, -z
//- 0, 0, 0, 1
//- }
//- 0
mLookatMx.put((float)ptTmpC.mX);
mLookatMx.put((float)ptTmpC.mY);
mLookatMx.put((float)ptTmpC.mZ);
mLookatMx.put(0);
//- 4
mLookatMx.put((float)ptTmpD.mX);
mLookatMx.put((float)ptTmpD.mY);
mLookatMx.put((float)ptTmpD.mZ);
mLookatMx.put(0);
//- 12
mLookatMx.put((float)ptTmpA.mX *-1.0f);
mLookatMx.put((float)ptTmpA.mY *-1.0f);
mLookatMx.put((float)ptTmpA.mZ *-1.0f);
mLookatMx.put(0);
//- 16
mLookatMx.put(0);
mLookatMx.put(0);
mLookatMx.put(0);
mLookatMx.put(1.0f);
mLookatMx.position(0);
}
void myDraw(){
glDisable(GL_DEPTH_BITS);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(mGL, 90.0, mScreen.mX / mScreen.mY, 0.01, 10.0);
//- This works
//gluLookAt(mGL,
// mx, my, mz,
// mfx, mfy, mfz,
// 0.0f, 1.0f, 0.0f
//);
//- This is distorted
glMultMatrixf(mLookatMx);
glTranslatef(mx * -1.0f, my * -1.0f, mz * -1.0f)
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//- draw models as seen in screen shot
}
1 ответ
OpenGL использует матрицы столбцов. Вы, кажется, заполняете свою матрицу построчно, это фактически будет работать в Direct3D (основной ряд).
Самое простое решение было бы позвонить mLookatMx.transpose (...)
(если вы использовали правильный класс матрицы); это перевернет строки и столбцы для вас.
Тем не менее, так как вы используете простой старый FloatBuffer
, вам нужно put (...)
содержимое вашей матрицы по одному столбцу за раз вместо одной строки за раз.
Кстати, в своей основе OpenGL всегда использует матрицы для преобразований. Эти вспомогательные утилиты Euler angle и quaternion просто создают матрицы преобразования для OpenGL. Вы можете реализовать нематричные преобразования в вершинных шейдерах, но это намного сложнее, чем вы думаете сейчас:)