diff --git a/offline_ray_tracer/include/settings.hpp b/offline_ray_tracer/include/settings.hpp
index ac18a8a74569aa16c3957aa0789a213dc0441565..90655bdb69595d4bf84b7cd7fa0102050a786fe7 100644
--- a/offline_ray_tracer/include/settings.hpp
+++ b/offline_ray_tracer/include/settings.hpp
@@ -40,12 +40,13 @@ struct settings
       if (iteratee.isMember("volume"  )) entry.volume   = iteratee["volume"  ].asString();
     }
 
-    time_scale  =  root["time_scale" ]   .asFloat();
-    loop        =  root["loop"       ]   .asBool ();
-    image_size  = {root["image_size" ][0].asUInt (),
-                   root["image_size" ][1].asUInt ()};
-    samples     =  root["samples"    ]   .asUInt ();
-    update_rate =  root["update_rate"]   .asFloat();
+    transfer_function =  root["transfer_function"]   .asString();
+    time_scale        =  root["time_scale"       ]   .asFloat ();
+    loop              =  root["loop"             ]   .asBool  ();
+    image_size        = {root["image_size"       ][0].asUInt  (),
+                         root["image_size"       ][1].asUInt  ()};
+    samples           =  root["samples"          ]   .asUInt  ();
+    update_rate       =  root["update_rate"      ]   .asFloat ();
 
     for (auto& iteratee : root["key_frames"])
     {
@@ -72,17 +73,18 @@ struct settings
   settings& operator=(      settings&& temp) = default; 
 
   // Input settings.
-  std::vector<data_filepath> data_filepaths = {};
-  float                      time_scale     = 1000.0f;
-  bool                       loop           = true;
+  std::vector<data_filepath> data_filepaths    = {};
+  std::string                transfer_function = "";
+  float                      time_scale        = 1000.0f;
+  bool                       loop              = true;
 
   // Ray tracer settings.
-  std::array<std::size_t, 2> image_size     = { 4096, 2160 };
-  std::size_t                samples        = 8;
+  std::array<std::size_t, 2> image_size        = { 4096, 2160 };
+  std::size_t                samples           = 8;
 
   // Animation settings.
-  float                      update_rate    = 16.0f;
-  std::vector<key_frame>     key_frames     = {};
+  float                      update_rate       = 16.0f;
+  std::vector<key_frame>     key_frames        = {};
 };
 }
 
diff --git a/offline_ray_tracer/include/transfer_function.hpp b/offline_ray_tracer/include/transfer_function.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..230f2f9fc34369c171d53ad65fd2591351ec1a85
--- /dev/null
+++ b/offline_ray_tracer/include/transfer_function.hpp
@@ -0,0 +1,64 @@
+#ifndef TRANSFER_FUNCTION_HPP
+#define TRANSFER_FUNCTION_HPP
+
+#include <cstdint>
+#include <fstream>
+#include <string>
+
+#include <vtk_jsoncpp.h>
+#include <vtkColorTransferFunction.h>
+#include <vtkPiecewiseFunction.h>
+#include <vtkSmartPointer.h>
+
+namespace rt
+{
+class transfer_function
+{
+public:
+  explicit transfer_function  (const std::string& filepath)
+  {
+    std::ifstream   file_stream(filepath);
+    vtkJson::Reader reader;
+    vtkJson::Value  root;
+    reader.parse(file_stream, root);
+
+    auto& opacity = root[0]["Points"   ];
+    auto& color   = root[0]["RGBPoints"];
+    auto  space   = root[0]["ColorSpace"].asString();
+
+    for (auto i = 0; i < opacity.size(); i += 4)
+      opacity_function->AddPoint(
+        opacity[i    ].asFloat(), 
+        opacity[i + 1].asFloat());
+    for (auto i = 0; i < color.size(); i += 4)
+      color_function->AddRGBPoint(
+        color  [i    ].asFloat(), 
+        color  [i + 1].asFloat(), 
+        color  [i + 2].asFloat(), 
+        color  [i + 3].asFloat());
+
+    if      (space == "RGB")
+      color_function->SetColorSpace(VTK_CTF_RGB);
+    else if (space == "HSV")
+      color_function->SetColorSpace(VTK_CTF_HSV);
+    else if (space == "LAB")
+      color_function->SetColorSpace(VTK_CTF_LAB);
+    else if (space == "CIEDE2000")
+      color_function->SetColorSpace(VTK_CTF_LAB_CIEDE2000);
+    else if (space == "Diverging")
+      color_function->SetColorSpace(VTK_CTF_DIVERGING);
+    else if (space == "Step")
+      color_function->SetColorSpace(VTK_CTF_STEP);
+  }
+  transfer_function           (const transfer_function&  that) = default;
+  transfer_function           (      transfer_function&& temp) = default;
+ ~transfer_function           ()                               = default;
+  transfer_function& operator=(const transfer_function&  that) = default;
+  transfer_function& operator=(      transfer_function&& temp) = default; 
+
+  vtkSmartPointer<vtkPiecewiseFunction>     opacity_function = vtkSmartPointer<vtkPiecewiseFunction>    ::New();
+  vtkSmartPointer<vtkColorTransferFunction> color_function   = vtkSmartPointer<vtkColorTransferFunction>::New();
+};
+}
+
+#endif
diff --git a/offline_ray_tracer/source/main.cpp b/offline_ray_tracer/source/main.cpp
index 03ef7b033ede9da9713bfd55000097c7b3157c4c..3527c796fcd5aafcbd8335aa1ecc349a2256aa19 100644
--- a/offline_ray_tracer/source/main.cpp
+++ b/offline_ray_tracer/source/main.cpp
@@ -4,24 +4,29 @@
 #include <vtkActor.h>
 #include <vtkCamera.h>
 #include <vtkCameraInterpolator.h>
