Friday 10 July 2015

GSoC Week 4 and 5: Triplanar Texture Mapping

According to my proposed timeline, I was supposed to work on quadtree LOD implementation in this week. As I already implemented procedural terrain generator noise functions, I just needed to work on Landscape class to implement the LOD functionality. This would have increased the rendering performance for larger terrains. But as I started into the fourth week, I realized that I might have to make changes in the terrain shaders. This shifted my focus on understanding the shaders, as I am new to GLSL. So finally I decided to implement the Triplanar Texture mapping instead of quadtree.


Problem with Planar texture mapping

Normally, textures are mapped to a surface using UV coordinates which are 2D in nature. So while mapping textures to a particular surface, we don't take height difference between two neighbouring UV coordinates under consideration and just map the textures. This is the gist of Planar texture mapping. So the problem arises is that the neglected hight difference might cause texture to stretch between two points. Unlike simple objects where we can try unwrapping the UV coordinates, in procedurally generated objects it is hard to know the nature of surface and so the corresponding UV unwrapping.


Solution? Triplanar Texture mapping

The Triplanar texture mapping is rendering the same texture three times along the X, Y and Z axis. The fragments that are facing the corresponding directions gets that texture applied to it according to the ratio of the normalized and absolute world space normal. So essentially we are using actual world coordinates instead of UV to apply texture three times in the fragment shader of the material.


Above is a comparison of Planar vs Triplanar texture mapping using the same texture. Notice how textures are stretched in the upper picture while in the lower picture, the original dimensions are somewhat maintained. Although the results are somewhat less desirable due to blending anomalies. 


Steps to Triplanar Textures

  1. Calculate blend factor for each fragment for all the three directions using the world space normal.
  2. Now sample the texture along the three directions using the world texture coordinates.
  3. Mix the blend values with the textures to get a final value of textures for that fragment.
  4. Similar steps can be applied for normal/bump maps.

Here is an another example with a grassy rock texture to the same terrain that we used earlier. Normal texture mapping stretches the whole texture to the surface which gives it an unrealistic look. Here Triplanar texture comes to the rescue.

Using Planar Textures with UV mapping
Triplanar Texture mapping


Problems Faced

I am still having trouble with properly blending the textures from different directions. Some textures like the black and white tiles look really bad as the terrain gets rough. I am still trying to formulate a blending method that will result in better blend factors.

Wednesday 1 July 2015

GSoC Midterm Status

So here I will give an update to the progress that I have made so far, the milestones that I completed and timeline changes that happened so far!

Overview

Week 1: Fixed a few bugs, fixed landscape example and added a basic noise generator function in Heightmap class.

Week 2: Started implementing Simplex Noise and added C bindings for new API call soy.textures.Heightmap(texture_width, texture_height, frequency).

Week 3: Implemented Simplex Noise and later did some performance improvement to it by removing redundant code. Milestone 1 completed.

Week 4 and 5: Supposed to work on LOD and quad tree implementation but decided to work on Texture mapping instead. Implemented Tri planar texture mapping. Added Triplanar class to soy.materials, added corresponding shaders and the C bindings.

About week 4 and 5

Due to some reasons, I got a bit slow in this week but I will catch up with the upcoming milestones. At first I started working on Level of Detail but soon realized that I have to write separate shaders for Terrain, so why not implement Tri Planar Texture mapping first. In that way, I will gain more knowledge over GLSL and that will facilitate the implementation of LOD.

So pretty much I wasted a few days struggling to decide between two sub milestones as both of them are important. Well then finally with the consent of my mentor, I decided to go for texture mapping. There is still some work left as the output is not as desired.

Problems Faced

Currently there is a small problem with the fragment shader for Tri planar texture mapping as the normal vectors are not giving the correct values, so the final texture is somewhat gibberish.


Schedule and Lagging

I am running about four to five days behind the schedule but I hope to cover all the left out work before the next milestone. I will complete the automatic texture mapping by this week and move to the next sub milestone as soon as possible.


GSoC Week 3 : Further into Terrain Generation

I spent most of my third week by adding improvements to the noise functions and experimenting with different parameters of it. In my previous blog on Simplex Noise I explained the different terminologies used in Noise such as Amplitude, Frequency , Octaves and Persistence. Here I will be showing the different type of terrains generated by changing these parameters.

Just to recall, the current API call for creating a heightmap texture is
soy.textures.Heightmap(texture_width, texture_height, frequency)

So I am planning to add amplitude, number of octaves and persistence as parameters to the API as well. But for now, just to avoid confusion for the new developers I have kept it simple. So here I will be manually altering the parameters from libsoy library to show some of the terrains created by the Noise functions I implemented.

