Como uso o vetor de gravidade para transformar corretamente a cena para a realidade aumentada?

StackOverflow https://stackoverflow.com/questions/2541668

  •  23-09-2019
  •  | 
  •  

Pergunta

Estou tentando descobrir como obter um objeto especificado para o OpenGL a ser exibido corretamente de acordo com a orientação do dispositivo (ou seja, de acordo com o vetor de gravidade do acelerômetro e a direção da bússola).

O projeto de amostra Glgravity tem um exemplo que é quase assim (apesar de ignorar o cabeçalho), mas tem algumas falhas. Por exemplo, o bule salta 180deg como o ângulo de visualização do dispositivo atravessa o horizonte e também gira com esperança se você inclinar o dispositivo do retrato para a paisagem. Isso é bom para o contexto deste aplicativo, pois mostra um objeto e não importa que faça essas coisas. Mas isso significa que o código simplesmente não funciona quando você tenta imitar a visualização da vida real de um objeto OpenGL de acordo com a orientação do dispositivo. O que acontece é que ele quase funciona, mas a rotação de título que você aplica na bússola é "corrompida" pelas rotações adicionais espúrias vistas no projeto de exemplo GLgravity.

Alguém pode fornecer código de amostra que mostre como ajustar corretamente a orientação do dispositivo (por exemplo, vetor de gravidade) ou para corrigir o exemplo do glgravity para que não inclua alterações espúrias de título?

//Clear matrix to be used to rotate from the current referential to one based on the gravity vector
bzero(matrix, sizeof(matrix));
matrix[3][3] = 1.0;

//Setup first matrix column as gravity vector
matrix[0][0] = accel[0] / length;
matrix[0][1] = accel[1] / length;
matrix[0][2] = accel[2] / length;

//Setup second matrix column as an arbitrary vector in the plane perpendicular to the gravity vector {Gx, Gy, Gz} defined by by the equation "Gx * x + Gy * y + Gz * z = 0" in which we arbitrarily set x=0 and y=1
matrix[1][0] = 0.0;
matrix[1][1] = 1.0;
matrix[1][2] = -accel[1] / accel[2];
length = sqrtf(matrix[1][0] * matrix[1][0] + matrix[1][1] * matrix[1][1] + matrix[1][2] * matrix[1][2]);
matrix[1][0] /= length;
matrix[1][1] /= length;
matrix[1][2] /= length;

//Setup third matrix column as the cross product of the first two
matrix[2][0] = matrix[0][1] * matrix[1][2] - matrix[0][2] * matrix[1][1];
matrix[2][1] = matrix[1][0] * matrix[0][2] - matrix[1][2] * matrix[0][0];
matrix[2][2] = matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];

//Finally load matrix
glMultMatrixf((GLfloat*)matrix);

Aqui está um esclarecimento mostrando como obter a elevação e a inclinação necessárias para a solução de Glulookat, como mostrado na minha última resposta:

// elevation comes from z component (0 = facing horizon)
elevationRadians = asin(gravityVector.z / Vector3DMagnitude(gravityVector));

// tilt is how far screen is from vertical, looking along z axis
tiltRadians = atan2(-gravityVector.y, -gravityVector.x) - M_PI_2;

Seguindo a sugestão de Chris: não tenho certeza se tenho tudo correto devido a convenções diferentes do pedido de linha/coluna e cabeçalho CW ou CCW. No entanto, o código a seguir é o que eu criei:

Vector3D forward = Vector3DMake(0.0f, 0.0f, -1.0f);

// Multiply it by current rotation matrix to get teapot direction
Vector3D direction;     
direction.x = matrix[0][0] * forward.x + matrix[1][0] * forward.y + matrix[2][0] * forward.z;
direction.y = matrix[0][1] * forward.x + matrix[1][1] * forward.y + matrix[2][1] * forward.z;
direction.z = matrix[0][2] * forward.x + matrix[1][2] * forward.y + matrix[2][2] * forward.z;

