diff --git a/exercise2/glsl/terrain.vert b/exercise2/glsl/terrain.vert index adf3242..62c62a1 100644 --- a/exercise2/glsl/terrain.vert +++ b/exercise2/glsl/terrain.vert @@ -48,8 +48,8 @@ float perlinNoise(vec2 p ) { vec2 ij = floor(p); vec2 xy = p - ij; - //xy = 3.*xy*xy-2.*xy*xy*xy; - xy = .5*(1.-cos(3.1415926 * xy)); + xy = 3.*xy*xy-2.*xy*xy*xy; + //xy = .5*(1.-cos(3.1415926 * xy)); float a = rand((ij+vec2(0.,0.))); float b = rand((ij+vec2(1.,0.))); float c = rand((ij+vec2(0.,1.))); @@ -77,8 +77,9 @@ float getTerrainHeight(vec2 p) } vec4 offset_point(vec4 base, vec2 offset) { - float y = getTerrainHeight(vec2(base.x, base.z) + offset); - return vec4(base.x + offset.x, y, base.z + offset.y, base.w); + vec2 new_pos = vec2(base.x, base.z) + offset; + float y = getTerrainHeight(new_pos); + return vec4(new_pos.x, y, new_pos.y, base.w); } // calculate the position of the first vertex in this square @@ -115,11 +116,11 @@ vec3 calculate_normal(vec4 terrain_position) { } // create connection vectors - vec3 p1 = (points[0] - terrain_position).xyz; - vec3 p2 = (points[1] - terrain_position).xyz; + vec3 v1 = (points[0] - terrain_position).xyz; + vec3 v2 = (points[1] - terrain_position).xyz; // calculate normal - vec3 normal = normalize(cross(p1, p2)); + vec3 normal = normalize(cross(v1, v2)); // naively assume that a normal always has to look upwards if (normal.y < 0.0) { diff --git a/exercise2/src/Viewer.cpp b/exercise2/src/Viewer.cpp index b15807e..d52f068 100644 --- a/exercise2/src/Viewer.cpp +++ b/exercise2/src/Viewer.cpp @@ -10,15 +10,22 @@ #include -#include - #include #include "glsl.h" #include "textures.h" #include +#include #include +#include + +//using vec4 = Eigen::Vector4f; +/* +#define Eigen::Vector4f vec4 +#define Eigen::Vector3f vec3 +#define Eigen::Vector2f vec2 +*/ constexpr bool REFERENCE = true; constexpr uint32_t PATCH_SIZE = 256; //number of vertices along one side of the terrain patch @@ -154,11 +161,142 @@ std::string vec4_to_str(Eigen::Vector4f &v) << v.z() << ", " << v.w() << ")"; - //std::cout << ss.str() << std::endl; + return ss.str(); +} + +std::string vec3_to_str(Eigen::Vector3f& v) { + std::stringstream ss; + + ss << "(" << v.x() << ", " + << v.y() << ", " + << v.z() << ")"; return ss.str(); } +float length(Eigen::Vector2f v) { + return sqrt(v.x() * v.x() + v.y() * v.y()); +} + +float dot(Eigen::Vector2f v1, Eigen::Vector2f v2) { + return (v1.x() * v2.x() + v1.y() * v2.y()) / length(v1) * length(v2); +} + +float mix(float f1, float f2, float value) { + float difference = f1 - f2; + + return f1 + difference * value; +} + +//source: https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 +float rand(Eigen::Vector2f c) +{ + double dummy; + return 2 * modf(sin(dot(c, Eigen::Vector2f(12.9898f, 78.233f))) * 43758.5453, &dummy) - 1; +} + +Eigen::Vector2f mul(Eigen::Vector2f v1, Eigen::Vector2f v2) { + return Eigen::Vector2f(v1.x() * v2.x(), v1.y() * v2.y()); +} + +float perlinNoise(Eigen::Vector2f p) +{ + Eigen::Vector2f ij = Eigen::Vector2f(floor(p.x()), floor(p.y())); + Eigen::Vector2f xy = p - ij; + xy = (3.0f * mul(xy, xy)) - (2.0f * mul(mul(xy, xy), xy)); + //xy = 0.5f * (1.0f - cos(3.1415926 * xy)); + float a = rand((ij + Eigen::Vector2f(0.0f, 0.0f))); + float b = rand((ij + Eigen::Vector2f(1.0f, 0.0f))); + float c = rand((ij + Eigen::Vector2f(0.0f, 1.0f))); + float d = rand((ij + Eigen::Vector2f(1.0f, 1.0f))); + float x1 = mix(a, b, xy.x()); + float x2 = mix(c, d, xy.x()); + return mix(x1, x2, xy.y()); +} + +//based on https://www.seedofandromeda.com/blogs/58-procedural-heightmap-terrain-generation +float getTerrainHeight(Eigen::Vector2f p) +{ + float total = 0.0; + float maxAmplitude = 0.0; + float amplitude = 1.0; + float frequency = 0.02; + for (int i = 0; i < 11; i++) + { + total += ((1.0 - abs(perlinNoise(p * frequency))) * 2.0 - 1.0) * amplitude; + frequency *= 2.0; + maxAmplitude += amplitude; + amplitude *= 0.45; + } + return 15 * total / maxAmplitude; +} + +Eigen::Vector3f vec4_to_vec3(Eigen::Vector4f v) { + return Eigen::Vector3f(v.x(), v.y(), v.z()); +} + +Eigen::Vector3f calculate_gpu_normal(Eigen::Vector4f pos, int gl_VertexID) { + const Eigen::Vector2f offsets[6] = { + Eigen::Vector2f(0.0, 0.0), + Eigen::Vector2f(0.0, 1.0), + Eigen::Vector2f(1.0, 0.0), + Eigen::Vector2f(0.0, 1.0), + Eigen::Vector2f(1.0, 0.0), + Eigen::Vector2f(1.0, 1.0) + }; + + int offset_index = gl_VertexID % 6; + + Eigen::Vector4f base_vertex = Eigen::Vector4f(pos.x() - offsets[offset_index].x(), pos.y(), pos.z() - offsets[offset_index].y(), pos.w()); + + // the other 2 points from the triangle + int points_index = 0; + Eigen::Vector4f points[2]; + + int iterator_offset = 0; + + // second triangle offset + if (offset_index > 2) { + iterator_offset = 3; + } + + for (int i = iterator_offset; i < (3 + iterator_offset); i++) { + // skip for current vertex + if (i == offset_index) { + continue; + } + + Eigen::Vector2f new_pos = Eigen::Vector2f(base_vertex.x(), base_vertex.z()) + offsets[i]; + float y = getTerrainHeight(new_pos); + points[points_index] = Eigen::Vector4f(new_pos.x(), y, new_pos.y(), base_vertex.w()); + + points_index++; + } + + // create connection vectors + Eigen::Vector3f v1 = vec4_to_vec3(points[0] - pos); + Eigen::Vector3f v2 = vec4_to_vec3(points[1] - pos); + + v1.normalize(); + v2.normalize(); + + Eigen::Vector3f normal = v1.cross(v2).normalized(); + + return normal; +} + +Eigen::Vector3f calculate_reference_normal(Eigen::Vector4f p1, Eigen::Vector4f p2, Eigen::Vector4f p3) { + Eigen::Vector3f v1 = vec4_to_vec3(p1 - p2); + Eigen::Vector3f v2 = vec4_to_vec3(p1 - p3); + + v1.normalize(); + v2.normalize(); + + Eigen::Vector3f normal = v1.cross(v2).normalized(); + + return normal; +} + void Viewer::CreateGeometry() { //empty VAO for sky @@ -219,7 +357,7 @@ void Viewer::CreateGeometry() { for (int j = 0; j < PATCH_SIZE; j++) { - ref_pos.emplace_back((float)j, 0.0f, (float)i, 1.0f); + ref_pos.emplace_back((float)j, getTerrainHeight(Eigen::Vector2f((float)j, (float)i)), (float)i, 1.0f); } } @@ -244,6 +382,67 @@ void Viewer::CreateGeometry() } } + // -------------------------------------------------- + // ---------------- Debugging Area ------------------ + // -------------------------------------------------- + + /* + for (int i = 0; i < ref_ind.size(); i+=6) { + struct Point { + Eigen::Vector4f position; + Eigen::Vector3f normal; + }; + + std::vector gpu_points; + std::vector ref_points; + + for (int j = i; j < i+6; j++) { + // gpu + { + auto position = ref_pos[j]; + auto normal = calculate_gpu_normal(position, j); + + gpu_points.emplace_back(Point { position, normal }); + } + + // reference + { + int index = j % 6; + auto position = ref_pos[j]; + Eigen::Vector3f normal; + + if (index < 3) { + normal = calculate_reference_normal(ref_pos[i], ref_pos[i+1], ref_pos[i+2]); + } else { + normal = calculate_reference_normal(ref_pos[i+3], ref_pos[i+4], ref_pos[i+5]); + } + + ref_points.emplace_back(Point { position, normal }); + } + } + + std::string gpu_output = "gpu_points: \n"; + std::string ref_output = "ref_points: \n"; + + for (Point& point : gpu_points) { + gpu_output += "\tpos: " + vec4_to_str(point.position) + "\tnormal: " + vec3_to_str(point.normal) + "\n"; + } + + for (Point& point : ref_points) { + ref_output += "\tpos: " + vec4_to_str(point.position) + "\tnormal: " + vec3_to_str(point.normal) + "\n"; + } + + std::cout << gpu_output << std::endl; + std::cout << ref_output << std::endl; + + abort(); + } + */ + + // -------------------------------------------------- + // -------------- Debugging Area End ---------------- + // -------------------------------------------------- + referenceVB.uploadData(ref_pos) .bindToAttribute("position"); referenceIB.uploadData(ref_ind.size() * sizeof(uint32_t), ref_ind.data());