Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly handle refraction in raytracing

Tags:

I am currently working on a raytracer just for fun and I have trouble with the refraction handling.

The code source of the whole raytracer can be found on Github EDIT: The code migrated to Gitlab.

Here is an image of the render:

Refraction bug

The right sphere is set to have a refraction indice of 1.5 (glass).

On top of the refraction, I want to handle a "transparency" coefficient which is defined as such :

  • 0 --> Object is 100% opaque
  • 1 --> Object is 100% transparent (no trace of the original object's color)

This sphere has a transparency of 1.

Here is the code handling the refraction part. It can be found on github here.

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 is simply calling glm::refract internally. (So that I can change easily if I want)

I don't handle notions of n1 and n2: n2 is considered to always be 1 for air.

Am I mising something obvious ?


EDIT

After adding a way to know if a ray is inside an object (and negating the normal if so) I have this :

Refraction problem

While looking around to find help, I stumbled upon this post but I don't think the answer answers anything. By reading it, I don't understand what I'm supposed to do at all.


EDIT 2

I've tried a lot of things and I am currently at this point :

Current

It's better but I'm still not sure if it's right. I'm using this image as an inspiration :

Example

But this one is using two indexes of refraction (To be closer to reality) while I want to simplify and always consider air as the second (in or out) material.

What I essentially changed in my code is here :

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

Here is another view of the same spheres :

Spheres

like image 611
Telokis Avatar asked Feb 14 '17 05:02

Telokis


People also ask

Is Ray Tracing reflection?

A reflection ray is traced in the mirror-reflection direction. The closest object it intersects is what will be seen in the reflection. A refraction ray traveling through transparent material works similarly, with the addition that a refractive ray could be entering or exiting a material.

How do you find Vector refraction?

You want to calculate V_refraction. Let n be the normalized normal vector. V_refraction = r*V_incedence + (rc - sqrt(1-Math. pow(r,2)(1-Math.


1 Answers

EDIT: I've figured that the previous version of this was not entirely correct so I edit the answer.

After reading all the comments, the new versions of the question and doing some experimentation myself I produced the following version of refract routine:

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

This time calling it does not require any additional operations:

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

The only thing I am still not sure is the inverting of the refractive index when doing the inside ray intersection. In my test the produced image haven't differ much no matter I inverted the index or not.

Below some images with different indices:

enter image description here enter image description here

For more images see the link, because the site do not allow me to put more of them here.

like image 66
Matso Avatar answered Sep 21 '22 16:09

Matso