Profile for cwnation

The Story Continues...

...on facebook.com/cwnation. you would have to create an account at www.facebook.com to follow, but trust me, there is plenty going on....

this site will stay maintained, but there will not be much news here. feel free to search the past if you are feeling nostalgic.

Tuesday, April 02, 2019

One of my goals for 2019 is to improve my existing pages. Yesterday I decided to work on my old 2D noise page. We normally use Perlin/Simplex noise to make terrain heightmaps, but on that page I used Fourier transforms instead. Perlin/Simplex noise are a fast approximation of the things you can get from Fourier transforms.

The 3D renderer on that page always bothered me. It was one of my early experiments with WebGL. I had never been able to figure out exactly what I didn't like or how to fix it.

I decided to improve the renderer.

I read through the code to and realized it was written so long ago that let and const weren't even widely available! I started by updating the code style to match the code I write today. I read through the rendering code and decided to switch from raw WebGL to regl.js, which lets me experiment and iterate much more quickly.

I wanted to compare the old and new output easily, so I put them side by side on the page. I wanted to try two techniques with the new renderer:

  1. Instead of building a new 3D quad mesh on the CPU every time the data changed, I built a single 2D triangle mesh with x,y, and then read the heightmap data from a texture on the GPU. Reading textures from vertex shaders is widely supported now. This way, I only have to update the texture on each render instead of rebuilding the mesh. Unfortunately I couldn't figure out why it wasn't interpolating pixels values; I had to put in my own interpolation.
  2. Instead of calculating normals and lighting on the CPU every frame, I calculated them in the fragment shader on the GPU. But I didn't want to use a standard lighting system; I wanted to apply outlines instead. I had learned how to do this in mapgen4 and wanted to try an even simpler approach here. This worked out really well.

In this comparison you can see how the dark/light spots in the noise are renderered in the two renderers. With the old renderer (green) the color changes but the shapes are all mushy. With the new renderer (gray) the dark/light matches the noise, and the mountain peaks are easier to see.

2D noise values Old renderer New renderer
Landscape: old (green) and new (gray) renderer

In smooth areas you can see how the outlines help show the shapes. You can also see that the old renderer flipped the elevation upside down (oops!).

2D noise values Old renderer New renderer
Smooth area: old (green) and new (gray) renderer

With blue noise (positive exponents) the new renderer looks much better:

2D noise values Old renderer New renderer
Blue noise: old (green) and new (gray) renderer

I'm really really happy with outlines! Compare:

No outlines Light outlines Medium outlines
Without and with outlines

The outlines are one line of code:

void main() {   float light = 0.1 + z - fwidth(z) * u_outline;   gl_FragColor = vec4(light, light, light, 1); } 

I darken the color based on fwidth(z). The GL extension OES_standard_derivatives calculates how much an expression changes from the current pixel(fragment) to adjacent pixels. When z changes a lot, that usually means it's changing from one mountain peak to another, so I darken the output color.

There are still more things I'd like to improve on this page, but the renderer was the thing that bothered me the most, and I'm now happy with it. The other changes will wait until another day.

In the last post I described how I sometimes describe a problem with a matrix, and then look at the matrix transpose to see if it gives me new ideas. Another technique I use is to look for a factoring.

In algebra, factoring transforms a polynomial like 5x² + 8x - 21 into (x + 3)·(5x - 7). To solve 5x² + 8x - 21 = 0, we can first factor into (x + 3)·(5x - 7) = 0. Then we say that x + 3 = 0 or 5x - 7 = 0. Factoring turns a problem into several easier problems.

x 3
5x 5x² 15x
-7 -7x -21

Let's look at an example: I have six classes, File, EncryptedFile, GzipFile, EncryptedGzipFile, BzipFile, EncryptedBzipFile. I can factor these into a matrix:

Uncompressed Gzip Bzip
Unencrypted File Gzip(File) Bzip(File)
Encrypted Encrypt(File) Encrypt(Gzip(File)) Encrypt(Bzip(File))

Using the Decorator pattern (or mixins), I've turned six different types of files into four components: plain, gzip, bzip, encrypt. This doesn't seem like much savings, but if I add more variations, the savings will add up. Factoring turns O(M*N) components into O(M+N) components.

Another example comes up when people ask me things like "how do you write linear interpolation in C#?" There are a lot of potential tutorials I could write:

C++ Python Java C# Javascript Rust Idris
Interpolation
Neighbors
Pathfinding
Distances
River maps
Isometric
Voronoi
Transforms

If there are M topics and N languages, I could write M*N tutorials. However, that's a lot of work. Instead, I write a tutorial about interpolation, someone else writes a tutorial about C#, and then the reader combines knowledge of C# with knowledge about interpolation to write the C# version of interpolation.

Like transpose, factoring only helps sometimes, but when it applies, it can be quite useful.

Monday, April 01, 2019

This year we travelled to a (not so) sunny San Francisco on Epic Games' invitation to show Oceanhorn 2 at the Unreal Engine venue at GDC 18. What an amazing opportunity! We prepared a great demo to show at the expo, so if you're an Oceanhorn fan, make sure to drop by and play the latest build of our game. You will also meet us, the developers, and have a chat!


Look at that! Our latest demo takes you to the Great Jungle of Pirta, where the Owru nation is divided by an old grudge. Will our heroes be able to unite the owrus and get them to join the fight against Mesmeroth's Dark Army.

Yes, I said heroes! One of the defining features of Oceanhorn 2 is the party members, who will be on your side through the adventure. Trin, the granddaughter of Arcadia's leader Archimedes and Gen, a mysterious robot wielding an old samurai weapon eki.

The game is still far from being finished, but it will be worth the wait!