diff --git a/Base/Geometry/CMakeLists.txt b/Base/Geometry/CMakeLists.txt
index 3e381c566c71bca695a2638eec7df2dbc04c794b..18aef4f2a95c42784eaa97dca67405a43e1d488c 100644
--- a/Base/Geometry/CMakeLists.txt
+++ b/Base/Geometry/CMakeLists.txt
@@ -6,6 +6,7 @@ imstk_add_library( Geometry
   DEPENDS
     Core
     Assimp
+    Materials
     VegaFEM::volumetricMesh
     ${VTK_LIBRARIES}
   )
diff --git a/Base/Geometry/Mesh/imstkSurfaceMesh.cpp b/Base/Geometry/Mesh/imstkSurfaceMesh.cpp
index fd03ee867270bf82a8b71536a2bfaf8faa05b600..e1a180de37f2aa0732032cd8f74bc21d220930c2 100644
--- a/Base/Geometry/Mesh/imstkSurfaceMesh.cpp
+++ b/Base/Geometry/Mesh/imstkSurfaceMesh.cpp
@@ -342,44 +342,4 @@ SurfaceMesh::getDefaultTCoords()
     return m_defaultTCoords;
 }
 
-void
-SurfaceMesh::addTexture(std::string tFileName, std::string tCoordsName)
-{
-    if (tCoordsName == "")
-    {
-        tCoordsName = m_defaultTCoords;
-        if (tCoordsName == "")
-        {
-            LOG(WARNING) << "Can not add texture without default texture coordinates. ";
-            return;
-        }
-    }
-
-    if (!m_pointDataMap.count(tCoordsName))
-    {
-        LOG(WARNING) << "Mesh does not hold any array named " << tCoordsName << ". "
-                     << "Can not add texture.";
-        return;
-    }
-
-    m_textureMap[tCoordsName] = tFileName;
-}
-const std::map<std::string, std::string>&
-SurfaceMesh::getTextureMap() const
-{
-    return m_textureMap;
-}
-
-std::string
-SurfaceMesh::getTexture(std::string tCoordsName) const
-{
-    if (!m_textureMap.count(tCoordsName))
-    {
-        LOG(WARNING) << "No texture filename associated with coordinates " << tCoordsName << ".";
-        return "";
-    }
-
-    return m_textureMap.at(tCoordsName);
-}
-
 } // imstk
diff --git a/Base/Geometry/Mesh/imstkSurfaceMesh.h b/Base/Geometry/Mesh/imstkSurfaceMesh.h
index a3bb609990d8bf32d6bb7f1b80c80d7ef9e4bff9..28ffaf99475c8cfd037498be018659c71457f81b 100644
--- a/Base/Geometry/Mesh/imstkSurfaceMesh.h
+++ b/Base/Geometry/Mesh/imstkSurfaceMesh.h
@@ -147,18 +147,11 @@ public:
     int getNumTriangles() const;
 
     ///
-    /// \brief Set/Get the array defining the default texture coordinates
+    /// \brief Set/Get the array defining the default material coordinates
     ///
     void setDefaultTCoords(std::string arrayName);
     std::string getDefaultTCoords();
 
-    ///
-    /// \brief Add texture by giving the texture file name and the texture coordinates array name
-    ///
-    void addTexture(std::string tFileName, std::string tCoordsName = "");
-    const std::map<std::string, std::string>& getTextureMap() const;
-    std::string getTexture(std::string tCoordsName) const;
-
 protected:
 
     std::vector<TriangleArray> m_trianglesVertices; ///> Triangle connectivity
@@ -171,8 +164,7 @@ protected:
     StdVectorOfVec3d m_vertexTangents; ///> Tangents of the vertices
     StdVectorOfVec3d m_vertexBitangents; ///> Bitangents of the vertices
 
-    std::string m_defaultTCoords = ""; ///> Name of the array used as default texture coordinates
-    std::map<std::string, std::string> m_textureMap; ///> Mapping texture coordinates to texture
+    std::string m_defaultTCoords = ""; ///> Name of the array used as default material coordinates
 };
 
 } // imstk
diff --git a/Base/Geometry/imstkGeometry.cpp b/Base/Geometry/imstkGeometry.cpp
index d8411e5e70b22a9e904462462877f900e15ec39e..28056200a6fbe09212fb2b1ba64afd3b38e5c534 100644
--- a/Base/Geometry/imstkGeometry.cpp
+++ b/Base/Geometry/imstkGeometry.cpp
@@ -182,4 +182,16 @@ Geometry::getTypeName() const
     }
 }
 
+void
+Geometry::setRenderMaterial(std::shared_ptr<RenderMaterial> renderMaterial)
+{
+    m_renderMaterial = renderMaterial;
+}
+
+std::shared_ptr<RenderMaterial>
+Geometry::getRenderMaterial() const
+{
+    return m_renderMaterial;
+}
+
 } // imstk
diff --git a/Base/Geometry/imstkGeometry.h b/Base/Geometry/imstkGeometry.h
index d4097ee38ad84dc30ae852001c56973947da48b8..ed97f02264faad7f49b0ae504269486ea750323f 100644
--- a/Base/Geometry/imstkGeometry.h
+++ b/Base/Geometry/imstkGeometry.h
@@ -23,7 +23,9 @@
 #define imstkGeometry_h
 
 #include "g3log/g3log.hpp"
+
 #include "imstkMath.h"
+#include "imstkRenderMaterial.h"
 
 namespace imstk
 {
@@ -142,12 +144,19 @@ public:
     ///
     const std::string getTypeName() const;
 
+    ///
+    /// \brief Set/Get render material
+    ///
+    void setRenderMaterial(std::shared_ptr<RenderMaterial> renderMaterial);
+    std::shared_ptr<RenderMaterial> getRenderMaterial() const;
+
 protected:
 
     Type m_type; ///> Geometry type
     Vec3d  m_position; ///> position
     Quatd  m_orientation; ///> orientation
     double m_scaling = 1; ///> Scaling
+    std::shared_ptr<RenderMaterial> m_renderMaterial = nullptr; // Render material
 };
 
 } //imstk
