Unreal Engine 5.7 > plugin template + laser system implementation for custom RayTrace Shaders
- Injects custom RayGen/Closest Hit/Miss Shaders & Inline Compute Shader variants
- Structured Buffer to upload RayOrigin & RayTarget data
- RenderTarget & Material example to interpret RayTrace results in surface material domain
- SceneViewExtension to provide callbacks for rendering
- Use of scene TLAS acceleration structure
- Engine Subsystem to maintain system and keep data in sync
- Laser Projector actor with hit detection, visibility mask, custom color and toggle for triangle or plane shape
- Enable RayTracing in project
- Install RayTraceShaders to project plugins directory
- Implementation checks for engine CVars r.RayTracing.AllowPipeline (RGS) & r.RayTracing.AllowInline (CS) for toggling implementations in editor
- Plugin includes a demo level that is intended to use with a third person template or similar with engine prototype assets
This repository showcases a way to implement custom ray tracing shaders into Unreal Engine without modifying the engine code. At the time of writing this, I couldn't find too many implementations for ray trace shaders that would have extended tracing beyond simple view origin implemntations (depth, view based Lidar, shadows etc) so thought there could be a need for practical implementation example like this.
RayTracing provides a powerful way to extend visual possibilities that require scene geometry information. Similar laser system implementation with more traditional techniques (SceneCapture) can more than 100 times slower with equivalent fidelity.
While this implementation already allows pretty performant way to draw multiple systems, I've tried to keep the codebase lean and simple to read. One can easily extend the material implementation beyond to enhance visual details or gain further optimizations for example by adjusting tracing fidelity based on the distance. This repository is meant to be provide an example how to do custom raytracing implementations with Unreal Engine rather than a perfect laser system plug-in.
Actor that registers to laser system and updates transform changes to the subsystem. Uses a shared material (based on laser geometry) and passes additional data to surface material with custom primitive data to avoid additional load from creating material instances. Variables for actor specific material attributes.
Engine subsystem to keep transform data, view extension and render resources in sync. For more advanced system it would maybe make more sense to split some of these in to different systems but using engine subsystems provides convenient way to provide both editor and in-engine visibility with the penalty of having to share some of the resources when working in edtior.
Rendering framework for custom ray trace shaders. Adds shaders to engine pipeline and subscribes to dedicated callback with RDG to update dedciated Render Target with up to date raytrace data. Uses a structured buffer to upload raydata and engine subsystem maintained UTextureRenderTarget to output the raytrace data.
Draws x (512 by default) traces for each laser interpolating the world coordinates between two outer edges of the laser plane. Outputs hit/miss, raylength and hit distance. Uses 1 row as a slice in the texture per system.
Has both RGS and inline (Compute) shader variants in the same file. The shadercode is written for clarity and depending on system setup and used resources (texture fidelity, amount of laser systems etc) compute inline variant can be more performant than the raygen shaders. It is likely that performance is also GPU vendor dependant.
Loads render target data and uses opacity mask to cut out gemoetry that should be hidden according to raytracing data. Uses additional custom primitive data for coloring. Can be easily extended for various visual effects. Uses Load rather than TextureSample2D as we need exact data from the raytrace results. Triangle shape is done simply by squeezing one end of the quad and applying UV squeeze accordingly.
RayTrace shaders need to be available early in the pipeline FGlobalIlluminationPluginDelegates seem to be the one that can be used without forking the engine.
This implementation uses PostTLASBuild SceneViewExtension delegate that is called right after preapring raytrace data.
Some rendering/async calls are done before this, but don't know if there are any callbacks to inject custom RGS shader implementaions before this without modifying the engine.
Other opportunities to considr include: PrePostProcessPass_RenderThread and RenderDiffuseIndirectLight_RenderThread
This implementation is using results in a normal surface shader but as the results are stored in a UTextureRenderTarget2D resource, this could as well be in used any material domain.
Capturing tools might not work in favor for debugging raytrace shaders. Writing data to a fixed location in the render target and reading that back was one of that provided key insights for me during the development when traditional capturing tools failed to provide accurate data for feedback.
World positions, including how TLAS is created is something to consider when building custom implementations. I tried a few in-engine functions for world coordinate translation but didn't find anything that would have worked directly with this but happy to learn if there is proper method to implement this. In short, the world coordinates when raytracing are raytracingview dependant so offsetting/coordinate transforms are required.
Shader compilation and execution seems to be quite picky with ray tracing shaders. For example registering shaders and payload types were quite tricky to get right. This example has been checked to work with 5.7.3 but as this is likely an active field of development in the engine, implementation might need to be significantly adjusted to subsequent engine versions. In-engine implementations for debug shaders - for example RayTracingBarycentrics was a good resource in research.
I have tried to keep the code as simple as possible and document the most relevant features in code comments. If you have further questions or suggestions on how to improve this, please don't hesitate to contact me. Apologies for mistakes in language and code, all done with human labour and intended for human eyes only ;)
February 2026: Initial public release
