I want to simulate scene with a lot of glass and generate dataset, but I found there are many problems about UE4.24 raytracing. I understanding they are still developing and some bugs are features for better performance. In this topic, I will offer ways to solve some problems. I hope it could help somebody.
At first, I list what problems will be solved in this topic:
-
How to set IOR(Index of Refraction) of glass(transparent object);
-
Fix thick glass refraction error. (Thin glass only need to set IOR as 1.0)
-
Implement thick glass double reflection.
-
Implement multiple times reflection of glass. (It will cause serious performance problem, so be careful to use it)
-
Set IOR:
Before setting correct parameters in material, there is no refraction:
IOR_Ori.jpg
Ray tracing in UE4 actually uses “Specular” as IOR. We can use formula: Specular = sqr((IOR-1)/(IOR+1))/0.08 to convert IOR into specular. As follow, I create a material function:
IOR2Specular.jpg
“RayTracingQualitySwitchReplace” is a switch function which will choose RayTraced input as output in ray tracing mode, otherwise will choose Normal input.
Modify material as follow:
MB_Glass.jpg
Specular input doesn’t need Fresnel to adjust IOR.
Then, the refraction works and likes a solid glass ball.
IOR_New.jpg
- Thick glass refraction:
Because solid glass ball is hard to prove correctness of refraction. So I choosed thick flat glass which we can see it anywhere.
As follow image show, the refraction is error because it scale the chair so much.
FlatGlass_Ori.jpg
Then, we need to modify the shader file in UE4 to fix the problem.
- Add defination at the top area of file “RayTracingPrimaryRays.usf”:
#define MAX_IOR_STACK 3
- In the initialization part of RAY_TRACING_ENTRY_RAYGEN function, we need to define variables:
float IorStack[MAX_IOR_STACK];
int StackIdx = 0;
- Depend on the front/back face information, save/load IOR in stack before the call of RefractRay function:
if (bIsEntering)
{
// Push Ior2 into stack
​​​​​​​ if (StackIdx < MAX_IOR_STACK)
​​​​​​​ ​​​​​​​ IorStack[StackIdx] = Ior2;
​​​​​​​ ​​​​​​​ StackIdx++;
}
else
{
​​​​​​​ // Pop stack
​​​​​​​ StackIdx--;
​​​​​​​ if (StackIdx < MAX_IOR_STACK && StackIdx >= 0)
​​​​​​​ ​​​​​​​ Ior1 = IorStack[StackIdx];
}
- In RefractRay function, when we calculate Eta variable, it should be modified as follow:
/* Origin
float Eta = Ior1 / Ior2;
if (bIsEntering)
{
Eta = 1.0f / Ior1;
}
*/
float Eta = Ior2 / Ior1;
After modification:
FlatGlass_New1.jpg
FlatGlass_New2.jpg
We can see the chair only offsets a little and nearly no scales, which likes the real world glass.
- Thick glass double reflection
In real world, many glass has double reflection, but in UE4 glass only has one reflection.
RealWorld_DoubleReflection.jpg
DoubleReflection_Ori.jpg
There are serval methods to solve and I saw someone suggest use two side material. However, the two side material will affect refraction, because in shader code it would make we cannot get correct front/back face information.
My method is also modification of shader.
- In “RayTracingPrimaryRays.usf” file, after done once ray tracing (in other words after the call of TraceRayAndAccumulateResults function), we add:
// Correct surface normal when hit a back surface
if (dot(-Ray.Direction, Payload.WorldNormal) < 0.0f)
{
Payload.WorldNormal = -Payload.WorldNormal;
}
After modification:
DoubleReflection_New.jpg
The red ball and the bookcase were reflected twice in thick glass.
- Multiple times reflection of glass
Before modification:
MultiReflection_Ori1.jpg
MultiReflection_Ori2.jpg
This part need to code a lot in “RayTracingPrimaryRays.usf” file, so I only breifly explan how I implement it and attach modified “RayTracingPrimaryRays.usf”.
In the original codes of RAY_TRACING_ENTRY_RAYGEN function, it only does once reflection in every ray (the reflection does not recurse). And here only recurse the transparent face.
What I do is using stack to implement recurse of ray tracing both on reflection and refraction, because HLSL doesn’t support recursion of function.
In my attached usf file, I used macros to swith my modification:
#define FIX_IOR 1
#define FIX_DOUBLE_REFLECTION 1
#define MAX_IOR_STACK 10
#define USE_ALTERNATIVE_RENDER 1
#define MAX_ALTERNATIVE_NUM 3 //alternative between transparency object and mirror object, be careful about the 2^num rays will be detected
#define MAX_ALTERNATIVE_STACK 10
After modification:
MultiReflection_New.jpg
After all modifications, here I post comparison of the refraction of glass ball between UE4 and real world:
Attachment: RayTracingPrimaryRays.txt (If you want to run it, you should change the file extension as usf and replace the original one)