diff --git a/Base/Materials/CMakeLists.txt b/Base/Materials/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..70ccae2133c1ba206b2f6e9f00821e970be3b6ce
--- /dev/null
+++ b/Base/Materials/CMakeLists.txt
@@ -0,0 +1,15 @@
+#-----------------------------------------------------------------------------
+# Create target
+#-----------------------------------------------------------------------------
+include(imstkAddLibrary)
+imstk_add_library( Materials
+  DEPENDS
+    Core
+  )
+
+#-----------------------------------------------------------------------------
+# Testing
+#-----------------------------------------------------------------------------
+if( iMSTK_BUILD_TESTING )
+  add_subdirectory( Testing )
+endif()
diff --git a/Base/Materials/imstkRenderMaterial.cpp b/Base/Materials/imstkRenderMaterial.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..67e0aef2a616dcc2ff7399e4a7034dab7b6defc0
--- /dev/null
+++ b/Base/Materials/imstkRenderMaterial.cpp
@@ -0,0 +1,180 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+=========================================================================*/
+
+#include "imstkRenderMaterial.h"
+
+namespace imstk
+{
+
+RenderMaterial::RenderMaterial()
+{
+    // Instantiating one type of each texture per material
+    for (int i = 0; i < (int)Texture::Type::NONE; i++)
+    {
+        m_textures.emplace_back(std::make_shared<Texture>("", (Texture::Type)i));
+    }
+}
+
+const RenderMaterial::DisplayMode
+RenderMaterial::getDisplayMode() const
+{
+    return m_displayMode;
+}
+
+void
+RenderMaterial::setDisplayMode(const DisplayMode displayMode)
+{
+    if (displayMode == m_displayMode)
+    {
+        return;
+    }
+    m_displayMode = displayMode;
+    m_stateModified = true;
+    m_modified = true;
+}
+
+const float
+RenderMaterial::getLineWidth() const
+{
+    return m_lineWidth;
+}
+
+void
+RenderMaterial::setLineWidth(const float width)
+{
+    if (width == m_lineWidth)
+    {
+        return;
+    }
+    m_lineWidth = width;
+    m_stateModified = true;
+    m_modified = true;
+}
+
+const float
+RenderMaterial::getPointSize() const
+{
+    return m_pointSize;
+}
+
+void
+RenderMaterial::setPointSize(const float size)
+{
+    if (size == m_pointSize)
+    {
+        return;
+    }
+    m_pointSize = size;
+    m_stateModified = true;
+    m_modified = true;
+}
+
+const bool
+RenderMaterial::getBackFaceCulling() const
+{
+    return m_backfaceCulling;
+}
+
+void
+RenderMaterial::setBackFaceCulling(const bool culling)
+{
+    if (culling == m_backfaceCulling)
+    {
+        return;
+    }
+    m_backfaceCulling = culling;
+    m_stateModified = true;
+    m_modified = true;
+}
+
+void
+RenderMaterial::backfaceCullingOn()
+{
+    this->setBackFaceCulling(true);
+}
+
+void
+RenderMaterial::backfaceCullingOff()
+{
+    this->setBackFaceCulling(false);
+}
+
+const Color&
+RenderMaterial::getDiffuseColor() const
+{
+    return m_diffuseColor;
+}
+
+void
+RenderMaterial::setDiffuseColor(const Color color)
+{
+    m_diffuseColor = color;
+    m_modified = true;
+}
+
+const Color&
+RenderMaterial::getSpecularColor() const
+{
+    return m_specularColor;
+}
+
+void
+RenderMaterial::setSpecularColor(const Color color)
+{
+    m_specularColor = color;
+    m_modified = true;
+}
+
+const float&
+RenderMaterial::getSpecularity() const
+{
+    return m_specularity;
+}
+
+void
+RenderMaterial::setSpecularity(const float specularity)
+{
+    m_specularity = specularity;
+}
+
+std::shared_ptr<Texture>
+RenderMaterial::getTexture(Texture::Type type)
+{
+    if (type >= Texture::Type::NONE)
+    {
+        LOG(WARNING) << "RenderMaterial::getTexture error: Invalid texture format";
+        return nullptr;
+    }
+    return m_textures[type];
+}
+
+void
+RenderMaterial::addTexture(std::shared_ptr<Texture> texture)
+{
+    if (texture->getType() >= Texture::Type::NONE)
+    {
+        LOG(WARNING) << "RenderMaterial::addTexture: Invalid texture format";
+        return;
+    }
+    m_textures[texture->getType()] = texture;
+}
+
+}
diff --git a/Base/Materials/imstkRenderMaterial.h b/Base/Materials/imstkRenderMaterial.h
new file mode 100644
index 0000000000000000000000000000000000000000..06daa677bc762548816d8841915fa5b067f6d82d
--- /dev/null
+++ b/Base/Materials/imstkRenderMaterial.h
@@ -0,0 +1,130 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+=========================================================================*/
+
+#ifndef imstkRenderMaterial_h
+#define imstkRenderMaterial_h
+
+#include "imstkTexture.h"
+#include "imstkColor.h"
+#include "imstkTextureManager.h"
+
+#include <memory>
+#include <vector>
+
+#include "g3log/g3log.hpp"
+
+namespace imstk
+{
+
+
+
+class RenderMaterial
+{
+public:
+    enum DisplayMode
+    {
+        SURFACE,
+        WIREFRAME,
+        POINTS,
+        WIREFRAME_SURFACE
+    };
+
+    ///
+    /// \brief Constructor
+    ///
+    RenderMaterial();
+
+    ///
+    /// \brief Get/Set display mode
+    ///
+    const DisplayMode getDisplayMode() const;
+    void setDisplayMode(const DisplayMode displayMode);
+
+    ///
+    /// \brief Get/Set line width or the wireframe
+    ///
+    const float getLineWidth() const;
+    void setLineWidth(const float width);
+
+    ///
+    /// \brief Get/Set point size
+    ///
+    const float getPointSize() const;
+    void setPointSize(const float size);
+
+    ///
+    /// \brief Backface culling on/off
+    ///
+    const bool getBackFaceCulling() const;
+    void setBackFaceCulling(const bool culling);
+    void backfaceCullingOn();
+    void backfaceCullingOff();
+
+    ///
+    /// \brief Get/Set the diffuse color
+    ///
+    const Color& getDiffuseColor() const;
+    void setDiffuseColor(const Color color);
+
+    ///
+    /// \brief Get/Set the specular color (only set for metals)
+    ///
+    const Color& getSpecularColor() const;
+    void setSpecularColor(const Color color);
+
+    ///
+    /// \brief Get/Set the specularity
+    ///
+    const float& getSpecularity() const;
+    void setSpecularity(const float specularity);
+
+    ///
+    /// \brief Add/Get texture
+    ///
+    void addTexture(std::shared_ptr<Texture> texture);
+    std::shared_ptr<Texture> getTexture(Texture::Type type);
+
+protected:
+    friend class VTKRenderDelegate;
+
+    // State
+    DisplayMode m_displayMode = DisplayMode::SURFACE;
+    float m_lineWidth = 1.0;
+    float m_pointSize = 1.0;
+    bool m_backfaceCulling = true; ///< For performance, uncommon for this to be false
+
+    // Colors
+    Color m_diffuseColor = Color::White;
+    Color m_specularColor = Color::Black;
+
+    // Classical values
+    float m_specularity = 0.0; ///< Not shiny by default
+
+    // Textures
+    std::vector<std::shared_ptr<Texture>> m_textures; ///< Ordered by Texture::Type
+
+    bool m_stateModified = true; ///< Flag for expensive state changes
+    bool m_modified = true; ///< Flag for any material property changes
+};
+
+}
+
+#endif
diff --git a/Base/Materials/imstkTexture.cpp b/Base/Materials/imstkTexture.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2b9dc0a678dabb04ab660f99ed0860e83a49f494
--- /dev/null
+++ b/Base/Materials/imstkTexture.cpp
@@ -0,0 +1,45 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+=========================================================================*/
+
+#include "imstkTexture.h"
+
+namespace imstk
+{
+
+Texture::Texture(std::string path, Type type)
+{
+    m_path = path;
+    m_type = type;
+}
+
+const Texture::Type
+Texture::getType() const
+{
+    return m_type;
+}
+
+const std::string
+Texture::getPath() const
+{
+    return m_path;
+}
+
+}
\ No newline at end of file
diff --git a/Base/Materials/imstkTexture.h b/Base/Materials/imstkTexture.h
new file mode 100644
index 0000000000000000000000000000000000000000..4f11ebde804c27dbf3ac0c43f8e644035d73bb89
--- /dev/null
+++ b/Base/Materials/imstkTexture.h
@@ -0,0 +1,110 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+=========================================================================*/
+
+#ifndef imstkTexture_h
+#define imstkTexture_h
+
+#include <string>
+#include <memory>
+
+namespace imstk
+{
+
+///
+/// \class Texture
+///
+/// \brief iMSTK texture class. There are a few texture types that
+///     dictate how texture are to be treated.
+///
+class Texture
+{
+
+public:
+    ///
+    /// \brief Texture type - determines filtering
+    ///
+    enum Type
+    {
+        DIFFUSE = 0, // Also used for albedo
+        NORMAL,
+        SPECULAR,
+        ROUGHNESS,
+        METALNESS,
+        AMBIENT_OCCLUSION,
+        CAVITY,
+        CUBEMAP,
+        NONE
+    };
+
+    ///
+    /// \brief Constructor
+    /// \param path Path to the texture source file
+    /// \param type Type of texture
+    ///
+    Texture(std::string path = "", Type type = DIFFUSE);
+
+    ///
+    /// \brief Destructor
+    ///
+    virtual ~Texture() {}
+
+    ///
+    /// \brief Get type
+    ///
+    const Type getType() const;
+
+    ///
+    /// \brief Get path
+    ///
+    const std::string getPath() const;
+
+protected:
+
+    Type m_type;            ///< Texture type
+    std::string m_path;     ///< Texture file path
+};
+
+}
+
+// This method is defined to allow for the map to be properly indexed by Texture objects
+namespace std
+{
+    template<> struct less<std::shared_ptr<imstk::Texture>>
+    {
+        bool operator() (const std::shared_ptr<imstk::Texture>& texture1,
+            const std::shared_ptr<imstk::Texture>& texture2) const
+        {
+            if (texture2->getType() != texture2->getType())
+            {
+                return (texture2->getType() < texture2->getType());
+            }
+
+            if (texture1->getPath() != texture2->getPath())
+            {
+                return (texture1->getPath() < texture2->getPath());
+            }
+
+            return false;
+        }
+    };
+}
+
+#endif
diff --git a/Base/Materials/imstkTextureDelegate.h b/Base/Materials/imstkTextureDelegate.h
new file mode 100644
index 0000000000000000000000000000000000000000..42ab1d7f02cc894617f5d8fa47d27f7827ccfb24
--- /dev/null
+++ b/Base/Materials/imstkTextureDelegate.h
@@ -0,0 +1,56 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+=========================================================================*/
+
+#ifndef imstkTextureDelegate_h
+#define imstkTextureDelegate_h
+
+#include <string>
+#include <memory>
+
+#include "imstkTexture.h"
+
+namespace imstk
+{
+
+///
+/// \class TextureDelegate
+///
+/// \brief iMSTK texture delegate abstract class
+///
+class TextureDelegate
+{
+protected:
+    ///
+    /// \brief Constructor
+    /// \param texture The texture
+    ///
+    TextureDelegate() {}
+
+    ///
+    /// \brief Abstract function to load textures
+    /// \param texture Texture to load
+    ///
+    virtual void loadTexture(std::shared_ptr<Texture> texture) = 0;
+};
+
+}
+
+#endif
diff --git a/Base/Materials/imstkTextureManager.h b/Base/Materials/imstkTextureManager.h
new file mode 100644
index 0000000000000000000000000000000000000000..8b8fee2812f22e45f48f01b1b0bdb840193fe36b
--- /dev/null
+++ b/Base/Materials/imstkTextureManager.h
@@ -0,0 +1,71 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+=========================================================================*/
+
+#ifndef imstkTextureManager_h
+#define imstkTextureManager_h
+
+#include "imstkTexture.h"
+#include "imstkTextureDelegate.h"
+
+#include <map>
+#include <string>
+#include <memory>
+
+namespace imstk
+{
+
+template<class T>
+class TextureManager
+{
+static_assert(std::is_base_of<TextureDelegate, T>::value, "T isn't a subclass of TextureDelegate");
+
+public:
+    ///
+    /// \brief Add texture
+    ///
+    std::shared_ptr<T> getTextureDelegate(std::shared_ptr<Texture> texture);
+
+protected:
+    friend class VTKRenderer;
+
+    ///
+    /// \brief Constructor
+    ///
+    TextureManager() {}
+
+    std::map<std::shared_ptr<Texture>, std::shared_ptr<T>> m_textureMap;
+};
+
+template<class T> std::shared_ptr<T>
+TextureManager<T>::getTextureDelegate(std::shared_ptr<Texture> texture)
+{
+    if (m_textureMap.count(texture) == 0)
+    {
+        auto textureDelegate = std::make_shared<T>();
+        textureDelegate->loadTexture(texture);
+        m_textureMap[texture] = textureDelegate;
+    }
+    return m_textureMap[texture];
+}
+
+}
+
+#endif
diff --git a/Base/Rendering/CMakeLists.txt b/Base/Rendering/CMakeLists.txt
index e9aefed55419cd58b322f77c3243324978ac968e..69f1827426d890be98b39ef45dde9edb34cd7f48 100644
--- a/Base/Rendering/CMakeLists.txt
+++ b/Base/Rendering/CMakeLists.txt
@@ -5,6 +5,7 @@ include(imstkAddLibrary)
 imstk_add_library( Rendering
   H_FILES
     imstkVTKRenderer.h
+    imstkVTKTextureDelegate.h
     RenderDelegate/imstkVTKCubeRenderDelegate.h
     RenderDelegate/imstkVTKLineMeshRenderDelegate.h
     RenderDelegate/imstkVTKPlaneRenderDelegate.h
@@ -18,6 +19,7 @@ imstk_add_library( Rendering
 
   CPP_FILES
     imstkVTKRenderer.cpp
+    imstkVTKTextureDelegate.cpp
     RenderDelegate/imstkVTKCubeRenderDelegate.cpp
     RenderDelegate/imstkVTKLineMeshRenderDelegate.cpp
     RenderDelegate/imstkVTKPlaneRenderDelegate.cpp
diff --git a/Base/Rendering/RenderDelegate/imstkVTKRenderDelegate.cpp b/Base/Rendering/RenderDelegate/imstkVTKRenderDelegate.cpp
index 88c3d3425c247d45d9750417776b74928163300f..47ac655270810d277fabda8cc21776f6574dea01 100644
--- a/Base/Rendering/RenderDelegate/imstkVTKRenderDelegate.cpp
+++ b/Base/Rendering/RenderDelegate/imstkVTKRenderDelegate.cpp
@@ -139,6 +139,7 @@ VTKRenderDelegate::update()
 {
     // TODO : only when rigid transform applied
     this->updateActorTransform();
+    this->updateActorProperties();
 }
 
 void
@@ -159,4 +160,64 @@ VTKRenderDelegate::updateActorTransform()
     m_transform->Translate(pos[0], pos[1], pos[2]);
 }
 
+void
+VTKRenderDelegate::updateActorProperties()
+{
+    auto material = this->getGeometry()->getRenderMaterial();
+
+    if (!material || !material->m_modified)
+    {
+        return;
+    }
+
+    auto actorProperty = m_actor->GetProperty();
+
+    // Colors & Light
+    auto diffuseColor = material->m_diffuseColor;
+    auto specularColor = material->m_specularColor;
+    auto specularity = material->m_specularity;
+    actorProperty->SetDiffuseColor(diffuseColor.r, diffuseColor.g, diffuseColor.b);
+    actorProperty->SetSpecularColor(specularColor.r, specularColor.g, specularColor.b);
+    actorProperty->SetSpecularPower(specularity);
+    actorProperty->SetSpecular(1.0);
+
+    // Material state is now up to date
+    material->m_modified = false;
+
+    if (!material->m_stateModified)
+    {
+        return;
+    }
+
+    // Display mode
+    switch (material->m_displayMode)
+    {
+    case RenderMaterial::DisplayMode::WIREFRAME:
+        actorProperty->SetRepresentationToWireframe();
+        actorProperty->SetEdgeVisibility(false);
+        break;
+    case RenderMaterial::DisplayMode::POINTS:
+        actorProperty->SetRepresentationToPoints();
+        actorProperty->SetEdgeVisibility(false);
+        break;
+    case RenderMaterial::DisplayMode::WIREFRAME_SURFACE:
+        actorProperty->SetRepresentationToSurface();
+        actorProperty->SetEdgeVisibility(true);
+        break;
+    case RenderMaterial::DisplayMode::SURFACE:
+    default:
+        actorProperty->SetRepresentationToSurface();
+        actorProperty->SetEdgeVisibility(false);
+        break;
+    }
+
+    // Display properties
+    actorProperty->SetLineWidth(material->m_lineWidth);
+    actorProperty->SetPointSize(material->m_pointSize);
+    actorProperty->SetBackfaceCulling(material->m_backfaceCulling);
+
+    // Material state is now up to date
+    material->m_stateModified = false;
+}
+
 } // imstk
