diff --git a/Modules/ROI/include/mitkROIMapper2D.h b/Modules/ROI/include/mitkROIMapper2D.h
index 430302e761..f7c81f61e9 100644
--- a/Modules/ROI/include/mitkROIMapper2D.h
+++ b/Modules/ROI/include/mitkROIMapper2D.h
@@ -1,56 +1,64 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef mitkROIMapper2D_h
 #define mitkROIMapper2D_h
 
 #include <mitkLocalStorageHandler.h>
 #include <mitkVtkMapper.h>
 #include <MitkROIExports.h>
 
 #include <vtkPropAssembly.h>
 #include <vtkSmartPointer.h>
 
 namespace mitk
 {
   class MITKROI_EXPORT ROIMapper2D : public VtkMapper
   {
     class LocalStorage : public Mapper::BaseLocalStorage
     {
     public:
       LocalStorage();
       ~LocalStorage() override;
 
       vtkPropAssembly* GetPropAssembly() const;
+      void SetPropAssembly(vtkPropAssembly* propAssembly);
+
+      const PlaneGeometry* GetLastPlaneGeometry() const;
+      void SetLastPlaneGeometry(const PlaneGeometry* planeGeometry);
 
     protected:
       vtkSmartPointer<vtkPropAssembly> m_PropAssembly;
+      PlaneGeometry::ConstPointer m_LastPlaneGeometry;
     };
 
   public:
     static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = nullptr, bool override = false);
 
     mitkClassMacro(ROIMapper2D, VtkMapper)
     itkFactorylessNewMacro(Self)
 
     vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override;
 
   protected:
     ROIMapper2D();
     ~ROIMapper2D() override;
 
+    void GenerateDataForRenderer(BaseRenderer* renderer) override;
+    void ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) override;
+
   private:
     LocalStorageHandler<LocalStorage> m_LocalStorageHandler;
   };
 }
 
 #endif
diff --git a/Modules/ROI/src/mitkROIMapper2D.cpp b/Modules/ROI/src/mitkROIMapper2D.cpp
index c2adedc3fc..9caece5caf 100644
--- a/Modules/ROI/src/mitkROIMapper2D.cpp
+++ b/Modules/ROI/src/mitkROIMapper2D.cpp
@@ -1,45 +1,150 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include <mitkROIMapper2D.h>
+#include <mitkROI.h>
+
+#include <vtkActor.h>
+#include <vtkCubeSource.h>
+#include <vtkCutter.h>
+#include <vtkPlane.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkSmartPointer.h>
+
+// Implemented in mitkROIMapper3D.cpp
+extern void ApplyIndividualColorAndOpacityProperties(const mitk::IPropertyProvider* properties, vtkActor* actor);
 
 mitk::ROIMapper2D::LocalStorage::LocalStorage()
   : m_PropAssembly(vtkSmartPointer<vtkPropAssembly>::New())
 {
 }
 
 mitk::ROIMapper2D::LocalStorage::~LocalStorage()
 {
 }
 
 vtkPropAssembly* mitk::ROIMapper2D::LocalStorage::GetPropAssembly() const
 {
   return m_PropAssembly;
 }
 
+void mitk::ROIMapper2D::LocalStorage::SetPropAssembly(vtkPropAssembly* propAssembly)
+{
+  m_PropAssembly = propAssembly;
+}
+
+const mitk::PlaneGeometry* mitk::ROIMapper2D::LocalStorage::GetLastPlaneGeometry() const
+{
+  return m_LastPlaneGeometry;
+}
+
+void mitk::ROIMapper2D::LocalStorage::SetLastPlaneGeometry(const PlaneGeometry* planeGeometry)
+{
+  m_LastPlaneGeometry = planeGeometry;
+}
+
 void mitk::ROIMapper2D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override)
 {
-  Superclass::SetDefaultProperties(node, renderer, override);
+  // Implemented in ROIMapper3D
 }
 
 mitk::ROIMapper2D::ROIMapper2D()
 {
 }
 
 mitk::ROIMapper2D::~ROIMapper2D()
 {
 }
 
