diff --git a/demos/optical_bench/src/Optix Files/laser_caster.cu b/demos/optical_bench/src/Optix Files/laser_caster.cu
index fe91ede80250788fb22a67591a7008787e0e8138..c231060784026dd5199d5f68a0bba14f422bc8a7 100644
--- a/demos/optical_bench/src/Optix Files/laser_caster.cu	
+++ b/demos/optical_bench/src/Optix Files/laser_caster.cu	
@@ -1,9 +1,11 @@
 #include <optix.h>
 #include <optixu/optixu_math_namespace.h>
+#include <optixu/optixu_matrix_namespace.h>
 #include "prd.h"
 #include "helpers.h"
 #include "random.h"
 
+
 #define PERCENTILE 1.47579f
 
 using namespace optix;
@@ -20,10 +22,12 @@ rtDeclareVariable(float3,         laser_origin, , );
 rtDeclareVariable(float3,         laser_forward, , );
 rtDeclareVariable(float3,         laser_right, , );
 rtDeclareVariable(float3,         laser_up, , );
+rtDeclareVariable(Matrix3x3,      laser_rot, , );
 
 rtDeclareVariable(float,          laserBeamWidth, , );
 
 rtBuffer<int, 2>   			laserIndex;
+rtBuffer<float3, 2>   		laserDir;
 rtBuffer<float3, 1>			result_laser;
 
 