diff --git a/Base/Rendering/RenderDelegate/imstkVTKRenderDelegate.h b/Base/Rendering/RenderDelegate/imstkVTKRenderDelegate.h
index 759d6114a2168a4057274d97ac78fd13137b548f..11512be1499d7c575da1e7fd9eb1d5539aa68562 100644
--- a/Base/Rendering/RenderDelegate/imstkVTKRenderDelegate.h
+++ b/Base/Rendering/RenderDelegate/imstkVTKRenderDelegate.h
@@ -25,12 +25,16 @@
 #include <memory>
 
 #include "imstkGeometry.h"
+#include "imstkRenderMaterial.h"
+#include "imstkVTKTextureDelegate.h"
+#include "imstkTextureManager.h"
 
 #include "vtkSmartPointer.h"
 #include "vtkAlgorithmOutput.h"
 #include "vtkActor.h"
 #include "vtkPolyDataMapper.h"
 #include "vtkTransform.h"
+#include "vtkProperty.h"
 
 namespace imstk
 {
@@ -50,35 +54,40 @@ public:
     ~VTKRenderDelegate() = default;
 
     ///
-    /// \brief
+    /// \brief Instantiate proper render delegate
     ///
-    static std::shared_ptr<VTKRenderDelegate> make_delegate(std::shared_ptr<Geometry>geom);
+    static std::shared_ptr<VTKRenderDelegate> make_delegate(std::shared_ptr<Geometry> geom);
 
     ///
-    /// \brief
+    /// \brief Set up normals and mapper
     ///
     void setUpMapper(vtkAlgorithmOutput *source, const bool rigid);
 
     ///
-    /// \brief
+    /// \brief Return geometry to render
     ///
     virtual std::shared_ptr<Geometry> getGeometry() const = 0;
 
     ///
-    /// \brief
+    /// \brief Get VTK renderered object
     ///
     vtkSmartPointer<vtkActor> getVtkActor() const;
 
     ///
-    /// \brief
+    /// \brief Update render delegate
     ///
     virtual void update();
 
     ///
-    /// \brief
+    /// \brief Update rendere delegate transform based on the geometry shallow transform
     ///
     void updateActorTransform();
 
+    ///
+    /// \brief Update render delegate properties based on the geometry render material
+    ///
+    void updateActorProperties();
+
 protected:
     ///
     /// \brief Default constructor (protected)
diff --git a/Base/Rendering/RenderDelegate/imstkVTKSurfaceMeshRenderDelegate.cpp b/Base/Rendering/RenderDelegate/imstkVTKSurfaceMeshRenderDelegate.cpp
index 6b580e35226f2c9003e606406e8947fd8fa9e29d..2dc69671a2a396df647952cab9c1c8b3da3ab82e 100644
--- a/Base/Rendering/RenderDelegate/imstkVTKSurfaceMeshRenderDelegate.cpp
+++ b/Base/Rendering/RenderDelegate/imstkVTKSurfaceMeshRenderDelegate.cpp
@@ -36,6 +36,7 @@
 #include <vtkImageReader2.h>
 #include <vtkTexture.h>
 #include <vtkProperty.h>
+#include <vtkOpenGLPolyDataMapper.h>
 
 #include "g3log/g3log.hpp"
 
@@ -85,62 +86,27 @@ VTKSurfaceMeshRenderDelegate::VTKSurfaceMeshRenderDelegate(std::shared_ptr<Surfa
     auto source = vtkSmartPointer<vtkTrivialProducer>::New();
     source->SetOutput(polydata);
 
-    // Setup Mapper & Actor
-    this->setUpMapper(source->GetOutputPort(), false);
-    this->updateActorTransform();
-
-    // Copy textures
-    int unit = 0;
-    auto readerFactory = vtkSmartPointer<vtkImageReader2Factory>::New();
-    for (auto const &texturePair : surfaceMesh->getTextureMap())
+    if (m_geometry->getDefaultTCoords() != "")
     {
-        std::string tCoordsName = texturePair.first;
-        std::string tFileName = texturePair.second;
-
         // Convert texture coordinates
-        auto tcoords = surfaceMesh->getPointDataArray(tCoordsName);
+        auto tcoords = m_geometry->getPointDataArray(m_geometry->getDefaultTCoords());
         auto vtkTCoords = vtkSmartPointer<vtkFloatArray>::New();
         vtkTCoords->SetNumberOfComponents(2);
-        vtkTCoords->SetName(tCoordsName.c_str());
+        vtkTCoords->SetName(m_geometry->getDefaultTCoords().c_str());
+
         for (auto const tcoord : tcoords)
         {
-            double tuple[2] = {tcoord[0], tcoord[1]};
+            double tuple[2] = { tcoord[0], tcoord[1] };
             vtkTCoords->InsertNextTuple(tuple);
         }
-        polydata->GetPointData()->SetTCoords(vtkTCoords);
 
-        // Read texture image
-        auto imgReader = readerFactory->CreateImageReader2(tFileName.c_str());
-        if (!imgReader)
-        {
-            LOG(WARNING) << "Could not find reader for " << tFileName;
-            continue;
-        }
-        imgReader->SetFileName(tFileName.c_str());
-        imgReader->Update();
-        auto texture = vtkSmartPointer<vtkTexture>::New();
-        texture->SetInputConnection(imgReader->GetOutputPort());
-        texture->SetBlendingMode(vtkTexture::VTK_TEXTURE_BLENDING_MODE_ADD);
-        /* /!\ VTKTextureWrapMode not yet supported in VTK 7
-         * See here for some work that needs to be imported back to upstream:
-         * https://gitlab.kitware.com/iMSTK/vtk/commit/62a7ecd8a5f54e243c26960de22d5d1d23ef932b
-         *
-        texture->SetWrapMode(vtkTexture::VTKTextureWrapMode::ClampToBorder);
-
-         * /!\ MultiTextureAttribute not yet supported in VTK 7
-         * See here for some work that needs to be imported back to upstream:
-         * https://gitlab.kitware.com/iMSTK/vtk/commit/ae373026755db42b6fdce5093109ef1a39a76340
-         *
-        // Link texture unit to texture attribute
-        m_mapper->MapDataArrayToMultiTextureAttribute(unit, tCoordsName.c_str(),
-                                                      vtkDataObject::FIELD_ASSOCIATION_POINTS);
-        */
-
-        // Set texture
-        m_actor->GetProperty()->SetTexture(unit, texture);
-
-        unit++;
+        polydata->GetPointData()->SetTCoords(vtkTCoords);
     }
+
+    // Setup Mapper & Actor
+    this->setUpMapper(source->GetOutputPort(), false);
+    this->updateActorTransform();
+    this->updateActorProperties();
 }
 
 void
@@ -158,4 +124,46 @@ VTKSurfaceMeshRenderDelegate::getGeometry() const
     return m_geometry;
 }
 
