Part two of working sloppily in Unreal covers setting up raytracing and materials. Also we’ll tackle the generation of normal maps from a grayscale image in Houdini, talk a bit about different approximations for the Fresnel term and finally poke around in Unreal’s console in order to nudge it’s raytracer to where we want it.
Unreal materials excpect only the base reflection fraction as an input for specular, because unreal calculates the fresnel internally when rendering using this formula :
vec3 fresnelSchlick(float cosTheta, vec3 f0) {
return f0 + (1.0 – f0) * pow(1.0 – cosTheta, 5.0);
}
So you should not use your own fresnel term and instead play around with the base reflection fraction, the default value used by unreal is 0.5.
Just thought i’d let you know, otherwise great video.
Hi Lucca,
very interesting, is there a link to some docs that go over UE’s material internals? I’m especially interested what happens when raytracing is enabled, as in my tests the fresnel node was absolutely necessary (A is with fresnel node, B is a specular of 0.04):
https://entagma.com/site/uploads/2020/12/fresnel_vs_const_specular_02.png
Cheers, Mo
Hello Moritz,
i could not find too much information on the internal workings of unreals materials, so i tried to recreate unreals material preview in C4D using Redshift and found out something:
First i converted the F0 value of 0.04 the the corresponding ior of 1.5 using this formula:
ior = -(f0 + 1 + 2 * sqrt(f0)) / (f0 – 1)
Doing this i found out, that unreal internally divides the specular value you give it by 2, to prevent unrealisticly reflective materials.
I came to this conclusion after using a F0 = 0.02 to calculate an ior = 1.33.
When i used this ior value in my redshift material i got this image:
https://cdn.discordapp.com/attachments/209049190293045249/785623615278284830/RedshiftTest.PNG
Wich looks very similar to the B-Image you provided above, so to me it would seem, that the more subtle specular without the custom ior node is more accurate.
I have wrote some code for you, with wich you can convert an ior to the corresponding F0 value, wich is already multiplied by 2 to make it “unreal ready”, just copy and paste this code into your material and pipe in the ior you want:
Begin Object Class=/Script/UnrealEd.MaterialGraphNode Name=”MaterialGraphNode_2″
Begin Object Class=/Script/Engine.MaterialExpressionCustom Name=”MaterialExpressionCustom_0″
End Object
Begin Object Name=”MaterialExpressionCustom_0″
Code=”float f0 = abs((1.0 – Ior) / (1.0 + Ior));\r\nreturn f0 * f0 * 2.0;”
OutputType=CMOT_Float1
Description=”IorToF0″
Inputs(0)=(InputName=”Ior”,Input=(Expression=MaterialExpressionConstant'”MaterialExpressionConstant_2″‘))
MaterialExpressionEditorX=-528
MaterialExpressionEditorY=320
MaterialExpressionGuid=46F9D2C74AD7AFF4C18A14A7FFBCC1D8
Material=PreviewMaterial'”/Engine/Transient.NewMaterial”‘
bCollapsed=True
End Object
MaterialExpression=MaterialExpressionCustom'”MaterialExpressionCustom_0″‘
NodePosX=-528
NodePosY=320
NodeGuid=3DF03C524EDEE6DF5B4BB99DB39FEC78
CustomProperties Pin (PinId=F3A1EF4449C6203278C5DC925F8E4890,PinName=”Ior”,PinType.PinCategory=”required”,PinType.PinSubCategory=””,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=True,LinkedTo=(MaterialGraphNode_4 8B358BF2493237423B071B87BE8E60E6,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=220B15F7400EC69FF4DEF8B2B9C39636,PinName=”Output”,PinFriendlyName=NSLOCTEXT(“MaterialGraphNode”, “Space”, ” “),Direction=”EGPD_Output”,PinType.PinCategory=””,PinType.PinSubCategory=””,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=True,LinkedTo=(MaterialGraphNode_Root_0 48B7BAF244CF8B31082019AD3D2B51F5,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
I hope i could help, Lucca
Just noticed, that the code is sent does not work when pasting it inside a material, this code should work now:
Begin Object Class=/Script/UnrealEd.MaterialGraphNode Name=”MaterialGraphNode_6″
Begin Object Class=/Script/Engine.MaterialExpressionCustom Name=”MaterialExpressionCustom_0″
End Object
Begin Object Name=”MaterialExpressionCustom_0″
Code=”float f0 = abs((1.0 – Ior) / (1.0 + Ior));\r\nreturn f0 * f0 * 2.0;”
OutputType=CMOT_Float1
Description=”IorToF0″
Inputs(0)=(InputName=”Ior”,Input=(Expression=MaterialExpressionConstant'”MaterialExpressionConstant_2″‘))
MaterialExpressionEditorX=-528
MaterialExpressionEditorY=320
MaterialExpressionGuid=46F9D2C74AD7AFF4C18A14A7FFBCC1D8
Material=PreviewMaterial'”/Engine/Transient.NewMaterial”‘
bCollapsed=True
End Object
MaterialExpression=MaterialExpressionCustom'”MaterialExpressionCustom_0″‘
NodePosX=-528
NodePosY=320
NodeGuid=3DF03C524EDEE6DF5B4BB99DB39FEC78
CustomProperties Pin (PinId=F3A1EF4449C6203278C5DC925F8E4890,PinName=”Ior”,PinType.PinCategory=”required”,PinType.PinSubCategory=””,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=True,LinkedTo=(MaterialGraphNode_4 8B358BF2493237423B071B87BE8E60E6,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=220B15F7400EC69FF4DEF8B2B9C39636,PinName=”Output”,PinFriendlyName=NSLOCTEXT(“MaterialGraphNode”, “Space”, ” “),Direction=”EGPD_Output”,PinType.PinCategory=””,PinType.PinSubCategory=””,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=True,LinkedTo=(MaterialGraphNode_Root_0 48B7BAF244CF8B31082019AD3D2B51F5,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
Copy and paste of the code does not work, because it gets reformatted when posting it here, making it unusable, so simply create a custom expresion node inside your material and configuere it like so:
https://cdn.discordapp.com/attachments/785643496672722964/785643696724246558/unknown.png
Hope i could help and sorry for spamming, Lucca
Thanks Lucca,
indeed – I just ran your tests in Octane too and you’re absolutely right – thanks for pointing this out and thanks for the script!
Cheers, Mo
No problem, happy i could help
Really enjoying this series! Small hiccup, no matter how I import the lego model (as .obj) no materials assignments are included.