3d - terror - detras de camaras roast yourself
Mueve la cámara para adaptarse a la escena 3D (6)
Como tiene un cuadro delimitador, debe tener una base que describa su orientación. Parece que desea colocar la cámara en la línea coincidente con el vector base que describe la dimensión más pequeña de la caja, luego hacer rodar la cámara para que la dimensión más grande sea horizontal (suponiendo que tiene OBB y no AABB). Esto supone que la relación de aspecto es mayor que 1.0; Si no, querrás usar la dimensión vertical.
Lo que intentaría:
- Encuentra la dimensión de caja más pequeña.
- Encuentra el vector base asociado.
- Escale el vector base según la distancia desde el centro de la caja que debe ser la cámara. Esta distancia es solo
boxWidth / (2 * tan(horizontalFov / 2))
. Tenga en cuenta queboxWidth
es el ancho de la dimensión más grande de la caja. - Coloque la cámara en
boxCenter + scaledBasis
mirando elboxCenter
. - Gire la cámara si es necesario para alinear el vector ascendente de la cámara con el vector de base de cuadro apropiado.
Editar:
Así que creo que lo que estás tratando de hacer es que tienes la cámara en una posición arbitraria buscando un lugar, y tienes una AABB en otra posición. Sin mover la cámara para mirar hacia un lado de la caja, debes:
- Mira el centro de la caja.
- Traduzca la cámara a lo largo de su apariencia vectorial para que la caja ocupe la mayor cantidad de espacio de pantalla.
Si este es el caso tendrás un poco más de trabajo; Esto es lo que sugiero:
- Gire la cámara para mirar el centro del cuadro delimitador.
- Proyecte todos los puntos del cuadro en el espacio de la pantalla y encuentre el cuadro delimitador mínimo / máximo en el espacio de la pantalla (ya tiene esto).
- Ahora
Unproject
dos esquinas opuestas del espacio delimitador en el espacio del mundo. Para un valor de Z, use los puntos espaciales del mundo más cercanos de su AABB a la cámara. - Esto debería darle un plano espacial mundial frente a la cámara situada en el punto de la AABB que está más cerca de la cámara.
- Ahora use nuestro método de orientación lateral existente para mover la cámara al lugar adecuado, tratando este plano como el lado de su caja.
Estoy buscando un algoritmo para ajustar un cuadro delimitador dentro de una ventana gráfica (en mi caso, una escena de DirectX). Sé de algoritmos para centrar una esfera delimitadora en una cámara ortográfica pero necesitaría lo mismo para un cuadro delimitador y una cámara en perspectiva. No puedo simplemente cambiar el FOV porque esta aplicación tiene FOV como una variable editable por el usuario, por lo que debe mover la cámara.
Tengo la mayoría de los datos:
- Tengo el up-vector para la cámara.
- Tengo el punto central del cuadro delimitador
- Tengo el vector de observación (dirección y distancia) desde el punto de la cámara al centro de la caja
- He proyectado los puntos en un plano perpendicular a la cámara y he recuperado los coeficientes que describen cuánto están las cuerdas X / Y max / min dentro o fuera del plano de visualización.
Problemas que tengo:
- El centro del cuadro delimitador no está necesariamente en el centro de la ventana gráfica (es decir, es un rectángulo delimitador después de la proyección).
- Dado que el campo de visión "sesga" la proyección (consulte http://en.wikipedia.org/wiki/File:Perspective-foreshortening.svg ) no puedo simplemente usar los coeficientes como factor de escala para mover la cámara porque se sobrepasará / debajo de la posición deseada de la cámara
¿Cómo encuentro la posición de la cámara para que llene la ventana de visualización con el píxel más perfecto posible (con la excepción de que la relación de aspecto está lejos de 1.0, solo necesita llenar uno de los ejes de la pantalla)?
He intentado algunas otras cosas:
- Usando una esfera delimitadora y Tangente para encontrar un factor de escala para mover la cámara. Esto no funciona bien porque no tiene en cuenta la proyección en perspectiva y, en segundo lugar, las esferas son malos volúmenes de delimitación para mi uso porque tengo muchas geometrías planas y largas.
- Iterando llamadas a la función para obtener un error cada vez más pequeño en la posición de la cámara. Esto ha funcionado un poco, pero a veces me puedo encontrar con casos extraños en los que la posición de la cámara se excede demasiado y aumenta el factor de error. Además, al hacer esto no recalifiqué el modelo según la posición del rectángulo delimitador. No pude encontrar una forma sólida y robusta de hacerlo de manera confiable.
¡Ayuda por favor!
Consulte este enlace https://msdn.microsoft.com/en-us/library/bb197900.aspx
distancia de flotación = esfera.radio / pecado (fov / 2);
float3 eyePoint = sphere.centerPoint - distance * camera.frontVector;
Esto se copia directamente de mi motor, crea 6 planos que representan cada uno de los seis lados del frutsum. Espero que sea de utilidad.
internal class BoundingFrustum
{
private readonly float4x4 matrix = new float4x4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
private readonly Plane[] planes;
internal BoundingFrustum(float4x4 value)
{
planes = new Plane[6];
for (int i = 0; i < 6; i++)
planes[i] = new Plane();
Setfloat4x4(value);
}
private void Setfloat4x4(float4x4 value)
{
planes[2].Normal.X = -value.M14 - value.M11;
planes[2].Normal.Y = -value.M24 - value.M21;
planes[2].Normal.Z = -value.M34 - value.M31;
planes[2].D = -value.M44 - value.M41;
planes[3].Normal.X = -value.M14 + value.M11;
planes[3].Normal.Y = -value.M24 + value.M21;
planes[3].Normal.Z = -value.M34 + value.M31;
planes[3].D = -value.M44 + value.M41;
planes[4].Normal.X = -value.M14 + value.M12;
planes[4].Normal.Y = -value.M24 + value.M22;
planes[4].Normal.Z = -value.M34 + value.M32;
planes[4].D = -value.M44 + value.M42;
planes[5].Normal.X = -value.M14 - value.M12;
planes[5].Normal.Y = -value.M24 - value.M22;
planes[5].Normal.Z = -value.M34 - value.M32;
planes[5].D = -value.M44 - value.M42;
planes[0].Normal.X = -value.M13;
planes[0].Normal.Y = -value.M23;
planes[0].Normal.Z = -value.M33;
planes[0].D = -value.M43;
planes[1].Normal.X = -value.M14 + value.M13;
planes[1].Normal.Y = -value.M24 + value.M23;
planes[1].Normal.Z = -value.M34 + value.M33;
planes[1].D = -value.M44 + value.M43;
for (int i = 0; i < 6; i++)
{
float num2 = planes[i].Normal.Length();
planes[i].Normal = planes[i].Normal / num2;
planes[i].D /= num2;
}
}
internal Plane Bottom
{
get { return planes[5]; }
}
internal Plane Far
{
get { return planes[1]; }
}
internal Plane Left
{
get { return planes[2]; }
}
internal Plane Near
{
get { return planes[0]; }
}
internal Plane Right
{
get { return planes[3]; }
}
internal Plane Top
{
get { return planes[4]; }
}
}
Hay muchas posiciones posibles de la cámara + orientaciones donde el cuadro delimitador encajaría dentro del frustum de la vista. Pero cualquier procedimiento seleccionaría una posición y orientación específicas de la cámara.
Si consideras esferas limitantes, una solución podría ser
- Primer cambio de orientación para mirar el centro de la esfera delimitadora.
- luego retroceda lo suficiente (dirección de aspecto negativo) para que la esfera delimitante se ajuste dentro del frustum
Con los cuadros delimitadores , podría considerar un paso anterior para colocar primero la cámara en posición perpendicular al centro de la cara del cubo más grande (o más pequeña, cualquiera que prefiera).
No tengo experiencia con DirectX, pero mover y cambiar la orientación de la cámara para centrar un punto determinado debería ser fácil. La parte difícil es hacer la matemática de decidir a qué distancia moverse para ver el objeto.
Mates
Si conoce el tamaño delimitador s
del objeto en coordenadas mundiales (no nos interesan los píxeles ni las coordenadas de la cámara, ya que dependen de su distancia) a partir de la orientación de la cámara, puede calcular la distancia requerida d
de la cámara para la forma delimitada si conoce el ángulo de campo de visión x e y a
de la proyección en perspectiva.
frustum ------
------ ***** -
----- * * |
-=== ) FOV a *bounding box | BB size s
camera ----- * * |
------ ***** -
------
|-------------------|
distance d
Por lo tanto, las math son tan(a/2) = (s/2) / d
=> d = (s/2) / tan(a/2)
que le dará la distancia a la que la cámara debe colocarse desde el límite más cercano superficie.
No lo tengo a mano en este momento, pero el libro que desea es http://www.amazon.com/Jim-Blinns-Corner-Graphics-Pipeline/dp/1558603875/ref=ntt_at_ep_dpi_1
Él tiene todo un capítulo sobre esto.
Sé que hay algunas respuestas excelentes arriba, pero quería agregar una solución ridículamente simple para encajar la esfera delimitada dentro de la cámara frustrum. Supone que desea mantener el vector Target y Forward de la cámara igual, y simplemente ajustar la distancia de la cámara al objetivo.
Tenga en cuenta que esto no le dará el mejor ajuste, pero le dará un ajuste aproximado, mostrando toda la geometría, y solo en unas pocas líneas de código, y sin transformaciones de pantalla a mundo
// Compute camera radius to fit bounding sphere
// Implementation in C#
//
// Given a bounding box around your scene
BoundingBox bounds = new BoundingBox();
// Compute the centre point of the bounding box
// NOTE: The implementation for this is to take the mid-way point between
// two opposing corners of the bounding box
Vector3 center = bounds.Center;
// Find the corner of the bounding box which is maximum distance from the
// centre of the bounding box. Vector3.Distance computes the distance between
// two vectors. Select is just nice syntactic sugar to loop
// over Corners and find the max distance.
double boundSphereRadius = bounds.Corners.Select(x => Vector3.Distance(x, bounds.Center)).Max();
// Given the camera Field of View in radians
double fov = Math3D.DegToRad(FieldOfView);
// Compute the distance the camera should be to fit the entire bounding sphere
double camDistance = (boundSphereRadius * 2.0) / Math.Tan(fov / 2.0);
// Now, set camera.Target to bounds.Center
// set camera.Radius to camDistance
// Keep current forward vector the same
La implementación de BoundingBox en C # se encuentra a continuación. Los puntos importantes son las propiedades Centro y Esquinas. Vector3 es una implementación bastante estándar de un vector de 3 componentes (X, Y, Z)
public struct BoundingBox
{
public Vector3 Vec0;
public Vector3 Vec1;
public BoundingBox(Vector3 vec0, Vector3 vec1)
{
Vec0 = vec0;
Vec1 = vec1;
}
public Vector3 Center
{
get { return (Vec0 + Vec1)*0.5; }
}
public IList<Vector3> Corners
{
get
{
Vector3[] corners = new[]
{
new Vector3( Vec0.X, Vec0.Y, Vec0.Z ),
new Vector3( Vec1.X, Vec0.Y, Vec0.Z ),
new Vector3( Vec0.X, Vec1.Y, Vec0.Z ),
new Vector3( Vec0.X, Vec0.Y, Vec1.Z ),
new Vector3( Vec1.X, Vec1.Y, Vec0.Z ),
new Vector3( Vec1.X, Vec0.Y, Vec1.Z ),
new Vector3( Vec0.X, Vec1.Y, Vec1.Z ),
new Vector3( Vec1.X, Vec1.Y, Vec1.Z ),
};
return corners;
}
}
}