+#include <vtkColorTransferFunction.h>
 #include <vtkOggTheoraWriter.h>
 #include <vtkOSPRayPass.h>
+#include <vtkPiecewiseFunction.h>
 #include <vtkPolyDataMapper.h>
 #include <vtkRenderer.h>
 #include <vtkRenderWindow.h>
 #include <vtkSmartPointer.h>
 #include <vtkUnstructuredGridVolumeRayCastMapper.h>
 #include <vtkVolume.h>
+#include <vtkVolumeProperty.h>
 #include <vtkWindowToImageFilter.h>
 
 #include <poly_data_io.hpp>
 #include <settings.hpp>
+#include <transfer_function.hpp>
 #include <unstructured_grid_io.hpp>
 
 std::int32_t main(std::int32_t argc, char** argv)
 {
   std::cout << "Parsing settings.\n";
-  auto settings = rt::settings(argv[1]);
+  auto settings          = rt::settings(argv[1]);
+  auto transfer_function = rt::transfer_function(settings.transfer_function);
 
   std::cout << "Setting up renderer.\n";
   auto renderer    = vtkSmartPointer<vtkRenderer>    ::New();
@@ -39,23 +44,25 @@ std::int32_t main(std::int32_t argc, char** argv)
   for (auto& key_frame : settings.key_frames)
   {
     auto camera = renderer->GetActiveCamera();
-    camera      ->SetPosition  (key_frame.position[0] , key_frame.position[1] , key_frame.position[2]);
+    camera      ->SetPosition  (key_frame.position[0] , key_frame.position[1], key_frame.position[2]);
     camera      ->SetFocalPoint(key_frame.position[0] + key_frame.forward [0], 
                                 key_frame.position[1] + key_frame.forward [1], 
                                 key_frame.position[2] + key_frame.forward [2]);
-    camera      ->SetViewUp    (key_frame.up      [0] , key_frame.up      [1] , key_frame.up      [2]);
+    camera      ->SetViewUp    (key_frame.up      [0] , key_frame.up      [1], key_frame.up      [2]);
     interpolator->AddCamera    (key_frame.time, camera);
   }
 
   std::cout << "Setting up actors and volumes.\n";
-  auto pd_mapper = vtkSmartPointer<vtkPolyDataMapper>                     ::New();
-  auto ug_mapper = vtkSmartPointer<vtkUnstructuredGridVolumeRayCastMapper>::New();
-  auto pd_actor  = vtkSmartPointer<vtkActor>                              ::New();
-  auto ug_volume = vtkSmartPointer<vtkVolume>                             ::New();
-  pd_actor ->SetMapper(pd_mapper);
-  ug_volume->SetMapper(ug_mapper);
-  renderer ->AddActor (pd_actor );
-  //renderer ->AddVolume(ug_volume);
+  auto pd_mapper  = vtkSmartPointer<vtkPolyDataMapper>                     ::New();
+  auto ug_mapper  = vtkSmartPointer<vtkUnstructuredGridVolumeRayCastMapper>::New();
+  auto pd_actor   = vtkSmartPointer<vtkActor>                              ::New();
+  auto ug_volume  = vtkSmartPointer<vtkVolume>                             ::New();
+  pd_actor ->SetMapper                      (pd_mapper);
+  ug_volume->SetMapper                      (ug_mapper);
+  renderer ->AddActor                       (pd_actor );
+  renderer ->AddVolume                      (ug_volume);
+  ug_volume->GetProperty()->SetScalarOpacity(transfer_function.opacity_function);
+  ug_volume->GetProperty()->SetColor        (transfer_function.color_function  );
 
   std::cout << "Setting up video writer.\n";
   auto window_to_image = vtkSmartPointer<vtkWindowToImageFilter>::New();
@@ -79,7 +86,7 @@ std::int32_t main(std::int32_t argc, char** argv)
       std::cout << "Loading slice " << slice << ".\n";
       last_slice = slice;
       pd_mapper->SetInputData(rt::poly_data_io        ::read(settings.data_filepaths[slice].geometry));
-      //ug_mapper->SetInputData(rt::unstructured_grid_io::read(settings.data_filepaths[slice].volume  ));
+      ug_mapper->SetInputData(rt::unstructured_grid_io::read(settings.data_filepaths[slice].volume  ));
     }
 
     std::cout << "Rendering frame " << current_time << ".\n";
