2012-03-07 36 views
6

Estoy desarrollando una pequeña herramienta para la visualización 3D de moléculas. Para mi proyecto elijo hacer algo en el camino de lo que el Sr. "Brad Larson" hizo con su software de Apple "Moléculas". Un enlace donde se puede encontrar una pequeña presentación de la técnica utilizada: Brad Larsson software presentationcilindro impostor en GLSL

por hacer mi trabajo i debe calcular esfera impostor y cilindro impostor.

Por el momento tengo éxito para hacer el "Esfera impostor" con la ayuda de otro tutorial Lies and Impostors

para resumir el cálculo del impostor esfera: primero enviar una "posición esfera" y el "radio de la esfera "al" sombreador de vértices "que creará en el espacio de la cámara un cuadrado que siempre estará frente a la cámara, luego enviaremos nuestro cuadrado al sombreador de fragmentos donde usaremos un simple trazado de rayos para encontrar qué fragmento del cuadrado está incluido en la esfera, y finalmente calculamos la normal y la posición del fragmento para calcular la iluminación. (¡Otra cosa también escribimos el gl_fragdepth para dar una buena profundidad a nuestra esfera de impostores!)

Pero ahora estoy bloqueado en la computación del cilindro impostor, trato de hacer un paralelo entre el impostor de la esfera y el impostor del cilindro pero no encuentro nada, mi problema es que para la esfera fue algo fácil porque la esfera es siempre la misma sin importar cómo la veamos, siempre veremos lo mismo: "un círculo" y otra cosa es que la esfera estaba perfectamente definida por Math, entonces podemos encontrar fácilmente la posición y la normalidad para la iluminación informática y crear nuestro impostor.

Para el cilindro no es lo mismo, y no pude encontrar una pista para modelar una forma que pueda usarse como "cilindro impostor", porque el cilindro muestra muchas formas diferentes según el ángulo en que lo veamos.

así que mi solicitud es preguntarle acerca de una solución o una indicación para mi problema de "impostor cilindro".

+3

¿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. –

+0

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

+0

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

Respuesta

1

Por lo que puedo entender del artículo, lo interpretaría de la siguiente manera.

Un cilindro impostor, visto desde cualquier ángulo tiene las siguientes características.

  1. Desde la parte superior, es un círculo. Por lo tanto, teniendo en cuenta que nunca necesitará ver un cilindro de arriba hacia abajo, no necesita renderizar nada.
  2. Desde el lado, es un rectángulo. El sombreador de píxeles solo necesita calcular la iluminación de forma normal.
  3. Desde cualquier otro ángulo, es un rectángulo (el mismo calculado en el paso 2) que curva. Su curvatura se puede modelar dentro del sombreador de píxeles como la curvatura de la elipse superior. Esta curvatura se puede considerar simplemente como un desplazamiento de cada "columna" en el espacio de textura, dependiendo del ángulo de visión. El eje menor de esta elipse se puede calcular multiplicando el eje principal (grosor del cilindro) por un factor del ángulo de visión actual (ángulo/90), suponiendo que 0 significa que está mirando el cilindro de costado.

Fig 1. Ángulos de visión. Solo tomé en cuenta el caso 0-90 en los cálculos a continuación, pero los otros casos son trivialmente diferentes.

Fig 2. Dado el ángulo de visión (phi) y el diámetro del cilindro (a) aquí es como el shader necesita para deformar el eje Y en el espacio textura Y sin = b'(phi). Y b '= a * (phi/90). Los casos phi = 0 y phi = 90 nunca deberían representarse.

Por supuesto, no he tomado la longitud de este cilindro en cuenta - que depende de su proyección en particular y no es un problema de imagen-espacio.

+0

Gracias por Explicación, entiendo un poco mejor mi problema, pero no veo cómo vincular todo lo que has dicho e implementarlo, así que si tienes tiempo para explicar más en detalle, será muy útil para mí. en todo caso ¡gracias por su respuesta precedente! – nadir

+0

Muchas gracias por sus claras explicaciones, solo tengo pocas preguntas, primero ¿qué quiere decir con "espacio de textura"? y para estudiar estos casos y encontrar el ángulo de visión creo que tendré necesidad de "lo normal" pero mi cilindro es solo impostor y entonces lo normal se calcula en el sombreador de fragmentos para cada fragmento, esto es lo que sucede con el impostor de esfera , cómo encontrar este ángulo de visión sin una normalidad – nadir

+0

En pocas palabras, el espacio de textura es (u, v) espacio: el espacio con el que trabajas dentro de un sombreador de fragmentos. En las explicaciones anteriores, no he considerado la iluminación en absoluto; todo lo que explico es cómo crear curvatura a medida que cambia el ángulo de visión (alrededor del eje X). Yo recomendaría que obtengas una renderización básica de impostor de cilindro sin encender primero y luego lo agregas más tarde. – Ani

2

Sé que esta pregunta es más de un año de edad, pero todavía me gustaría dar mis 2 centavos.

yo era capaz de producir impostores cilindros con otra técnica, que se inspiró en el código de pymol. Aquí está la estrategia básica:

1) que desea dibujar un cuadro delimitador (un cuboid) para el cilindro. Para hacer eso, necesitas 6 caras, que se traduce en 18 triángulos que se traducen en 36 vértices triangulares. Suponiendo que no tiene acceso a los sombreadores de geometría, pasa a un sombreador de vértices 36 veces el punto de inicio del cilindro, 36 veces la dirección del cilindro, y para cada uno de esos vértices pasa el punto correspondiente del cuadro delimitador . Por ejemplo, un vértice asociado con el punto (0, 0, 0) significa que se transformará en la esquina inferior izquierda del cuadro delimitador, (1,1,1) significa el punto diagonalmente opuesto, etc.

2) en el shader de vértice, puede construir los puntos de cilindro, mediante el desplazamiento de cada vértice (que ha pasado 36 vértices iguales) de acuerdo con los puntos correspondientes que se ha pasado. al final de esta etapa, se debe tener un delimitador caja para el cilindro.

3) Aquí tiene que reconstruir los puntos en la superficie visible del cuadro delimitador. Desde el punto que obtienes, debes realizar una intersección de rayos y cilindros.

4) Desde el punto de intersección puede reconstruir la profundidad y la normalidad. También debe descartar los puntos de intersección que se encuentran fuera del cuadro delimitador (esto puede ocurrir cuando ve el cilindro a lo largo de su eje, el punto de intersección irá infinitamente lejos).

Por cierto que es una tarea muy difícil, si alguien está interesado aquí está el código fuente:

https://github.com/chemlab/chemlab/blob/master/chemlab/graphics/renderers/shaders/cylinderimp.frag

https://github.com/chemlab/chemlab/blob/master/chemlab/graphics/renderers/shaders/cylinderimp.vert

2

Además de pygabriels respuesta que quiero compartir una aplicación independiente mediante el código de sombreador mencionado de Blaine Bell (PyMOL, Schrödinger, Inc.).

El enfoque, explicado por pygabriel, también se puede mejorar. El cuadro delimitador se puede alinear de tal manera que siempre esté frente al espectador. Solo dos caras son visibles a lo sumo. Por lo tanto, solo se necesitan 6 vértices (es decir, dos caras formadas por 4 triángulos).

Ver la imagen aquí, la caja (su vector director) siempre se enfrenta al espectador:
Image: Aligned bounding box

para el código fuente, descarga: cylinder impostor source code

El código no cubre todo el gorras y proyecciones ortográficas. Utiliza el sombreador de geometría para la generación de vértices. Puede usar el código de sombreado bajo el acuerdo de licencia de PyMOL.

1

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; 
}