+void
+VTKSurfaceMeshRenderDelegate::initializeTextures(TextureManager<VTKTextureDelegate>& textureManager)
+{
+    auto material = m_geometry->getRenderMaterial();
+    if (material == nullptr)
+    {
+        return;
+    }
+
+    // Go through all of the textures
+    for (int unit = 0; unit < Texture::Type::NONE; unit++)
+    {
+        // Get imstk texture
+        auto texture = material->getTexture((Texture::Type)unit);
+        if (texture->getPath() == "")
+        {
+            continue;
+        }
+
+        // Get vtk texture
+        auto textureDelegate = textureManager.getTextureDelegate(texture);
+
+        /* /!\ VTKTextureWrapMode not yet supported in VTK 7
+        * See here for some work that needs to be imported back to upstream:
+        * https://gitlab.kitware.com/iMSTK/vtk/commit/62a7ecd8a5f54e243c26960de22d5d1d23ef932b
+        *
+        texture->SetWrapMode(vtkTexture::VTKTextureWrapMode::ClampToBorder);
+
+        * /!\ MultiTextureAttribute not yet supported in VTK 7
+        * See here for some work that needs to be imported back to upstream:
+        * https://gitlab.kitware.com/iMSTK/vtk/commit/ae373026755db42b6fdce5093109ef1a39a76340
+        *
+        // Link texture unit to texture attribute
+        m_mapper->MapDataArrayToMultiTextureAttribute(unit, tCoordsName.c_str(),
+                                                    vtkDataObject::FIELD_ASSOCIATION_POINTS);
+        */
+
+        // Set texture
+        m_actor->GetProperty()->SetTexture(unit, textureDelegate->getTexture());
+    }
+}
+
 } // imstk
