When I was working on my own engine, I used to write my shaders in raw HLSL. Here’s my top resource/reference:
Writing HLSL Shaders in Direct3D 9 - Win32 apps | Microsoft Learn
Granted, that’s DirectX 9, which is pretty old and doesn’t come with the fragment shaders and other new features. But, it’s enough to get your feet wet with raw HLSL. Raw HLSL is really challenging to debug properly though. You have to use a scene debugger which lets you sample an individual pixel, view the draw calls on the GPU, and figure out what is happening to make the pixel the color it is. There may be multiple shaders which play a role in that, so it can be very much like searching for a needle in a hay stack. After using the UE4 material shaders, I never looked back or want to touch HLSL again, directly. Only in the very most dire of circumstances, will I create a custom node in a material to inject HLSL code (which has only happened once!).
The key thing to note (and this is in the documentation for the custom node), is that injecting your own HLSL into the material node graph can limit the amount of optimization UE4 can do. Large blocks of HLSL code can make a shader really hard to optimize, and thus, it may run slower.