@@ -50,7 +54,7 @@ RT_PROGRAM void laser_caster(){
 	
 	// next ray to be traced
 	prd.origin = ray_origin;
-	prd.direction = laser_forward;
+	prd.direction = laser_rot * normalize(make_float3(1,1,-1)*laserDir[make_uint2(launch_index)]);
 	
 	unsigned int startIndex = (launch_index.y * 50 + launch_index.x) * max_depth_laser * 2;
 	
diff --git a/demos/optical_bench/src/file_reader_zmx.cpp b/demos/optical_bench/src/file_reader_zmx.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d56fdb3b383e182cc75e018250df38f61f03a9b0
--- /dev/null
+++ b/demos/optical_bench/src/file_reader_zmx.cpp
@@ -0,0 +1,157 @@
+#include "file_reader_zmx.hpp"
+
+#include <codecvt>
+#include <fstream>
+#include <iostream>
+#include <locale>
+#include <string>
+
+template <typename T>
+int sgn(T val) {
+  return (T(0) < val) - (val < T(0));
+}
+
+ZMXLoader::ZMXLoader(ObjectManager* manager, phx::RayPass* raypass,
+                     std::string file) {
+  std::wifstream zmx(file, std::ifstream::binary);
+
+  if (!zmx.good()) {
+    phx::error(("Failed opening file '" + file + "'").c_str());
+    return;
+  }
+  zmx.imbue(std::locale(
+      zmx.getloc(),
+      new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>));
+
+  phx::info(("Found '" + file + "', starting to parse it.").c_str());
+
+  std::wstring first;
+  std::wstring second;
+  std::wstring third;
+
+  float wavelength = 500.0f;  // nm
+
+  Lens::GlassType glass = static_cast<Lens::GlassType>(-1);
+
+  float acc_dist = NAN;
+
+  float curvature = NAN;
+  float pos = NAN;
+  float diameter = NAN;
+
+  float curvature2 = NAN;
+  float thickness = NAN;
+  float diameter2 = NAN;
+
+  bool first_side = true;
+
+  while (!zmx.eof()) {
+    zmx >> first;
+    // Catch lines without second parameter
+    if (first.compare(L"FIMP") == 0 || first.compare(L"STOP") == 0) continue;
+    zmx >> second;  // sort out empty lines
+
+    if (first.compare(L"WAVM") == 0) {
+      zmx >> third;
+      wavelength = std::stof(third) * 1000.0f;
+      manager->GetMenuManager()->GetLaserMenu()->SetCurrentWaveLength(
+          wavelength);
+    } else if (first.compare(L"SURF") == 0) {
+      if (first_side) {  // possible first half of lens
+        // completed lens side
+        if (!std::isnan(curvature) && !std::isnan(diameter) && glass >= 0) {
+          first_side = false;
+        } else {
+          // reset
+          curvature = pos = diameter = curvature2 = thickness = diameter2 = NAN;
+          first_side = true;
+          glass = static_cast<Lens::GlassType>(-1);
+        }
+      } else {
+        // lens completed
+        if (!std::isnan(curvature2) && !std::isnan(thickness) &&
+            !std::isnan(diameter2) && glass >= 0) {
+          auto lens = manager->CreateLens(
+              glm::vec3(manager->GetCenterAxisPos().x,
+                        manager->GetCenterAxisPos().y, pos));
+          lens->SetLensRadius(diameter / 2.0f);
+          lens->SetRadius(1.0f / abs(curvature));
+          lens->SetRadius2(1.0f / abs(curvature2));
+          lens->SetLensSideTypes(
+              static_cast<Lens::LensSideType>(sgn(curvature)),
+              static_cast<Lens::LensSideType>(sgn(curvature2)));
+          lens->SetThickness(abs(thickness));
+          lens->SetGlassType(glass);
+
+          phx::info(("    Loaded Lens: (" + lens->GetGlassTypeName() + ", " +
+                     std::to_string(pos) + "Z, " +
+                     std::to_string(diameter / 2.0f) + "LR, " +
+                     std::to_string(abs(thickness)) + "T, " +
+                     std::to_string(1.0f / abs(curvature)) + "R1, " +
+                     std::to_string(1.0f / abs(curvature2)) + "R2)")
+                        .c_str());
+
+          // reset
+          curvature = diameter = curvature2 = thickness = diameter2 = NAN;
+          first_side = true;
+          glass = static_cast<Lens::GlassType>(-1);
+        }
+      }
+    } else if (first.compare(L"CURV") == 0) {
+      if (first_side) {
+        curvature = std::stof(second);
+      } else {
+        curvature2 = std::stof(second);
+      }
+    } else if (first.compare(L"DISZ") == 0) {
+      if (first_side) {
+        thickness = std::stof(second) / 1000.0f;
+        pos = (std::isnan(acc_dist)) ? 0 : acc_dist;
+      }
+
+      if (std::isnan(acc_dist)) {
+        acc_dist = std::stof(second) / 1000.0f;
+      } else {
+        acc_dist += std::stof(second) / 1000.0f;
+      }
+    } else if (first.compare(L"DIAM") == 0) {
+      if (first_side) {
+        diameter = std::stof(second) / 1000.0f;
+      } else {
+        diameter2 = std::stof(second) / 1000.0f;
+      }
+    } else if (first.compare(L"GLAS") == 0) {
+      if (second.compare(L"BK7") == 0) {
+        glass = Lens::GlassType::BK7;
+      } else if (second.compare(L"FS") == 0) {
+        glass = Lens::GlassType::FUSED_SILICA;
+      } else if (second.compare(L"SF11") == 0) {
+        glass = Lens::GlassType::SF11;
+      } else if (second.compare(L"SF5") == 0) {
+        glass = Lens::GlassType::SF5;
+      } else if (second.compare(L"SK16") == 0) {
+        glass = Lens::GlassType::SK16;
+      } else if (second.compare(L"F2") == 0) {
+        glass = Lens::GlassType::F2;
+      } else {
+        phx::warn(("Unrecognized glass type: " +
+                   std::string(second.begin(), second.end()))
+                      .c_str());
+        phx::warn("Using BK7 instead.");
+        glass = Lens::GlassType::BK7;
+      }
+    }
+
+    zmx.ignore(std::numeric_limits<std::streamsize>::max(),
+               '\n');  // ignore rest of line
+  }
+
+  phx::info(("    Moved Laser to " + std::to_string(pos) + "Z.").c_str());
+
+  raypass->moveLaserObj(glm::vec3(manager->GetCenterAxisPos().x,
+                                  manager->GetCenterAxisPos().y, pos));
+
+  phx::info("Finished parsing");
+}
+
+ZMXLoader::~ZMXLoader() {}
diff --git a/demos/optical_bench/src/file_reader_zmx.hpp b/demos/optical_bench/src/file_reader_zmx.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..25d20b8c07ddf4fd30556ad6c4edfc6e46ddb78b
--- /dev/null
+++ b/demos/optical_bench/src/file_reader_zmx.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "object_manager.hpp"
+#include "ray_pass.hpp"
+
+class ZMXLoader {
+ public:
+  ZMXLoader(ObjectManager* manager, phx::RayPass* raypass, std::string file);
+  ~ZMXLoader();
+
+ private:
+};
diff --git a/demos/optical_bench/src/laser_menu.cpp b/demos/optical_bench/src/laser_menu.cpp
index 4b44457ed7207de2c6d0be354ebabe4e156848c8..d3e67fd0dc6d04f338b4f89ebf45b63b718b7ff0 100644
--- a/demos/optical_bench/src/laser_menu.cpp
+++ b/demos/optical_bench/src/laser_menu.cpp
@@ -57,6 +57,7 @@ LaserMenu::LaserMenu(phx::Scene* scene, phx::Engine* engine) {
     text_p_->SetText(rayPass->GetCurrentPatternName());
   });
   text_p_->SetText(rayPass->GetCurrentPatternName());