diff --git a/Base/Rendering/RenderDelegate/imstkVTKSurfaceMeshRenderDelegate.h b/Base/Rendering/RenderDelegate/imstkVTKSurfaceMeshRenderDelegate.h
index d7d64729c6e0231d1d85f35f80a392d9c314c3b2..fd4e96b6e03a4e7c184d5f2b89c5858759b69364 100644
--- a/Base/Rendering/RenderDelegate/imstkVTKSurfaceMeshRenderDelegate.h
+++ b/Base/Rendering/RenderDelegate/imstkVTKSurfaceMeshRenderDelegate.h
@@ -25,6 +25,8 @@
 #include <memory>
 
 #include "imstkVTKRenderDelegate.h"
+#include "imstkVTKTextureDelegate.h"
+#include "imstkTextureManager.h"
 
 class vtkDoubleArray;
 
@@ -61,11 +63,15 @@ public:
     ///
     std::shared_ptr<Geometry> getGeometry() const override;
 
+    ///
+    /// \brief Initialize textures
+    ///
+    void initializeTextures(TextureManager<VTKTextureDelegate>& textureManager);
+
 protected:
 
     std::shared_ptr<SurfaceMesh> m_geometry; ///> Geometry to render
     vtkSmartPointer<vtkDoubleArray> m_mappedVertexArray; ///> Mapped array of vertices
-
 };
 
 }
diff --git a/Base/Rendering/imstkVTKRenderer.cpp b/Base/Rendering/imstkVTKRenderer.cpp
index ec982c2bef7f87df54fff3f87c50b68c9951942a..02a6ce6fb41bbc3d26254ffed96b0ce36d44b3cf 100644
--- a/Base/Rendering/imstkVTKRenderer.cpp
+++ b/Base/Rendering/imstkVTKRenderer.cpp
@@ -24,6 +24,7 @@
 #include "imstkScene.h"
 #include "imstkCamera.h"
 #include "imstkVTKRenderDelegate.h"
+#include "imstkVTKSurfaceMeshRenderDelegate.h"
 
 #include "vtkLightActor.h"
 #include "vtkCameraActor.h"
@@ -60,6 +61,16 @@ VTKRenderer::VTKRenderer(std::shared_ptr<Scene> scene)
         m_objectVtkActors.push_back( delegate->getVtkActor() );
     }
 
+    // Initialize textures for surface mesh render delegates
+    for ( const auto& renderDelegate : m_renderDelegates )
+    {
+        auto smRenderDelegate = std::dynamic_pointer_cast<VTKSurfaceMeshRenderDelegate>(renderDelegate);
+        if (smRenderDelegate)
+        {
+            smRenderDelegate->initializeTextures(m_textureManager);
+        }
+    }
+
     // Lights and light actors
     for ( const auto& light : scene->getLights() )
     {
diff --git a/Base/Rendering/imstkVTKRenderer.h b/Base/Rendering/imstkVTKRenderer.h
index ff89867bc221e633efbf86194a36378e919a1d18..9e8b3bee445f955ff117a8aa98bebb4241e2e1db 100644
--- a/Base/Rendering/imstkVTKRenderer.h
+++ b/Base/Rendering/imstkVTKRenderer.h
@@ -26,6 +26,8 @@
 #include <vector>
 
 #include "imstkMath.h"
+#include "imstkTextureManager.h"
+#include "imstkVTKTextureDelegate.h"
 
 #include "vtkSmartPointer.h"
 #include "vtkRenderer.h"
@@ -115,6 +117,8 @@ protected:
     std::vector<std::shared_ptr<VTKRenderDelegate>> m_renderDelegates;
 
     Mode m_currentMode = Mode::EMPTY;
+
+    TextureManager<VTKTextureDelegate> m_textureManager;
 };
 }
 