+void mitk::ROIMapper2D::GenerateDataForRenderer(BaseRenderer* renderer)
+{
+  const auto* dataNode = this->GetDataNode();
+
+  if (dataNode == nullptr)
+    return;
+
+  auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer);
+  const auto* planeGeometry = renderer->GetCurrentWorldPlaneGeometry();
+
+  if (localStorage->GetLastPlaneGeometry() != nullptr && localStorage->GetLastPlaneGeometry()->IsOnPlane(planeGeometry) &&
+      localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime())
+  {
+    return;
+  }
+
+  localStorage->SetLastPlaneGeometry(planeGeometry->Clone());
+
+  const auto* data = dynamic_cast<ROI*>(this->GetData());
+
+  if (data == nullptr)
+    return;
+
+  auto propAssembly = vtkSmartPointer<vtkPropAssembly>::New();
+
+  if (dataNode->IsVisible(renderer))
+  {
+    const auto* geometry = data->GetGeometry();
+    const auto halfSpacing = geometry->GetSpacing() * 0.5f;
+
+    auto plane = vtkSmartPointer<vtkPlane>::New();
+    plane->SetOrigin(planeGeometry->GetOrigin().data());
+    plane->SetNormal(planeGeometry->GetNormal().data());
+
+    for (const auto& roi : *data)
+    {
+      Point3D min;
+      geometry->IndexToWorld(roi.Min, min);
+      min -= halfSpacing;
+
+      Point3D max;
+      geometry->IndexToWorld(roi.Max, max);
+      max += halfSpacing;
+
+      auto cube = vtkSmartPointer<vtkCubeSource>::New();
+      cube->SetBounds(min[0], max[0], min[1], max[1], min[2], max[2]);
+
+      auto cutter = vtkSmartPointer<vtkCutter>::New();
+      cutter->SetInputConnection(cube->GetOutputPort());
+      cutter->SetCutFunction(plane);
+      cutter->Update();
+
+      auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+      mapper->SetInputConnection(cutter->GetOutputPort());
+      mapper->SetResolveCoincidentTopologyToPolygonOffset();
+
+      auto actor = vtkSmartPointer<vtkActor>::New();
+      actor->SetMapper(mapper);
+
+      this->ApplyColorAndOpacityProperties(renderer, actor);
+      ApplyIndividualColorAndOpacityProperties(roi.Properties, actor);
+
+      propAssembly->AddPart(actor);
+    }
+  }
+
+  localStorage->SetPropAssembly(propAssembly);
+  localStorage->UpdateGenerateDataTime();
+}
+
+void mitk::ROIMapper2D::ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor)
+{
+  auto* property = actor->GetProperty();
+
+  float opacity = 1.0f;
+  this->GetOpacity(opacity, renderer);
+  property->SetOpacity(opacity);
+}
+
 vtkProp* mitk::ROIMapper2D::GetVtkProp(BaseRenderer* renderer)
 {
   return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly();
 }
diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp
index 73afe959c7..a91446f479 100644
--- a/Modules/ROI/src/mitkROIMapper3D.cpp
+++ b/Modules/ROI/src/mitkROIMapper3D.cpp
@@ -1,157 +1,154 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include <mitkROIMapper3D.h>
 #include <mitkROI.h>
 
 #include <vtkActor.h>
 #include <vtkCubeSource.h>
 #include <vtkPolyDataMapper.h>
 #include <vtkSmartPointer.h>
 