+  text_p_->SetTextHeight(0.005f);  // bit smaller
 
   // Target Color
   MenuHelper::createButtonPairVertical(up_tc_, down_tc_, text_tc_, 0.11f,
@@ -170,3 +171,5 @@ LaserMenu::~LaserMenu() {
   delete text_width_;
   delete text_tc_;
 }
+
+void LaserMenu::SetCurrentWaveLength(float f) { slider_wlength_->SetValue(f); }
diff --git a/demos/optical_bench/src/laser_menu.hpp b/demos/optical_bench/src/laser_menu.hpp
index cd9a7c3bad3991cc04b8e945cc0cb20d9d4ef960..f7e5732de7bdd33438447c6a56426ce0c1795722 100644
--- a/demos/optical_bench/src/laser_menu.hpp
+++ b/demos/optical_bench/src/laser_menu.hpp
@@ -30,6 +30,7 @@ class LaserMenu {
   };
 
   float GetCurrentWaveLength() { return slider_wlength_->GetValue(); };
+  void SetCurrentWaveLength(float f);
 
  private:
   phx::Entity* entity_;
diff --git a/demos/optical_bench/src/lens.cpp b/demos/optical_bench/src/lens.cpp
index dd13c003a5ef357002a91ee08a65c19585902e38..589485e7d69e12fdd052d6aa77c2720796c721ee 100644
--- a/demos/optical_bench/src/lens.cpp
+++ b/demos/optical_bench/src/lens.cpp
@@ -151,12 +151,12 @@ void Lens::PreviousLensSideType2() {
 }
 
 void Lens::NextGlassType() {
-  glass_type_ = static_cast<GlassType>((glass_type_ + 1) % 3);
+  glass_type_ = static_cast<GlassType>((glass_type_ + 1) % 5);
   ChangedWaveLength(current_wavelength_nm_);
 }
 
 void Lens::PreviousGlassType() {
-  glass_type_ = static_cast<GlassType>((glass_type_ - 1 + 3) % 3);
+  glass_type_ = static_cast<GlassType>((glass_type_ - 1 + 5) % 5);
   ChangedWaveLength(current_wavelength_nm_);
 }
 
@@ -180,6 +180,10 @@ std::string Lens::GetGlassTypeName() {
       return "SF11";
     case FUSED_SILICA:
       return "Fused Silica";
+    case SK16:
+      return "SK16";
+    case F2:
+      return "F2";
   }
   return "Unknown";
 }
