105 lines
5.6 KiB
Markdown
105 lines
5.6 KiB
Markdown
# Creating the hills
|
|
|
|
To make my plane of grass I first needed a plane. I added a generator function in `glutils.cpp` which makes a segmented plane for me with as many vertices and faces as specified. This was needed since I plan to add a displacement map capability, but only do so in the vertex shader. I thus need a lot of vertexes. The other route would be to make a texel shader which divides the faces into smaller faces and use it on the plane, which would save memory bandwith. But making it from the start is the easier option.
|
|
|
|
I added the plane to the scene and set the cobble texture on it.
|
|
|
|
It didn't look right, so I went through the shader again to make sure the lighting was correct. I failed to pass the specular shininess factor properly as a uniform. I also had failed to account for the specular component being negative (before raising it to the power of the shininess). I added this check and now the lighting looks correct.
|
|
|
|
![](images/0-base.png)
|
|
|
|
While at it I added the ability to specify all the color components and the attenuation per light source. With this I'm able to create a sun far away without being bothered by attenuation.
|
|
|
|
Now for the displacement of the plane. I created a `PNGImage` generator function which generates a perlin noise texture with the amount of layers and scales specified.
|
|
This I registered as the displacement texture for the plane. In the vertex shader I added a `isDisplacementMapped` uniform shader flag which adds the normal vector multiplied by the displacement to each vertex.
|
|
|
|
![](images/1-perlin-displacement.png)
|
|
|
|
```{.shebang im_out="stdout"}
|
|
#!/usr/bin/env bash
|
|
echo A M A S I N G | boxes -d unicornsay -a c -p h10
|
|
```
|
|
|
|
I've yet to modify the normals from the displacement, so the hill currently won't cast shadows. I currently don't have a plan on how to fix this. Perhaps use the bitangents to calculate the slope of the displacement in the vertex shader?
|
|
|
|
At this point I went online a found myself a grass texture and normal map.
|
|
|
|
![](images/2-wrong-handedness.png)
|
|
|
|
```{.shebang im_out="stdout"}
|
|
#!/usr/bin/env bash
|
|
printf " Something's wrong...! \n" | cowsay -f head-in | sed -e "s/^/ /"
|
|
```
|
|
|
|
Apparently, the direction of the normal map colors aren't the same everywhere. I therefore added a flag to the image loader function which will flip the handedness. (inverting the R and G channels).
|
|
|
|
![](images/3-flipped-handedness.png)
|
|
|
|
*Much better*
|
|
|
|
Now we'll up the granularity by decreasing the UV step per vertex along the plane, and enable the displacement map:
|
|
|
|
![](images/4-fine-plane.png)
|
|
|
|
## Scrolling the field
|
|
|
|
Now, how can we scroll this plane?
|
|
|
|
I decided the easies way would be to add a uniform variable called `uvOffset` to the vertex shader. Now I can simply scroll the plane by adding to this offset to all the UV coordinates in the vertex shader before passing it to the fragment shader:
|
|
|
|
```c++
|
|
/*vec2*/plainNode->uvOffset += /*vec2*/speed * timeDelta;
|
|
```
|
|
|
|
The code above works since I added in some operator overloads for `vec2`, `vec3`, and `vec4` with scalars.
|
|
|
|
Now we unfortunately see steep cuts where the perlin noise texture repeats. This we simply fix by mirroring the texture on repeat with `GL_MIRRORED_REPEAT`:
|
|
|
|
![](images/5-gl-mirror.jpg)
|
|
|
|
An another solution to making the perlin noise repeatable is to pass the repeat size into the `glm::gtx::perlin` function as I create the texture. But as of now I chose the quick and dirty solution. It also has the added effect of creating a less repeating texture. It repeats from 0-2 instead of 0-1.
|
|
|
|
At this point I was stuck with a bug where the coordinates of the lights were doubled. After two days of debugging I found the line where I update pass the light position into the uniform in the fragment shader:
|
|
|
|
```c++
|
|
lights[id].position = vec3(node->MV * vec4(node->position, 1.0));
|
|
```
|
|
|
|
Which *should* have been
|
|
|
|
```c++
|
|
lights[id].position = vec3(node->MV * vec4(vec3(0.0), 1.0));
|
|
```
|
|
|
|
*...yeah.*
|
|
|
|
## Normal mapping the displacement
|
|
|
|
After that goober, I moved on to try to rotate the normals according the displacement map. After some playing around I landed on this solution in glsl:
|
|
|
|
\small
|
|
```c++
|
|
if (isDisplacementMapped) {
|
|
float o = texture(displaceTex, UV).r * 2 - 1;
|
|
float u = (texture(displaceTex, UV + vec2(0.0001, 0)).r*2-1 - o) / 0.0004; // magic numbers!
|
|
float v = (texture(displaceTex, UV + vec2(0, 0.0001)).r*2-1 - o) / 0.0004; // magic numbers!
|
|
TBN = mat3(
|
|
normalize(tangent + normal*u),
|
|
normalize(bitangent + normal*v),
|
|
normalize(cross(tangent + normal*u, bitangent + normal*v))
|
|
);
|
|
}
|
|
```
|
|
\normalsize
|
|
|
|
Here I find the slope along the displacement map along the `U` and `V` direction. `o` is the 'origin', and `u` and `v` are the tangent and bitangent slope cosines *(derived with the help of a few magic numbers which should be made into uniforms really,s ince they only apply for the plane as of now)*. Using these cosines I can simply add the normal vector multiplied by the cosine to the tangent and the bitangent and normalize them, giving me the new tangents. I can from here derive the new normal vector by simply computing the cross product of the two tangents.
|
|
|
|
This did however give me a pretty coarse image, so I moved the computation of the TBN matrix from the vertex shader to the fragement shader. This will give me a slight performance penalty, but I can undo the change in a simplified shader should I need the performance boost later. Here we can see how the displacement affects the normals along the displaced plane:
|
|
|
|
![](images/6-displacement-normals.png)
|
|
|
|
```{.shebang im_out="stdout"}
|
|
#!/usr/bin/env bash
|
|
echo Windows XP background incoming? | boxes -d whirly -a c -p h15
|
|
```
|