Posts Tagged '3D'



Improving BokehShader in three.js’ depth-of-field effect

In the previous post, we have improved tDepth input for BokehShader in three.js depth-of-field example. This spared us of ugly rectangular artifacts, but the result is still far from perfect:

bokeh_before

Well, BokehShader was created 5 years ago, and it is about time that someone made it better ;) In particular, you can see a sort of halo around completely sharp objects (the girl). This happens because BokehShader only takes in account the depth of target pixel, causing nearby objects to leak into the output even if they are in focus. To fight it, we can weight the pixels with abs(theirDepth.x – focus). This obviously doubles the number of texture sampling, so we have to cut the number of samples to maintain reasonable performance. The result, however, looks somewhat better:

bokeh_after

Thoughts?

Three.js’ depth-of-field effect and transparent objects

Last month I had to create spine runtime for three.js for one of the projects. Basically, animating a number of planes with transparent texture(s), nothing fancy. But, new year brings new challenge – the client wants these animations to work with dof effect. However, adding dof effect in the project “as is” did this:
The cat warps space around itself Continue reading ‘Three.js’ depth-of-field effect and transparent objects’

Three.js dissolving effect

Oh btw, I have added dissolving effect to my shooter game from last post. The effect is basically one grayscale noise texture + custom shader material. Fragment shader is nothing fancy, you can understand how it works by looking at this simplified demo:

Vertex shader, however, was a bit tricky, because I had these animated objects (imps), and there are no animated vertex shader examples around. The trick? Shader editor in firefox devtools. All I had to do was create simple three.js demo with animated object using MeshBasicMaterial, open shader editor and find corresponding program. This program, however, looks like this:

Screen Shot 2014-08-22 at 20.20.30and you might want to clean that up before using in your custom material. To that end, I used good old GPP. Finally, putting it all together was just a matter of guessing which uniforms I need to add myself, and which are provided by the engine.

First-person shooter using three.js

I am no longer working on COLT and currently contemplating what to do next. I have strong flash background, but this technology does not feel well. With WebGL technology exploding, on the other hand, I’ve decided to do old-school first-person shooter to see if this kind of work is something I want to do from now on, and get better idea of all the challenges involved.

Click to play

The result

The result is this sort of game where you pointlessly run around the platform and shoot monsters. Originally I thought I would complete it in 2-3 days, Ludum Dare style, but since I had no enough motivation to spend all my time on this, it took me whole month of random short coding sessions to “complete” it (and there are still some bugs to fix).

Another thing I hoped for was to write “how to make complete game with three.js” tutorial that all the internet would link to, as apparently there are no tutorials like that. It is easy to write “how to move the box” tutorial, but something more takes time that noone is really willing to spend. Well, the bad news are that I still can’t write the tutorial. I would need to make 10 more games like this before I can write one :) So the rest of this post is simply going to be adventure log, noting some tricks I used and some mistakes I made along the way.

Continue reading ‘First-person shooter using three.js’

3D bar chart on the globe using three.js

When people need to present per-country data such as GDP, they usually resort to color-coded maps or simple bar charts. In 3D, you could combine these two and end up with something like this:

globe bar chartThis is very obvious idea, yet I had no time to try it until last November. It worked, and I went on with my life, leaving the code to collect the dust. Then I saw Kaspersky cyberthreat map this March, which used extruded country shapes to support zooming. This reminded me this gold I was sitting on, and now I was finally able to fork out some time to undust it and push to github.

Overview

Basically, you take 2D country shapes and tessellate it on the sphere surface, then extrude towards the sphere center. You end up with cone-shaped meshes that you can then scale to build this kind of geo- bar chart. The catch is that corresponding three.js scene weighs much more than its 2D source data (ObjectExporter blows 100 KB of GeoJSON up to many MBs). Much of this extra weight comes in the form of useless digits in vertex data, but even with that removed there is extra dimension, data duplication and so on. To work around this issue you have to build 3D geometry from tessellated 2D data on the fly – this puts 3D data size at ~150% of 2D. Still sucks, if compared to TopoJSON, but tolerable. Corresponding code looks like

for (var name in data) {
    geometry = new Map3DGeometry (data[name], innerRadius);
    globe.add (data[name].mesh = new THREE.Mesh (geometry, material));
}

Map3DGeometry class and the tool to create the data are now at github.

Tessellation

Following documents the problems you would encounter if you did it yourself, so you may probably skip it. Continue reading ‘3D bar chart on the globe using three.js’

Transparent video texture in three.js

Some weeks ago I was asked if it is possible to have transparent video texture in THREE.js and, although I was sure it’s totally possible, I could not find any examples of it. Today I am sharing one such example with you, so you don’t have to code it yourself in case someone will ask you the same question :)

Ladies

This demo is using good old “green screen” trick to create transparency in otherwise opaque texture. You can find similar examples out there, but they are neither THREE.js- nor GPU- based.

So, where do you start? I started here – you can easily tell how much code was borrowed from Lee Stemkoski. Basically, I only had to replace MeshBasicMaterial with ShaderMaterial. Creating custom material sounded scary at first, but turned out to be really easy in the end thanks to helpful tutorials (such as this one). Finally, the shader – I want it to be as simple as possible, so no fancy color conversion stuff in there – it operates directly in RGB using 1st method from my earlier post (err, no, it’s different method). The result is basically two lines of code:

