问题
I'm looking forward to implement a LookAt( forward, up ) method where:
Vector3 forward = eyePosition - targetPosition;
Vector3 up;
I've seen some implementations over the web, but analizing the code, I've found potencial misbehaviors...
I list them from easiest to hardest:
1) This won't work if UP is a zero vector.
2) This won't work if FORWARD is a zero vector.
3) This won't work if UP and FORWARD are parallels.
...
So.. How should this method correctly be implemented?
Here is a code example I've found.
/*
* gluLookAt.h
*
* This is a modified version of the function of the same name from
* the Mesa3D project ( http://mesa3d.org/ ), which is licensed
* under the MIT license, which allows use, modification, and
* redistribution
*
* In order to work under OpenGL ES, all instances of GLdouble
* had to be changed to GLfloat, and all "d" function calls had
* to be changed to the "f" versions.
*
* Original developer's comments have been left in place.
*
* Out of respect for the original authors, this is licensed under
* the Mesa (MIT) license. Original license follows:
*
* -----------------------------------------------------------------------
*
* Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#import <Foundation/Foundation.h>
#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>
void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez,
GLfloat centerx, GLfloat centery, GLfloat centerz,
GLfloat upx, GLfloat upy, GLfloat upz);
gluLookAt.m
/*
* gluLookAt.c
*/
#include "gluLookAt.h"
void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez,
GLfloat centerx, GLfloat centery, GLfloat centerz,
GLfloat upx, GLfloat upy, GLfloat upz)
{
GLfloat m[16];
GLfloat x[3], y[3], z[3];
GLfloat mag;
/* Make rotation matrix */
/* Z vector */
z[0] = eyex - centerx;
z[1] = eyey - centery;
z[2] = eyez - centerz;
mag = sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);
if (mag) { /* mpichler, 19950515 */
z[0] /= mag;
z[1] /= mag;
z[2] /= mag;
}
/* Y vector */
y[0] = upx;
y[1] = upy;
y[2] = upz;
/* X vector = Y cross Z */
x[0] = y[1] * z[2] - y[2] * z[1];
x[1] = -y[0] * z[2] + y[2] * z[0];
x[2] = y[0] * z[1] - y[1] * z[0];
/* Recompute Y = Z cross X */
y[0] = z[1] * x[2] - z[2] * x[1];
y[1] = -z[0] * x[2] + z[2] * x[0];
y[2] = z[0] * x[1] - z[1] * x[0];
/* mpichler, 19950515 */
/* cross product gives area of parallelogram, which is < 1.0 for
* non-perpendicular unit-length vectors; so normalize x, y here
*/
mag = sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
if (mag) {
x[0] /= mag;
x[1] /= mag;
x[2] /= mag;
}
mag = sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);
if (mag) {
y[0] /= mag;
y[1] /= mag;
y[2] /= mag;
}
#define M(row,col) m[col*4+row]
M(0, 0) = x[0];
M(0, 1) = x[1];
M(0, 2) = x[2];
M(0, 3) = 0.0;
M(1, 0) = y[0];
M(1, 1) = y[1];
M(1, 2) = y[2];
M(1, 3) = 0.0;
M(2, 0) = z[0];
M(2, 1) = z[1];
M(2, 2) = z[2];
M(2, 3) = 0.0;
M(3, 0) = 0.0;
M(3, 1) = 0.0;
M(3, 2) = 0.0;
M(3, 3) = 1.0;
#undef M
glMultMatrixf(m);
/* Translate Eye to Origin */
glTranslatef(-eyex, -eyey, -eyez);
}
回答1:
Ok.. I've just got to an idea to solve this problem. I haven't test it, not even code it yet, but I share the idea to discuss it, I appreciate your opinion!!
Pseudo code: (using column major matrix and right-handed coordinate system)
// transformation matrix (column major)
float matrix[16];
void crossProduct( const flaot a[3], const float b[3], float result[3] ) {
result[0] = a[1] * b[2] - a[2] * b[1];
result[1] = -a[0] * b[2] + a[2] * b[0];
result[2] = a[0] * b[1] - a[1] * b[0];
}
void LookAt( float forward[3], float up[3] ) {
// get forward length
int length = sqrt( forward[0] * forward[0] + forward[1] * forward[1] + forward[2] * forward[2] );
// if not zero, normalize it
if ( length ) {
forward[0] /= length;
forward[1] /= length;
forward[2] /= length;
} else {
// get last forward direction
forward[0] = matrix[8];
forward[1] = matrix[9];
forward[2] = matrix[10];
}
// get up length
length = sqrt( up[0] * up[0] + up[1] * up[1] + up[2] * up[2] );
// if not zero, normalize it
if ( length ) {
up[0] /= length;
up[1] /= length;
up[2] /= length;
} else {
// get last up direction
up[0] = matrix[4];
up[1] = matrix[5];
up[2] = matrix[6];
}
float left[3];
crossProduct( up, forward, left);
// if cross product is zero, forward and up are parallels. get last left direction
if ( left[0]==0 && left[1]==0 && left[2]==0 ) {
left[0] = matrix[0];
left[1] = matrix[1];
left[2] = matrix[2];
}
// recalculate orthogonal up
crossProduct( forward, left, up );
// update matrix values
matrix[0] = left[0];
matrix[1] = left[1];
matrix[2] = left[2];
matrix[3] = 0;
matrix[4] = up[0];
matrix[5] = up[1];
matrix[6] = up[2];
matrix[7] = 0;
matrix[8] = forward[0];
matrix[9] = forward[1];
matrix[10] = forward[2];
matrix[11] = 0;
matrix[12] = 0;
matrix[13] = 0;
matrix[14] = 0;
matrix[15] = 1;
}
来源:https://stackoverflow.com/questions/11008303/lookat-implementation-misbehaviors