diff --git a/components/renderers/cpp/src/splat_render.cpp b/components/renderers/cpp/src/splat_render.cpp
index 9c507fc432c2ce2d9037d98840798bc47c63ff7c..1b39ccebf69e589372ab2944cb907bb03d1dbdd8 100644
--- a/components/renderers/cpp/src/splat_render.cpp
+++ b/components/renderers/cpp/src/splat_render.cpp
@@ -28,6 +28,8 @@ void Splatter::renderChannel(
 	temp_.get<GpuMat>(Channel::Depth2).setTo(cv::Scalar(0x7FFFFFFF), cvstream);
 	temp_.get<GpuMat>(Channel::Colour).setTo(cv::Scalar(0.0f,0.0f,0.0f,0.0f), cvstream);
 	temp_.get<GpuMat>(Channel::Contribution).setTo(cv::Scalar(0.0f), cvstream);
+
+	bool is_float = ftl::rgbd::isFloatChannel(channel);
 	
 	// Render each camera into virtual view
 	for (size_t i=0; i < scene_->frames.size(); ++i) {
@@ -75,23 +77,53 @@ void Splatter::renderChannel(
 			cv::cuda::cvtColor(tmp,col, cv::COLOR_BGR2BGRA);
 		}
 	
-		ftl::cuda::dibr_attribute(
-			f.createTexture<uchar4>(Channel::Colour),
-			f.createTexture<float4>(Channel::Points),
-			temp_.getTexture<int>(Channel::Depth),
-			temp_.getTexture<float4>(Channel::Colour),
-			temp_.getTexture<float>(Channel::Contribution),
-			params, stream
-		);
+		if (is_float) {
+			ftl::cuda::dibr_attribute(
+				f.createTexture<float>(channel),
+				f.createTexture<float4>(Channel::Points),
+				temp_.getTexture<int>(Channel::Depth),
+				temp_.getTexture<float4>(Channel::Colour),
+				temp_.getTexture<float>(Channel::Contribution),
+				params, stream
+			);
+		} else if (channel == Channel::Colour || channel == Channel::Right) {
+			ftl::cuda::dibr_attribute(
+				f.createTexture<uchar4>(Channel::Colour),
+				f.createTexture<float4>(Channel::Points),
+				temp_.getTexture<int>(Channel::Depth),
+				temp_.getTexture<float4>(Channel::Colour),
+				temp_.getTexture<float>(Channel::Contribution),
+				params, stream
+			);
+		} else {
+			ftl::cuda::dibr_attribute(
+				f.createTexture<uchar4>(channel),
+				f.createTexture<float4>(Channel::Points),
+				temp_.getTexture<int>(Channel::Depth),
+				temp_.getTexture<float4>(Channel::Colour),
+				temp_.getTexture<float>(Channel::Contribution),
+				params, stream
+			);
+		}
 	}
 
-	// Normalise attribute contributions
-	ftl::cuda::dibr_normalise(
-		temp_.createTexture<float4>(Channel::Colour),
-		out.createTexture<uchar4>(channel),
-		temp_.createTexture<float>(Channel::Contribution),
-		stream
-	);
+	if (is_float) {
+		// Normalise attribute contributions
+		ftl::cuda::dibr_normalise(
+			temp_.createTexture<float4>(Channel::Colour),
+			out.createTexture<float>(channel),
+			temp_.createTexture<float>(Channel::Contribution),
+			stream
+		);
+	} else {
+		// Normalise attribute contributions
+		ftl::cuda::dibr_normalise(
+			temp_.createTexture<float4>(Channel::Colour),
+			out.createTexture<uchar4>(channel),
+			temp_.createTexture<float>(Channel::Contribution),
+			stream
+		);
+	}
 }
 
 bool Splatter::render(ftl::rgbd::VirtualSource *src, ftl::rgbd::Frame &out, cudaStream_t stream) {
@@ -165,9 +197,9 @@ bool Splatter::render(ftl::rgbd::VirtualSource *src, ftl::rgbd::Frame &out, cuda
 	{
 		temp_.get<GpuMat>(Channel::Depth).convertTo(out.get<GpuMat>(Channel::Depth), CV_32F, 1.0f / 1000.0f, cvstream);
 	}
-	else if (chan == Channel::Energy)
+	else if (chan == Channel::Contribution)
 	{
-		cv::cuda::swap(temp_.get<GpuMat>(Channel::Energy), out.create<GpuMat>(Channel::Energy));
+		cv::cuda::swap(temp_.get<GpuMat>(Channel::Contribution), out.create<GpuMat>(Channel::Contribution));
 	}
 	else if (chan == Channel::Right)
 	{
diff --git a/components/renderers/cpp/src/splatter.cu b/components/renderers/cpp/src/splatter.cu
index c1b46fc1d5768dc15fe54bcf6e37f4655a076b56..3b1ae4b47ef0fe6b29b15d1fa20fdc9a0fd0b9bb 100644
--- a/components/renderers/cpp/src/splatter.cu
+++ b/components/renderers/cpp/src/splatter.cu
@@ -137,6 +137,70 @@ __global__ void dibr_attribute_contrib_kernel(
 	}
 }
 
+__global__ void dibr_attribute_contrib_kernel(
+    TextureObject<float> colour_in,    // Original colour image
+    TextureObject<float4> points,       // Original 3D points
+    TextureObject<int> depth_in,        // Virtual depth map
+    TextureObject<float4> colour_out,   // Accumulated output
+    //TextureObject<float4> normal_out,
+    TextureObject<float> contrib_out,
+    SplatParams params) {
+    
+    //const ftl::voxhash::DepthCameraCUDA &camera = c_cameras[cam];
+
+    const int tid = (threadIdx.x + threadIdx.y * blockDim.x);
+    //const int warp = tid / WARP_SIZE;
+    const int x = (blockIdx.x*blockDim.x + threadIdx.x) / WARP_SIZE;
+    const int y = blockIdx.y*blockDim.y + threadIdx.y;
+
+    const float3 worldPos = make_float3(points.tex2D(x, y));
+    //const float3 normal = make_float3(tex2D<float4>(camera.normal, x, y));
+    if (worldPos.x == MINF) return;
+    //const float r = (camera.poseInverse * worldPos).z / camera.params.fx;
+
+    const float3 camPos = params.m_viewMatrix * worldPos;
+    if (camPos.z < params.camera.minDepth) return;
+    if (camPos.z > params.camera.maxDepth) return;
+    const uint2 screenPos = params.camera.camToScreen<uint2>(camPos);
+
+    const int upsample = 8; //min(UPSAMPLE_MAX, int((5.0f*r) * params.camera.fx / camPos.z));
+
+    // Not on screen so stop now...
+    if (screenPos.x >= depth_in.width() || screenPos.y >= depth_in.height()) return;
+            
+    // Is this point near the actual surface and therefore a contributor?
+    const float d = ((float)depth_in.tex2D((int)screenPos.x, (int)screenPos.y)/1000.0f);
+    //if (abs(d - camPos.z) > DEPTH_THRESHOLD) return;
+
+    // TODO:(Nick) Should just one thread load these to shared mem?
+    const float colour = (colour_in.tex2D(x, y));
+    //const float4 normal = tex2D<float4>(camera.normal, x, y);
+
+    // Each thread in warp takes an upsample point and updates corresponding depth buffer.
+    const int lane = tid % WARP_SIZE;
+    for (int i=lane; i<upsample*upsample; i+=WARP_SIZE) {
+        const float u = (i % upsample) - (upsample / 2);
+        const float v = (i / upsample) - (upsample / 2);
+
+        // Use the depth buffer to determine this pixels 3D position in camera space
+        const float d = ((float)depth_in.tex2D(screenPos.x+u, screenPos.y+v)/1000.0f);
+        const float3 nearest = params.camera.screenToCam((int)(screenPos.x+u),(int)(screenPos.y+v),d);
+
+        // What is contribution of our current point at this pixel?
+        const float weight = ftl::cuda::spatialWeighting(length(nearest - camPos), SMOOTHING_MULTIPLIER_C*(nearest.z/params.camera.fx));
+        if (screenPos.x+u < colour_out.width() && screenPos.y+v < colour_out.height() && weight > 0.0f) {  // TODO: Use confidence threshold here
+            const float wcolour = colour * weight;
+            //const float4 wnormal = normal * weight;
+            
+            //printf("Z %f\n", d);
+
+            // Add this points contribution to the pixel buffer
+            atomicAdd((float*)&colour_out(screenPos.x+u, screenPos.y+v), wcolour);
+            atomicAdd(&contrib_out(screenPos.x+u, screenPos.y+v), weight);
+        }
+    }
+}
+
 void ftl::cuda::dibr_attribute(
         TextureObject<uchar4> &colour_in,    // Original colour image
         TextureObject<float4> &points,       // Original 3D points
@@ -159,6 +223,28 @@ void ftl::cuda::dibr_attribute(
     cudaSafeCall( cudaGetLastError() );
 }
 
+void ftl::cuda::dibr_attribute(
+        TextureObject<float> &colour_in,    // Original colour image
+        TextureObject<float4> &points,       // Original 3D points
+        TextureObject<int> &depth_in,        // Virtual depth map
+        TextureObject<float4> &colour_out,   // Accumulated output
+        //TextureObject<float4> normal_out,
+        TextureObject<float> &contrib_out,
+        SplatParams &params, cudaStream_t stream) {
+    const dim3 gridSize((depth_in.width() + 2 - 1)/2, (depth_in.height() + T_PER_BLOCK - 1)/T_PER_BLOCK);
+    const dim3 blockSize(2*WARP_SIZE, T_PER_BLOCK);
+
+    dibr_attribute_contrib_kernel<<<gridSize, blockSize, 0, stream>>>(
+        colour_in,
+        points,
+        depth_in,
+        colour_out,
+        contrib_out,
+        params
+    );
+    cudaSafeCall( cudaGetLastError() );
+}
+
 //==============================================================================
 
 __global__ void dibr_normalise_kernel(
@@ -181,6 +267,26 @@ __global__ void dibr_normalise_kernel(
     }
 }
 
+__global__ void dibr_normalise_kernel(
+        TextureObject<float4> colour_in,
+        TextureObject<float> colour_out,
+        //TextureObject<float4> normals,
+        TextureObject<float> contribs) {
+    const unsigned int x = blockIdx.x*blockDim.x + threadIdx.x;
+    const unsigned int y = blockIdx.y*blockDim.y + threadIdx.y;
+
+    if (x < colour_in.width() && y < colour_in.height()) {
+        const float4 colour = colour_in.tex2D((int)x,(int)y);
+        //const float4 normal = normals.tex2D((int)x,(int)y);
+        const float contrib = contribs.tex2D((int)x,(int)y);
+
+        if (contrib > 0.0f) {
+            colour_out(x,y) = colour.x / contrib;
+            //normals(x,y) = normal / contrib;
+        }
+    }
+}
+
 void ftl::cuda::dibr_normalise(TextureObject<float4> &colour_in, TextureObject<uchar4> &colour_out, TextureObject<float> &contribs, cudaStream_t stream) {
     const dim3 gridSize((colour_in.width() + T_PER_BLOCK - 1)/T_PER_BLOCK, (colour_in.height() + T_PER_BLOCK - 1)/T_PER_BLOCK);
     const dim3 blockSize(T_PER_BLOCK, T_PER_BLOCK);
@@ -188,3 +294,11 @@ void ftl::cuda::dibr_normalise(TextureObject<float4> &colour_in, TextureObject<u
     dibr_normalise_kernel<<<gridSize, blockSize, 0, stream>>>(colour_in, colour_out, contribs);
     cudaSafeCall( cudaGetLastError() );
 }
+
+void ftl::cuda::dibr_normalise(TextureObject<float4> &colour_in, TextureObject<float> &colour_out, TextureObject<float> &contribs, cudaStream_t stream) {
+    const dim3 gridSize((colour_in.width() + T_PER_BLOCK - 1)/T_PER_BLOCK, (colour_in.height() + T_PER_BLOCK - 1)/T_PER_BLOCK);
+    const dim3 blockSize(T_PER_BLOCK, T_PER_BLOCK);
+
+    dibr_normalise_kernel<<<gridSize, blockSize, 0, stream>>>(colour_in, colour_out, contribs);
+    cudaSafeCall( cudaGetLastError() );
+}
diff --git a/components/renderers/cpp/src/splatter_cuda.hpp b/components/renderers/cpp/src/splatter_cuda.hpp
index 3cd22a1441f10bcf0e62d0512ff16473b3196201..8c57d58486c04aff5960f215c6a6308719e6af7b 100644
--- a/components/renderers/cpp/src/splatter_cuda.hpp
+++ b/components/renderers/cpp/src/splatter_cuda.hpp
@@ -13,17 +13,32 @@ namespace cuda {
 		cudaStream_t stream);
 
 	void dibr_attribute(
-		ftl::cuda::TextureObject<uchar4> &colour_in,	// Original colour image
+		ftl::cuda::TextureObject<uchar4> &in,	// Original colour image
 		ftl::cuda::TextureObject<float4> &points,		// Original 3D points
 		ftl::cuda::TextureObject<int> &depth_in,		// Virtual depth map
-		ftl::cuda::TextureObject<float4> &colour_out,	// Accumulated output
+		ftl::cuda::TextureObject<float4> &out,	// Accumulated output
 		//TextureObject<float4> normal_out,
 		ftl::cuda::TextureObject<float> &contrib_out,
 		ftl::render::SplatParams &params, cudaStream_t stream);
 
+	void dibr_attribute(
+		ftl::cuda::TextureObject<float> &in,	// Original colour image
+		ftl::cuda::TextureObject<float4> &points,		// Original 3D points
+		ftl::cuda::TextureObject<int> &depth_in,		// Virtual depth map
+		ftl::cuda::TextureObject<float4> &out,	// Accumulated output
+		//TextureObject<float4> normal_out,
+		ftl::cuda::TextureObject<float> &contrib_out,
+		ftl::render::SplatParams &params, cudaStream_t stream);
+
+	void dibr_normalise(
+		ftl::cuda::TextureObject<float4> &in,
+		ftl::cuda::TextureObject<uchar4> &out,
+		ftl::cuda::TextureObject<float> &contribs,
+		cudaStream_t stream);
+
 	void dibr_normalise(
-		ftl::cuda::TextureObject<float4> &colour_in,
-		ftl::cuda::TextureObject<uchar4> &colour_out,
+		ftl::cuda::TextureObject<float4> &in,
+		ftl::cuda::TextureObject<float> &out,
 		ftl::cuda::TextureObject<float> &contribs,
 		cudaStream_t stream);
 }