My latest project finally gone live. I spend some efforts working on the liquid/fluid looks of the bubble and found some interesting techniques. Really thankful for the people who create these techniques and willing to share with everyone.

Here is a demo link to the bubble, you can click on the bubble to launch a wave as well.

http://www.bongiovi.tw/projects/bubble/

## Animate bubble in shader and normal

The first task is to animate the bubble and getting the right normal. The way we’ve done it is to put everything to the vertex shader and then calculate the normals based on the vertices positions. This approach makes it really easy for us when we decide to add the ripples in the bubble. We only need to calculate the position offset caused by the ripple and added to the vertex position, then the normal map is updated.

In order to do so, in the positions buffer instead of putting in the position of the vertex, I put the rotation of x , rotation of y and the size of the bubble. And to get the position of vertex you can use this function :

vec3 getPosition(vec3 values) { float rx = values.y / numSeg * PI - PI; float ry = values.x / numSeg * PI * 2.0; vec3 pos = vec3(0.0); pos.y = cos(rx) * values.z; float r = sin(rx) * values.z; pos.x = cos(ry) * r; pos.z = sin(ry) * r; return pos; }

Then using the position to get the 3D noise ( I’m using this noise function) and the ripple height.

At the end, the final position of the vertex is the original position (sphere) + noise + ripple.

Because we are using the rotation X and rotation Y to get the vertex position so we can get the neighbor position just by offset this rotation X and rotation Y. And with the position of the neighbors, we can calculate a simple normal by using cross product. The shader code looks like this:

vec3 currPos = getFinalPosition(position); // getPosition() + noise + ripple vec3 rightPos = getFinalPosition(position+vec3(1.0, 0.0, 0.0); vec3 bottomPos = getFinalPosition(position+vec3(0.0, 1.0, 0.0); vec3 vRight = rightPos - currPos; vec3 vBottom = bottomPos - currPos; vec3 normal = normalize(cross(vBottom, vRight));

This way you could get a animate bubble and a normal with it.

## Distortion with background image

The second task is to distort the background behind the bubble. I started with the refract function but that requires a cube map, we’ve only got an image. So i start look around to see if there’s a simpler way to create the refraction effect, then I found this article :

http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter19.html

In short you can create a refraction effect by just using the normal.xy as a displacement map.

vec2 newUV =uv + normal * distortionRate; gl_FragColor = texture2D(texture, newUV);

with this you can achieve a good simulate refraction effect with just a background image instead of a cube map.

## Lights

At the beginning of the project we started with the traditional diffuse and specular lighting. It works however the bubble lacks one important feature : reflection. I went back to search for possible solutions and then found this amazing article :

https://www.clicktorelease.com/blog/creating-spherical-environment-mapping-shader

using this effect add a lots to the bubble and gives it a very strong glassy/fluid look, which is exactly what the client after.

## Small Details

We also add 2 small detail to the bubble :

- Distorted a bit more toward the edge of the bubble.
- Darker on the edge of the bubble.

These 2 works in the same way and I need a value that changes from the center of the bubble to the edge. A quick way to do it is to get the dot product of the normal and the vector(0.0, 0.0, 1.0). Once you got this you can add this to the distrotionRate and get the different distortion between the center and the edge.

## Summary

It is really fun and good learning process to go through all these steps in order to create the final look of it. I believe there are more ways to achieve this look but we really happy with this one. I’m also trying and learning cubemap now. For the next time I might try with the cubemap to recreate this fresnel effect. And even more a dynamic cubemap could be an interesting effect to add on this.

One of my codevember experiment is based on this technique. I only remove the noise animation also replace the lighting map with a much simpler one ( just a glow on the edge )

http://yiwenl.github.io/Codevember/labs/wen/24_bubble/dist/index.html

and also the case study of the whole project on stink digital’s site :

http://www.stinkdigital.com/work/sky-q/