vec3 tColor = texture2D(texture, vUv).rgb;
gl_FragColor = vec4(tColor, (0.9 - dot(color, tColor) / (length(tColor) + 0.05)) * 3.0);

Let’s break 2nd line down. It starts at the dot product of colors – the dot product approaches 1 when texture color approaches the color we want to remove. In theory, it should have been dot (normalize(color), normalize(tColor)), but normalize(tColor) part fails on pure black pixels (such as girl hair), so I had to use tColor / (length(tColor) + small number) instead. This handles black colors better, but now the dot is always less than 1 – hence you see 0.9 in there. You can also see that I do not normalize “color” parameter – there is no good reason to do it inside the shader for every pixel. Finally, 3.0 is randomly chosen magic number that determines how fast texture colors become transparent as they approach the color to remove – check out this graph to see its effect.

Update: I have just made the shader even simpler using length(tColor – color). Initially I thought absolute color values will not work well, but the actual test says they do:

Angle-based shader on the left, distance-based shader on the right.

Angle-based shader on the left, distance-based shader on the right.

Using D3.js with THREE.js, part II

So, our little D3 + 3D experiment continues. This time we will be trying to replicate bar chart tutorial 2 in 3D.

Transitions

Two things need to be done before D3 transitions could work. First, D3 must be able to interpolate “attribute” values, so we need to implement both setAttribute and getAttribute methods in THREE.js:

// this one is to use D3's .attr() on THREE's objects
THREE.Object3D.prototype.setAttribute =
  function (name, value) {
    var chain = name.split('.');
    var object = this;
    for (var i = 0; i < chain.length - 1; i++) {
        object = object[chain[i]];
    }
    object[chain[chain.length - 1]] = value;
}

// and this one is to make'em work with D3's .transition()-s
THREE.Object3D.prototype.getAttribute =
  function (name) {
    var chain = name.split('.');
    var object = this;
    for (var i = 0; i < chain.length - 1; i++) {
        object = object[chain[i]];
    }
    return object[chain[chain.length - 1]];
}

Second, D3’s selectAll() now needs to actually select something to build updating selection. We can make it select various descendants of Object3D type, for example:

// now selectAll must actually do something
THREE.Object3D.prototype.querySelectorAll =
  function (selector) {
    var matches = [];
    var type = eval(selector);
    for (var i = 0; i < this.children.length; i++) {
        var child = this.children[i];
        if (child instanceof type) {
            matches.push(child);
        }
    }
    return matches;
}

These changes are already enough to replicate their simple transition example in THREE.js:

var t = 1297110663,
    v = 30,
    data = d3.range(9).map(next);

function next() {
  return {
    time: ++t,
    value: v = ~~Math.max(10,
      Math.min(90, v + 10 * (Math.random() - .5))
    )
  };
}

setInterval(function() {
  data.shift();
  data.push(next());
  redraw();
}, 1500);

function redraw() {
  d3.select( chart3d )
    .selectAll("THREE.Mesh")
    .data(data)
  .transition()
    .duration(1000)
    .attr("position.y", function(d, i) { return d.value; })
    .attr("scale.y", function(d, i) { return d.value / 10; })
}

But to replicate their final keyed data join example, we need to take care of one last thing…

Exit selections

We want D3’s remove() to actually remove 3D objects. Now, you might be thinking, this is easy one, we’ll just add removeChild() to Object3D prototype

THREE.Object3D.prototype.removeChild =
  function (c) {
    this.remove(c);
  }

and it should work, right? Wrong, it does not! To solve this one, I had to check D3 source. Turns out D3 does not call removeChild on selection parentNode but on node’s parentNode itself, so we need to modify our appendChild as follows:

THREE.Object3D.prototype.appendChild = function (c) {
    this.add(c);
    // create parentNode property
    c.parentNode = this;
    return c;
}

Final result

With these changes, it is now possible to do this:

function redraw() {

	var bars = d3.select( chart3d )
		.selectAll("THREE.Mesh")
		.data(data, function(d) { return d.time; });

	// move existing bars to their new place
	bars.transition()
		.duration(1000)
		.attr("position.x",
			function(d, i) { return 30 * i; })

	// add new bar and make it grow
	bars.enter().append( newBar )
		.attr("position.x",
			function(d, i) { return 30 * (i + 1); })
		.attr("position.y", 0)
		.attr("scale.y", 1e-3)
	  .transition()
		.duration(1000)
		.attr("position.x",
			function(d, i) { return 30 * i; })
		.attr("position.y",
			function(d, i) { return d.value; })
		.attr("scale.y",
			function(d, i) { return d.value / 10; })

	// remove the bar that is no longer in data set
	bars.exit().transition()
		.duration(1000)
		.attr("position.x",
			function(d, i) { return 30 * (i - 1); })
		.attr("position.y", 0)
		.attr("scale.y", 1e-3)
		.remove()
}

Final demo

This is still not complete THREE.js “plugin” but, as you can see, it’s a nice start.


Old stuff

December 2016
M T W T F S S
« Jan    
 1234
567891011
12131415161718
19202122232425
262728293031  

Oh, btw…