diff --git a/applications/gui/src/camera.cpp b/applications/gui/src/camera.cpp
index db1ee404deba24d35168e828aca96ebe34190394..48858d57fb74273d712a0c2e0ef48d87197e6d31 100644
--- a/applications/gui/src/camera.cpp
+++ b/applications/gui/src/camera.cpp
@@ -46,7 +46,8 @@ static Eigen::Affine3d create_rotation_matrix(float ax, float ay, float az) {
 	return rz * rx * ry;
 }
 
-ftl::gui::Camera::Camera(ftl::gui::Screen *screen, int fsmask, int fid, ftl::codecs::Channel c) : screen_(screen), fsmask_(fsmask), fid_(fid), channel_(c),channels_(0u) {
+ftl::gui::Camera::Camera(ftl::gui::Screen *screen, int fsmask, int fid, ftl::codecs::Channel c)
+		: screen_(screen), fsmask_(fsmask), fid_(fid), texture1_(GLTexture::Type::BGRA), texture2_(GLTexture::Type::BGRA), depth1_(GLTexture::Type::Float), channel_(c),channels_(0u) {
 	eye_ = Eigen::Vector3d(0.0f, 0.0f, 0.0f);
 	neye_ = Eigen::Vector4d(0.0f, 0.0f, 0.0f, 0.0f);
 	rotmat_.setIdentity();
@@ -295,17 +296,21 @@ void ftl::gui::Camera::_draw(std::vector<ftl::rgbd::FrameSet*> &fss) {
 
 	// Make sure an OpenGL pixel buffer exists
 	texture1_.make(state_.getLeft().width, state_.getLeft().height);
+	depth1_.make(state_.getLeft().width, state_.getLeft().height);
 	if (isStereo()) texture2_.make(state_.getRight().width, state_.getRight().height);
 
 	// Map the GL pixel buffer to a GpuMat
 	frame_.create<cv::cuda::GpuMat>(Channel::Colour) = texture1_.map(renderer_->getCUDAStream());
+	frame_.create<cv::cuda::GpuMat>(Channel::Depth) = depth1_.map(renderer_->getCUDAStream());
+	frame_.createTexture<float>(Channel::Depth);
 	if (isStereo()) frame_.create<cv::cuda::GpuMat>(Channel::Colour2) = texture2_.map((renderer2_) ? renderer2_->getCUDAStream() : 0);
 
+	// TODO: Remove;
 	overlay_.create(state_.getLeft().height, state_.getLeft().width, CV_8UC4);
-	frame_.create<cv::Mat>(Channel::Overlay) = overlay_;
+	//frame_.create<cv::Mat>(Channel::Overlay) = overlay_;
 
-	overlay_.setTo(cv::Scalar(0,0,0,0));
-	bool enable_overlay = overlayer_->value("enabled", false);
+	//overlay_.setTo(cv::Scalar(0,0,0,0));
+	//bool enable_overlay = overlayer_->value("enabled", false);
 
 	{
 		FTL_Profile("Render",0.034);
@@ -325,11 +330,11 @@ void ftl::gui::Camera::_draw(std::vector<ftl::rgbd::FrameSet*> &fss) {
 				renderer_->submit(fs, ftl::codecs::Channels<0>(Channel::Colour), transforms_[fs->id]);
 				if (isStereo()) renderer2_->submit(fs, ftl::codecs::Channels<0>(Channel::Colour), transforms_[fs->id]);
 
-				if (enable_overlay) {
+				//if (enable_overlay) {
 					// Generate and upload an overlay image.
-					overlayer_->apply(*fs, overlay_, state_);
-					frame_.upload(Channel::Overlay, renderer_->getCUDAStream());
-				}
+				//	overlayer_->apply(*fs, overlay_, state_);
+				//	frame_.upload(Channel::Overlay, renderer_->getCUDAStream());
+				//}
 			}
 
 			if (channel_ != Channel::Left && channel_ != Channel::Right && channel_ != Channel::None) {
@@ -339,9 +344,9 @@ void ftl::gui::Camera::_draw(std::vector<ftl::rgbd::FrameSet*> &fss) {
 				}
 			}
 
-			if (enable_overlay) {
-				renderer_->blend(Channel::Overlay);
-			}
+			//if (enable_overlay) {
+			//	renderer_->blend(Channel::Overlay);
+			//}
 
 			renderer_->end();
 			if (isStereo()) renderer2_->end();
@@ -364,8 +369,12 @@ void ftl::gui::Camera::_draw(std::vector<ftl::rgbd::FrameSet*> &fss) {
 
 	channels_ = frame_.getChannels();
 
+	// Normalize depth map
+	frame_.get<cv::cuda::GpuMat>(Channel::Depth).convertTo(frame_.get<cv::cuda::GpuMat>(Channel::Depth), CV_32F, 1.0/8.0);
+
 	// Unmap GL buffer from CUDA and finish updating GL texture
 	texture1_.unmap(renderer_->getCUDAStream());
+	depth1_.unmap(renderer_->getCUDAStream());
 	if (isStereo()) texture2_.unmap(renderer2_->getCUDAStream());
 
 	width_ = texture1_.width();
@@ -582,6 +591,17 @@ void ftl::gui::Camera::active(bool a) {
 	}
 }
 
+void ftl::gui::Camera::drawOverlay(const Eigen::Vector2f &s) {
+	if (!framesets_) return;
+	//UNIQUE_LOCK(mutex_,lk);
+	for (auto *fs : *framesets_) {
+		if (!usesFrameset(fs->id)) continue;
+
+		// Generate and upload an overlay image.
+		overlayer_->draw(*fs, state_, s);
+	}
+}
+
 const void ftl::gui::Camera::captureFrame() {
 	float now = (float)glfwGetTime();
 	if (!screen_->isVR() && (now - ftime_) < 0.04f) return;
diff --git a/applications/gui/src/camera.hpp b/applications/gui/src/camera.hpp
index c64bdfcaa12596460b63c00d303f7df1cab8ada9..2072d60f73e8e7580879e0fae97057c853288640 100644
--- a/applications/gui/src/camera.hpp
+++ b/applications/gui/src/camera.hpp
@@ -76,8 +76,13 @@ class Camera {
 
 	void draw(std::vector<ftl::rgbd::FrameSet*> &fss);
 
+	void drawOverlay(const Eigen::Vector2f &);
+
 	inline int64_t getFrameTimeMS() const { return int64_t(delta_ * 1000.0f); }
 
+	const ftl::rgbd::Camera &getIntrinsics() const { return state_.getLeft(); }
+	const Eigen::Matrix4d &getPose() const { return state_.getPose(); }
+
 	/**
 	 * @internal. Used to inform the camera if it is the active camera or not.
 	 */
@@ -86,6 +91,7 @@ class Camera {
 	const void captureFrame();
 	const GLTexture &getLeft() const { return texture1_; }
 	const GLTexture &getRight() const { return texture2_; }
+	const GLTexture &getDepth() const { return depth1_; }
 
 	void snapshot(const std::string &filename);
 
@@ -116,9 +122,9 @@ class Camera {
 	int width_;
 	int height_;
 
-	GLTexture thumb_;
 	GLTexture texture1_; // first channel (always left at the moment)
 	GLTexture texture2_; // second channel ("right")
+	GLTexture depth1_;
 
 	ftl::gui::PoseWindow *posewin_;
 	//nlohmann::json meta_;
diff --git a/applications/gui/src/gltexture.cpp b/applications/gui/src/gltexture.cpp
index ec83997a3ba72ad744e4fd429ccc89151f24247a..5084c947c8b67fe4942c663c77fff12ffd752b68 100644
--- a/applications/gui/src/gltexture.cpp
+++ b/applications/gui/src/gltexture.cpp
@@ -10,13 +10,14 @@
 
 using ftl::gui::GLTexture;
 
-GLTexture::GLTexture() {
+GLTexture::GLTexture(GLTexture::Type type) {
 	glid_ = std::numeric_limits<unsigned int>::max();
 	glbuf_ = std::numeric_limits<unsigned int>::max();
 	cuda_res_ = nullptr;
 	width_ = 0;
 	height_ = 0;
 	changed_ = true;
+	type_ = type;
 }
 
 GLTexture::~GLTexture() {
@@ -30,7 +31,11 @@ void GLTexture::update(cv::Mat &m) {
 		glGenTextures(1, &glid_);
 		glBindTexture(GL_TEXTURE_2D, glid_);
 		//cv::Mat m(cv::Size(100,100), CV_8UC3);
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m.cols, m.rows, 0, GL_BGRA, GL_UNSIGNED_BYTE, m.data);
+		if (type_ == Type::BGRA) {
+			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m.cols, m.rows, 0, GL_BGRA, GL_UNSIGNED_BYTE, m.data);
+		} else if (type_ == Type::Float) {
+			glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, m.cols, m.rows, 0, GL_RED, GL_FLOAT, m.data);
+		}
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -60,13 +65,18 @@ void GLTexture::make(int width, int height) {
 		glGenTextures(1, &glid_);
 		glBindTexture(GL_TEXTURE_2D, glid_);
 		//cv::Mat m(cv::Size(100,100), CV_8UC3);
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr);
+		//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr);
+		if (type_ == Type::BGRA) {
+			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr);
+		} else if (type_ == Type::Float) {
+			glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, width, height, 0, GL_RED, GL_FLOAT, nullptr);
+		}
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-		//auto err = glGetError();
-		//if (err != 0) LOG(ERROR) << "OpenGL Texture error: " << err;
+		auto err = glGetError();
+		if (err != 0) LOG(ERROR) << "OpenGL Texture error: " << err;
 
 		glBindTexture(GL_TEXTURE_2D, 0);
 
@@ -100,7 +110,7 @@ cv::cuda::GpuMat GLTexture::map(cudaStream_t stream) {
 	size_t size;
 	cudaSafeCall(cudaGraphicsMapResources(1, &cuda_res_, stream));
 	cudaSafeCall(cudaGraphicsResourceGetMappedPointer(&devptr, &size, cuda_res_));
-	return cv::cuda::GpuMat(height_, width_, CV_8UC4, devptr);
+	return cv::cuda::GpuMat(height_, width_, (type_ == Type::BGRA) ? CV_8UC4 : CV_32F, devptr);
 }
 
 void GLTexture::unmap(cudaStream_t stream) {
@@ -112,7 +122,11 @@ void GLTexture::unmap(cudaStream_t stream) {
 	// Select the appropriate texture
 	glBindTexture( GL_TEXTURE_2D, glid_);
 	// Make a texture from the buffer
-	glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+	if (type_ == Type::BGRA) {
+		glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+	} else {
+		glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_RED, GL_FLOAT, NULL);
+	}
 	glBindBuffer( GL_PIXEL_UNPACK_BUFFER, 0);
 }
 
diff --git a/applications/gui/src/gltexture.hpp b/applications/gui/src/gltexture.hpp
index c9e5146bbc26ebfd3175bd37ad1113d0f3360415..c263a055a4582ee45eac96acee9dd6303cea92fb 100644
--- a/applications/gui/src/gltexture.hpp
+++ b/applications/gui/src/gltexture.hpp
@@ -12,7 +12,13 @@ namespace gui {
 
 class GLTexture {
 	public:
-	GLTexture();
+	enum class Type {
+		RGBA,
+		BGRA,
+		Float
+	};
+
+	explicit GLTexture(Type);
 	~GLTexture();
 
 	void update(cv::Mat &m);
@@ -34,6 +40,7 @@ class GLTexture {
 	int width_;
 	int height_;
 	bool changed_;
+	Type type_;
 
 	cudaGraphicsResource *cuda_res_;
 };
diff --git a/applications/gui/src/screen.cpp b/applications/gui/src/screen.cpp
index 7d144d5cbe727d110133281e5adec09b726ee3ab..7073e6aec85701d57685f0e2faec7326dd51d3dd 100644
--- a/applications/gui/src/screen.cpp
+++ b/applications/gui/src/screen.cpp
@@ -49,12 +49,14 @@ namespace {
 		R"(#version 330
 		uniform sampler2D image1;
 		uniform sampler2D image2;
+		uniform sampler2D depthImage;
 		uniform float blendAmount;
 		out vec4 color;
 		in vec2 uv;
 		void main() {
 			color = blendAmount * texture(image1, uv) + (1.0 - blendAmount) * texture(image2, uv);
 			color.w = 1.0f;
+			gl_FragDepth = texture(depthImage, uv).r;
 		})";
 }
 
@@ -545,16 +547,25 @@ void ftl::gui::Screen::draw(NVGcontext *ctx) {
 			//camera_->getLeft().texture();
 			glActiveTexture(GL_TEXTURE1);
 			glBindTexture(GL_TEXTURE_2D, (camera_->isStereo() && camera_->getRight().isValid()) ? rightEye_ : leftEye_);
+			glActiveTexture(GL_TEXTURE2);
+			glBindTexture(GL_TEXTURE_2D, camera_->getDepth().texture());
 			//(camera_->isStereo() && camera_->getRight().isValid()) ? camera_->getRight().texture() : camera_->getLeft().texture();
 			mShader.setUniform("image1", 0);
 			mShader.setUniform("image2", 1);
+			mShader.setUniform("depthImage", 2);
 			mShader.setUniform("blendAmount", (camera_->isStereo()) ? root_->value("blending", 0.5f) : 1.0f);
 			mShader.setUniform("scaleFactor", scaleFactor);
 			mShader.setUniform("position", imagePosition);
+
+			glEnable(GL_DEPTH_TEST); 
+			glDepthMask(GL_TRUE);
+			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
 			mShader.drawIndexed(GL_TRIANGLES, 0, 2);
 			//glDisable(GL_SCISSOR_TEST);
-
 			glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+
+			camera_->drawOverlay(screenSize); 
 		}
 	} else {
 		// Must periodically render the cameras here to update any thumbnails.
diff --git a/applications/gui/src/screen.hpp b/applications/gui/src/screen.hpp
index 92c619551a3478773e44cc917f80dbe8cb2c23e8..c87ff2ff9ab3ee8780ba991a4d09b325c9225364 100644
--- a/applications/gui/src/screen.hpp
+++ b/applications/gui/src/screen.hpp
@@ -79,7 +79,7 @@ class Screen : public nanogui::Screen {
 	nanogui::GLShader mShader;
 	GLuint mImageID;
 	//Source *src_;
-	GLTexture texture_;
+	//GLTexture texture_;
 	Eigen::Vector3f eye_;
 	Eigen::Vector4f neye_;
 	Eigen::Vector3f orientation_;
diff --git a/components/renderers/cpp/CMakeLists.txt b/components/renderers/cpp/CMakeLists.txt
index 516369fddf779c5949ca9ae1b2c79ce9fced866e..9706be7c62bc97045431b213cf0388c66d3a3b51 100644
--- a/components/renderers/cpp/CMakeLists.txt
+++ b/components/renderers/cpp/CMakeLists.txt
@@ -12,10 +12,17 @@ add_library(ftlrender
 	src/overlay.cpp
 )
 
+# Various preprocessor definitions have been generated by NanoGUI
+add_definitions(${NANOGUI_EXTRA_DEFS})
+
+# On top of adding the path to nanogui/include, you may need extras
+include_directories(${NANOGUI_EXTRA_INCS})
+
 target_include_directories(ftlrender PUBLIC
 	$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+	$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/ext/nanogui/include>
 	$<INSTALL_INTERFACE:include>
 	PRIVATE src)
-target_link_libraries(ftlrender ftlrgbd ftlcommon Eigen3::Eigen Threads::Threads ${OpenCV_LIBS})
+target_link_libraries(ftlrender ftlrgbd ftlcommon Eigen3::Eigen Threads::Threads nanogui ${NANOGUI_EXTRA_LIBS} ${OpenCV_LIBS})
 
 #ADD_SUBDIRECTORY(test)
diff --git a/components/renderers/cpp/include/ftl/render/overlay.hpp b/components/renderers/cpp/include/ftl/render/overlay.hpp
index 477b4419c8adb336693c5126bdd04d1b64c4c60b..bef452f0936c49807d4b722675c054ae5ce1ca80 100644
--- a/components/renderers/cpp/include/ftl/render/overlay.hpp
+++ b/components/renderers/cpp/include/ftl/render/overlay.hpp
@@ -4,19 +4,38 @@
 #include <opencv2/core/mat.hpp>
 #include <Eigen/Eigen>
 #include <ftl/rgbd/frameset.hpp>
+#include <nanogui/glutil.h>
 
 namespace ftl {
 namespace overlay {
 
+enum class Shape {
+    BOX,
+    CAMERA,
+    PLANE,
+    GRID
+};
+
 class Overlay : public ftl::Configurable {
 	public:
 	explicit Overlay(nlohmann::json &config);
 	~Overlay();
 
-	void apply(ftl::rgbd::FrameSet &fs, cv::Mat &out, ftl::rgbd::FrameState &state);
+	//void apply(ftl::rgbd::FrameSet &fs, cv::Mat &out, ftl::rgbd::FrameState &state);
+
+	void draw(ftl::rgbd::FrameSet &fs, ftl::rgbd::FrameState &state, const Eigen::Vector2f &);
 
 	private:
-	cv::Mat over_depth_;
+	nanogui::GLShader oShader;
+	bool init_;
+
+    std::vector<float3> shape_verts_;
+    std::vector<unsigned int> shape_tri_indices_;
+    std::unordered_map<ftl::overlay::Shape, std::tuple<int,int,int,int>> shapes_;
+
+    void _createShapes();
+    void _drawFilledShape(ftl::overlay::Shape shape, const Eigen::Matrix4d &pose, float scale, uchar4 colour);
+    void _drawOutlinedShape(Shape shape, const Eigen::Matrix4d &pose, const Eigen::Vector3f &scale, uchar4 fill, uchar4 outline);
 };
 
 void draw3DLine(
diff --git a/components/renderers/cpp/src/CUDARender.cpp b/components/renderers/cpp/src/CUDARender.cpp
index 9a62807cca29a5e296628138cbb63d88a398ca12..3affec4bdf172f61b88e7c24824996ac2e7fcd9b 100644
--- a/components/renderers/cpp/src/CUDARender.cpp
+++ b/components/renderers/cpp/src/CUDARender.cpp
@@ -332,13 +332,13 @@ void CUDARender::_allocateChannels(ftl::rgbd::Frame &out, ftl::codecs::Channel c
 
 	// Allocate left channel buffers and clear them
 	if (chan == Channel::Colour) {
-		if (!out.hasChannel(Channel::Depth)) {
+		//if (!out.hasChannel(Channel::Depth)) {
 			out.create<GpuMat>(Channel::Depth, Format<float>(camera.width, camera.height));
 			out.create<GpuMat>(Channel::Colour, Format<uchar4>(camera.width, camera.height));
 			out.create<GpuMat>(Channel::Normals, Format<half4>(camera.width, camera.height));
 			out.createTexture<uchar4>(Channel::Colour, true);  // Force interpolated colour
 			out.get<GpuMat>(Channel::Depth).setTo(cv::Scalar(1000.0f), cvstream);
-		}
+		//}
 	// Allocate right channel buffers and clear them
 	} else {
 		if (!out.hasChannel(Channel::Depth2)) {
diff --git a/components/renderers/cpp/src/overlay.cpp b/components/renderers/cpp/src/overlay.cpp
index 697a5c23243f6a5b784ab51d92b3e852d5b81d00..436163f544e8148cd346f6745b758645b3b1c754 100644
--- a/components/renderers/cpp/src/overlay.cpp
+++ b/components/renderers/cpp/src/overlay.cpp
@@ -1,4 +1,5 @@
 #include <ftl/render/overlay.hpp>
+#include <ftl/utility/matrix_conversion.hpp>
 
 #include <opencv2/imgproc.hpp>
 
@@ -9,27 +10,222 @@
 
 using ftl::overlay::Overlay;
 using ftl::codecs::Channel;
+using ftl::overlay::Shape;
+
+namespace {
+	constexpr char const *const overlayVertexShader =
+		R"(#version 330
+		in vec3 vertex;
+		uniform float focal;
+		uniform float width;
+		uniform float height;
+		uniform float far;
+		uniform float near;
+        uniform mat4 pose;
+        uniform vec3 scale;
+
+		void main() {
+            vec4 vert = pose*(vec4(scale*vertex,1.0));
+            vert = vert / vert.w;
+			vec4 pos = vec4(-vert.x*focal / -vert.z / (width/2.0),
+				vert.y*focal / -vert.z / (height/2.0),
+				(vert.z-near) / (far-near) * 2.0 - 1.0, 1.0);
+			gl_Position = pos;
+		})";
+
+	constexpr char const *const overlayFragmentShader =
+		R"(#version 330
+		uniform vec4 blockColour;
+		out vec4 color;
+		
+		void main() {
+			color = blockColour;
+		})";
+}
 
 Overlay::Overlay(nlohmann::json &config) : ftl::Configurable(config) {
-
+	init_ = false;
 }
 
 Overlay::~Overlay() {
 
 }
 
-void Overlay::apply(ftl::rgbd::FrameSet &fs, cv::Mat &out, ftl::rgbd::FrameState &state) {
-	over_depth_.create(out.size(), CV_32F);
+void Overlay::_createShapes() {
+    shape_verts_ = {
+        // Box
+        {-1.0, -1.0, -1.0},
+        {1.0, -1.0, -1.0},
+        {1.0, 1.0, -1.0},
+        {-1.0, 1.0, -1.0},
+        {-1.0, -1.0, 1.0},
+        {1.0, -1.0, 1.0},
+        {1.0, 1.0, 1.0},
+        {-1.0, 1.0, 1.0},
+
+        // Camera
+        {0.0, 0.0, 0.0},        // 8
+        {0.5, 0.28, 0.5},
+        {0.5, -0.28, 0.5},
+        {-0.5, 0.28, 0.5},
+        {-0.5, -0.28, 0.5},
+
+        // Plane Y simple
+        {-1.0, 0.0, -1.0},
+        {1.0, 0.0, -1.0},
+        {1.0, 0.0, 1.0},
+        {-1.0, 0.0, 1.0}
+    };
+
+    shape_tri_indices_ = {
+        // Box
+        0, 1, 2,
+        0, 2, 3,
+        1, 5, 6,
+        1, 6, 2,
+        0, 4, 7,
+        0, 7, 3,
+        3, 2, 6,
+        3, 6, 7,
+        0, 1, 5,
+        0, 5, 4,
+
+        // Box Lines
+        0, 1,       // 30
+        1, 5,
+        5, 6,
+        6, 2,
+        2, 1,
+        2, 3,
+        3, 0,
+        3, 7,
+        7, 4,
+        4, 5,
+        6, 7,
+        0, 4,
+
+        // Camera
+        8, 9, 10,      // 54
+        8, 11, 12,
+        8, 9, 11,
+        8, 10, 12,
+
+        // Camera Lines
+        8, 9,           // 66
+        8, 10,
+        8, 11,
+        8, 12,
+        9, 10,
+        11, 12,
+        9, 11,
+        10, 12
+    };
+
+    shapes_[Shape::BOX] = {0,30, 30, 12*2};
+    shapes_[Shape::CAMERA] = {54, 4*3, 66, 8*2};
+
+    oShader.uploadAttrib("vertex", sizeof(float3)*shape_verts_.size(), 3, sizeof(float), GL_FLOAT, false, shape_verts_.data());
+    oShader.uploadAttrib ("indices", sizeof(int)*shape_tri_indices_.size(), 1, sizeof(int), GL_UNSIGNED_INT, true, shape_tri_indices_.data());
+}
+
+void Overlay::_drawFilledShape(Shape shape, const Eigen::Matrix4d &pose, float scale, uchar4 c) {
+    if (shapes_.find(shape) ==shapes_.end()) {
+        return;
+    }
+
+    Eigen::Matrix4f mv = pose.cast<float>();
+
+    auto [offset,count, loffset, lcount] = shapes_[shape];
+    oShader.setUniform("scale", scale);
+    oShader.setUniform("pose", mv);
+    oShader.setUniform("blockColour", Eigen::Vector4f(float(c.x)/255.0f,float(c.y)/255.0f,float(c.z)/255.0f,float(c.w)/255.0f));
+	//oShader.drawIndexed(GL_TRIANGLES, offset, count);
+    glDrawElements(GL_TRIANGLES, (GLsizei) count, GL_UNSIGNED_INT,
+                   (const void *)(offset * sizeof(uint32_t)));
+}
+
+void Overlay::_drawOutlinedShape(Shape shape, const Eigen::Matrix4d &pose, const Eigen::Vector3f &scale, uchar4 fill, uchar4 outline) {
+    if (shapes_.find(shape) ==shapes_.end()) {
+        return;
+    }
+
+    Eigen::Matrix4f mv = pose.cast<float>();
+
+    auto [offset,count,loffset,lcount] = shapes_[shape];
+    oShader.setUniform("scale", scale);
+    oShader.setUniform("pose", mv);
+    oShader.setUniform("blockColour", Eigen::Vector4f(float(fill.x)/255.0f,float(fill.y)/255.0f,float(fill.z)/255.0f,float(fill.w)/255.0f));
+	//oShader.drawIndexed(GL_TRIANGLES, offset, count);
+    glDrawElements(GL_TRIANGLES, (GLsizei) count, GL_UNSIGNED_INT,
+                   (const void *)(offset * sizeof(uint32_t)));
+
+    if (lcount != 0) {
+        oShader.setUniform("blockColour", Eigen::Vector4f(float(outline.x)/255.0f,float(outline.y)/255.0f,float(outline.z)/255.0f,float(outline.w)/255.0f));
+        //oShader.drawIndexed(GL_LINE_LOOP, offset, count);
+        glDrawElements(GL_LINES, (GLsizei) lcount, GL_UNSIGNED_INT,
+                    (const void *)(loffset * sizeof(uint32_t)));
+    }
+}
+
+void Overlay::draw(ftl::rgbd::FrameSet &fs, ftl::rgbd::FrameState &state, const Eigen::Vector2f &screenSize) {
+	double zfar = 8.0f;
+	auto intrin = state.getLeft();
+	intrin = intrin.scaled(screenSize[0], screenSize[1]);
+
+	if (!init_) {
+		oShader.init("OverlayShader", overlayVertexShader, overlayFragmentShader);
+        oShader.bind();
+        _createShapes();
+		init_ = true;
+	} else {
+	    oShader.bind();
+    }
+
+	float3 tris[] = {
+		{0.5f, -0.7f, 2.0f},
+		{0.2f, -0.5f, 2.0f},
+		{0.8f, -0.4f, 2.0f}
+	};
+
+	auto pose = MatrixConversion::toCUDA(state.getPose().cast<float>().inverse());
+
+	tris[0] = pose * tris[0];
+	tris[1] = pose * tris[1];
+	tris[2] = pose * tris[2];
+
+	glFlush();
+
+	glDepthMask(GL_FALSE);
+	glEnable( GL_BLEND );
+	glBlendEquation( GL_FUNC_ADD );
+	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+	glEnable(GL_LINE_SMOOTH);
+
+	oShader.setUniform("focal", intrin.fx);
+	oShader.setUniform("width", float(intrin.width));
+	oShader.setUniform("height", float(intrin.height));
+	oShader.setUniform("far", zfar);
+	oShader.setUniform("near", 0.0f);  // TODO: but make sure CUDA depth is also normalised like this
+
+	/*oShader.setUniform("blockColour", Eigen::Vector4f(1.0f,1.0f,0.0f,0.5f));
+	oShader.uploadAttrib("vertex", sizeof(tris), 3, sizeof(float), GL_FLOAT, false, tris);
+	oShader.drawArray(GL_TRIANGLES, 0, 3);
+
+	oShader.setUniform("blockColour", Eigen::Vector4f(1.0f,1.0f,0.0f,1.0f));
+	//oShader.uploadAttrib("vertex", sizeof(tris), 3, sizeof(float), GL_FLOAT, false, tris);
+	oShader.drawArray(GL_LINE_LOOP, 0, 3);*/
+
+	//glFinish();
 
 	if (value("show_poses", false)) {
 		for (size_t i=0; i<fs.frames.size(); ++i) {
-			auto pose = fs.frames[i].getPose().inverse() * state.getPose();
-			Eigen::Vector4d pos = pose.inverse() * Eigen::Vector4d(0,0,0,1);
-			pos /= pos[3];
+			auto pose = fs.frames[i].getPose(); //.inverse() * state.getPose();
 
 			auto name = fs.frames[i].get<std::string>("name");
-			ftl::overlay::drawCamera(state.getLeft(), out, over_depth_, fs.frames[i].getLeftCamera(), pose, cv::Scalar(0,0,255,255), 0.2,value("show_frustrum", false));
-			if (name) ftl::overlay::drawText(state.getLeft(), out, over_depth_, *name, pos, 0.5, cv::Scalar(0,0,255,255));
+            _drawOutlinedShape(Shape::CAMERA, state.getPose().inverse() * pose, Eigen::Vector3f(0.2f,0.2f,0.2f), make_uchar4(255,0,0,80), make_uchar4(255,0,0,255));
+
+			//ftl::overlay::drawCamera(state.getLeft(), out, over_depth_, fs.frames[i].getLeftCamera(), pose, cv::Scalar(0,0,255,255), 0.2,value("show_frustrum", false));
+			//if (name) ftl::overlay::drawText(state.getLeft(), out, over_depth_, *name, pos, 0.5, cv::Scalar(0,0,255,255));
 		}
 	}
 
@@ -39,13 +235,16 @@ void Overlay::apply(ftl::rgbd::FrameSet &fs, cv::Mat &out, ftl::rgbd::FrameState
 			fs.get(Channel::Shapes3D, shapes);
 
 			for (auto &s : shapes) {
-				auto pose = s.pose.cast<double>().inverse() * state.getPose();
-				Eigen::Vector4d pos = pose.inverse() * Eigen::Vector4d(0,0,0,1);
-				pos /= pos[3];
+				auto pose = s.pose.cast<double>();
+				//Eigen::Vector4d pos = pose.inverse() * Eigen::Vector4d(0,0,0,1);
+				//pos /= pos[3];
+
+                Eigen::Vector3f scale(s.size[0]/2.0f, s.size[1]/2.0f, s.size[2]/2.0f);
 
-				ftl::overlay::drawFilledBox(state.getLeft(), out, over_depth_, pose, cv::Scalar(0,0,255,50), s.size.cast<double>());
-                ftl::overlay::drawBox(state.getLeft(), out, over_depth_, pose, cv::Scalar(0,0,255,255), s.size.cast<double>());
-				ftl::overlay::drawText(state.getLeft(), out, over_depth_, s.label, pos, 0.5, cv::Scalar(0,0,255,100));
+                _drawOutlinedShape(Shape::BOX, state.getPose().inverse() * pose, scale, make_uchar4(255,0,255,80), make_uchar4(255,0,255,255));
+				//ftl::overlay::drawFilledBox(state.getLeft(), out, over_depth_, pose, cv::Scalar(0,0,255,50), s.size.cast<double>());
+                //ftl::overlay::drawBox(state.getLeft(), out, over_depth_, pose, cv::Scalar(0,0,255,255), s.size.cast<double>());
+				//ftl::overlay::drawText(state.getLeft(), out, over_depth_, s.label, pos, 0.5, cv::Scalar(0,0,255,100));
 			}
 		}
 
@@ -59,13 +258,17 @@ void Overlay::apply(ftl::rgbd::FrameSet &fs, cv::Mat &out, ftl::rgbd::FrameState
 					Eigen::Vector4d pos = pose.inverse() * Eigen::Vector4d(0,0,0,1);
 					pos /= pos[3];
 
-					ftl::overlay::drawBox(state.getLeft(), out, over_depth_, pose, cv::Scalar(0,0,255,100), s.size.cast<double>());
-					ftl::overlay::drawText(state.getLeft(), out, over_depth_, s.label, pos, 0.5, cv::Scalar(0,0,255,100));
+					//ftl::overlay::drawBox(state.getLeft(), out, over_depth_, pose, cv::Scalar(0,0,255,100), s.size.cast<double>());
+					//ftl::overlay::drawText(state.getLeft(), out, over_depth_, s.label, pos, 0.5, cv::Scalar(0,0,255,100));
 				}
 			}
 		}
 	}
 
+    glDisable(GL_LINE_SMOOTH);
+	glDisable(GL_BLEND);
+	glDisable(GL_DEPTH_TEST);
+
 	//cv::flip(out, out, 0);
 }
 
diff --git a/components/rgbd-sources/src/source.cpp b/components/rgbd-sources/src/source.cpp
index 84a20c710b61d2282b658ea341005c789e0910a6..f2659128145d0908d75db567541ef974b5d14aa0 100644
--- a/components/rgbd-sources/src/source.cpp
+++ b/components/rgbd-sources/src/source.cpp
@@ -295,7 +295,7 @@ Camera Camera::scaled(int width, int height) const {
 	float scaleX = (float)width / (float)cam.width;
 	float scaleY = (float)height / (float)cam.height;
 
-	CHECK( abs(scaleX - scaleY) < 0.00000001f );
+	//CHECK( abs(scaleX - scaleY) < 0.00000001f );
 
 	Camera newcam = cam;
 	newcam.width = width;