diff --git a/Base/Rendering/imstkVTKTextureDelegate.cpp b/Base/Rendering/imstkVTKTextureDelegate.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..589512f8f846b82b8c1553f1d8f6e60eea54f999
--- /dev/null
+++ b/Base/Rendering/imstkVTKTextureDelegate.cpp
@@ -0,0 +1,57 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+=========================================================================*/
+
+#include "imstkVTKTextureDelegate.h"
+#include "g3log/g3log.hpp"
+
+namespace imstk
+{
+
+vtkSmartPointer<vtkTexture>
+VTKTextureDelegate::getTexture() const
+{
+    return m_sourceTexture;
+}
+
+void
+VTKTextureDelegate::loadTexture(std::shared_ptr<Texture> texture)
+{
+    std::string tFileName = texture->getPath();
+
+    auto readerFactory = vtkSmartPointer<vtkImageReader2Factory>::New();
+
+    // Read texture image
+    auto imgReader = readerFactory->CreateImageReader2(texture->getPath().c_str());
+    if (!imgReader)
+    {
+        LOG(WARNING) << "VTKTextureDelegate::loadTexture error: could not find reader for "
+                     << tFileName;
+        return;
+    }
+
+    imgReader->SetFileName(tFileName.c_str());
+    imgReader->Update();
+    m_sourceTexture = vtkSmartPointer<vtkTexture>::New();
+    m_sourceTexture->SetInputConnection(imgReader->GetOutputPort());
+    m_sourceTexture->SetBlendingMode(vtkTexture::VTK_TEXTURE_BLENDING_MODE_ADD);
+}
+
+}
diff --git a/Base/Rendering/imstkVTKTextureDelegate.h b/Base/Rendering/imstkVTKTextureDelegate.h
new file mode 100644
index 0000000000000000000000000000000000000000..05bbfb29c9ffc324babdec3f335388b741251018
--- /dev/null
+++ b/Base/Rendering/imstkVTKTextureDelegate.h
@@ -0,0 +1,77 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+=========================================================================*/
+
+#ifndef imstkVTKTexture_h
+#define imstkVTKTexture_h
+
+#include "imstkTexture.h"
+#include "imstkTextureDelegate.h"
+#include "imstkTextureManager.h"
+
+#include <vtkTexture.h>
+#include <vtkSmartPointer.h>
+#include <vtkImageReader2Factory.h>
+#include <vtkImageReader2.h>
+
+#include <string>
+#include <memory>
+
+namespace imstk
+{
+
+class VTKTextureDelegate;
+
+///
+/// \class VTKTexture
+///
+/// \brief VTK texture implementation.
+///
+class VTKTextureDelegate : TextureDelegate
+{
+public:
+    ///
+    /// \brief Default constructor
+    ///
+    VTKTextureDelegate() {}
+
+protected:
+    template<class T> friend class TextureManager;
+    friend class VTKSurfaceMeshRenderDelegate;
+
+    ///
+    /// \brief Gets the VTK texture
+    ///
+    /// \returns VTK texture
+    ///
+    vtkSmartPointer<vtkTexture> getTexture() const;
+
+    ///
+    /// \brief Implementation of texture loading
+    ///
+    virtual void loadTexture(std::shared_ptr<Texture> texture);
+
+
+    vtkSmartPointer<vtkTexture> m_sourceTexture;    ///< VTK texture
+};
+
+}
+
+#endif
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 53d4085603874d009836bd1710d1ae6d42f1879f..86f10615394cbffdde9ad97916db75c284c9afd3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -200,6 +200,7 @@ add_subdirectory(Base/Collision)
 add_subdirectory(Base/Scene)
 add_subdirectory(Base/SimulationManager)
 add_subdirectory(Base/Constraint)
+add_subdirectory(Base/Materials)
 
 #--------------------------------------------------------------------------
 # Export Targets
