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:
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 :
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 :
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 :
It's better but I'm still not sure if it's right. I'm using this image as an inspiration :
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 :
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.
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.
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:
For more images see the link, because the site do not allow me to put more of them here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With