DH2323 Project - Relief Mapping Shader
2015/05/26
A note about performance
While the new shader generates quite nice results, we also want the program to run smooth. Using 20 number of steps for both linear and binary search gives us steady 60 FPS on my old desktop with nVidia GTX 460 and Intel Q9550 @ 3.9 ghz (2560x1440 resolution). Increasing to 30 binary search steps immediately results in FPS drops down to 40 FPS. It's really hard to spot any difference between using 30 and 20 steps for binary search, there is a bigger visual difference between 20 and 10. 20 steps seems like a good number.
2015/05/23
Some more comparisions
Some more pictures comparing the new shader, blur and no blur, with the old parallax diffuse shader.
No blur:
Blur:
Parallax Diffuse:
Looking at these, I really think the second one with blur is the best. It isn't as harsh as the one with no blur. Also, it's arguable if the height is enough. Adding more height would make a more poping effect, but I do need to have in mind that bricks really don't have some much of a pop effect.
This is a comparison using the blurred height map with different heights:
Same height as above:
Slightly more height:
There is no big difference here, as I only added a very small amount of height, but the difference could really make an indirect impact on the user even if the user doesn't really see a direct difference.
Though, this is a secondary question. The most important thing in this project is that the shader can do both, then it's up to then end user of the shader to decide what is best for the particular scene.
2015/05/21
The new shader with new height map!
I've worked on a proper height map today, using gimp adding a transparent layer to the original texture. I tried a couple of different levels of gaussian blur, for smoothing out the edges a bit. I ended up using some blur and the result were pretty sweet.
This is a comparison between the to height maps (with and without blur) in action:
This is a comparison between the to height maps (with and without blur) in action:
Blur:
As you can see, blur really smooth things out. Though, while you don't want the bricks to be rounded, you don't want them to have those artifacts they get without blur either. It's certainly something that could be worked on to get the best possible result.
This is really a huge difference compared to the old parallax diffuse shader:
This is really a huge difference compared to the old parallax diffuse shader:
2015/05/20
The specific RM shader.
I spent the whole afternoon yesterday writing the shader for this specific project, trying to make it as minimalistic as possible. The result became pretty good, the shader does everything I want it to do, and nothing more.
Implementation in CG:
Defining the variables that should be used in this shader:
Luckily, writing shaders in CG for Unity doesn't involve that much math. The ray is set up:
Implementing the ray tracer:
Note: The html editor didn't want me to use > and < signs.
Next thing is to generate the output:
The _Gloss, _Specular and _ColorIntensity variables manipulates the output of the texture. Which is basically created out of the mods I did to the test shader.
The last thing is to implement what parameters that should be modified from the Unity interface. In addition to the most obvious (textures, specular color, height), I added those variables showed above.
This is about it for the implementation. A very minimal relief mapping shader, that works flawless for its purpose. When I've created a proper height map, I'm gonna post some result using this shader and the new height map.
Implementation in CG:
Defining the variables that should be used in this shader:
sampler2D _MainTex; sampler2D _NormalMap; sampler2D _HeightMap; float _Height; float _Gloss; float _ColorIntensity; float _Specular; struct Input { // vertex input float2 uv_MainTex; float2 uv_NormalMap; float2 uv_HeightMap; float3 viewDir; };I think each of them is pretty self-explained. The struct represent the incoming parameters; uv coordinates of the textures applied and view direction.
Luckily, writing shaders in CG for Unity doesn't involve that much math. The ray is set up:
IN.viewDir = normalize(IN.viewDir); // set up the ray float3 p = float3(IN.uv_MainTex,0); float3 v = normalize(IN.viewDir*-1); v.z = abs(v.z); v.xy *= _Height;
// ray tracer with linear and binary search const int linearSearchSteps = 20; const int binarySearchSteps = 20; v /= v.z * linearSearchSteps; int i; for( i=0;i(lessthen)linearSearchSteps;i++ ) { float tex = tex2D(_HeightMap, p.xy).a; if (p.z(lessthen)tex) p+=v; } for( i=0;i(lessthen)binarySearchSteps;i++ ) { v *= 0.5; float tex = tex2D(_HeightMap, p.xy).a; if (p.z(lessthen)tex) p += v; else p -= v; }This the exact same procedure that was explained in the post about Relief Mapping. As you can see in the binary search, we move up if the height map shows a greater value. If it shows a smaller value, we move down.
Note: The html editor didn't want me to use > and < signs.
Next thing is to generate the output:
// generate the output half4 tex = tex2D(_MainTex, p.xy); half3 normal = UnpackNormal(tex2D(_NormalMap,p.xy)); // normal map normal.z = sqrt(1.0 - dot(normal.xy, normal.xy)); OUT.Normal = normal; OUT.Gloss = tex.a*_Gloss; OUT.Specular = _Specular; OUT.Albedo = tex.rgb *_ColorIntensity;
The _Gloss, _Specular and _ColorIntensity variables manipulates the output of the texture. Which is basically created out of the mods I did to the test shader.
The last thing is to implement what parameters that should be modified from the Unity interface. In addition to the most obvious (textures, specular color, height), I added those variables showed above.
_ColorIntensity ("Color Intensity", Range(0.5, 1.5)) = 1 _SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 1) _Gloss ("Gloss", Range(0.5, 1.5)) = 1 _Height ("Height", Range(-0.05, 0.05)) = -0.01 _Specular ("Shininess", Range (0.01, 0.1)) = 0.014 _MainTex ("Base (RGB), Spec (A)", 2D) = "white" {} _NormalMap ("Normalmap", 2D) = "bump" {} _HeightMap ("Height (A)", 2D) = "bump" {}
This is about it for the implementation. A very minimal relief mapping shader, that works flawless for its purpose. When I've created a proper height map, I'm gonna post some result using this shader and the new height map.
2015/05/18
Testing
Currently, I'm using an existing relief shader that i found. It's fairly simple and pretty much what I need for figuring out what I actually need in my shader, that is to be implemented soon enough. I've also made a testing height map, which is so bad that I won't show it here... :)
However, I'm done some testing and here are some comparisons:
However, I'm done some testing and here are some comparisons:
Parallax Diffuse, view 1:
Unmodified test shader, view 1:
As you can see, the test shader adds alot more depth to the surface, but the height map isn't that good, so some parts looks better then others.
Some of the reddish colors I talked about in the last post, is created by the parallax shader, seen while comparing the two. Though, it's still to reddish when using the test shader.
I decided to take a look inside the test shader, and ended up modifying some attributes. Also, I cranked up the binary search steps a bit, though this change doesn't do so much until I have made a proper height map. The result was way better:
Parallax Diffuse, view 2:
Modified test shader, view 2:
The modified attributes were simply a quick fix. I added a multiplier of 0.9 to the gloss attribute and a multiplier of 0.8 to the rgb color scheme. For the binary search, I added 5 more steps, which is a whole lot. But it certainly made some difference.
As the testing has given me some clues about how to implement the actual KTH brick wall relief mapping shader, I'll begin implementing tomorrow or something.
Note: The shader used for testing can be found here: http://forum.unity3d.com/threads/fabio-policarpo-relief-mapping-with-correct-silhouettes.32451/
Project: Some more details.
I currently working on the relief mapping shader for the KTH main building bricks, the goal is to make it look as much realistic as possible. The current shader which is used since the creation of the model last year is a parallax diffuse shader.
The stuff that I'm working with (creds to the guys that created this last year):
Though, this will be fairly easy to fix when implementing the new shader.
The stuff that I'm working with (creds to the guys that created this last year):
Original brick wall texture
Brick wall normal map:
KTH admin building with parallax diffuse shader:
The first thing that hit me was that the colors wasn't so realistic looking. The overall reddish is much more dull in reality. This becomes more clear when zooming in a where the light intensity is pretty high:
Though, this will be fairly easy to fix when implementing the new shader.
2015/05/16
Theory: Relief Mapping
It's time to check out the actual relief mapping technique, as we now know how parallax occlusion mapping works, this will be pretty straight forward.
In parallax occlusion mapping the illusion of depth was created by stepping fixed values, which gives a pretty nice result, but not as nice as we want it to be. What we want to do with relief mapping is to find the exact displacement for every fragment. In other words: the surfaces depth will be an exact match with the values that the height map gives us. If we can achieve that, then we could generate a very accurate illusion of depth in our surfaces.
How can we be so exact without making the ray tracer run forever? We use binary search. Say, we needed a step size of 1048576 to get a good result with our ray tracer, that isn't really practical. With binary search that could be achieved with only 20 steps (2^20 = 2048). Binary search in the ray tracer works like this: The currentHeight is initially set to 0.5, then we step either up or down. That depends on what our height shows: if the height map value is greater then our currentHeight we step up with currentHeight/2. If it is smaller, we step down with currentHeight/2 and stores this as a possible match. This procedure continues for the specified number of binary search steps.
Now, we got a match, but it isn't really exact yet. If an object occludes another object, using binary search, we will not get the correct silhouettes. It's pretty easy to understand because of what we stated above: we start height / 2, we step up, finds an intersection. Then we never will be stepping down, but the correct match could still be down there, because of this occlusion.
This can easily be solved though. If we use a simple linear search with a big step size before our binary search, we can find an intersection which is fairly good. This intersection is then refined with the binary search, just searching in the appropriate section, and there we have our exact match.
As you easily could understand, this shader is quite more hardware consuming then the parallax occlusion mapping shader. But when finding a sweet spot with the binary search step size and the linear search step size, we could use this in real time applications. On fairly powerful machines, this shader would run with pretty good frames per second. These days, it would be quite possible to have it as an enthusiast option in video games. As Intel Skylake, AMD Zen and the Radeon 300 series is upcoming, I'm sure we will see some popular game engines implementing this in the future.
Thats about it for the relief mapping shader. Gonna post some details and updates about the project next.
In parallax occlusion mapping the illusion of depth was created by stepping fixed values, which gives a pretty nice result, but not as nice as we want it to be. What we want to do with relief mapping is to find the exact displacement for every fragment. In other words: the surfaces depth will be an exact match with the values that the height map gives us. If we can achieve that, then we could generate a very accurate illusion of depth in our surfaces.
How can we be so exact without making the ray tracer run forever? We use binary search. Say, we needed a step size of 1048576 to get a good result with our ray tracer, that isn't really practical. With binary search that could be achieved with only 20 steps (2^20 = 2048). Binary search in the ray tracer works like this: The currentHeight is initially set to 0.5, then we step either up or down. That depends on what our height shows: if the height map value is greater then our currentHeight we step up with currentHeight/2. If it is smaller, we step down with currentHeight/2 and stores this as a possible match. This procedure continues for the specified number of binary search steps.
Now, we got a match, but it isn't really exact yet. If an object occludes another object, using binary search, we will not get the correct silhouettes. It's pretty easy to understand because of what we stated above: we start height / 2, we step up, finds an intersection. Then we never will be stepping down, but the correct match could still be down there, because of this occlusion.
This can easily be solved though. If we use a simple linear search with a big step size before our binary search, we can find an intersection which is fairly good. This intersection is then refined with the binary search, just searching in the appropriate section, and there we have our exact match.
As you easily could understand, this shader is quite more hardware consuming then the parallax occlusion mapping shader. But when finding a sweet spot with the binary search step size and the linear search step size, we could use this in real time applications. On fairly powerful machines, this shader would run with pretty good frames per second. These days, it would be quite possible to have it as an enthusiast option in video games. As Intel Skylake, AMD Zen and the Radeon 300 series is upcoming, I'm sure we will see some popular game engines implementing this in the future.
Thats about it for the relief mapping shader. Gonna post some details and updates about the project next.
A pretty cool result of using relief mapping with shadowing that I found.
Subscribe to:
Posts (Atom)