diff --git a/Data/textured_organs/ReadMe.txt.sha512 b/Data/textured_organs/ReadMe.txt.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..e8129b4d15a0e1f3d7a06eed21adffd0bd686efc
--- /dev/null
+++ b/Data/textured_organs/ReadMe.txt.sha512
@@ -0,0 +1 @@
+657fcea4ae912bd177f88f801ad0160387027bec22949d1cfa92820f518be557b02f8649600170bd925dca6602f939bb7cc98876faa5119ea1225bcac7d48042
\ No newline at end of file
diff --git a/Data/textured_organs/heart.blend.sha512 b/Data/textured_organs/heart.blend.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..69d3a403d7ae6ab44e090f4cdc33e855fb6711af
--- /dev/null
+++ b/Data/textured_organs/heart.blend.sha512
@@ -0,0 +1 @@
+8488e382e19acb15f16e2cede096f78e35336a213d3f0b92b9f877592fbdbe911f8764db41d8167fb5f62b6585772dbb9db3f2f4fd4884eda0ddd07b4d332fd4
\ No newline at end of file
diff --git a/Data/textured_organs/heart.dae.sha512 b/Data/textured_organs/heart.dae.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..8c65506660b5c445f0d835900960a1ef0738abba
--- /dev/null
+++ b/Data/textured_organs/heart.dae.sha512
@@ -0,0 +1 @@
+b3362367b1e9089f683abe753f044e2bf9fc7d350b80519b2a9cc6053b0dd6a7d12fc948515dfccaba4e03a9dfe1479e79baaeb66a0547c9968e4fb2b82d8f8b
\ No newline at end of file
diff --git a/Data/textured_organs/heart.mtl.sha512 b/Data/textured_organs/heart.mtl.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..3bf7a0f7779e8d613a8386ca34d587f373c3a3ca
--- /dev/null
+++ b/Data/textured_organs/heart.mtl.sha512
@@ -0,0 +1 @@
+a0eed9c0600f70a364beb6de0b06c2bf4f4d647070c4e8f55d452571dbfb57004d19d4352ebd04ed20d1647590872229dbb7b78c209b5103ae33cc2228efdecb
\ No newline at end of file
diff --git a/Data/textured_organs/heart.obj.sha512 b/Data/textured_organs/heart.obj.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..744851896d2dca401505e531e95ce32da895ab06
--- /dev/null
+++ b/Data/textured_organs/heart.obj.sha512
@@ -0,0 +1 @@
+37513026be259aac1a002a958de3aaec66022e90491e57140835ce8bbf693f6f3f1d7deac1589b9804e92b56c53ef3576ad6de8bb42e5fb74bc6ae704b0f032f
\ No newline at end of file
diff --git a/Data/textured_organs/lung.blend.sha512 b/Data/textured_organs/lung.blend.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..c99ed93f4ab468288f2309d4ec1757b0f59b6107
--- /dev/null
+++ b/Data/textured_organs/lung.blend.sha512
@@ -0,0 +1 @@
+809d7c18ab9288fdd8930673760915fdc9f7e7ba1e22ef27c86d9052df141a175f299c2b032ddf4c2540d9a0f2450d8de4e74288b44cd8f1a2e8a1c66fffba12
\ No newline at end of file
diff --git a/Data/textured_organs/lung.dae.sha512 b/Data/textured_organs/lung.dae.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..263e70d63b4505e183f5eab7404d7bad40ea353b
--- /dev/null
+++ b/Data/textured_organs/lung.dae.sha512
@@ -0,0 +1 @@
+73ef35e73145de4eaef7fba89009fab8e85da5d4a9cba9dd22854ba6ebddf73abdb9d72f4d4fa87ba0ca2b718ec61786030791a5312cfe67821662bde6cc4cb5
\ No newline at end of file
diff --git a/Data/textured_organs/lung.mtl.sha512 b/Data/textured_organs/lung.mtl.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..ffcbabc2830da4ea9b4b4735438a10a324161f98
--- /dev/null
+++ b/Data/textured_organs/lung.mtl.sha512
@@ -0,0 +1 @@
+dce5c74679821632c724ea91ba45e00afa099b0d261be4dcab59b3667e3ecc66018fafed4c08d54ead3d7f27a1af9b06796767247ca708601ba7937d2c9dcd0f
\ No newline at end of file
diff --git a/Data/textured_organs/lung.obj.sha512 b/Data/textured_organs/lung.obj.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..986de2722fb9dd1562225d5279b40f77186a55b2
--- /dev/null
+++ b/Data/textured_organs/lung.obj.sha512
@@ -0,0 +1 @@
+c14ac761ebb611e350d45c28acd6c476d813a06ec5120d25a48bf5313488c9619d0942639a81bb2c79036d618e7b682a3203737d850867c1abf1ba6b2629bf8e
\ No newline at end of file
diff --git a/Data/textured_organs/texture_set_1/diffuse.png.sha512 b/Data/textured_organs/texture_set_1/diffuse.png.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..45315fc5c7738cf4471801982d657dbe1f97f91c
--- /dev/null
+++ b/Data/textured_organs/texture_set_1/diffuse.png.sha512
@@ -0,0 +1 @@
+9627ec6abb62960aa9a73e70c391c19a83cc628d67b7608cfe8f18ee1de34a9daec05dba6f02d8814e8af05d42b891e1da1cd2d21601101b71a0be4b6aeaaae9
\ No newline at end of file
diff --git a/Data/textured_organs/texture_set_1/emissive.png.sha512 b/Data/textured_organs/texture_set_1/emissive.png.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..968e8b55776901250652b6de601a91ccd8086828
--- /dev/null
+++ b/Data/textured_organs/texture_set_1/emissive.png.sha512
@@ -0,0 +1 @@
+c15540d15d7c8110a779df8de34472a95f0b3b8346ff46a8681fb0756d81ca5dd2698db30f4d96400484f9da2805cb101ca47fdcd02bf8afdbfb475304916e14
\ No newline at end of file
diff --git a/Data/textured_organs/texture_set_1/height.png.sha512 b/Data/textured_organs/texture_set_1/height.png.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..19d0bbd22542dd5cf26057d912a6704b32fe1b6b
--- /dev/null
+++ b/Data/textured_organs/texture_set_1/height.png.sha512
@@ -0,0 +1 @@
+3387abdea400d8c2ee0bc8ed0e095f3fdeb16cb2a68508fced19a1b1840b0e1e0387a944c823240828f55eccd541f179654996169e7e07808c0f87650316778f
\ No newline at end of file
diff --git a/Data/textured_organs/texture_set_1/normal.png.sha512 b/Data/textured_organs/texture_set_1/normal.png.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..2c69df972212f3215717b992637743164dfd70ed
--- /dev/null
+++ b/Data/textured_organs/texture_set_1/normal.png.sha512
@@ -0,0 +1 @@
+40ad7af9b37331ae07041a56ed105ff38c80d071b8d1b6b5a4d118fe010c8a41e2ffae5cf3cbe4f079b1e36e6dd7bffc1336ebd8c76956ebca13c30752d994e2
\ No newline at end of file
diff --git a/Data/textured_organs/texture_set_1/preview.jpg.sha512 b/Data/textured_organs/texture_set_1/preview.jpg.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..ee69c59554c6e7e894688425aaf866a339971025
--- /dev/null
+++ b/Data/textured_organs/texture_set_1/preview.jpg.sha512
@@ -0,0 +1 @@
+1b3d670b0000743ec6e506c6b51a0dd16683f617f0a239ac691780f812e58cdf58504981d7cbcbb8e9579ee9bed2e9e5eab02778c9fdfc57b01db1e173744805
\ No newline at end of file
diff --git a/Data/textured_organs/texture_set_1/readme.txt.sha512 b/Data/textured_organs/texture_set_1/readme.txt.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..75f405429faa38ee27b06edbcc3127591d84d1ab
--- /dev/null
+++ b/Data/textured_organs/texture_set_1/readme.txt.sha512
@@ -0,0 +1 @@
+24b575090c85109cf935e1f764076e9e439d532af0b82d7f181b97a46157f6b565c658790326a9511b75cbd802c81cae6a6d172f7414f31c76aa2f875c27fbc6
\ No newline at end of file
diff --git a/Data/textured_organs/texture_set_1/specular.png.sha512 b/Data/textured_organs/texture_set_1/specular.png.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..3bc224171cb8491a98bbc33ca8d38332058e3da9
--- /dev/null
+++ b/Data/textured_organs/texture_set_1/specular.png.sha512
@@ -0,0 +1 @@
+7a947152c151073531a62d2b792c6275748a59f17123c471aa4498125ce9f0914ba773c5604594d6ce1be2f5dabdd3612cf8ea4a7e790ff55dfb302d865367a7
\ No newline at end of file
diff --git a/Data/textured_organs/texture_set_2/cavity.png.sha512 b/Data/textured_organs/texture_set_2/cavity.png.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..c6900fd87c89259011133cb34cc40d7e7132e03a
--- /dev/null
+++ b/Data/textured_organs/texture_set_2/cavity.png.sha512
@@ -0,0 +1 @@
+3bc600838e90b9ba16db106c182fd1f03e52f271300ab7583931ba9a71d27b1b2f92146b8c2d6067622b6327290ed57c923eb3cbb27d8f82763e6b89d0c0eeac
\ No newline at end of file
diff --git a/Data/textured_organs/texture_set_2/diffuse.png.sha512 b/Data/textured_organs/texture_set_2/diffuse.png.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..ab5dcb0f9434085ba792e6d14f8b15da8f32f954
--- /dev/null
+++ b/Data/textured_organs/texture_set_2/diffuse.png.sha512
@@ -0,0 +1 @@
+0248a3deac63bcd0410dd87fa438de69c668ccc6d0c49ddb86720eee8651c0f9b0a9f0de0a709efad2546393aa5c992399835be7545c78448cc1848345de0ee2
\ No newline at end of file
diff --git a/Data/textured_organs/texture_set_2/normal.png.sha512 b/Data/textured_organs/texture_set_2/normal.png.sha512
new file mode 100644
index 0000000000000000000000000000000000000000..cf0f98ed1612584349a044453d622f019b395127
--- /dev/null
+++ b/Data/textured_organs/texture_set_2/normal.png.sha512
@@ -0,0 +1 @@
+6ed533bd1cfbf9f23014daee41629132d6c4bc0c176a8964ccdf31dc3e15dc6f428d42216331080c98670bde99e1796b219f7dcd5427b72319ad0b5860788bfd
\ No newline at end of file
diff --git a/Examples/Sandbox/CMakeLists.txt b/Examples/Sandbox/CMakeLists.txt
index f64cd74a5867911d5e1e31c9959bd9c5c2ea2879..79c5e3b9292eaa55350c5c65eb72b6fd9c821152 100644
--- a/Examples/Sandbox/CMakeLists.txt
+++ b/Examples/Sandbox/CMakeLists.txt
@@ -47,6 +47,9 @@ list(APPEND FILE_LIST
     oneTet/,REGEX:.*
     spheres/,REGEX:.*
     spring/,REGEX:.*
-    tetBeads/,REGEX:.*)
+    tetBeads/,REGEX:.*
+    textured_organs/,REGEX:.*
+    textured_organs/texture_set_1/,REGEX:.*
+    textured_organs/texture_set_2/,REGEX:.*)
 
 imstk_add_data(${PROJECT_NAME} ${FILE_LIST})
diff --git a/Examples/Sandbox/main.cpp b/Examples/Sandbox/main.cpp
index 863d1cda7a6349e0ffa72806d707032344bf6819..6398d62a66c87510a04c257ee8022748f971afd0 100644
--- a/Examples/Sandbox/main.cpp
+++ b/Examples/Sandbox/main.cpp
@@ -99,7 +99,6 @@
 
 using namespace imstk;
 
-void testMultiTextures();
 void testMeshCCD();
 void testPenaltyRigidCollision();
 void testTwoFalcons();
@@ -115,7 +114,6 @@ void testOneToOneNodalMap();
 void testExtractSurfaceMesh();
 void testSurfaceMeshOptimizer();
 void testDeformableBody();
-void testVTKTexture();
 void testMultiObjectWithTextures();
 void testTwoOmnis();
 void testVectorPlotters();
@@ -140,9 +138,7 @@ int main()
     /*------------------
     Test rendering
     ------------------*/
-    //testMultiTextures();
-    //testVTKTexture();
-    //testMultiObjectWithTextures();
+    testMultiObjectWithTextures();
     //testViewer();
     //testScreenShotUtility();
     //testCapsule();
@@ -307,81 +303,6 @@ void testMshAndVegaIO()
     sdk->startSimulation(true);
 }
 
