diff --git a/applications/reconstruct/src/ilw/ilw.cu b/applications/reconstruct/src/ilw/ilw.cu
index 831ff641b50dd7396a7282b99119a72164f6a34e..97ea74eebdc8f3b3d8aeec75f5153ce468808a33 100644
--- a/applications/reconstruct/src/ilw/ilw.cu
+++ b/applications/reconstruct/src/ilw/ilw.cu
@@ -195,14 +195,14 @@ __global__ void correspondence_energy_vector_kernel(
 	const float avgcolour = totalcolour/(float)count;
     const float confidence = bestcolour / totalcolour; //bestcolour - avgcolour;
 
-    if (bestweight > 0.0f) {
+    //if (bestweight > 0.0f) {
         float old = conf.tex2D(x,y);
 
-        if (bestweight * confidence > old) {
+        if (bestweight * confidence >= old) {
 			dout(x,y) = bestdepth;
 			conf(x,y) = bestweight * confidence;
 		}
-    }
+    //}
 }
 
 void ftl::cuda::correspondence(
diff --git a/components/renderers/cpp/src/splat_render.cpp b/components/renderers/cpp/src/splat_render.cpp
index 1ccaf156cc31d7ba2097364e627d6d93500d293a..895ea79ff5bdcf1dd476ecb441fad2af7fcc6f79 100644
--- a/components/renderers/cpp/src/splat_render.cpp
+++ b/components/renderers/cpp/src/splat_render.cpp
@@ -134,8 +134,8 @@ void Splatter::renderChannel(
 	cv::cuda::Stream cvstream = cv::cuda::StreamAccessor::wrapStream(stream);
 	temp_.get<GpuMat>(Channel::Depth).setTo(cv::Scalar(0x7FFFFFFF), cvstream);
 	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);
+	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);
 
 	if (scene_->frames.size() < 1) return;
 	bool is_float = out.get<GpuMat>(channel).type() == CV_32F; //ftl::rgbd::isFloatChannel(channel);
@@ -162,23 +162,33 @@ void Splatter::renderChannel(
 		//LOG(INFO) << "DIBR DONE";
 	}
 
-	//temp_.createTexture<float4>(Channel::Colour);
-	//temp_.createTexture<float>(Channel::Contribution);
+	temp_.createTexture<float4>(Channel::Colour);
+	temp_.createTexture<float>(Channel::Contribution);
 	out.create<GpuMat>(Channel::Normals, Format<float4>(params.camera.width, params.camera.height));
+	out.get<GpuMat>(Channel::Normals).setTo(cv::Scalar(0.0f,0.0f,0.0f,0.0f), cvstream);
 
 	// Create normals first
 	for (auto &f : scene_->frames) {
+
 		ftl::cuda::dibr_attribute(
 			f.createTexture<float4>(Channel::Normals),
 			f.createTexture<float4>(Channel::Points),
 			temp_.getTexture<int>(Channel::Depth2),
 			out.createTexture<float4>(Channel::Normals),
+			temp_.getTexture<float>(Channel::Contribution),
 			params, stream
 		);
 	}
 
-	//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);
+	ftl::cuda::dibr_normalise(
+		out.getTexture<float4>(Channel::Normals),
+		out.getTexture<float4>(Channel::Normals),
+		temp_.getTexture<float>(Channel::Contribution),
+		stream
+	);
+
+	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);
 
 	if (is_4chan) {
 		temp_.create<GpuMat>(Channel::Colour2, Format<float4>(params.camera.width, params.camera.height));
@@ -207,7 +217,8 @@ void Splatter::renderChannel(
 				f.createTexture<float4>(channel),
 				f.createTexture<float4>(Channel::Points),
 				temp_.getTexture<int>(Channel::Depth2),
-				(splat_) ? temp_.createTexture<float4>(Channel::Colour2) : out.createTexture<float4>(channel),
+				temp_.getTexture<float4>(Channel::Colour),
+				temp_.getTexture<float>(Channel::Contribution),
 				params, stream
 			);
 		} else if (is_float) {
@@ -215,7 +226,8 @@ void Splatter::renderChannel(
 				f.createTexture<float>(channel),
 				f.createTexture<float4>(Channel::Points),
 				temp_.getTexture<int>(Channel::Depth2),
-				(splat_) ? temp_.createTexture<float>(Channel::Colour2) : out.createTexture<float>(channel),
+				temp_.createTexture<float>(Channel::Colour2),
+				temp_.getTexture<float>(Channel::Contribution),
 				params, stream
 			);
 		} else {
@@ -223,12 +235,36 @@ void Splatter::renderChannel(
 				f.createTexture<uchar4>(channel),
 				f.createTexture<float4>(Channel::Points),
 				temp_.getTexture<int>(Channel::Depth2),
-				(splat_) ? temp_.createTexture<uchar4>(Channel::Colour2) : out.createTexture<uchar4>(channel),
+				temp_.createTexture<float4>(Channel::Colour),
+				temp_.getTexture<float>(Channel::Contribution),
 				params, stream
 			);
 		}
 	}
 
