Un cilindro impostor se puede hacer de hecho de la misma manera que una esfera, como Nicol Bolas lo hizo en su tutorial. Puedes hacer un cuadrado frente a la cámara y colorearlo para que parezca un cilindro, exactamente como Nicol lo hizo para las esferas. Y no es que es difícil.
La forma en que se hace es ray-tracing, por supuesto. Tenga en cuenta que un cilindro que mira hacia arriba en el espacio de la cámara es bastante fácil de implementar. Por ejemplo, la intersección con el lado puede proyectarse a la llanura xz, es un problema 2D de una línea que se cruza con un círculo. Tampoco es más difícil obtener la parte superior e inferior, se da la coordenada z de la intersección, por lo que realmente se conoce el punto de intersección del rayo y la llanura del círculo, todo lo que tienes que hacer es verificar si está dentro del círculo. Y, básicamente, eso es todo, obtienes dos puntos y vuelves uno más cercano (las normales también son bastante triviales).
Y cuando se trata de un eje arbitrario, resulta ser casi el mismo problema. Cuando resuelve ecuaciones en el cilindro del eje fijo, las está resolviendo para un parámetro que describe cuánto tiempo tiene que pasar desde un punto dado en una dirección determinada para llegar al cilindro. A partir de la "definición" de este, debe observar que este parámetro no cambia si gira el mundo. Entonces puede rotar el eje arbitrario para convertirse en el eje Y, resolver el problema en un espacio donde las ecuaciones son más fáciles, obtener el parámetro para la ecuación de línea en ese espacio, pero devolver el resultado en el espacio de la cámara.
Puede descargar los archivos de sombreado de here. Sólo una imagen de él en acción: screenshot http://oi40.tinypic.com/2h5tqhy.jpg
El código donde sucede la magia (Es sólo el tiempo 'porque está lleno de comentarios, pero el código en sí es como máximo 50 líneas):
void CylinderImpostor(out vec3 cameraPos, out vec3 cameraNormal)
{
// First get the camera space direction of the ray.
vec3 cameraPlanePos = vec3(mapping * max(cylRadius, cylHeight), 0.0) + cameraCylCenter;
vec3 cameraRayDirection = normalize(cameraPlanePos);
// Now transform data into Cylinder space wherethe cyl's symetry axis is up.
vec3 cylCenter = cameraToCylinder * cameraCylCenter;
vec3 rayDirection = normalize(cameraToCylinder * cameraPlanePos);
// We will have to return the one from the intersection of the ray and circles,
// and the ray and the side, that is closer to the camera. For that, we need to
// store the results of the computations.
vec3 circlePos, sidePos;
vec3 circleNormal, sideNormal;
bool circleIntersection = false, sideIntersection = false;
// First check if the ray intersects with the top or bottom circle
// Note that if the ray is parallel with the circles then we
// definitely won't get any intersection (but we would divide with 0).
if(rayDirection.y != 0.0){
// What we know here is that the distance of the point's y coord
// and the cylCenter is cylHeight, and the distance from the
// y axis is less than cylRadius. So we have to find a point
// which is on the line, and match these conditions.
// The equation for the y axis distances:
// rayDirection.y * t - cylCenter.y = +- cylHeight
// So t = (+-cylHeight + cylCenter.y)/rayDirection.y
// About selecting the one we need:
// - Both has to be positive, or no intersection is visible.
// - If both are positive, we need the smaller one.
float topT = (+cylHeight + cylCenter.y)/rayDirection.y;
float bottomT = (-cylHeight + cylCenter.y)/rayDirection.y;
if(topT > 0.0 && bottomT > 0.0){
float t = min(topT,bottomT);
// Now check for the x and z axis:
// If the intersection is inside the circle (so the distance on the xz plain of the point,
// and the center of circle is less than the radius), then its a point of the cylinder.
// But we can't yet return because we might get a point from the the cylinder side
// intersection that is closer to the camera.
vec3 intersection = rayDirection * t;
if(length(intersection.xz - cylCenter.xz) <= cylRadius) {
// The value we will (optianally) return is in camera space.
circlePos = cameraRayDirection * t;
// This one is ugly, but i didn't have better idea.
circleNormal = length(circlePos - cameraCylCenter) <
length((circlePos - cameraCylCenter) + cylAxis) ? cylAxis : -cylAxis;
circleIntersection = true;
}
}
}
// Find the intersection of the ray and the cylinder's side
// The distance of the point and the y axis is sqrt(x^2 + z^2), which has to be equal to cylradius
// (rayDirection.x*t - cylCenter.x)^2 + (rayDirection.z*t - cylCenter.z)^2 = cylRadius^2
// So its a quadratic for t (A*t^2 + B*t + C = 0) where:
// A = rayDirection.x^2 + rayDirection.z^2 - if this is 0, we won't get any intersection
// B = -2*rayDirection.x*cylCenter.x - 2*rayDirection.z*cylCenter.z
// C = cylCenter.x^2 + cylCenter.z^2 - cylRadius^2
// It will give two results, we need the smaller one
float A = rayDirection.x*rayDirection.x + rayDirection.z*rayDirection.z;
if(A != 0.0) {
float B = -2*(rayDirection.x*cylCenter.x + rayDirection.z*cylCenter.z);
float C = cylCenter.x*cylCenter.x + cylCenter.z*cylCenter.z - cylRadius*cylRadius;
float det = (B * B) - (4 * A * C);
if(det >= 0.0){
float sqrtDet = sqrt(det);
float posT = (-B + sqrtDet)/(2*A);
float negT = (-B - sqrtDet)/(2*A);
float IntersectionT = min(posT, negT);
vec3 Intersect = rayDirection * IntersectionT;
if(abs(Intersect.y - cylCenter.y) < cylHeight){
// Again it's in camera space
sidePos = cameraRayDirection * IntersectionT;
sideNormal = normalize(sidePos - cameraCylCenter);
sideIntersection = true;
}
}
}
// Now get the results together:
if(sideIntersection && circleIntersection){
bool circle = length(circlePos) < length(sidePos);
cameraPos = circle ? circlePos : sidePos;
cameraNormal = circle ? circleNormal : sideNormal;
} else if(sideIntersection){
cameraPos = sidePos;
cameraNormal = sideNormal;
} else if(circleIntersection){
cameraPos = circlePos;
cameraNormal = circleNormal;
} else
discard;
}
¿por qué se usar un impostor para esto? ¿Por qué no dibujar un cilindro? Además, hay una razón por la que elijo esferas y no cilindros cuando escribí ese tutorial. Las esferas son simétricas; están definidos por una posición y un radio. La matemática de raytracing es simple. Los cilindros son * mucho * más complicados. Sería mucho más fácil para ti extraer el modelo de cilindro que utilizo fuera del tutorial y renderizarlo. –
Como dije, desarrollé una pequeña herramienta para la visualización de moléculas 3D para un proyecto escolar, así que decidí realizar un impostor de esfera 3D y un impostor de cilindro primero de acuerdo con la técnica que Brad Larson usa en su aplicación Brad Larson y como otra razón es que los impostores son más ligeros que dibujar un cilindro real que comprenda cientos de polígonos, ¡y todo esto es realmente importante para una visualización 3D de moléculas debido a la gran cantidad de moléculas que se computarán! Pero si me dijiste que es demasiado difícil, ¿estoy empezando a tener miedo? – nadir
No puedo responder su pregunta, pero el documento con el que se vincula por Larsson es muy interesante, así que gracias por eso. Si fuera a ofrecer un consejo, diría que simplemente lo dejé en esferas e ignoro los cilindros: p. – Robinson