-void testVTKTexture()
-{
-    // Parse command line arguments
-
-    std::string inputFilename = iMSTK_DATA_ROOT"/ETI/resources/OperatingRoom/cloth.obj";
-    std::string texturename = iMSTK_DATA_ROOT"/ETI/resources/TextureOR/cloth.jpg";
-
-    std::string inputFilename1 = iMSTK_DATA_ROOT"/ETI/resources/OperatingRoom/bed1.obj";
-    std::string texturename1 = iMSTK_DATA_ROOT"/ETI/resources/TextureOR/bed-1.jpg";
-
-    vtkSmartPointer<vtkOBJReader> reader =
-        vtkSmartPointer<vtkOBJReader>::New();
-    reader->SetFileName(inputFilename.c_str());
-    reader->Update();
-
-
-    vtkSmartPointer<vtkOBJReader> reader1 =
-        vtkSmartPointer<vtkOBJReader>::New();
-    reader1->SetFileName(inputFilename1.c_str());
-    reader1->Update();
-
-    // Visualize
-    vtkSmartPointer<vtkPolyDataMapper> mapper =
-        vtkSmartPointer<vtkPolyDataMapper>::New();
-    mapper->SetInputConnection(reader->GetOutputPort());
-
-    vtkSmartPointer<vtkPolyDataMapper> mapper1 =
-        vtkSmartPointer<vtkPolyDataMapper>::New();
-    mapper1->SetInputConnection(reader1->GetOutputPort());
-
-    vtkSmartPointer<vtkActor> actor =
-        vtkSmartPointer<vtkActor>::New();
-    actor->SetMapper(mapper);
-
-    vtkSmartPointer<vtkActor> actor1 =
-        vtkSmartPointer<vtkActor>::New();
-    actor1->SetMapper(mapper1);
-
-    vtkSmartPointer<vtkJPEGReader> jpgReader =
-        vtkSmartPointer<vtkJPEGReader>::New();
-    jpgReader->SetFileName(texturename.c_str());
-    jpgReader->Update();
-    vtkSmartPointer<vtkTexture> texture = vtkSmartPointer<vtkTexture>::New();
-    texture->SetInputConnection(jpgReader->GetOutputPort());
-    texture->InterpolateOn();
-    actor->SetTexture(texture);
-
-    vtkSmartPointer<vtkJPEGReader> jpgReader1 =
-        vtkSmartPointer<vtkJPEGReader>::New();
-    jpgReader1->SetFileName(texturename1.c_str());
-    jpgReader1->Update();
-    vtkSmartPointer<vtkTexture> texture1 = vtkSmartPointer<vtkTexture>::New();
-    texture1->SetInputConnection(jpgReader1->GetOutputPort());
-    texture1->InterpolateOn();
-    actor1->SetTexture(texture1);
-
-    vtkSmartPointer<vtkRenderer> renderer =
-        vtkSmartPointer<vtkRenderer>::New();
-    renderer->AddActor(actor);
-    renderer->AddActor(actor1);
-    renderer->SetBackground(.3, .6, .3); // Background color green
-
-    vtkSmartPointer<vtkRenderWindow> renderWindow =
-        vtkSmartPointer<vtkRenderWindow>::New();
-    renderWindow->AddRenderer(renderer);
-
-    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
-        vtkSmartPointer<vtkRenderWindowInteractor>::New();
-    renderWindowInteractor->SetRenderWindow(renderWindow);
-
-    renderWindowInteractor->Start();
-
-}
-
-
 void testMultiObjectWithTextures()
 {
     // SDK and Scene
@@ -389,55 +310,47 @@ void testMultiObjectWithTextures()
     auto scene = sdk->createNewScene("multiObjectWithTexturesTest");
 
     // Read surface mesh
-    auto objMesh = imstk::MeshIO::read(iMSTK_DATA_ROOT"/ETI/resources/OperatingRoom/cloth.obj");
+    auto objMesh = imstk::MeshIO::read(iMSTK_DATA_ROOT"/textured_organs/heart.obj");
     auto surfaceMesh = std::dynamic_pointer_cast<imstk::SurfaceMesh>(objMesh);
-    surfaceMesh->addTexture(iMSTK_DATA_ROOT"/ETI/resources/TextureOR/cloth.jpg");
+    surfaceMesh->setPosition(-8, 0, 0);
+
+    // Read and setup texture/material
+    auto texture = std::make_shared<Texture>(iMSTK_DATA_ROOT"/textured_organs/texture_set_1/diffuse.png");
+    auto material = std::make_shared<RenderMaterial>();
+    material->addTexture(texture);
+    surfaceMesh->setRenderMaterial(material);
 
     // Create object and add to scene
     auto object = std::make_shared<imstk::VisualObject>("meshObject");
-    object->setVisualGeometry(surfaceMesh); // change to any mesh created above
+    object->setVisualGeometry(surfaceMesh);
     scene->addSceneObject(object);
 
+    // Second object
     bool secondObject = true;
     bool secondObjectTexture = true;
-
-    if (secondObject){
+    if (secondObject)
+    {
         // Read surface mesh1
-        auto objMesh1 = imstk::MeshIO::read(iMSTK_DATA_ROOT"/ETI/resources/OperatingRoom/bed1.obj");
+        auto objMesh1 = imstk::MeshIO::read(iMSTK_DATA_ROOT"/textured_organs/heart.obj");
         auto surfaceMesh1 = std::dynamic_pointer_cast<imstk::SurfaceMesh>(objMesh1);
+        surfaceMesh1->setPosition(0, 0, 0);
+
+        // Read and setup texture/material
         if (secondObjectTexture)
-            surfaceMesh1->addTexture(iMSTK_DATA_ROOT"/ETI/resources/TextureOR/bed-1.jpg");
+        {
+            auto texture1 = std::make_shared<Texture>(iMSTK_DATA_ROOT"/textured_organs/texture_set_2/diffuse.png");
+            auto material1 = std::make_shared<RenderMaterial>();
+            material1->addTexture(texture1);
+            material1->setDisplayMode(RenderMaterial::DisplayMode::WIREFRAME_SURFACE);
+            surfaceMesh1->setRenderMaterial(material1);
+        }
 
         // Create object and add to scene
         auto object1 = std::make_shared<imstk::VisualObject>("meshObject1");
         object1->setVisualGeometry(surfaceMesh1); // change to any mesh created above
         scene->addSceneObject(object1);
     }
-    // Run
-    sdk->setCurrentScene(scene);
-    sdk->startSimulation(true);
-}
-
-
-void testMultiTextures()
-{
-    // SDK and Scene
-    auto sdk = std::make_shared<SimulationManager>();
-    auto scene = sdk->createNewScene("multitexturestest");
-
-    // Read surface mesh
-    auto objMesh = imstk::MeshIO::read(iMSTK_DATA_ROOT"/textures/Fox skull OBJ/fox_skull.obj");
-    auto surfaceMesh = std::dynamic_pointer_cast<imstk::SurfaceMesh>(objMesh);
-    surfaceMesh->addTexture(iMSTK_DATA_ROOT"/textures/Fox skull OBJ/fox_skull_0.jpg",
-        "material_0");
-    surfaceMesh->addTexture(iMSTK_DATA_ROOT"/textures/Fox skull OBJ/fox_skull_1.jpg",
-        "material_1");
-
-    // Create object and add to scene
-    auto object = std::make_shared<imstk::VisualObject>("meshObject");
-    object->setVisualGeometry(surfaceMesh); // change to any mesh created above
-    scene->addSceneObject(object);
-
+    
     // Run
     sdk->setCurrentScene(scene);
     sdk->startSimulation(true);
@@ -2584,5 +2497,4 @@ void testVirtualCoupling()
     //Run
     sdk->setCurrentScene(scene);
     sdk->startSimulation(false);
-
 }