total resueltos refraccion reflexion prisma planos luz indice fisica experimentos espejos ejercicios c++ raytracing

c++ - resueltos - refraccion de la luz



Cómo manejar adecuadamente la refracción en el trazado de rayos. (2)

Actualmente estoy trabajando en un trazador de rayos por diversión y tengo problemas con el manejo de la refracción.

El código fuente de todo el trazador de rayos se puede encontrar en Github .

Aquí hay una imagen del render:

La esfera derecha está configurada para tener un índice de refracción de 1.5 (vidrio).

Además de la refracción, quiero manejar un coeficiente de "transparencia" que se define como tal:

  • 0 -> Objeto es 100% opaco
  • 1 -> El objeto es 100% transparente (sin rastro del color del objeto original)

Esta esfera tiene una transparencia de 1.

Aquí está el código que maneja la parte de refracción. Se puede encontrar en github aquí .

Color handleTransparency(const Scene& scene, const Ray& ray, const IntersectionData& data, uint8 depth) { Ray refracted(RayType::Transparency, data.point, ray.getDirection()); Float_t eta = data.material->getRefraction(); if (eta != 1 && eta > Globals::Epsilon) refracted.setDirection(Tools::Refract(ray.getDirection(), data.normal, eta)); refracted.setOrigin(data.point + Globals::Epsilon * refracted.getDirection()); return inter(scene, refracted, depth + 1); } // http://graphics.stanford.edu/courses/cs148-10-summer/docs/2006--degreve--reflection_refraction.pdf Float_t getFresnelReflectance(const IntersectionData& data, const Ray& ray) { Float_t n = data.material->getRefraction(); Float_t cosI = -Tools::DotProduct(ray.getDirection(), data.normal); Float_t sin2T = n * n * (Float_t(1.0) - cosI * cosI); if (sin2T > 1.0) return 1.0; using std::sqrt; Float_t cosT = sqrt(1.0 - sin2T); Float_t rPer = (n * cosI - cosT) / (n * cosI + cosT); Float_t rPar = (cosI - n * cosT) / (cosI + n * cosT); return (rPer * rPer + rPar * rPar) / Float_t(2.0); } Color handleReflectionAndRefraction(const Scene& scene, const Ray& ray, const IntersectionData& data, uint8 depth) { bool hasReflexion = data.material->getReflexion() > Globals::Epsilon; bool hasTransparency = data.material->getTransparency() > Globals::Epsilon; if (!(hasReflexion || hasTransparency) || depth >= MAX_DEPTH) return 0; Float_t reflectance = data.material->getReflexion(); Float_t transmittance = data.material->getTransparency(); Color reflexion; Color transparency; if (hasReflexion && hasTransparency) { reflectance = getFresnelReflectance(data, ray); transmittance = 1.0 - reflectance; } if (hasReflexion) reflexion = handleReflection(scene, ray, data, depth) * reflectance; if (hasTransparency) transparency = handleTransparency(scene, ray, data, depth) * transmittance; return reflexion + transparency; }

Tools::Refract es simplemente llamar glm::refract internamente. (Para que pueda cambiar fácilmente si quiero).

No manejo las nociones de n1 y n2 : n2 se considera que siempre es 1 para el aire.

¿Estoy haciendo algo obvio?

EDITAR

Después de agregar una manera de saber si un rayo está dentro de un objeto (y negar lo normal, si es así), tengo esto:

Mientras miraba alrededor para encontrar ayuda, me topé con this publicación, pero no creo que la respuesta responda a nada. Al leerlo, no entiendo lo que se supone que debo hacer en absoluto.

Editar 2

He probado muchas cosas y actualmente estoy en este punto:

Es mejor, pero todavía no estoy seguro de si es correcto. Estoy usando esta imagen como inspiración:

Pero este está usando dos índices de refracción (para estar más cerca de la realidad) mientras que quiero simplificar y siempre considerar el aire como el segundo material (dentro o fuera).

Lo que esencialmente he cambiado en mi código está aquí:

inline Vec_t Refract(Vec_t v, const IntersectionData& data, Float_t eta) { Float_t n = eta; if (data.isInside) n = 1.0 / n; double cosI = Tools::DotProduct(v, data.normal); return v * n - data.normal * (-cosI + n * cosI); }

Aquí hay otra vista de las mismas esferas:


EDITAR: Me he dado cuenta de que la versión anterior de esto no era del todo correcta, así que edito la respuesta.

Después de leer todos los comentarios, las nuevas versiones de la pregunta y hacer algunos experimentos, produje la siguiente versión de la rutina de refract :

float3 refract(float3 i, float3 n, float eta) { eta = 2.0f - eta; float cosi = dot(n, i); float3 o = (i * eta - n * (-cosi + eta * cosi)); return o; }

Esta vez llamarlo no requiere ninguna operación adicional:

float3 refr = refract(rayDirection, normal, refrIdx);

Lo único que todavía no estoy seguro es la inversión del índice de refracción cuando se realiza la intersección interna del rayo. En mi prueba, la imagen producida no ha diferido mucho, no importa si invirtí el índice o no.

A continuación algunas imágenes con diferentes índices:

Para más imágenes vea el link , porque el sitio no me permite poner más de ellas aquí.


Estoy respondiendo esto como un físico en lugar de un programador, ya que no he tenido tiempo de leer todo el código, por lo que no le daré el código para hacer la corrección solo la idea general.

Por lo que ha dicho anteriormente, el anillo negro es para cuando n_object es menor que n_air. Esto solo suele ser cierto si está dentro de un objeto, por ejemplo, si estuvo dentro del agua o algo similar, pero los materiales se han construido con propiedades extrañas como esta y deberían ser compatibles.

En este tipo de situación, hay rayos de luz que no se pueden difractar, ya que la fórmula de difracción pone el rayo refractado en el MISMO lado de la interfaz entre los materiales, lo que obviamente no tiene sentido como difracción. En esta situación, la superficie actuará como una superficie reflectante. Esta es la situación que a menudo se denomina reflexión interna total.

Si es totalmente exacto, casi siempre el objeto refractivo también será parcialmente reflexivo y la fracción de luz reflejada o transmitida (y por lo tanto refractada) viene dada por las ecuaciones de Fresnel . Para este caso, aunque todavía sería una buena aproximación, el solo tratamiento es tan reflexivo si el ángulo es demasiado lejano y de transmisión (y por lo tanto refractivo) de lo contrario.

También hay situaciones en las que este efecto de anillo negro se puede ver si la reflexión no es posible (debido a que es oscuro en esas direcciones), pero la luz que se transmite es posible. Esto podría hacerse tomando, por ejemplo, un tubo de tarjeta que se ajuste firmemente al borde del objeto y apunte directamente hacia afuera y que solo brille la luz dentro del tubo, no afuera.