In this post, I will go over the components and scripts used in the editor. You can check my GitHub repository for the whole project.

Scene Layout and Hierarchy Window

Editor-hierarchy-layout

As it can be seen from the scene hierarchy window, our scene consists of Scene Geometry, Scene Cameras, and Scene Lights.

SceneParser object contains the SceneParser script which is responsible for parsing the scene and storing the data in a custom SceneData object. SceneData object is then used while setting the buffers of our compute shader. SceneParser script will be further explained.

Object Components

Editor-Mesh-Inspector

Using the Transform component of each gameobject, their position, rotation, and scale can easily be set.

For Scene Geometry objects, Mesh property of the Mesh Renderer component is used to get the vertex positions. In the case of spheres, their mathematical representation is used. The radius of the spheres is taken as half of their x scale. This way, spheres can be represented cheaper yet more accurately. Although, this representation will cause problems when I will implement instancing and ovoid objects. Scene Geometry objects also have a RayTracingMaterial. Ambient, diffuse, specular, Phong exponent, mirror reflectance, transparency, and the refraction index of the object can be set here. xyz attributes represent RGB color reflectance coefficients respectively.

Currently, only directional light and point lights are supported. Direction (directional light only), intensity, and color of the lights can be set in the Inspector.

Editor-Camera-Inspector

Camera object has Transform and Camera components. Field of view and projection of the camera can be set here. This object also contains the RayTracingMaster script. This script is responsible for creating the render texture and the compute shader. It also sets the compute shaders buffers and finally displays the render texture. For camera objects to work, they have to be the child of SceneCameras gameobject.

Editor-SceneParser-Inspector

SceneParser object can be used to play with various render settings. Camera No slider can be used to change the active camera. Using Generate Scene from XML option, an XML file can be parsed to generate the scene in the editor mode. Some example XML can be found in the _SceneXMLs folder. GenerateScene Scene Editor option lets you update the scene during play mode as you desire, or you can enable Realtime Update. This feature is one of the main reasons I wanted to implement a ray tracer in Unity. We can easily create dynamic scenes and see how they play out in the play mode.

PNGConverter script saves the first rendered frame as a PNG into Resources folder. I wish to work on this script in the future and be able to take videos of our render.

One thing to note here is Inspector Conditional Hide code I have used is acquired from Brecht Lecluyes. You can also find them here, used by Sebastian Lague. SebLague has an amazing procedural planet generation tutorial which you can check out.

Scripts

SceneParser script locates scene objects and stores their data in a SceneData object. For SceneParser to work properly, scene objects have to be correctly categorized as it was in the Hierarchy window.

There are some important things to note related to my SceneParser script and the way I parse the data: To set up my camera rays in the compute shader I use CameraToWorldMatrix and CameraInverseProjectionMatrix. This, unfortunately, arrises problems I can’t specify at the moment and requires a rework. Another problem I faced was while acquiring the vertex data of the meshes. Since vertex data in Mesh is stored in object space, they have to be rotated, scaled and translated according to the Transform component of the object.

Mesh mesh = meshObject.GetComponent<MeshFilter>().mesh;

Vector3[] newVertexList = mesh.vertices;

for (int i = 0; i < newVertexList.Length; i++)
{
    newVertexList[i] = meshObject.rotation * newVertexList[i];
    newVertexList[i] = meshObject.localScale.x * newVertexList[i];
    newVertexList[i] = meshObject.position + newVertexList[i];
}

SceneData class is organized in the way it is so that RayTracingMaster script can set the GPU structured buffers easier. Lists of Structs can be set to ComputeBuffers using ComputeBuffer.SetData(int struct_count, int struct_size_as_float) function. These buffers will be later used in compute shader for our calculations.

EditorDemons