+	if (is_4chan) {
+		ftl::cuda::dibr_normalise(
+			temp_.getTexture<float4>(Channel::Colour),
+			(splat_) ? temp_.createTexture<float4>(Channel::Colour2) : out.createTexture<float4>(channel),
+			temp_.getTexture<float>(Channel::Contribution),
+			stream
+		);
+	} else if (is_float) {
+		ftl::cuda::dibr_normalise(
+			temp_.createTexture<float>(Channel::Colour2),
+			(splat_) ? temp_.createTexture<float>(Channel::Colour2) : out.createTexture<float>(channel),
+			temp_.getTexture<float>(Channel::Contribution),
+			stream
+		);
+	} else {
+		ftl::cuda::dibr_normalise(
+			temp_.getTexture<float4>(Channel::Colour),
+			(splat_) ? temp_.createTexture<uchar4>(Channel::Colour2) : out.createTexture<uchar4>(channel),
+			temp_.getTexture<float>(Channel::Contribution),
+			stream
+		);
+	}
+
 	//out.get<GpuMat>(Channel::Left).setTo(cv::Scalar(0,0,0,0), cvstream);
 
 	// Now splat the points
@@ -277,8 +313,8 @@ bool Splatter::render(ftl::rgbd::VirtualSource *src, ftl::rgbd::Frame &out, cuda
 	out.create<GpuMat>(Channel::Colour, Format<uchar4>(camera.width, camera.height));
 
 	// FIXME: Use source resolutions, not virtual resolution
-	//temp_.create<GpuMat>(Channel::Colour, Format<float4>(camera.width, camera.height));
-	//temp_.create<GpuMat>(Channel::Contribution, Format<float>(camera.width, camera.height));
+	temp_.create<GpuMat>(Channel::Colour, Format<float4>(camera.width, camera.height));
+	temp_.create<GpuMat>(Channel::Contribution, Format<float>(camera.width, camera.height));
 	temp_.create<GpuMat>(Channel::Depth, Format<int>(camera.width, camera.height));
 	temp_.create<GpuMat>(Channel::Depth2, Format<int>(camera.width, camera.height));
 	temp_.create<GpuMat>(Channel::Normals, Format<float4>(camera.width, camera.height));
@@ -302,6 +338,7 @@ bool Splatter::render(ftl::rgbd::VirtualSource *src, ftl::rgbd::Frame &out, cuda
 	//LOG(INFO) << "Render ready: " << camera.width << "," << camera.height;
 
 	temp_.createTexture<int>(Channel::Depth);
+	//temp_.get<GpuMat>(Channel::Normals).setTo(cv::Scalar(0.0f,0.0f,0.0f,0.0f), cvstream);
 
 	// First make sure each input has normals
 	temp_.createTexture<float4>(Channel::Normals);
diff --git a/components/renderers/cpp/src/splatter.cu b/components/renderers/cpp/src/splatter.cu
index 3567d5c07dcdc819b93989992069b3244149855f..36c4fa6d5ca01a4c5bbbc57bce3f376c08925d36 100644
--- a/components/renderers/cpp/src/splatter.cu
+++ b/components/renderers/cpp/src/splatter.cu
@@ -127,6 +127,22 @@ __device__ inline float make(const float4 &v) {
 	return v.x;
 }
 
+template <typename T>
+__device__ inline T make(const uchar4 &v);
+
+template <>
+__device__ inline float4 make(const uchar4 &v) {
+	return make_float4((float)v.x, (float)v.y, (float)v.z, (float)v.w);
+}
+
+template <typename T>
+__device__ inline T make(float v);
+
+template <>
+__device__ inline float make(float v) {
+	return v;
+}
+
 /*
  * Pass 1b: Expand splats to full size and merge
  */
@@ -302,15 +318,45 @@ __device__ inline uchar4 generateInput(const uchar4 &in, const SplatParams &para
 		in;
 }
 
+template <typename A, typename B>
+__device__ inline B weightInput(const A &in, float weight) {
+	return in * weight;
+}
+
+template <>
+__device__ inline float4 weightInput(const uchar4 &in, float weight) {
+	return make_float4(
+		(float)in.x * weight,
+		(float)in.y * weight,
+		(float)in.z * weight,
+		(float)in.w * weight);
+}
+
+template <typename T>
+__device__ inline void accumulateOutput(TextureObject<T> &out, TextureObject<float> &contrib, const uint2 &pos, const T &in, float w) {
+	atomicAdd(&out(pos.x, pos.y), in);
+	atomicAdd(&contrib(pos.x, pos.y), w);
+} 
+
+template <>
+__device__ inline void accumulateOutput(TextureObject<float4> &out, TextureObject<float> &contrib, const uint2 &pos, const float4 &in, float w) {
+	atomicAdd((float*)&out(pos.x, pos.y), in.x);
+	atomicAdd(((float*)&out(pos.x, pos.y))+1, in.y);
+	atomicAdd(((float*)&out(pos.x, pos.y))+2, in.z);
+	atomicAdd(((float*)&out(pos.x, pos.y))+3, in.w);
+	atomicAdd(&contrib(pos.x, pos.y), w);
+} 
+
 /*
  * Pass 2: Accumulate attribute contributions if the points pass a visibility test.
  */
- template <typename T>
+ template <typename A, typename B>
 __global__ void dibr_attribute_contrib_kernel(
-        TextureObject<T> in,				// Attribute input
+        TextureObject<A> in,				// Attribute input
         TextureObject<float4> points,       // Original 3D points
         TextureObject<int> depth_in,        // Virtual depth map
-        TextureObject<T> out,			// Accumulated output
+		TextureObject<B> out,			// Accumulated output
+		TextureObject<float> contrib,
         SplatParams params) {
         
 	const int x = (blockIdx.x*blockDim.x + threadIdx.x);
@@ -328,25 +374,26 @@ __global__ void dibr_attribute_contrib_kernel(
 	if (screenPos.x >= depth_in.width() || screenPos.y >= depth_in.height()) return;
             
     // Is this point near the actual surface and therefore a contributor?
-    const int d = depth_in.tex2D((int)screenPos.x, (int)screenPos.y);
-
-	const T input = generateInput(in.tex2D(x, y), params, worldPos);
+    const float d = (float)depth_in.tex2D((int)screenPos.x, (int)screenPos.y) / 1000.0f;
 
-	//const float3 nearest = params.camera.screenToCam((int)(screenPos.x),(int)(screenPos.y),d);
+	const A input = generateInput(in.tex2D(x, y), params, worldPos);
+	const float weight = ftl::cuda::weighting(fabs(camPos.z - d), 0.01f);
+	const B weighted = make<B>(input) * weight; //weightInput(input, weight);
 
-	//const float l = length(nearest - camPos);
-	if (d == (int)(camPos.z*1000.0f)) {
-		out(screenPos.x, screenPos.y) = input;
+	if (weight > 0.0f) {
+		accumulateOutput(out, contrib, screenPos, weighted, weight);
+		//out(screenPos.x, screenPos.y) = input;
 	}
 }
 
 
-template <typename T>
+template <typename A, typename B>
 void ftl::cuda::dibr_attribute(
-        TextureObject<T> &in,
+        TextureObject<A> &in,
         TextureObject<float4> &points,       // Original 3D points
         TextureObject<int> &depth_in,        // Virtual depth map
-        TextureObject<T> &out,   // Accumulated output
+		TextureObject<B> &out,   // Accumulated output
+		TextureObject<float> &contrib,
         SplatParams &params, cudaStream_t stream) {
 	const dim3 gridSize((in.width() + T_PER_BLOCK - 1)/T_PER_BLOCK, (in.height() + T_PER_BLOCK - 1)/T_PER_BLOCK);
 	const dim3 blockSize(T_PER_BLOCK, T_PER_BLOCK);
@@ -355,115 +402,68 @@ void ftl::cuda::dibr_attribute(
         in,
         points,
         depth_in,
-        out,
+		out,
+		contrib,
         params
     );
     cudaSafeCall( cudaGetLastError() );
 }
 
-template void ftl::cuda::dibr_attribute<uchar4>(
+template void ftl::cuda::dibr_attribute(
 	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<uchar4> &out,	// Accumulated output
+	ftl::cuda::TextureObject<float4> &out,	// Accumulated output
+	ftl::cuda::TextureObject<float> &contrib,
 	ftl::render::SplatParams &params, cudaStream_t stream);
 
-template void ftl::cuda::dibr_attribute<float>(
+template void ftl::cuda::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<float> &out,	// Accumulated output
+	ftl::cuda::TextureObject<float> &contrib,
 	ftl::render::SplatParams &params, cudaStream_t stream);
 
-template void ftl::cuda::dibr_attribute<float4>(
+template void ftl::cuda::dibr_attribute(
 	ftl::cuda::TextureObject<float4> &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
+	ftl::cuda::TextureObject<float> &contrib,
 	ftl::render::SplatParams &params, cudaStream_t stream);
 
 //==============================================================================
 
-/*__global__ void dibr_normalise_kernel(
-        TextureObject<float4> colour_in,
-        TextureObject<uchar4> 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) = make_uchar4(colour.x / contrib, colour.y / contrib, colour.z / contrib, 0);
-            //normals(x,y) = normal / contrib;
-        }
-    }
-}
-
+template <typename A, typename B>
 __global__ void dibr_normalise_kernel(
-        TextureObject<float4> colour_in,
-        TextureObject<float> colour_out,
-        //TextureObject<float4> normals,
+        TextureObject<A> in,
+        TextureObject<B> out,
         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);
+    if (x < in.width() && y < in.height()) {
+        const A a = 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;
+            out(x,y) = make<B>(a / contrib);
             //normals(x,y) = normal / contrib;
         }
     }
 }
 
-__global__ void dibr_normalise_kernel(
-        TextureObject<float4> colour_in,
-        TextureObject<float4> 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) = make_float4(colour.x / contrib, colour.y / contrib, colour.z / contrib, 0);
-            //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);
-
-    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);
+template <typename A, typename B>
+void ftl::cuda::dibr_normalise(TextureObject<A> &in, TextureObject<B> &out, TextureObject<float> &contribs, cudaStream_t stream) {
+    const dim3 gridSize((in.width() + T_PER_BLOCK - 1)/T_PER_BLOCK, (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);
+    dibr_normalise_kernel<<<gridSize, blockSize, 0, stream>>>(in, out, contribs);
     cudaSafeCall( cudaGetLastError() );
 }
 
-void ftl::cuda::dibr_normalise(TextureObject<float4> &colour_in, TextureObject<float4> &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() );
-}*/
+template void ftl::cuda::dibr_normalise<float4,uchar4>(TextureObject<float4> &in, TextureObject<uchar4> &out, TextureObject<float> &contribs, cudaStream_t stream);
+template void ftl::cuda::dibr_normalise<float,float>(TextureObject<float> &in, TextureObject<float> &out, TextureObject<float> &contribs, cudaStream_t stream);
+template void ftl::cuda::dibr_normalise<float4,float4>(TextureObject<float4> &in, TextureObject<float4> &out, TextureObject<float> &contribs, cudaStream_t stream);
diff --git a/components/renderers/cpp/src/splatter_cuda.hpp b/components/renderers/cpp/src/splatter_cuda.hpp
index 1888a586720d388567a6c4d48f4a29b6b50848ac..463beefb18b54879a401580083cf21ca58b5d38f 100644
--- a/components/renderers/cpp/src/splatter_cuda.hpp
+++ b/components/renderers/cpp/src/splatter_cuda.hpp
@@ -23,13 +23,21 @@ namespace cuda {
 		ftl::cuda::TextureObject<T> &colour_out,
         const ftl::render::SplatParams &params, cudaStream_t stream);
 
-	template <typename T>
+	template <typename A, typename B>
 	void dibr_attribute(
-		ftl::cuda::TextureObject<T> &in,	// Original colour image
+		ftl::cuda::TextureObject<A> &in,	// Original colour image
 		ftl::cuda::TextureObject<float4> &points,		// Original 3D points
 		ftl::cuda::TextureObject<int> &depth_in,		// Virtual depth map
-		ftl::cuda::TextureObject<T> &out,	// Accumulated output
+		ftl::cuda::TextureObject<B> &out,	// Accumulated output
+		ftl::cuda::TextureObject<float> &contrib,
 		ftl::render::SplatParams &params, cudaStream_t stream);
+
+	template <typename A, typename B>
+	void dibr_normalise(
+		ftl::cuda::TextureObject<A> &in,
+		ftl::cuda::TextureObject<B> &out,
+		ftl::cuda::TextureObject<float> &contribs,
+		cudaStream_t stream);
 }
 }