diff --git a/settings.json b/settings.json
index 4289a056443b077c550926070fdd243928d49008..5639f0703acb2f9fc4b773982e0a1ef50f4edb81 100644
--- a/settings.json
+++ b/settings.json
@@ -5,14 +5,15 @@
     { "geometry": "/hpcwork/rwth0432/data/oxyflame/GEOM_0013200.vtp", "volume": "/hpcwork/rwth0432/data/oxyflame/QOUT_0013200.vtu" },
     { "geometry": "/hpcwork/rwth0432/data/oxyflame/GEOM_0013200.vtp", "volume": "/hpcwork/rwth0432/data/oxyflame/QOUT_0013200.vtu" }
   ],
-  "time_scale" : 1000.0,
-  "loop"       : true,
+  "transfer_function": "/hpcwork/rwth0432/data/oxyflame/transfer_function.json",
+  "time_scale"       : 1000.0,
+  "loop"             : true,
 
-  "image_size" : [1920, 1080],
-  "samples"    : 32,
+  "image_size"       : [1920, 1080],
+  "samples"          : 32,
   
-  "update_rate": 16.0,
-  "key_frames" :
+  "update_rate"      : 16.0,
+  "key_frames"       :
   [
     { "time": 0.0   , "position": [   0.0,    0.0, -100.0], "forward": [0.0, 0.0, 1.0], "up": [0.0, 1.0, 0.0] },
     { "time": 1000.0, "position": [   0.0, -100.0,    0.0], "forward": [0.0, 1.0, 0.0], "up": [0.0, 0.0, 1.0] },
diff --git a/transfer_function.json b/transfer_function.json
new file mode 100644
index 0000000000000000000000000000000000000000..54bbc7ebe54ff9e2740d973466b24ff4bdeb3ef1
--- /dev/null
+++ b/transfer_function.json
@@ -0,0 +1,33 @@
+[
+	{
+		"ColorSpace" : "Diverging",
+		"Name" : "Basic",
+		"Points" : 
+		[
+			200.0,
+			0.0,
+			0.5,
+			0.0,
+			287.01351928710938,
+			1.0,
+			0.5,
+			0.0
+		],
+		"RGBPoints" : 
+		[
+			200.0,
+			0.231373,
+			0.298039,
+			0.75294099999999997,
+			243.50675964355469,
+			0.86500299999999997,
+			0.86500299999999997,
+			0.86500299999999997,
+			287.01351928710938,
+			0.70588200000000001,
+			0.0156863,
+			0.14902000000000001
+		]
+	}
+]
+