heading = atan2(direction.z, direction.x) * 180 / M_PI;

// Use this heading to adjust the teapot direction back to keep it fixed
// Rotate about vertical axis (Y), as it is a heading adjustment
glRotatef(heading, 0.0, 1.0, 0.0);

Quando eu executo esse código, o comportamento do bule de chá aparentemente "melhorou", por exemplo. O cabeçalho não vira mais o 180DEG quando a tela do dispositivo (na exibição de retrato) é lançada para a frente/para trás na vertical. No entanto, ainda faz grandes saltos na direção quando o dispositivo (na vista da paisagem) é lançado para a frente/para trás. Então algo não está certo. Isso sugere que o cálculo acima do cabeçalho real está incorreto ...

Nenhuma solução correta

Outras dicas

Finalmente encontrei uma solução que funciona. :-)

Larguei a abordagem da matriz de rotação e, em vez disso, adotei Glulookat. Para fazer esse trabalho, você precisa conhecer o dispositivo "elevação" (ângulo de visualização em relação ao horizonte, ou seja, 0 no horizonte, +90 sobrecarga) e a "inclinação" da câmera (até que ponto o dispositivo está da vertical, seu plano x/y, ou seja, isto é, . 0 Quando vertical/retrato, +/- 90 quando horizontal/paisagem), ambos obtidos a partir dos componentes do vetor de gravidade do dispositivo.

Vector3D eye, scene, up;
CGFloat distanceFromScene = 0.8;
// Adjust eye position for elevation (y/z)
eye.x = 0;
eye.y = distanceFromScene * -sin(elevationRadians); // eye position goes down as elevation angle goes up
eye.z = distanceFromScene * cos(elevationRadians);  // z position is maximum when elevation is zero 
// Lookat point is origin
scene = Vector3DMake(0, 0, 0); // Scene is at origin
// Camera tilt - involves x/y plane only - arbitrary vector length
up.x = sin(tiltRadians);
up.y = cos(tiltRadians);
up.z = 0;

Então você apenas aplica a transformação de Glulookat e também gira a cena de acordo com o cabeçalho do dispositivo.

// Adjust view for device orientation
gluLookAt(eye.x, eye.y, eye.z, scene.x, scene.y, scene.z, up.x, up.y, up.z);
// Apply device heading to scene
glRotatef(currentHeadingDegrees, 0.0, 1.0, 0.0);

Tente girar o objeto, dependendo dos valores de aceleração do iPhone.

float angle = -atan2(accelX, accelY);

glPushMatrix();     
glTranslatef(centerPoint.x, centerPoint.y, 0);
glRotatef(angle, 0, 0, 1);
glTranslatef(-centerPoint.x, -centerPoint.y, 0);
glPopMatrix();

Onde o Centerpoint é o ponto médio, o objeto.

oo, bom.

Glgravity parece acertar tudo, exceto a guinada. Aqui está o que eu tentaria. Faça tudo o Glgravity faz, e então isso:

Projete um vetor na direção que você querer O bule de chá para enfrentar, usando a bússola ou o que você escolher. Em seguida, multiplique um vetor "para a frente" pela matriz de rotação atual do bule, o que lhe dará a direção do bule é voltado para. Achate os dois vetores no plano horizontal e pegue o ângulo entre eles.

Este ângulo é a sua guinada corretiva. Então apenas glRotatef por isso.

Se a bússola do 3GS é ou não confiável e robusta o suficiente para que isso funcione é outra coisa. As bússolas normais não funcionam quando o vetor norte é perpendicular ao rosto. Mas eu apenas experimentei o aplicativo Maps no 3GS do meu colega de trabalho e parece lidar, então talvez eles tenham uma solução mecânica lá. Saber o que o dispositivo está realmente fazendo ajudará a interpretar os resultados que ele fornece.

Certifique -se de testar seu aplicativo nos postes norte e sul assim que terminar. :-)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top