-namespace
+// Used by both ROIMapper2D and ROIMapper3D
+void ApplyIndividualColorAndOpacityProperties(const mitk::IPropertyProvider* properties, vtkActor* actor)
 {
-  void ApplyIndividualColorAndOpacityProperties(const mitk::IPropertyProvider* properties, vtkActor* actor)
+  actor->GetProperty()->SetRepresentationToWireframe();
+  actor->GetProperty()->LightingOff();
+
+  auto property = properties->GetConstProperty("color");
+
+  if (property.IsNotNull())
   {
-    auto property = properties->GetConstProperty("color");
+    auto colorProperty = dynamic_cast<const mitk::ColorProperty*>(property.GetPointer());
 
-    if (property.IsNotNull())
+    if (colorProperty != nullptr)
     {
-      auto colorProperty = dynamic_cast<const mitk::ColorProperty*>(property.GetPointer());
-
-      if (colorProperty != nullptr)
-      {
-        const auto color = colorProperty->GetColor();
-        actor->GetProperty()->SetColor(color[0], color[1], color[2]);
-      }
+      const auto color = colorProperty->GetColor();
+      actor->GetProperty()->SetColor(color[0], color[1], color[2]);
     }
+  }
 
-    property = properties->GetConstProperty("opacity");
+  property = properties->GetConstProperty("opacity");
 
-    if (property.IsNotNull())
-    {
-      auto opacityProperty = dynamic_cast<const mitk::FloatProperty*>(property.GetPointer());
+  if (property.IsNotNull())
+  {
+    auto opacityProperty = dynamic_cast<const mitk::FloatProperty*>(property.GetPointer());
 
-      if (opacityProperty != nullptr)
-      {
-        const auto opacity = opacityProperty->GetValue();
-        actor->GetProperty()->SetOpacity(actor->GetProperty()->GetOpacity() * opacity);
-      }
+    if (opacityProperty != nullptr)
+    {
+      const auto opacity = opacityProperty->GetValue();
+      actor->GetProperty()->SetOpacity(actor->GetProperty()->GetOpacity() * opacity);
     }
   }
 }
 
 mitk::ROIMapper3D::LocalStorage::LocalStorage()
   : m_PropAssembly(vtkSmartPointer<vtkPropAssembly>::New())
 {
 }
 
 mitk::ROIMapper3D::LocalStorage::~LocalStorage()
 {
 }
 
 vtkPropAssembly* mitk::ROIMapper3D::LocalStorage::GetPropAssembly() const
 {
   return m_PropAssembly;
 }
 
 void mitk::ROIMapper3D::LocalStorage::SetPropAssembly(vtkPropAssembly* propAssembly)
 {
   m_PropAssembly = propAssembly;
 }
 
 void mitk::ROIMapper3D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override)
 {
   Superclass::SetDefaultProperties(node, renderer, override);
   node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, override);
 }
 
 mitk::ROIMapper3D::ROIMapper3D()
 {
 }
 
 mitk::ROIMapper3D::~ROIMapper3D()
 {
 }
 
 void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer)
 {
   const auto* dataNode = this->GetDataNode();
 
   if (dataNode == nullptr)
     return;
 
   auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer);
 
   if (localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime())
     return;
 
   const auto* data = dynamic_cast<ROI*>(this->GetData());
 
   if (data == nullptr)
     return;
 
-  const auto* geometry = data->GetGeometry();
-  const auto halfSpacing = geometry->GetSpacing() * 0.5f;
-
   auto propAssembly = vtkSmartPointer<vtkPropAssembly>::New();
 
   if (dataNode->IsVisible(renderer))
   {
+    const auto* geometry = data->GetGeometry();
+    const auto halfSpacing = geometry->GetSpacing() * 0.5f;
+
     for (const auto& roi : *data)
     {
       Point3D min;
       geometry->IndexToWorld(roi.Min, min);
       min -= halfSpacing;
 
       Point3D max;
       geometry->IndexToWorld(roi.Max, max);
       max += halfSpacing;
 
       auto cube = vtkSmartPointer<vtkCubeSource>::New();
       cube->SetBounds(min[0], max[0], min[1], max[1], min[2], max[2]);
       cube->Update();
 
       auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
       mapper->SetInputConnection(cube->GetOutputPort());
 
       auto actor = vtkSmartPointer<vtkActor>::New();
       actor->SetMapper(mapper);
 
       this->ApplyColorAndOpacityProperties(renderer, actor);
       ApplyIndividualColorAndOpacityProperties(roi.Properties, actor);
 
-      auto* property = actor->GetProperty();
-      property->SetRepresentationToWireframe();
-      property->LightingOff();
-
       propAssembly->AddPart(actor);
     }
   }
 
   localStorage->SetPropAssembly(propAssembly);
   localStorage->UpdateGenerateDataTime();
 }
 
 void mitk::ROIMapper3D::ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor)
 {
   auto* property = actor->GetProperty();
 
   float opacity = 1.0f;
   this->GetOpacity(opacity, renderer);
   property->SetOpacity(opacity);
 }
 
 vtkProp* mitk::ROIMapper3D::GetVtkProp(BaseRenderer* renderer)
 {
   return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly();
 }