So here a terrain with persistence equals to 0.8, number of octaves added is equal to 12 and frequency equals to 5. I am currently working on removing the sharp edges from terrain as shown in the example. We can smoothen the terrain by reducing the frequency and increasing the persistence. So here is another example (persistence = 1.0,octave = 12,  frequency = 1).


Increasing the frequency will bring in some new sharp peaks bur the overall appearance of terrain will remain the same. Here is an example below after increasing the frequency to 10.


I will be adding a dictionary in the future with some custom keywords such as mountains, plateau, desert, etc. which will automatically select the best possible terrain configuration for the user. But due to the bad performance for rendering larger terrains, I am keeping it in my bucket list until completion of some terrain rendering optimization techniques and automatic texture mapping of terrains. So for now I am moving onto my next milestone, but I will keep optimizing and improving the procedural terrain generation mechanism.


 

GSoC Update: Understanding Simplex Noise

Before diving into the concept of Simplex Noise, we need to understand what noise is and what are the applications of it. Here I will be giving an overview about different type of noises and why and how they were developed.


Background

So it all started back in 1980s when Ken Perlin was working on the CGI for the animated movie Tron. The graphics were not realistic at that time and have a machine like appearance which made them look artificial. So Perlin developed a gradient noise which was pseudo random in nature to create natural looking textures. After that, people started using noise extensively all over the world.

Source: GPU Gems 2

The picture above is an example of procedural bump mapping using pseudo random noise. I have written pseudo random and not just random because we need to have a predictable texture, making it completely random might cause problems. For more noise related example, you can have a look at my previous blog.


About Perlin Noise

Perlin noise is a gradient noise, which means that we put pseudo random gradient points at fixed interval of time and then interpolate them into a smooth function as shown in the figure below. 


For 1D noise, we assign a gradient values to integer coordinates and then interpolate between the points keeping the value of function zero at coordinate points.

A gradient function in 1D

For 2D noise, the method is more or less similar. Here we have a two dimensional square grid and at any point on surface, the function value is calculated by taking contribution from the four corners of the grid cell. The value from each corner is extrapolation of the linear ramp with assigned gradient.

There are few disadvantages of using Perlin noise, First of all the complexity of the algorithm is O(N^2) and secondly there are directional artifacts which is not desirable. 


Terminologies used in Noise

Earlier we talked about different wave like functions that were created by interpolating gradient points. Now we have a understanding that noise is like a wave and in fact a fifth degree polynomial blending function is used by Perlin noise for interpolation. So noise is also represented with similar terminologies as that of a regular wave i.e. sine or cosine wave.

Amplitude: It's the difference between maximum and minimum value of the function.

Wavelength: It's the distance between two gradient points on the function.

Frequency: It is inverse of wavelength.

Octave: Just like musical octaves, each successive noise function have a frequency double than the previous one.

Persistence: For simplicity, we can think of it as a factor with value between 0 and 1. It is multiplied with amplitude in successive octaves.


Simplex Noise

In Simplex noise, we try to fill the space with simplices (geometrically compact shapes) having the minimum number of corners. For example, in one dimension we use straight line, in two dimension we use equilateral triangle( three corners), in three dimension we use tetrahedron(4 corners) and so on.

Here I am going to talk about its implementation in mainly 2D textures for generating heightmaps for terrains. So as I mentioned, for 2D space a simplex grid is made up of equilateral triangles.

A 2D Simplex Grid

So similar to Perlin Noise, the value on the surface is calculated by summation of contribution from each corner of simplex grid. Note that Perlin noise had four corners corresponding to a two dimensional grid, but here we have only three.


  The most important part of calculation of noise value at surface is to find the simplex in which we are currently present. In 2D grid we can skew the equilateral triangles to form a grid of right angled isosceles triangles such that two such triangles form a square with side of length one. 

Now by looking and comparing the values of the transformed coordinates (x,y) , we can easily find out about the position of the point in the grid.

Due to lesser computational complexity of O(N) and due to the ease of implementation, Simplex Noise is preferred over Perlin noise.


Adding octaves to the noise function

Multiple octaves of noise can be added by summing multiple passes of noise. The contribution of each succeeding octave is determined by the persistence. The larger the persistence, the more of each octave is included in the total.Here is an example where six octaves of noise is added up and the final result is shown.

Different Octaves of Noise

Final noise pattern after multiple passes of noise function

This might not be the best explanation of Simplex Noise but I have written it to make my own understanding clear about the noise algorithm. You can find a detailed and simplified explanation of Simplex Noise in the article by Stefan Gustavsan. Other references include Perlin Noise explanation by Hugo Elias and a talk by Ken Perlin himself.

Leave a comment if you find anything out of place.