diff --git a/demos/optical_bench/src/lens.hpp b/demos/optical_bench/src/lens.hpp
index 2a183ac02b7c50693abef918ca0ffd55640b46ec..46bb523d0cfd207445b94d699a11b9e97982ef77 100644
--- a/demos/optical_bench/src/lens.hpp
+++ b/demos/optical_bench/src/lens.hpp
@@ -20,7 +20,14 @@ SUPPRESS_WARNINGS_END
 class Lens {
  public:
   enum LensSideType { CONVEX = 1, CONCAVE = -1, PLANE = 0 };
-  enum GlassType { BK7 = 0, SF5 = 1, SF11 = 2, FUSED_SILICA = 3 };
+  enum GlassType {
+    BK7 = 0,
+    SF5 = 1,
+    SF11 = 2,
+    FUSED_SILICA = 3,
+    SK16 = 4,
+    F2 = 5
+  };
   Lens(phx::Engine* engine, phx::Scene* scene, OptixContextManager* manager,
        LensSideType type1, LensSideType type2, optix::float3 frontCenter,
        float lensradius, float thickness, float radius, float radius2 = 0.0f);
@@ -77,6 +84,10 @@ class Lens {
   void PreviousLensSideType2();
   void NextGlassType();
   void PreviousGlassType();
+  void SetGlassType(GlassType g) {
+    glass_type_ = g;
+    ChangedWaveLength(current_wavelength_nm_);
+  }
 
   std::string GetLensTypeName1();
   std::string GetLensTypeName2();
@@ -117,7 +128,7 @@ class Lens {
 
   // clang-format off
   //Keep Clang out of here, else it is barely readable
-  const std::array<std::array<glm::vec3, 2>, 4> glass_definitions = {{
+  const std::array<std::array<glm::vec3, 2>, 6> glass_definitions = {{
       {//BK7
           glm::vec3(1.03961212f, 0.231792344f, 1.01046945f), //B
           glm::vec3(0.00600069867f, 0.0200179144f, 103.560653f) //C
@@ -130,6 +141,12 @@ class Lens {
       },{ //Fused Silica
           glm::vec3(0.6961663f, 0.4079426f, 0.8974794f), //B
           glm::vec3(0.0684043f, 0.1162414f, 9.896161f) //C
+      },{ //SK16
+          glm::vec3(1.34317774f, 0.241144399f, 0.994317969f), //B
+          glm::vec3(0.00704687339f, 0.0229005f, 92.7508526f) //C
+      } ,{ //F2
+          glm::vec3(1.34533359f, 0.209073176f, 0.937357162f), //B
+          glm::vec3(0.00997743871f, 0.0470450767f, 111.886764f) //C
       } }};
   // clang-format on
 };
diff --git a/demos/optical_bench/src/object_manager.hpp b/demos/optical_bench/src/object_manager.hpp
index 71b4d45eca4f4ddd52c34d25187d814ebbfee017..a10e180a299644eeffe042634d8bd548ad8e9744 100644
--- a/demos/optical_bench/src/object_manager.hpp
+++ b/demos/optical_bench/src/object_manager.hpp
@@ -37,6 +37,7 @@ class ObjectManager : public phx::System {
   void KillThread();
 
   void SetMenuManager(MenuManager* manager);
+  MenuManager* GetMenuManager() { return menu_; };
 
   void registerHMDNav(HMDNavigationBehavior* nav) { hmd_nav_ = nav; }
 
diff --git a/demos/optical_bench/src/optical_bench.cpp b/demos/optical_bench/src/optical_bench.cpp
index bb9b40010652c824a50c983818bd4af64d76ee27..c90805387bda2b0f780942fd9d810006065bad8f 100644
--- a/demos/optical_bench/src/optical_bench.cpp
+++ b/demos/optical_bench/src/optical_bench.cpp
@@ -54,6 +54,7 @@
 #include "phx/setup.hpp"
 
 #include "desktop_navigation_behavior.hpp"
+#include "file_reader_zmx.hpp"
 #include "hmd_navigation_behavior.hpp"
 #include "menu_manager.hpp"
 #include "object_manager.hpp"
@@ -80,7 +81,7 @@ void MessageCallback(GLenum, GLenum type, GLuint, GLenum severity, GLsizei,
           message);
 }
 
-int main(int, char**) {
+int main(int argc, char** args) {
   std::shared_ptr<OptixContextManager> contextManager =
       std::make_shared<OptixContextManager>();
 
@@ -92,11 +93,6 @@ int main(int, char**) {
   auto menu_manager = std::make_unique<MenuManager>(scene.get(), engine.get());
   object_manager->SetMenuManager(menu_manager.get());
 
-  object_manager->CreateLens(glm::vec3(0.f, 1.20f, 0.0f));
-  object_manager->CreateLens(glm::vec3(0.f, 1.20f, -0.2f));
-  object_manager->CreateLens(glm::vec3(0.f, 1.20f, 0.2f));
-  object_manager->CreateTarget(glm::vec3(0, 1.20f, -1.0f), "Checker.png");
-
   auto rendering_system = engine->GetSystem<phx::RenderingSystem>();
   rendering_system->SetEnabled(false);
 
@@ -171,11 +167,12 @@ int main(int, char**) {
     virtual_platform->AddComponent<DesktopNavigationBehavior>(
         engine->GetSystem<phx::InputSystem>());
   } else {  // HMD Rendering
-	  object_manager->registerHMDNav(virtual_platform->AddComponent<HMDNavigationBehavior>(
-        engine->GetSystem<phx::DisplaySystemOpenVR>()->GetHMD(), rayPass,
-        scene.get(), engine->GetSystem<SelectionSystem>(), engine.get(),
-        menu_manager.get()));
-  }  
+    object_manager->registerHMDNav(
+        virtual_platform->AddComponent<HMDNavigationBehavior>(
+            engine->GetSystem<phx::DisplaySystemOpenVR>()->GetHMD(), rayPass,
+            scene.get(), engine->GetSystem<SelectionSystem>(), engine.get(),
+            menu_manager.get()));
+  }
 
   /*
   auto box = phx::SceneLoader::InsertModelIntoScene(
@@ -184,6 +181,14 @@ int main(int, char**) {
   box->GetFirstComponent<phx::Transform>()->Translate(glm::vec3(0.5f, 1.2f, 0));
   box->AddComponent<phx::Selector>(box);
   */
+  if (argc > 1) {
+    ZMXLoader(object_manager, rayPass, args[1]);
+  } else {
+    object_manager->CreateLens(glm::vec3(0.f, 1.20f, 0.0f));
+    object_manager->CreateLens(glm::vec3(0.f, 1.20f, -0.2f));
+    object_manager->CreateLens(glm::vec3(0.f, 1.20f, 0.2f));
+    object_manager->CreateTarget(glm::vec3(0, 1.20f, -1.0f), "Checker.png");
+  }
 
   // During init, enable debug output
   glEnable(GL_DEBUG_OUTPUT);
diff --git a/demos/optical_bench/src/ray_pass.cpp b/demos/optical_bench/src/ray_pass.cpp
index 87ba813aa3d05c77782c6ad6d22a58b4db16319b..dd2b08299bb987025a11d65b56d668a1bc41513c 100644
--- a/demos/optical_bench/src/ray_pass.cpp
+++ b/demos/optical_bench/src/ray_pass.cpp
@@ -85,6 +85,7 @@ RayPass::~RayPass() {
   targetTransform->destroy();
   targetMaterial->destroy();
   targetAcc->destroy();
+  bufferLaserDirOptix->destroy();
 
   free(textureDataBuffer);
 
@@ -244,10 +245,6 @@ void RayPass::createLaser() {
   laser_entry = context->getEntryPointCount();
   context->setEntryPointCount(laser_entry + 1);  // Add new Entrypoint
 
-  optix::Program exception_program = context->createProgramFromPTXString(
-      OptixContextManager::getPtxString("exception.cu"), "exception");
-  context->setExceptionProgram(laser_entry, exception_program);
-
   // Ray generation program
   optix::Program rayProg = context->createProgramFromPTXString(
       OptixContextManager::getPtxString("laser_caster.cu"), "laser_caster");
@@ -260,11 +257,15 @@ void RayPass::createLaser() {
   bufferLaserIndexOptix =
       context->createBuffer(RT_BUFFER_INPUT, RT_FORMAT_INT, 50, 50);
 
+  bufferLaserDirOptix =
+      context->createBuffer(RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 50, 50);
+
   loadAllPatterns("Rainbow");
   SetLaserWaveLength(450.0f);
 
   context["allowTir"]->setInt(0);
   context["laserIndex"]->set(bufferLaserIndexOptix);
+  context["laserDir"]->set(bufferLaserDirOptix);
 
   bufferLaserOptix = context->createBufferFromGLBO(RT_BUFFER_OUTPUT,
                                                    bufferOpenGLLaserVertices);
@@ -453,11 +454,11 @@ void RayPass::moveLaser(glm::mat4 transform) {
 
   ori += 0.03626f * forward;  // Center at beginning of "front"
 
-  moveLaser(ori, forward, right, up);
+  moveLaser(ori, forward, right, up, mat_rot);
 }
 
 void RayPass::moveLaser(glm::vec3 pos, glm::vec3 forward, glm::vec3 right,
-                        glm::vec3 up) {
+                        glm::vec3 up, glm::mat3 rot_mat) {
   forward = normalize(forward);
   right = normalize(right);
   up = normalize(up);
@@ -467,6 +468,18 @@ void RayPass::moveLaser(glm::vec3 pos, glm::vec3 forward, glm::vec3 right,
   context["laser_forward"]->setFloat(forward.x, forward.y, forward.z);
   context["laser_right"]->setFloat(right.x, right.y, right.z);
   context["laser_up"]->setFloat(up.x, up.y, up.z);
+
+  context["laser_rot"]->setMatrix3x3fv(true, &rot_mat[0][0]);
+}
+
+void RayPass::moveLaserObj(glm::vec3 pos) {
+  glm::vec3 f = glm::mat3(laser_object_->GetFirstComponent<phx::Transform>()
+                              ->GetGlobalMatrix()) *
+                glm::vec3(0, 0, -1);
+
+  laser_object_->GetFirstComponent<phx::Transform>()->SetGlobalTranslation(
+      pos - f * 0.03626f);
+  laser_object_->GetFirstComponent<phx::Selector>()->ExternalUpdate();
 }
 
 void RayPass::SetLaserRayWidth(float width) {
@@ -477,9 +490,9 @@ void RayPass::SetLaserRayWidth(float width) {
 
 void RayPass::SetLaserPattern(unsigned int index) {
   current_pattern_index_ = index % patterns_.size();
-  auto image = patterns_[current_pattern_index_];
-  bufferLaserColors(image);
-  bufferLaserIndex(image);
+  bufferLaserColors(patterns_[current_pattern_index_]);
+  bufferLaserIndex(patterns_[current_pattern_index_]);
+  bufferLaserDirection(patterns_dir_[current_pattern_index_]);
 }
 
 void RayPass::NextLaserPattern() {
@@ -535,7 +548,8 @@ void RayPass::bufferLaserIndex(phx::ResourcePointer<phx::Image> image) {
   }
 
   // Create buffer and populate with data
-  int* buffer_data = static_cast<int*>(bufferLaserIndexOptix->map());
+  int* buffer_data = static_cast<int*>(
+      bufferLaserIndexOptix->map(0, RT_BUFFER_MAP_WRITE_DISCARD));
 
   for (unsigned int i = 0; i < dims[0]; ++i) {
     for (unsigned int j = 0; j < dims[1]; ++j) {
@@ -554,14 +568,44 @@ void RayPass::bufferLaserIndex(phx::ResourcePointer<phx::Image> image) {
   bufferLaserIndexOptix->unmap();
 }
 
+void RayPass::bufferLaserDirection(phx::ResourcePointer<phx::Image> image) {
+  std::array<size_t, 2> dims = {50, 50};
+  if (image->GetDimensions() != dims) {
+    phx::warn("Only 50x50px laser pattern are allowed. Skipping.");
+    return;
+  }
+
+  // Create buffer and populate with data
+  float* buffer_data = static_cast<float*>(
+      bufferLaserDirOptix->map(0, RT_BUFFER_MAP_WRITE_DISCARD));
+
+  for (size_t x = 0; x < dims[0]; x++) {
+    for (size_t y = 0; y < dims[1]; y++) {
+      auto color = image->GetPixelColor({dims[0] - x - 1, y});
+      auto index = (y * 50 + x) * 3;
+
+      // explicitly wrongly normalized to allow for value 0
+      buffer_data[index] = (color[0] / 256.0f) * 2 - 1;
+      buffer_data[index + 1] = (color[1] / 256.0f) * 2 - 1;
+      buffer_data[index + 2] = (color[2] / 256.0f);
+    }
+  }
+
+  bufferLaserDirOptix->unmap();
+}
+
 void RayPass::loadAllPatterns(std::string use_directly) {
   for (auto i = 0u; i < pattern_files_.size(); i++) {
     auto image = phx::ResourceUtils::LoadResourceFromFile<phx::Image>(
         "laser/pattern/" + pattern_files_[i] + ".png");
+    auto image_dir = phx::ResourceUtils::LoadResourceFromFile<phx::Image>(
+        "laser/pattern/" + pattern_files_[i] + "_dir.png");
     patterns_.push_back(image);
-    if (pattern_files_[i] == use_directly) {
+    patterns_dir_.push_back(image_dir);
+    if (pattern_files_[i].compare(use_directly) == 0) {
       bufferLaserColors(image);
       bufferLaserIndex(image);
+      bufferLaserDirection(image_dir);
       current_pattern_index_ = i;
     }
   }
diff --git a/demos/optical_bench/src/ray_pass.hpp b/demos/optical_bench/src/ray_pass.hpp
index a801ce35e5353f7e748d9b10c4881ef2a71ce799..2b43f94dd74bb3f888906f401ba962529db744d4 100644
--- a/demos/optical_bench/src/ray_pass.hpp
+++ b/demos/optical_bench/src/ray_pass.hpp
@@ -73,7 +73,8 @@ class RayPass : public RenderPass {
   void launchLaser();
   void moveLaser(glm::mat4 transform);
   void moveLaser(glm::vec3 pos, glm::vec3 forward, glm::vec3 right,
-                 glm::vec3 up);
+                 glm::vec3 up, glm::mat3 rot_mat);
+  void moveLaserObj(glm::vec3 pos);
   unsigned int GetLaserMaxDepth() { return laser_max_depth; }
 
   // Ray Parameter
@@ -117,14 +118,17 @@ class RayPass : public RenderPass {
 
   optix::Buffer bufferLaserOptix;
   optix::Buffer bufferLaserIndexOptix;
+  optix::Buffer bufferLaserDirOptix;
   unsigned int laser_entry;
 
   void render(RenderTarget* render_target);
   void bufferLaserColors(phx::ResourcePointer<phx::Image> image);
   void bufferLaserIndex(phx::ResourcePointer<phx::Image> image);
+  void bufferLaserDirection(phx::ResourcePointer<phx::Image> image);
   void loadAllPatterns(std::string use_directly);
 
   std::vector<phx::ResourcePointer<phx::Image>> patterns_;
+  std::vector<phx::ResourcePointer<phx::Image>> patterns_dir_;
   unsigned int current_pattern_index_;
 
   // target buffer
diff --git a/demos/optical_bench/src/text.hpp b/demos/optical_bench/src/text.hpp
index 0277e3a7e8ec778f5d164dc875d7b5a549468770..b18f21ab1f0da73faa0d965f20e0bf879606f52d 100644
--- a/demos/optical_bench/src/text.hpp
+++ b/demos/optical_bench/src/text.hpp
@@ -58,6 +58,7 @@ class Text : public Component {
     if (manual_update_function_ != nullptr) manual_update_function_();
   }
 
+  void SetTextHeight(float h) { text_height_ = h; }
   void SetText(std::string text);
   void SetVisible(bool visible) { visible_ = visible; }
 
diff --git a/resources/laser/pattern/Crosshair_dir.png b/resources/laser/pattern/Crosshair_dir.png
new file mode 100644
index 0000000000000000000000000000000000000000..a057de6c407429af289de99f159783726dc1ca19
--- /dev/null
+++ b/resources/laser/pattern/Crosshair_dir.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e4b8bd178632f218bed787ba4ce505e17103ee50a541f9910770dc54e048b5c4
+size 195
diff --git a/resources/laser/pattern/Pointer_dir.png b/resources/laser/pattern/Pointer_dir.png
new file mode 100644
index 0000000000000000000000000000000000000000..a057de6c407429af289de99f159783726dc1ca19
--- /dev/null
+++ b/resources/laser/pattern/Pointer_dir.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e4b8bd178632f218bed787ba4ce505e17103ee50a541f9910770dc54e048b5c4
+size 195
diff --git a/resources/laser/pattern/Rainbow_dir.png b/resources/laser/pattern/Rainbow_dir.png
new file mode 100644
index 0000000000000000000000000000000000000000..9b1c3efe01cbc643d188dd501e8d04318361434e
--- /dev/null
+++ b/resources/laser/pattern/Rainbow_dir.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:43d040b86b6cfe657cbc2dce82657b97cf8826fb7163d84e0dfd569a46da1aec
+size 1659
diff --git a/resources/laser/pattern/RayTest_dir.png b/resources/laser/pattern/RayTest_dir.png
new file mode 100644
index 0000000000000000000000000000000000000000..a057de6c407429af289de99f159783726dc1ca19
--- /dev/null
+++ b/resources/laser/pattern/RayTest_dir.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e4b8bd178632f218bed787ba4ce505e17103ee50a541f9910770dc54e048b5c4
+size 195