TDT4230_final_project/report/report_part2_hills.md

5.6 KiB

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.

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.

#!/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.

#!/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).

Much better

Now we'll up the granularity by decreasing the UV step per vertex along the plane, and enable the displacement map:

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:

/*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:

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:

lights[id].position = vec3(node->MV * vec4(node->position, 1.0));

Which should have been

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

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:

#!/usr/bin/env bash
echo Windows XP background incoming? | boxes -d whirly -a c -p h15