Initial commit

This commit is contained in:
github-classroom[bot] 2024-11-29 09:50:03 +00:00 committed by GitHub
commit 686dcaf351
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
68 changed files with 6230 additions and 0 deletions

40
.gitignore vendored Normal file
View file

@ -0,0 +1,40 @@
# Compiled Object files
*.slo
*.lo
*.o
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# Visual Studio
.vs/*
out/*
CMakeSettings.json
*.un~
*.swo
*.swp
build/*
bin/*
*.DS_Store

6
.gitmodules vendored Normal file
View file

@ -0,0 +1,6 @@
[submodule "lib/glfw"]
path = lib/glfw
url = https://github.com/glfw/glfw.git
[submodule "lib/eigen"]
path = lib/eigen
url = https://github.com/eigenteam/eigen-git-mirror

61
CMakeLists.txt Normal file
View file

@ -0,0 +1,61 @@
cmake_minimum_required(VERSION 3.6)
# Set project name
project(shaderpipeline)
# Check for C++17 support
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++17" COMPILER_SUPPORTS_CXX17)
if(COMPILER_SUPPORTS_CXX17)
set(CMAKE_CXX_STANDARD 17)
else()
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++17 support. Please use a different C++ compiler.")
endif()
# Add headers
include_directories("include/")
# OpenGL library
find_package(OpenGL REQUIRED)
# Add Glad
add_library(glad "glad.c")
include_directories(include)
# Check if GLFW and Eigen are not empty
file(GLOB GLFW_DIR lib/glfw/*)
list(LENGTH GLFW_DIR NR_FILES_IN_GLFW_DIR)
if(NR_FILES_IN_GLFW_DIR LESS_EQUAL 1)
message(WARNING "Did you forget git submodule update --init --recursive ?")
endif()
# Add GLFW as a submodule
add_subdirectory(lib/glfw)
include_directories(lib/glfw/include)
# Add Eigen as a submodule
add_subdirectory(lib/eigen)
include_directories(lib/eigen)
# Define the glsl files
# Note: ${RESOURCEFILES} is not used, since it is not part of the C++ compilation step.
# If you use an extension to your IDE that provides syntax highlighting etc for OpenGL,
# you might need to add ${RESOURCEFILES} in add_executable().
file(GLOB RESOURCEFILES "src/*" "glsl/*")
# Add c++ files
set(SOURCES main.cpp glHelper.h)
add_executable(${PROJECT_NAME} ${SOURCES})
#add_executable(${PROJECT_NAME} ${SOURCES} ${RESOURCEFILES}) # in case you need the glsl files for an IDE extension
target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${OPENGL_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} glfw ${OPENGL_gl_LIBRARY} glad)
# make the current path available to the source code
target_compile_definitions(${PROJECT_NAME} PUBLIC CMAKELISTS_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
# if using Visual Studio, set the startup project
if(MSVC)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})
endif()

315
README.md Normal file
View file

@ -0,0 +1,315 @@
# Computer Graphics Lab5 - Shader Pipeline
**Deadline:** Dec. 13 2024, 22:00
## Building with CMake
This project depends on Eigen and GLFW, which are added as submodules in the `lib/` folder. After cloning the repository, you need to `cd` into it and run:
```
git submodule update --init --recursive
```
OpenGL is already setup by default on most systems. For example, Windows includes the `opengl32.dll` library by default in `C:/Windows/System32`. Drivers for OpenGL are automatically included in most graphics drivers.
**Linux:** if this was not already installed, run:
```
sudo apt install libgl1-mesa-dev
sudo apt install freeglut3-dev
```
-----
## Background
**Review chapters 6, 7 and sections 8.18.2 of Fundamentals of Computer Graphics (4th Edition).**
**Read Sections 11.411.5 and Chapter 17 of Fundamentals of Computer Graphics (4th Edition)**
In this assignment, we will use the “real-time rendering” shader pipeline of **OpenGL**. In this assignment, you will build on this workflow to **procedurally render** a planet. You will be combining knowledge from all the previous assignments: ray tracing, normals, mesh subdivision and perlin noise.
![](css/earth.gif)
## The building blocks of an OpenGL application
### 1. Shaders written in GLSL
Just like OpenCL had the `.cl` file with a specific C-style language, OpenGL has the  [OpenGL shading language (glsl)](https://en.wikipedia.org/wiki/OpenGL_Shading_Language). The extensions are commonly `.glsl, .vs, .fs, .tcs` The programs in these files are called shaders.
In many ways, glsl code looks like C++ code. However, there are many builtin linear algebra types (e.g., `vec3` is a 3D-vector type) and geometric functions (e.g., `dot(a,b)` computes the [dot product](https://en.wikipedia.org/wiki/Dot_product) between vectors `a` and `b`. Since vectors are often used to represent spatial coordinates *or* colors. We can index the coordinates of a vector (`vec3 a`) using `a.r`, `a.g`, `a.b` or `a.x`, `a.y`, `a.z`. When working with [perspective projection](https://en.wikipedia.org/wiki/3D_projection#Perspective_projection) its often useful to employ 4D [homogeneous coordinates](https://en.wikipedia.org/wiki/Homogeneous_coordinates) vectors: `vec4` in glsl. Glsl has many builtin ways to work with differently sized vectors and matrices. For example, if we have `vec4 h` then we can write `vec3 p = h.xyz;` to grab the first three coordinates. Similarly, we could write: `vec4 h = vec4(p,1.0)` to convert a 3D Cartesian point to a 4D homogeneous point.
Fortunately, there are many online resources and googling a glsl-related question often returns helpful answers.
### 2. On the CPU side
The shaders you write in this assignment will run on the [GPU](https://en.wikipedia.org/wiki/Graphics_processing_unit). Lets briefly describe whats happening on the [CPU](https://en.wikipedia.org/wiki/Central_processing_unit) side.
A pseudo-code version of `main.cpp` might look like:
```
main()
initialize window
copy mesh vertex positions V and face indices F to GPU
while window is open
if shaders have not been compiled or files have changed
compile shaders and send to GPU
send "uniform" data to GPU
set all pixels to background color
tell GPU to draw mesh
sleep a few milliseconds
```
### 3. Window
Creating a window is clearly something that will depend on the operating system (e.g., Mac OS X, Linux, Windows). This assignment, like many small scale graphics programs or games, uses an open source windowing toolkit called [glfw](https://en.wikipedia.org/wiki/GLFW). It works on all major operating systems. Once the window is open we have access to its contents as an RGB image. The job of our programs are to fill in all the pixels of this image with colors. The windowing toolkit also handles interactions with the mouse and keyboard as well as window resizing.
## OpenGL shaders
Shaders are compiled *at runtime*. Compilation errors (usually syntax errors) will be output from the main program and the window will turn black (background color).
In this project, you will also be using 4 types of shaders. Each time `glDrawElements()` is called on the CPU, the shaders are executed in the following order:
1. vertex shader
2. tessellation control shader
3. tessellation evaluation shader
4. fragment shader
Below is a simplified example of a **vertex shader**, which is called once per vertex. In this project, the 3D position `pos_vs_in` of each vertex is passed to OpenGL in `GlHelper::createVAO()`. This is indicated through the keyword `in`. This data is considered an “attribute” of each vertex. Constants (or `uniform`s) like `project` have the same value over all shaders. Uniform data is usually changed once per draw frame, or per "object". Variables that need to be passed to the next shader are indicated by `out`. The goal of the vertex shader is to write the _screen space_ position of the vertex to `gl_Position`.
```cpp
#version 410 core // alway necessary as first line
uniform mat4 project; // perspective projection matrix
in vec3 pos_vs_in; // 3D position of mesh vertex
out vec3 pos_fs_in; // passed to next shader
void main() {
pos_fs_in = pos_vs_in;
gl_Position = project * vec4(pos_vs_in, 1);
}
```
After rasterization, the **fragment shader** is called for each pixel, to determine the value (`color`) of that pixel. For example:
```cpp
#version 410 core // alway necessary as first line
in vec3 pos_fs_in; // passed from previous shader
out vec3 color; // OpenGL will write this to the screen
void main() {
color = 0.5+0.5*pos_fs_in;
}
```
Say that we draw a triangle to the screen. The vertex shader is called 3 times, so `pos_fs_in` has 3 different values ($A, B, C$ in the image below). Note however that after the triangle is rasterized, it can cover many pixels on the screen, hundreds to thousands. So what is the value of `pos_fs_in` ($P$) in the fragment shader (which is called once per pixel)? Answer: the value of `pos_fs_in` ($P$) for a pixel is linearly interpolated between the 3 `pos_fs_in` values ($A,B,C$) of the vertices of the triangle. The weights for the linear interpolation are the **barycentric coordinates** ($u,v,w$) of the pixel in the triangle. OpenGL takes care of this interpolation for you.
![](https://www.scratchapixel.com/images/ray-triangle/barycentriccolor.png?)
### Tessellation control shader
The tessellation control shader determines how to subdivide each input “patch” (i.e., triangle). Unlike the subdivision we saw with [subdivision surfaces](https://en.wikipedia.org/wiki/Subdivision_surface), the subdivision is determined independently for each triangle and *not* called recursively. The exact pattern of the resulting triangulation is left largely to implementation. As the shader programmer, you have control over:
- the number of new edges each input should split into (`gl_TessLevelOuter[1] = 5` means the edge across from vertex `1` (i.e., the edge between vertices `0` and `2`) should be split into 5 edges); and
- the number of edges to place toward the center of the patch (`gl_TessLevelInner[0] = 5` would be a good choice if `gl_TessLevelOuter[...] = 5` and a regular tessellation was desired).
To get a better understanding of tessellation, [please read this Stackoverflow post](https://stackoverflow.com/questions/24083656/tessellation-shader-opengl):
![](css/tessellation_levels.png)
Unlike the vertex or fragment shader, the tessellation control shader has access to attribute information at *all* of the vertices of a triangle. The main responsibility of this shader is setting the `gl_TessLevelOuter` and `gl_TessLevelInner` variables.
### Tesselation evaluation shader
The tessellation *evaluation* shader takes the result of the tessellation that the tessellation *control* shader has specified. This shader is called once for every vertex output during tessellation (including original corners). It has access to the attribute information of the original corners (e.g., in our code `in vec3 pos_es_in[]`) and a special variable `gl_TessCoord` containing the [barycentric coordinates](https://en.wikipedia.org/wiki/Barycentric_coordinate_system) of the current vertex. Using this information, it is possible to interpolate information stored at the original corners onto the current vertex: for example, the 3D position. Like the vertex and tessellation control shader, this shader can change the 3D position of a vertex. This is the *last opportunity* to do that, since the fragment shader cannot.
## Bump mapping
#### Intuition
[Bump mapping](https://en.wikipedia.org/wiki/Bump_mapping) a technique to make a **lit** surface look like it has a lot of surface detail. However, unlike [displacement mapping](https://en.wikipedia.org/wiki/Displacement_mapping), the vertices of the mesh are not actually modified/displaced. As you can see on the image below, the geometry of the base model is unaltered by the bump map, while the displacement map moves the vertices.
<img title="" src="https://spiderlilystudio.files.wordpress.com/2020/03/maps.jpg" alt="" width="563">
Bump mapping happens in the fragment shader. Conceptually, a bump map assigns an imaginary height offset ("bump") $`h(p)`$ to each point (pixel) $`p`$ on the mesh, along the surface normal $`n̂(p)`$ at that point. If you recalculate the normal vector for that pixel, now keeping in mind the bumpy surface, you get a new normal vector $`ñ`$. If you use the new normal during the lighting calculation, e.g. Blinn Phong shading, you will get the bumpy surface look. So compared to [normal mapping](https://en.wikipedia.org/wiki/Normal_mapping), where the new normal vector is already known for each pixel, in bump mapping the new vector needs to be calculated.
#### Calculating $`ñ`$
In a mathematical sense, a normal map is non-sense. A point on a surface has a specific normal completely determined by its local geometry. The normal is the direction that goes in the *most outward* direction from the surface. That is, the normal is perpendicular to the surface. Since a surface is two dimensional, the directions that *stay on* the surface are spanned by a two dimensional *tangent plane*.
Normal mapping is useful in computer graphics because we can drape the appearance of a complex surface on top a low resolution and simple one. To create a consistent and believable looking normal map, we can first generate a plausible bump map. Each point $`p∈³`$ on the surface is moved to a new position $`p̃∈³`$ :
$$p̃(p):=p+h(p) \hat{n}(p)$$
where $`h: ℝ³ → `$ is the bump height amount function (could be negative) and $`n̂(p): ℝ³ → ℝ³`$ is the *mathematically* correct normal at $`p`$.
If our bump height $`h`$ is a smooth function over the surface, we can compute the *perceived* normal vector $`ñ`$ by taking a small [finite difference](https://en.wikipedia.org/wiki/Finite_difference) of the 3D position:
$$ñ=\frac{∂p}{∂T} × \frac{∂p}{∂B} ≈ (\frac{p̃(p+εT)p̃(p)}{ε})×(\frac{p̃(p+εB)p̃(p)}{ε})$$
where $`T,B ∈ ℝ³`$ are orthogonal [tangent and bi-tangent vectors](https://en.wikipedia.org/wiki/Tangent_vector) in the tangent plane at $`p`$ and $`ε`$ is a small number (e.g., `0.0001`). By abuse of notation, well make sure that this approximate perceived normal is unit length by dividing by its length:
$$ñ←\frac{ñ}{∥ñ∥}$$
## Before you start implementing
### How come I cant use `#include` in the GLSL shader files?
Our glsl shader programs are not compiled beforehand from files. Instead the CPU-side program must read the file contents into memory as strings and provide the raw strings to the shader compiler. Unfortunately, this means there is no `#include` preprocessor directive and sharing code across different shaders is a burden. In this assignment, it is hard coded which glsl files are read in and compiled, take a look at `path_glsl_files.h`. The used glsl files will also always be printed to the command line.
### How to debug a shader program?
Debugging shader programs must be done _visually_. Since we only see the result of *all* computation, we can use the shader pipelines ability to set screen colors to debug *all* computation simultaneously. For example, when debugging the fragment shader we can check all values at once by setting the pixel color to a value we expect (or dont expect) depending on the computation. A few useful commands come in handy:
```cpp
color = 0.5 + 0.5 * n; // will set the color based on the normal n.
// green = points up, blue = points left, red = points right
```
<img src="css/normal-debug.png" title="" alt="" width="593">
```cpp
color = vec3(0.5,0.5,0) + vec3(0.5,0.5,0) * view_pos_fs_in.xyz; // will set the color based on the view position.
```
<img src="css/position-debug.png" title="" alt="" width="593">
```cpp
color = -view_pos_fs_in.z /15 *vec3(1,1,1); // will set the color based on the distance to the camera in the z-direction.
```
<img src="css/distance-debug.png" title="" alt="" width="599">
```cpp
color = vec3(float(is_moon),1,0); // will set the color to yellow or green based on a boolean value (in this case is_moon).
```
<img src="css/bool-debug.png" title="" alt="" width="605">
### Homogeneous coordinates, `vec4` vs. `vec3`
In computer graphics applications, 3D points are often stored as a `vec4 point = vec4(x,y,z,w) = vec4(x,y,z,1)` with `w = 1`. For our purposes, this makes it easy to transform the position of a vertex through multiplication with 4x4 matrices (`mat4` ). Notably a translation matrix uses the fourth column to translate the point. Most projection matrices also use the fourth column and row.
However, **some vectors purely represent a direction, for example a normal vector. For such vectors, `w` needs to be zero!** For example, multiplying a normal vector with a translation `mat4` should not alter the normal vector, since the direction should stay the same. This is achieved by setting `w = 0`, so `vec4 normal = vec4(x,y,z,0)`.
-------------
## Tasks
You will be implementing the glsl files in the `src/` folder. This assignment works best if you implement the following tasks *in order*.
> **Note:** the `glsl/` folder contains helper methods, for example `PI.glsl`. The glsl files in `src/` that you will implement will contain "Hints" as to which existing methods you can (and should) use.
> **Note:** we implemented the project so that **you can alter the glsl files while the `shaderpipeline` executable is running**, and the program will automatically detect the changed code, recompile the shader and use it to render the image in the window.
**Whitelist**: `mix, normalize, length, clamp, sin, cos, abs, pow, ...`
**Blacklist**: `noise1, noise2, noise3, noise4`
The `correct results` for each task are included in this readme file; their original gifs can be found in the `css/` folder (unordered).
**Task 0: check if OpenGL works.** Run:
```
./shaderpipeline 0 (Unix)
shaderpipeline.exe 0 (Windows)
```
You should see a window pop up that looks like the image below. If you see a completely black window, something went wrong. Feel free to contact us with info about your setup.
<img src="css/test-01.png" title="" alt="" width="270">
**Task 1: basic model, view, projection.** What you see above is actually two [icosahedrons](https://en.wikipedia.org/wiki/Regular_icosahedron) being rendered to the screen, one is supposed to be a planet and the other a moon. You need to implement the transformation to project the vertices (in object space) to the screen (in screen space):
1. `1_identity.glsl`
2. `1_uniform_scale.glsl`
3. `1_translate.glsl`
4. `1_rotate_about_y.glsl`
5. `1_model.glsl`
6. `1_model_view_projection.vs`: the vertex shader, which uses the methods above to project a vertex from `object -> world -> eye -> screen` space.
7. `1_blue_and_gray.fs`: the fragment shader, which gives each pixel a color.
These should be implemented so that when you run `./shaderpipeline 1`, you get an animation of a gray moon orbiting a blue planet:
<img title="" src="css/test-02.gif" alt="" width="508">
If you press `L` this should switch to a wireframe rendering:
![](css/test-02-wireframe.gif)
**Task 2: subdivide the mesh.** Our icosahedrons are rather low poly. To subdivide each face, implement:
1. `2_tessellate_5.tcs`
Running `./shaderpipeline 2` and pressing `L` should now show that the planet and moon are subdivided, i.e. have more triangles.
![](css/test-03-wireframe.gif)
**Task 3: make the mesh round.** Make the mesh vertices sit on a sphere by implementing:
1. `3_snap_to_sphere.tes` : the tessellation evaluation shader, executed right after `2_tessellate_5.tcs`.
Running `./shaderpipeline 3` should look like:
![](css/test-04.gif)
**Task 4: light.** Our scenario is looking quite flat, let's add a point light and implement Blinn Phong shading to show the roundness of the planet and moon:
1. `4_blinn_phong.glsl`
2. `4_lit.fs`
Running `./shaderpipeline 4` should look like:
![](css/test-05.gif)
**Task 5: procedural color.** As promised, perlin noise (along with random_direction and smooth_step) returns, in this case to build the color texture of the planet and moon. **Important:** the input is now a `vec3`, so implement 3D perlin noise instead of 2D! **Important:** do not normalize the perlin noise, let it keep its inherint (negative) minimum and (positive) maximum value.
1. `5_random_direction.glsl`
2. `5_smooth_step.glsl`: use the improved version, discussed in the epilogue of the previous assignment.
3. `5_perlin_noise.glsl`
4. `5_procedural_color.glsl`
Running `./shaderpipeline 5` should look like (feel free to zoom in using the scroll wheel):
![](css/test-06.gif)
**Task 6: procedural bump.** Your perlin noise method can also be used to vary the height of the planet's and moon's surface:
1. `6_bump_position.glsl`
2. `6_tangent.glsl`
3. `6_bump.fs` : use what you learned about bump mapping earlier in this document to recalculate the normals to create a bumpy looking surface.
Running `./shaderpipeline 6` should look like:
![](css/test-07.gif)
**Task 7: get creative.** Play around in `7_planet.fs` to build your own visualization, using everything you've implemented so far. You can visualize it by running `./shaderpipeline 7`. Here is an example, but fun variations on this are also welcome:
![](css/test-08.gif)
-----
## Epilogue
This section is dedicated to the enthousiasts that want to delve deeper into OpenGL as a hobby to create their own applications. The best resource to quickly learn OpenGL is [learnopengl.com](https://learnopengl.com/). You will draw and light your own meshes in no time, while mastering the fundamentals of computer graphics.
This marks the end of the practicals. Thank you for following the Computer Graphics course. We hope that you now have a better understanding of how the graphics on your screen are created and are inspired to develop your own visualizations.
Good luck on your journey!
- Julie and Bert

BIN
css/bool-debug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
css/distance-debug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
css/earth.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

BIN
css/normal-debug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

BIN
css/position-debug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
css/tessellation_levels.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
css/test-01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

BIN
css/test-02-wireframe.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
css/test-02.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
css/test-03-wireframe.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
css/test-04.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
css/test-05.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 KiB

BIN
css/test-06.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

BIN
css/test-07.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 KiB

BIN
css/test-08.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

265
glHelper.h Normal file
View file

@ -0,0 +1,265 @@
#ifndef GLHELPERS_H
#define GLHELPERS_H
#include "path_glsl_files.h"
class GlHelper {
public:
GLFWwindow* window;
std::vector<std::string> all_paths;
std::vector<std::string> vertex_shader_paths;
std::vector<std::string> tess_control_shader_paths;
std::vector<std::string> tess_evaluation_shader_paths;
std::vector<std::string> fragment_shader_paths;
double time_of_last_shader_compilation = 0;
bool isFirstCompilation = true;
GlHelper(int task) {
last_time = get_seconds();
setShaderPaths(task, all_paths, vertex_shader_paths, tess_control_shader_paths, tess_evaluation_shader_paths, fragment_shader_paths);
}
// Use GLFW and GLAD to create a window
GLFWwindow* createWindow() {
// Initialize GLFW
if (!glfwInit()) {
std::cerr << "Failed to initialize GLFW" << std::endl;
return NULL;
}
// Set GLFW to create an OpenGL context (version 440 core)
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow* window = glfwCreateWindow(width, height, "shader-pipeline", NULL, NULL);
if (!window) {
std::cerr << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return NULL;
}
// Make the OpenGL context current
glfwMakeContextCurrent(window);
// Initialize GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cerr << "Failed to initialized GLAD" << std::endl;
glfwTerminate();
return NULL;
}
this->window = window;
return window;
}
// Handle window rescaling and update the projection matrix accordingly
void setPerspectiveMatrixBasedOnWindowScale() {
const auto& reshape = [](
GLFWwindow* window,
int _width,
int _height)
{
::width = _width, ::height = _height;
// windows can't handle variables named near and far.
float nearVal = 0.01f;
float farVal = 100.0f;
float top = static_cast<float>(tan(35. / 360. * M_PI)) * nearVal;
float right = top * (float)::width / (float)::height;
float left = -right;
float bottom = -top;
proj.setConstant(4, 4, 0.);
proj(0, 0) = (2.0f * nearVal) / (right - left);
proj(1, 1) = (2.0f * nearVal) / (top - bottom);
proj(0, 2) = (right + left) / (right - left);
proj(1, 2) = (top + bottom) / (top - bottom);
proj(2, 2) = -(farVal + nearVal) / (farVal - nearVal);
proj(3, 2) = -1.0f;
proj(2, 3) = -(2.0f * farVal * nearVal) / (farVal - nearVal);
};
// Set up window resizing
glfwSetWindowSizeCallback(window, reshape);
{
int width_window, height_window;
glfwGetWindowSize(window, &width_window, &height_window);
reshape(window, width_window, height_window);
}
}
// Setup a VAO from a mesh
void createVAO() {
icosahedron(V, F);
mesh_to_vao(V, F, VAO);
igl::opengl::report_gl_error("mesh_to_vao");
}
// Make GLFW listen for keyboard and mouse inputs
// and change the user input state accordingly
void setKeyboardAndMouseCallbacks() {
std::cout << R"(
Usage:
[] the window can be rescaled
[Click and drag] to orbit view
[Scroll] to translate view in and out
A,a toggle animation
L,l toggle wireframe rending
Z,z reset view to look along z-axis
)";
// Close the window if user presses ESC or CTRL+C
glfwSetKeyCallback(
window,
[](GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == 256 || (key == 67 && (mods & GLFW_MOD_CONTROL)))
{
glfwSetWindowShouldClose(window, true);
}
});
// Listen to keypresses on A, L and Z
glfwSetCharModsCallback(
window,
[](GLFWwindow* window, unsigned int codepoint, int modifier)
{
switch (codepoint)
{
case 'A':
case 'a':
is_animating ^= 1;
if (is_animating)
{
last_time = get_seconds();
}
break;
case 'L':
case 'l':
wire_frame ^= 1;
if (wire_frame) {
glDisable(GL_CULL_FACE);
}
else {
glEnable(GL_CULL_FACE);
}
break;
case 'Z':
case 'z':
view.matrix().block(0, 0, 3, 3).setIdentity();
break;
default:
std::cout << "Unrecognized key: " << (unsigned char)codepoint << std::endl;
break;
}
});
glfwSetMouseButtonCallback(
window,
[](GLFWwindow* window, int button, int action, int mods)
{
mouse_down = action == GLFW_PRESS;
});
glfwSetCursorPosCallback(
window,
[](GLFWwindow* window, double x, double y)
{
static double mouse_last_x = x;
static double mouse_last_y = y;
float dx = static_cast<float>(x - mouse_last_x);
float dy = static_cast<float>(y - mouse_last_y);
if (mouse_down)
{
// Two axis valuator with fixed up
float factor = std::abs(view.matrix()(2, 3));
view.rotate(
Eigen::AngleAxisf(
dx * factor / float(width),
Eigen::Vector3f(0, 1, 0)));
view.rotate(
Eigen::AngleAxisf(
dy * factor / float(height),
view.matrix().topLeftCorner(3, 3).inverse() * Eigen::Vector3f(1, 0, 0)));
}
mouse_last_x = x;
mouse_last_y = y;
});
glfwSetScrollCallback(window,
[](GLFWwindow* window, double xoffset, double yoffset)
{
view.matrix()(2, 3) =
std::min(std::max(view.matrix()(2, 3) + (float)yoffset, -100.0f), -2.0f);
});
}
bool glslFileChanged() {
for (std::string& path : all_paths)
{
if (last_modification_time(path) > time_of_last_shader_compilation)
{
if (isFirstCompilation) {
isFirstCompilation = false;
}
else {
std::cout << path << " has changed since last compilation attempt." << std::endl;
}
return true;
}
}
return false;
}
bool compileShaderIfChanged() {
if (glslFileChanged()) {
time_of_last_shader_compilation = get_seconds();
// (re)compile shader
if (!create_shader_program_from_files(
vertex_shader_paths,
tess_control_shader_paths,
tess_evaluation_shader_paths,
fragment_shader_paths,
prog_id)) {
// failed to compile shader
glDeleteProgram(prog_id);
prog_id = 0;
return false;
}
}
return true;
}
// Update the uniforms used in the GLSL shaders
void updateShaderUniforms() {
// select program
glUseProgram(prog_id);
// Attach uniforms
{
if (is_animating)
{
double now = get_seconds();
animation_seconds += now - last_time;
last_time = now;
}
glUniform1f(glGetUniformLocation(prog_id, "animation_seconds"), static_cast<GLfloat>(animation_seconds));
}
glUniformMatrix4fv(
glGetUniformLocation(prog_id, "proj"), 1, false, proj.data());
glUniformMatrix4fv(
glGetUniformLocation(prog_id, "view"), 1, false, view.matrix().data());
// Draw mesh as wireframe
if (wire_frame)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
else
{
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
}
};
#endif

1239
glad.c Normal file

File diff suppressed because it is too large Load diff

2
glsl/PI.glsl Normal file
View file

@ -0,0 +1,2 @@
// Half the circumference of a unit circle
#define M_PI 3.1415926535897932384626433

27
glsl/bump_height.glsl Normal file
View file

@ -0,0 +1,27 @@
// Create a bumpy surface by using procedural noise to generate a height (
// displacement in normal direction).
//
// Inputs:
// is_moon whether we're looking at the moon or centre planet
// s 3D position of seed for noise generation
// Returns elevation adjust along normal (values between -0.1 and 0.1 are
// reasonable.
float bump_height( bool is_moon, vec3 s)
{
float b =
+0.05*(0.5+0.5*smooth_heaviside(perlin_noise( 1.0*s),10)*2-1)
+(0.5 + 0.44*float(!is_moon))*(0.5+0.5*smooth_heaviside((
+(0.6+0.14*float(is_moon))*perlin_noise( 2.0*s)
+(0.2-0.04*float(is_moon))*perlin_noise( 4.0*s)
+(0.2-0.1*float(is_moon))*perlin_noise( 8.0*s)
-0.005*perlin_noise( 64.0*s)
-0.001*perlin_noise( 128.0*s)
),8-float(is_moon)*-s.x*7)*2-1)
+0.01*(0.5+0.5*smooth_heaviside((
+0.1*perlin_noise( 16.0*s)
+0.8*perlin_noise( 32.0*s)
+0.1*perlin_noise( 64.0*s)
),4)*2-1)
-.5;
return 0.06*b+0.07;
}

16
glsl/interpolate.glsl Normal file
View file

@ -0,0 +1,16 @@
// Using gl_TessCoord interpolate between values stored at three corners of a
// triangle.
//
// Inputs:
// v0 value at corner 0
// v1 value at corner 1
// v2 value at corner 2
// Return linearly interpolated value based on gl_TessCoord.
vec3 interpolate(vec3 bary, vec3 v0, vec3 v1, vec3 v2)
{
return bary.x * v0 + bary.y * v1 + bary.z * v2;
}
vec4 interpolate(vec3 bary, vec4 v0, vec4 v1, vec4 v2)
{
return bary.x * v0 + bary.y * v1 + bary.z * v2;
}

8
glsl/pass-through.fs Normal file
View file

@ -0,0 +1,8 @@
in vec4 pos_fs_in;
out vec3 color;
void main()
{
// Set color to screen position to show something
color = 0.5+0.5*pos_fs_in.xyz;
}

18
glsl/pass-through.tcs Normal file
View file

@ -0,0 +1,18 @@
layout (vertices = 3) out;
in vec4 pos_cs_in[];
out vec4 pos_es_in[];
void main()
{
// Calculate the tess levels
if(gl_InvocationID == 0)
{
gl_TessLevelOuter[0] = 1;
gl_TessLevelOuter[1] = 1;
gl_TessLevelOuter[2] = 1;
gl_TessLevelInner[0] = 1;
}
pos_es_in[gl_InvocationID] = pos_cs_in[gl_InvocationID];
}

10
glsl/pass-through.tes Normal file
View file

@ -0,0 +1,10 @@
layout(triangles, equal_spacing, ccw) in;
in vec4 pos_es_in[];
out vec4 pos_fs_in;
// expects: interpolate
void main()
{
pos_fs_in = interpolate(gl_TessCoord,pos_es_in[0], pos_es_in[1], pos_es_in[2]);
gl_Position = pos_fs_in;
}

6
glsl/pass-through.vs Normal file
View file

@ -0,0 +1,6 @@
in vec4 pos_vs_in;
out vec4 pos_cs_in;
void main()
{
pos_cs_in = pos_vs_in;
}

21
glsl/random2.glsl Normal file
View file

@ -0,0 +1,21 @@
// Generate a pseudorandom 2D vector based on a 2D or 3D seed.
//
// https://thebookofshaders.com/edit.php#11/2d-gnoise.frag
//
// Inputs:
// st 2D seed
// Returns 2D random point in [0,1]²
vec2 random2(vec2 st){
st = vec2( dot(st,vec2(127.1,311.7)),
dot(st,vec2(269.5,183.3)) );
return fract(sin(st)*43758.5453123);
}
// Inputs:
// st 3D seed
// Returns 2D random point in [0,1]²
vec2 random2(vec3 st){
vec2 S = vec2( dot(st,vec3(127.1,311.7,783.089)),
dot(st,vec3(269.5,183.3,173.542)) );
return fract(sin(S)*43758.5453123);
}

View file

@ -0,0 +1,13 @@
// A useful filter, behaves like a smoothly parameterized smooth Heaviside
// function.
//
// Inputs:
// x input scalar (-inf, inf)
// t control steepness of step function: --> 0 more linear, --> inf more like
// Heaviside function (piecewise constant function x<0--> -1 , x>0 --> 1)
// Returns scalar value
float smooth_heaviside( float x, float t)
{
return (1./(1.+exp(-2.*t*(x)))-1./2.)/(1./(1.+exp(-2.*t*1.))-1./2.);
}

3
glsl/version410.glsl Normal file
View file

@ -0,0 +1,3 @@
#version 410 core
// We're using version 4.10. This should always be the first file listed and no
// other files should attempt to specify a version.

282
include/KHR/khrplatform.h Normal file
View file

@ -0,0 +1,282 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(_WIN32) && !defined(__SCITECH_SNAP__)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef _WIN64
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

57
include/REDRUM.h Normal file
View file

@ -0,0 +1,57 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#ifndef IGL_REDRUM_H
#define IGL_REDRUM_H
// Q: These should probably be inside the igl namespace. What's the correct
// way to do that?
// A: I guess the right way is to not use a macro but a proper function with
// streams as input and output.
// ANSI color codes for formatting iostream style output
#ifdef WIN32
// Bold Red, etc.
#define NORUM(X) X
#define BOLD(X) X
#define REDRUM(X) X
#define GREENRUM(X) X
#define YELLOWRUM(X) X
#define BLUERUM(X) X
#define MAGENTARUM(X) X
#define CYANRUM(X) X
// Regular Red, etc.
#define REDGIN(X) X
#define GREENGIN(X) X
#define YELLOWGIN(X) X
#define BLUEGIN(X) X
#define MAGENTAGIN(X) X
#define CYANGIN(X) X
#else
// Bold Red, etc.
#define NORUM(X) ""<<X<<""
#define REDRUM(X) "\e[1m\e[31m"<<X<<"\e[m"
#define GREENRUM(X) "\e[1m\e[32m"<<X<<"\e[m"
#define YELLOWRUM(X) "\e[1m\e[33m"<<X<<"\e[m"
#define BLUERUM(X) "\e[1m\e[34m"<<X<<"\e[m"
#define MAGENTARUM(X) "\e[1m\e[35m"<<X<<"\e[m"
#define CYANRUM(X) "\e[1m\e[36m"<<X<<"\e[m"
// Regular Red, etc.
#define REDGIN(X) "\e[31m"<<X<<"\e[m"
#define GREENGIN(X) "\e[32m"<<X<<"\e[m"
#define YELLOWGIN(X) "\e[33m"<<X<<"\e[m"
#define BLUEGIN(X) "\e[34m"<<X<<"\e[m"
#define MAGENTAGIN(X) "\e[35m"<<X<<"\e[m"
#define CYANGIN(X) "\e[36m"<<X<<"\e[m"
#define BOLD(X) "\e[1m"<<X<<"\e[m"
#endif
#endif

18
include/STR.h Normal file
View file

@ -0,0 +1,18 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#ifndef IGL_STR_H
#define IGL_STR_H
// http://stackoverflow.com/a/2433143/148668
#include <string>
#include <sstream>
// Suppose you have a function:
// void func(std::string c);
// Then you can write:
// func(STR("foo"<<1<<"bar"));
#define STR(X) static_cast<std::ostringstream&>(std::ostringstream().flush() << X).str()
#endif

View file

@ -0,0 +1,153 @@
#ifndef CREATE_SHADER_PROGRAM_FROM_FILES_H
#define CREATE_SHADER_PROGRAM_FROM_FILES_H
#include <string>
#include <vector>
// Create a GLSL shader program given a list of paths containing shader code for
// each shader in the vertex-tessellation-fragment shader pipeline. Prints error
// messages on failure.
//
// Inputs:
// vertex_shader_paths ordered list of paths to files containing glsl vertex
// shader code.
// tess_control_shader_paths ordered list of paths to files containing glsl
// tessellation control shader code.
// tess_evaluation_shader_paths ordered list of paths to files containing glsl
// tessellation evaluation shader code.
// fragment_shader_paths ordered list of paths to files containing glsl fragment
// shader code.
// Outputs:
// id identifier of compiled shader program
// Returns true iff success
inline bool create_shader_program_from_files(
const std::vector<std::string> & vertex_shader_paths,
const std::vector<std::string> & tess_control_shader_paths,
const std::vector<std::string> & tess_evaluation_shader_paths,
const std::vector<std::string> & fragment_shader_paths,
GLuint & id);
// Implementation
#include <iostream>
#include <fstream>
#include "REDRUM.h"
#include "STR.h"
#include "print_shader_info_log.h"
#include "print_program_info_log.h"
inline bool create_shader_program_from_files(
const std::vector<std::string> & vertex_shader_paths,
const std::vector<std::string> & tess_control_shader_paths,
const std::vector<std::string> & tess_evaluation_shader_paths,
const std::vector<std::string> & fragment_shader_paths,
GLuint & id)
{
const auto create_compile_attach = [](
const std::vector<std::string> & paths,
const GLenum type,
const GLuint prog_id,
GLuint & s) -> bool
{
const std::string type_str =
(type == GL_VERTEX_SHADER ? STR(BOLD("vertex shader")) :
(type == GL_FRAGMENT_SHADER ? STR(BOLD("fragment shader")) :
(type == GL_TESS_CONTROL_SHADER ? STR(BOLD("tessellation control shader")) :
(type == GL_TESS_EVALUATION_SHADER ? STR(BOLD("tessellation evaluation shader")) :
"unknown shader"))));
int total_length = 0;
std::vector<std::string> strs;
{
for(int p = 0;p<paths.size();p++)
{
const auto & path = paths[p];
std::ifstream t(path);
if(t.fail())
{
std::cerr<<REDRUM("ERROR")<<": failed to open "<<path<<std::endl;
return false;
}
std::stringstream buffer;
buffer << t.rdbuf();
if(p!=0)
{
strs.push_back(STR(std::endl<<"#line 1 "<<p<<std::endl));
}
strs.push_back(buffer.str());
total_length += static_cast<int>(buffer.str().length());
}
}
std::vector<const char *> cstrs;
for(const auto & str : strs)
{
cstrs.emplace_back(str.c_str());
}
if(total_length == 0)
{
std::cerr<<YELLOWRUM("WARNING")<<": "<<type_str<<" is empty..."<<std::endl;;
s = 0;
return false;
}
s = glCreateShader(type);
if(s == 0)
{
std::cerr<<"failed to create "<<type_str<<std::endl;
return false;
}
{
glShaderSource(s,(GLsizei)cstrs.size(),&cstrs[0],NULL);
}
glCompileShader(s);
print_shader_info_log(type_str,s,paths);
if(!glIsShader(s))
{
std::cerr<<type_str<<" failed to compile."<<std::endl;
return false;
}
glAttachShader(prog_id,s);
return true;
};
// create program
id = glCreateProgram();
if(id == 0)
{
std::cerr<<REDRUM("ERROR")<<": could not create shader program."<<std::endl;
return false;
}
// create shaders
GLuint v=0,tc=0,te=0,f=0;
create_compile_attach(vertex_shader_paths,GL_VERTEX_SHADER,id,v);
create_compile_attach(tess_control_shader_paths,GL_TESS_CONTROL_SHADER,id,tc);
create_compile_attach(tess_evaluation_shader_paths,GL_TESS_EVALUATION_SHADER,id,te);
create_compile_attach(fragment_shader_paths,GL_FRAGMENT_SHADER,id,f);
// Link program
glLinkProgram(id);
const auto & detach = [&id](const GLuint shader)
{
if(shader)
{
glDetachShader(id,shader);
glDeleteShader(shader);
}
};
detach(f);
detach(v);
// print log if any
print_program_info_log(id);
GLint status;
glGetProgramiv(id,GL_LINK_STATUS,&status);
if(status != GL_TRUE)
{
std::cerr<<REDRUM("ERROR")<<": Failed to link shader program"<<std::endl;
return false;
}
std::cout<<GREENRUM("shader compilation successful.")<<std::endl;
return true;
}
#endif

14
include/get_seconds.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef GET_SECONDS_H
#define GET_SECONDS_H
// Return current epoch time in seconds
double get_seconds();
// Implementation
#include <chrono>
double get_seconds()
{
return
std::chrono::duration<double>(
std::chrono::system_clock::now().time_since_epoch()).count();
}
#endif

2401
include/glad/glad.h Normal file

File diff suppressed because it is too large Load diff

63
include/icosahedron.h Normal file
View file

@ -0,0 +1,63 @@
#ifndef ICOSAHEDRON_H
#define ICOSAHEDRON_H
#include <Eigen/Core>
// Construct a triangle mesh of an icosahedron.
//
// Outputs:
// V 12 by 3 list of 3D mesh vertex positions
// F 20 by 3 list of triangle indices into V
template <
typename DerivedV,
typename DerivedF
>
inline void icosahedron(
Eigen::PlainObjectBase<DerivedV> & V,
Eigen::PlainObjectBase<DerivedF> & F);
// Implementation
template <
typename DerivedV,
typename DerivedF
>
inline void icosahedron(
Eigen::PlainObjectBase<DerivedV> & V,
Eigen::PlainObjectBase<DerivedF> & F)
{
V = (DerivedV(12,3) <<
0,0,1,
0.72360679774997894,-0.52573111211913359,0.44721359549995793,
0.72360679774997894,0.52573111211913359,0.44721359549995793,
-0.27639320225002095,0.85065080835203999,0.44721359549995793,
-0.89442719099991586,1.0953573965284052e-16,0.44721359549995793,
-0.27639320225002112,-0.85065080835203988,0.44721359549995793,
0.89442719099991586,0,-0.44721359549995793,
0.27639320225002106,0.85065080835203988,-0.44721359549995793,
-0.72360679774997883,0.5257311121191337,-0.44721359549995793,
-0.72360679774997894,-0.52573111211913348,-0.44721359549995793,
0.27639320225002084,-0.85065080835203999,-0.44721359549995793,
0,0,-1).finished();
F = (DerivedF(20,3)<<
0,1,2,
0,2,3,
0,3,4,
0,4,5,
0,5,1,
1,6,2,
2,7,3,
3,8,4,
4,9,5,
5,10,1,
6,7,2,
7,8,3,
8,9,4,
9,10,5,
10,6,1,
6,11,7,
7,11,8,
8,11,9,
9,11,10,
10,11,6).finished();
}
#endif

View file

@ -0,0 +1,64 @@
#ifndef LAST_MODIFICATION_TIME_H
#define LAST_MODIFICATION_TIME_H
#include <string>
// Inputs:
// path path to file in question
// Returns the last time this file has been modified in seconds.
double last_modification_time(const std::string & path);
// Implementation
#if WIN32
// Shot in the dark... I don't even know if this compiles
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <cassert>
double last_modification_time(const std::string & path)
{
// https://www.rosettacode.org/wiki/File_modification_time#Windows
FILETIME modtime;
//SYSTEMTIME st;
HANDLE fh;
std::wstring w_path = std::wstring(path.begin(), path.end());
fh = CreateFileW(w_path.c_str(), GENERIC_READ | FILE_WRITE_ATTRIBUTES,
0, NULL, OPEN_EXISTING, 0, NULL);
if(fh == INVALID_HANDLE_VALUE)
{
return -1;
}
if(GetFileTime(fh, NULL, NULL, &modtime) == 0)
{
return -1;
}
CloseHandle(fh);
// https://stackoverflow.com/a/19709740/148668
__int64* val = (__int64*) &modtime;
return static_cast<double>(*val) / 10000000.0 - 11644473600.0;
}
#else
#include <sys/stat.h>
#include <sys/time.h>
#include <ctime>
#include <fcntl.h>
double last_modification_time(const std::string & path)
{
struct stat s;
struct timespec t = {0,0};
if (stat(path.c_str(), &s) < 0) { return -1; }
#ifdef __APPLE__
t = s.st_mtimespec;
#else // Linux?
t = s.st_mtim;
#endif
return double(t.tv_sec) + double(t.tv_nsec)*1e-9;
}
#endif
#endif

38
include/load_shader.h Normal file
View file

@ -0,0 +1,38 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#ifndef IGL_OPENGL_LOAD_SHADER_H
#define IGL_OPENGL_LOAD_SHADER_H
#include "../igl_inline.h"
#include "gl.h"
#include <string>
namespace igl
{
namespace opengl
{
// Creates and compiles a shader from a given string
//
// Inputs:
// src string containing GLSL shader code
// type GLSL type of shader, one of:
// GL_VERTEX_SHADER
// GL_FRAGMENT_SHADER
// GL_GEOMETRY_SHADER
// Returns index id of the newly created shader, 0 on error
//
// Will immediately return 0 if src is empty.
IGL_INLINE GLuint load_shader(
const std::string & src,const GLenum type);
}
}
// Implementation
#endif

40
include/mesh_to_vao.h Normal file
View file

@ -0,0 +1,40 @@
#ifndef MESH_TO_VAO_H
#define MESH_TO_VAO_H
#include <Eigen/Core>
// Send a triangle mesh to the GPU using a vertex array object.
//
// Inputs:
// V #V by 3 list of 3D mesh vertex positions
// F #F by 3 list of triangle indices into V
// Outputs:
// VAO identifier of compiled vertex array object.
inline void mesh_to_vao(
const Eigen::Matrix< float,Eigen::Dynamic,3,Eigen::RowMajor> & V,
const Eigen::Matrix<GLuint,Eigen::Dynamic,3,Eigen::RowMajor> & F,
GLuint & VAO);
// Implementation
inline void mesh_to_vao(
const Eigen::Matrix< float,Eigen::Dynamic,3,Eigen::RowMajor> & V,
const Eigen::Matrix<GLuint,Eigen::Dynamic,3,Eigen::RowMajor> & F,
GLuint & VAO)
{
// Generate and attach buffers to vertex array
glGenVertexArrays(1, &VAO);
GLuint VBO, EBO;
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*V.size(), V.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*F.size(), F.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
#endif

269
include/path_glsl_files.h Normal file
View file

@ -0,0 +1,269 @@
#ifndef PATH_GLSL_FILES_H
#define PATH_GLSL_FILES_H
static void setShaderPaths(int task,
std::vector<std::string>& all_paths,
std::vector<std::string>& vertex_shader_paths,
std::vector<std::string>& tess_control_shader_paths,
std::vector<std::string>& tess_evaluation_shader_paths,
std::vector<std::string>& fragment_shader_paths) {
if (task == 0) {
vertex_shader_paths = {
"/glsl/pass-through.vs"
};
tess_control_shader_paths = {
"/glsl/pass-through.tcs"
};
tess_evaluation_shader_paths = {
"/glsl/interpolate.glsl",
"/glsl/pass-through.tes"
};
fragment_shader_paths = {
"/glsl/pass-through.fs"
};
}
else if (task == 1) {
vertex_shader_paths = {
"/glsl/PI.glsl",
"/src/1_identity.glsl",
"/src/1_uniform_scale.glsl",
"/src/1_translate.glsl",
"/src/1_rotate_about_y.glsl",
"/src/1_model.glsl",
"/src/1_model_view_projection.vs",
};
tess_control_shader_paths = {
"/glsl/pass-through.tcs",
};
tess_evaluation_shader_paths = {
"/glsl/interpolate.glsl",
"/glsl/pass-through.tes",
};
fragment_shader_paths = {
"/src/1_blue_and_gray.fs",
};
}
else if (task == 2) {
vertex_shader_paths = {
"/glsl/PI.glsl",
"/src/1_identity.glsl",
"/src/1_uniform_scale.glsl",
"/src/1_translate.glsl",
"/src/1_rotate_about_y.glsl",
"/src/1_model.glsl",
"/src/1_model_view_projection.vs",
};
tess_control_shader_paths = {
"/src/2_tessellate_5.tcs",
};
tess_evaluation_shader_paths = {
"/glsl/interpolate.glsl",
"/glsl/pass-through.tes",
};
fragment_shader_paths = {
"/src/1_blue_and_gray.fs",
};
}
else if (task == 3) {
vertex_shader_paths = {
"/glsl/pass-through.vs"
};
tess_control_shader_paths = {
"/src/2_tessellate_5.tcs",
};
tess_evaluation_shader_paths = {
"/glsl/PI.glsl",
"/src/1_identity.glsl",
"/src/1_uniform_scale.glsl",
"/src/1_translate.glsl",
"/src/1_rotate_about_y.glsl",
"/glsl/interpolate.glsl",
"/src/1_model.glsl",
"/src/3_snap_to_sphere.tes",
};
fragment_shader_paths = {
"/src/1_blue_and_gray.fs",
};
}
else if (task == 4) {
vertex_shader_paths = {
"/glsl/pass-through.vs"
};
tess_control_shader_paths = {
"/src/2_tessellate_5.tcs",
};
tess_evaluation_shader_paths = {
"/glsl/PI.glsl",
"/src/1_identity.glsl",
"/src/1_uniform_scale.glsl",
"/src/1_translate.glsl",
"/src/1_rotate_about_y.glsl",
"/glsl/interpolate.glsl",
"/src/1_model.glsl",
"/src/3_snap_to_sphere.tes",
};
fragment_shader_paths = {
"/glsl/PI.glsl",
"/src/4_blinn_phong.glsl",
"/src/4_lit.fs",
};
}
else if (task == 5) {
vertex_shader_paths = {
"/glsl/pass-through.vs"
};
tess_control_shader_paths = {
"/src/2_tessellate_5.tcs",
};
tess_evaluation_shader_paths = {
"/glsl/PI.glsl",
"/src/1_identity.glsl",
"/src/1_uniform_scale.glsl",
"/src/1_translate.glsl",
"/src/1_rotate_about_y.glsl",
"/glsl/interpolate.glsl",
"/src/1_model.glsl",
"/src/3_snap_to_sphere.tes",
};
fragment_shader_paths = {
"/glsl/PI.glsl",
"/glsl/random2.glsl",
"/src/5_random_direction.glsl",
"/src/5_smooth_step.glsl",
"/src/5_perlin_noise.glsl",
"/src/4_blinn_phong.glsl",
"/src/5_procedural_color.fs",
};
}
else if (task == 6) {
vertex_shader_paths = {
"/glsl/pass-through.vs"
};
tess_control_shader_paths = {
"/src/2_tessellate_5.tcs",
};
tess_evaluation_shader_paths = {
"/glsl/PI.glsl",
"/src/1_identity.glsl",
"/src/1_uniform_scale.glsl",
"/src/1_translate.glsl",
"/src/1_rotate_about_y.glsl",
"/glsl/interpolate.glsl",
"/src/1_model.glsl",
"/src/3_snap_to_sphere.tes",
};
fragment_shader_paths = {
"/glsl/PI.glsl",
"/src/1_identity.glsl",
"/src/1_uniform_scale.glsl",
"/src/1_translate.glsl",
"/src/1_rotate_about_y.glsl",
"/glsl/interpolate.glsl",
"/src/1_model.glsl",
"/glsl/random2.glsl",
"/src/5_random_direction.glsl",
"/src/5_smooth_step.glsl",
"/src/5_perlin_noise.glsl",
"/src/4_blinn_phong.glsl",
"/glsl/smooth_heaviside.glsl",
"/glsl/bump_height.glsl",
"/src/6_bump_position.glsl",
"/src/6_tangent.glsl",
"/src/6_bump.fs",
};
}
else if (task == 7) {
vertex_shader_paths = {
"/glsl/pass-through.vs"
};
tess_control_shader_paths = {
"/src/2_tessellate_5.tcs",
};
tess_evaluation_shader_paths = {
"/glsl/PI.glsl",
"/src/1_identity.glsl",
"/src/1_uniform_scale.glsl",
"/src/1_translate.glsl",
"/src/1_rotate_about_y.glsl",
"/glsl/interpolate.glsl",
"/src/1_model.glsl",
"/src/3_snap_to_sphere.tes",
};
fragment_shader_paths = {
"/glsl/PI.glsl",
"/src/1_identity.glsl",
"/src/1_uniform_scale.glsl",
"/src/1_translate.glsl",
"/src/1_rotate_about_y.glsl",
"/glsl/interpolate.glsl",
"/src/1_model.glsl",
"/glsl/random2.glsl",
"/src/5_random_direction.glsl",
"/src/5_smooth_step.glsl",
"/src/5_perlin_noise.glsl",
"/src/4_blinn_phong.glsl",
"/glsl/smooth_heaviside.glsl",
"/glsl/bump_height.glsl",
"/src/6_bump_position.glsl",
"/src/6_tangent.glsl",
"/src/7_planet.fs",
};
}
// Print to command line
std::cout << "==== Used glsl files: ====" << std::endl;
std::cout << " vertex:" << std::endl;
for (auto& s : vertex_shader_paths) {
std::cout << " " << s << std::endl;
}
std::cout << " tessellation control:" << std::endl;
for (auto& s : tess_control_shader_paths) {
std::cout << " " << s << std::endl;
}
std::cout << " tessellation evaluation:" << std::endl;
for (auto& s : tess_evaluation_shader_paths) {
std::cout << " " << s << std::endl;
}
std::cout << " fragment:" << std::endl;
for (auto& s : fragment_shader_paths) {
std::cout << " " << s << std::endl;
}
std::cout << "==========================" << std::endl;
// Add cmakelists_dir (from main.cpp)
for (std::string& s : vertex_shader_paths) {
s = cmakelists_dir + s;
}
for (std::string& s : tess_control_shader_paths) {
s = cmakelists_dir + s;
}
for (std::string& s : tess_evaluation_shader_paths) {
s = cmakelists_dir + s;
}
for (std::string& s : fragment_shader_paths) {
s = cmakelists_dir + s;
}
// Add "/glsl/version410.glsl" to the beginning of each vector
vertex_shader_paths.insert(vertex_shader_paths.begin(), cmakelists_dir + "/glsl/version410.glsl");
tess_control_shader_paths.insert(tess_control_shader_paths.begin(), cmakelists_dir + "/glsl/version410.glsl");
tess_evaluation_shader_paths.insert(tess_evaluation_shader_paths.begin(), cmakelists_dir + "/glsl/version410.glsl");
fragment_shader_paths.insert(fragment_shader_paths.begin(), cmakelists_dir + "/glsl/version410.glsl");
// Concatenate into one vector
all_paths.insert(all_paths.end(), vertex_shader_paths.begin(), vertex_shader_paths.end());
all_paths.insert(all_paths.end(), tess_control_shader_paths.begin(), tess_control_shader_paths.end());
all_paths.insert(all_paths.end(), tess_evaluation_shader_paths.begin(), tess_evaluation_shader_paths.end());
all_paths.insert(all_paths.end(), fragment_shader_paths.begin(), fragment_shader_paths.end());
}
#endif

View file

@ -0,0 +1,21 @@
#ifndef PRINT_OPENGL_INFO_H
#define PRINT_OPENGL_INFO_H
// Use glfw to print information about the current opengl context
// Should be called after glfwMakeContextCurrent(...)
void print_opengl_info(GLFWwindow * window);
// Implementation
#include <cstdio>
void print_opengl_info(GLFWwindow * window)
{
int major, minor, rev;
major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR);
minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR);
rev = glfwGetWindowAttrib(window, GLFW_CONTEXT_REVISION);
printf("OpenGL version recieved: %d.%d.%d\n", major, minor, rev);
printf("Supported OpenGL is %s\n", (const char*)glGetString(GL_VERSION));
printf("Supported GLSL is %s\n", (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
}
#endif

View file

@ -0,0 +1,36 @@
#ifndef PRINT_PROGRAM_INFO_LOG_H
#define PRINT_PROGRAM_INFO_LOG_H
#include <string>
// Print information about a given glsl shader program.
//
// Inputs:
// obj id of object we're querying
// Returns true if printed anything.
bool print_program_info_log(const GLuint obj);
// Implementation
#include "REDRUM.h"
#include "STR.h"
bool print_program_info_log(const GLuint obj)
{
GLint infologLength = 0;
GLint charsWritten = 0;
glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
if (infologLength > 0)
{
char * infoLog = new char[infologLength];
glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
std::string log(infoLog);
std::cerr<<log<<std::endl;
delete[] infoLog;
return true;
}
return false;
}
#endif

View file

@ -0,0 +1,51 @@
#ifndef PRINT_SHADER_INFO_LOG_H
#define PRINT_SHADER_INFO_LOG_H
#include <string>
#include <vector>
// Print information (e.g., compilation errors and warnings) about a give glsl
// shader.
//
// Inputs:
// type_str string identifying which kind of shader we're asking about (just
// a prefix to print)
// obj id of object we're querying
// paths list of file paths containing corresponding shader source code
// (assumings `#line ...` directive has been inserted between files).
// Returns true if printed anything.
bool print_shader_info_log(
const std::string & type_str,
const GLuint obj,
const std::vector<std::string> & paths);
// Implementation
#include "REDRUM.h"
#include "STR.h"
#include <iostream>
bool print_shader_info_log(
const std::string & type_str,
const GLuint obj,
const std::vector<std::string> & paths)
{
GLint infologLength = 0;
GLint charsWritten = 0;
glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
if (infologLength > 0)
{
char * infoLog = new char[infologLength];
std::cerr<<REDRUM("ERROR")<<": failed to compile "<<type_str<<std::endl;
glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
std::string log(infoLog);
std::cerr<<log<<std::endl;
delete[] infoLog;
return true;
}
return false;
}
#endif

94
include/report_gl_error.h Normal file
View file

@ -0,0 +1,94 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#ifndef IGL_OPENGL_REPORT_GL_ERROR_H
#define IGL_OPENGL_REPORT_GL_ERROR_H
#define IGL_INLINE inline
// Hack to allow both opengl/ and opengl2 to use this (we shouldn't allow this)
#ifndef __gl_h_
# include "gl.h"
#endif
#include <string>
namespace igl
{
namespace opengl
{
// Print last OpenGL error to stderr prefixed by specified id string
// Inputs:
// id string to appear before any error msgs
// Returns result of glGetError()
IGL_INLINE GLenum report_gl_error(const std::string id);
// No prefix
IGL_INLINE GLenum report_gl_error();
}
}
// Implementation
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "report_gl_error.h"
#include <cstdio>
IGL_INLINE GLenum igl::opengl::report_gl_error(const std::string id)
{
// http://stackoverflow.com/q/28485180/148668
// gluErrorString was deprecated
const auto gluErrorString = [](GLenum errorCode)->const char *
{
switch(errorCode)
{
default:
return "unknown error code";
case GL_NO_ERROR:
return "no error";
case GL_INVALID_ENUM:
return "invalid enumerant";
case GL_INVALID_VALUE:
return "invalid value";
case GL_INVALID_OPERATION:
return "invalid operation";
#ifndef GL_VERSION_3_0
case GL_STACK_OVERFLOW:
return "stack overflow";
case GL_STACK_UNDERFLOW:
return "stack underflow";
case GL_TABLE_TOO_LARGE:
return "table too large";
#endif
case GL_OUT_OF_MEMORY:
return "out of memory";
#ifdef GL_EXT_framebuffer_object
case GL_INVALID_FRAMEBUFFER_OPERATION_EXT:
return "invalid framebuffer operation";
#endif
}
};
GLenum err = glGetError();
if(GL_NO_ERROR != err)
{
fprintf(stderr,"GL_ERROR: %s%s\n",id.c_str(),gluErrorString(err));
}
return err;
}
IGL_INLINE GLenum igl::opengl::report_gl_error()
{
return igl::opengl::report_gl_error(std::string(""));
}
#endif

1
lib/eigen Submodule

@ -0,0 +1 @@
Subproject commit dd8c71e62852b2fe429edb6682ac91fd1c578a26

1
lib/glfw Submodule

@ -0,0 +1 @@
Subproject commit 23ea072c4157f2a7760d26f4d6e375833646bd88

147
main.cpp Normal file
View file

@ -0,0 +1,147 @@
#define NOMINMAX
#define _USE_MATH_DEFINES
#include <cmath>
#include <glad/glad.h>
#define GLFW_INCLUDE_GLU
#include <GLFW/glfw3.h>
#include "icosahedron.h"
#include "mesh_to_vao.h"
#include "print_opengl_info.h"
#include "get_seconds.h"
#include "report_gl_error.h"
#include "create_shader_program_from_files.h"
#include "last_modification_time.h"
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <thread>
#include <iostream>
#include <sstream>
#include <fstream>
#include <cstdlib>
#include <vector>
#include <algorithm>
// From CMAKE preprocessor
std::string cmakelists_dir = CMAKELISTS_SOURCE_DIR;
// Default width and height
int width = 640;
int height = 360;
GLuint prog_id = 0; // OpenGL index to shader
// some user input state
bool wire_frame = false;
bool mouse_down = false;
bool is_animating = true;
double last_time = 0;
double animation_seconds = 0;
Eigen::Affine3f view = Eigen::Affine3f::Identity() * Eigen::Translation3f(Eigen::Vector3f(0, 0, -10));
Eigen::Matrix4f proj = Eigen::Matrix4f::Identity();
GLuint VAO;
// Mesh data: RowMajor is important to directly use in OpenGL
Eigen::Matrix< float, Eigen::Dynamic, 3, Eigen::RowMajor> V;
Eigen::Matrix<GLuint, Eigen::Dynamic, 3, Eigen::RowMajor> F;
// needs to be included here
#include "glHelper.h"
int main(int argc, char* argv[])
{
// Process command line args
int task = -1;
if (argc == 2) {
try {
task = std::stoi(argv[1]); // char* to int
std::cout << "Using glsl files for task " << task << std::endl;
}
catch (...) {
std::cout << "Error: failed to parse second argument to an int: " << argv[1] << std::endl;
}
}
if (task < 0 || task > 8) {
std::cout << "Usage:" << std::endl;
std::cout << " Unix: ./shaderpipeline <task nr>" << std::endl;
std::cout << " Windows: shaderpipeline.exe <task nr>" << std::endl;
std::cout << "where <task nr> is an int in [0, 8]" << std::endl;
std::cout << "For example: ./shaderpipeline 0" << std::endl;
return 0;
}
GlHelper glHelper(task);
GLFWwindow* window = glHelper.createWindow();
if (!window) {
return -1;
}
print_opengl_info(window);
igl::opengl::report_gl_error("init");
glHelper.setPerspectiveMatrixBasedOnWindowScale();
glHelper.createVAO();
glHelper.setKeyboardAndMouseCallbacks();
// Enable depth testing and delete faces seen from the back
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
// Bind the VAO, which knows where to find the vertex and triangle data
glBindVertexArray(VAO);
// Main display routine
while (!glfwWindowShouldClose(window))
{
double tic = get_seconds();
// Compile the glsl files into a shader program
if (!glHelper.compileShaderIfChanged()) {
std::cout << "Error: failed to compile shader" << std::endl;
break;
}
// Clear screen
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Set viewport and output framebuffer size
glfwGetFramebufferSize(window, &::width, &::height);
glViewport(0, 0, ::width, ::height);
// Update the uniforms used in the GLSL shaders
glHelper.updateShaderUniforms();
// Render an icosahedron for the planet and moon
for (int i = 0; i < 2; i++)
{
// Set the shader uniform "is_moon"
glUniform1i(glGetUniformLocation(prog_id, "is_moon"), i == 1);
// Draw the vertices in the VAO
glDrawElements(GL_PATCHES, F.size(), GL_UNSIGNED_INT, 0);
}
// Swap the front and back buffers
glfwSwapBuffers(window);
// 60 fps
{
glfwPollEvents();
double duration = 1000000. * (get_seconds() - tic); // In microseconds
const double min_duration = 1000000. / 60.;
if (duration < min_duration)
{
std::this_thread::sleep_for(std::chrono::microseconds((int)(min_duration - duration)));
}
}
}
// Graceful exit
glfwDestroyWindow(window);
glfwTerminate();
return 1;
}

15
src/1_blue_and_gray.fs Normal file
View file

@ -0,0 +1,15 @@
// This is a fragment shader, so the pixel 'color' is set here.
// For the planet: pick (r,g,b) = (0.2,0.3,0.8)
// For the moon : pick (r,g,b) = (0.5,0.45,0.5)
// Uniforms:
uniform bool is_moon;
// Outputs:
out vec3 color;
void main()
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code:
color = vec3(1,1,1);
/////////////////////////////////////////////////////////////////////////////
}

16
src/1_identity.glsl Normal file
View file

@ -0,0 +1,16 @@
// Return a 4x4 identity matrix
// Checking the GLSL documentation can save you time.
// For example, browse 'glsl mat4' for more information.
mat4 identity()
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
return mat4(
0,0,0,0,
0,0,0,0,
0,0,0,0,
0,0,0,0);
/////////////////////////////////////////////////////////////////////////////
}

24
src/1_model.glsl Normal file
View file

@ -0,0 +1,24 @@
// Construct the model transformation matrix.
//
// 1. The model for the planet:
// No scaling, translation or rotation is necessary.
//
// 2. The model for the moon:
// Scaling: shrink the model by 70%.
// The moon should orbit around the origin (0,0,0) with
// a radius of 2 units and
// a frequency of 1 revolution per 4 seconds.
//
// Inputs:
// is_moon whether we're considering the moon
// time seconds on animation clock
// Returns affine model transformation as 4x4 matrix
//
// expects: identity, rotate_about_y, translate, PI
mat4 model(bool is_moon, float time)
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
return identity();
/////////////////////////////////////////////////////////////////////////////
}

View file

@ -0,0 +1,22 @@
// Project each vertex 'pos_vs_in' onto the screen ('pos_cs_in')
// using perspective projection and matrix multiplication.
// Uniforms:
uniform mat4 view; // 4x4 view transformation matrix: transforms "world
// coordinates" into camera coordinates.
uniform mat4 proj; // 4x4 perspective projection matrix: transforms
uniform float animation_seconds; // number of seconds animation has been running
uniform bool is_moon; // whether we're rendering the moon or the other object
// Inputs:
in vec3 pos_vs_in; // 3D position of mesh vertex
// Ouputs:
out vec4 pos_cs_in; // transformed and projected position in homogeneous coordinates
// expects: PI, model
void main()
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
pos_cs_in = vec4(pos_vs_in,1.0);
/////////////////////////////////////////////////////////////////////////////
}

12
src/1_rotate_about_y.glsl Normal file
View file

@ -0,0 +1,12 @@
// Inputs:
// theta amount y which to rotate (in radians)
// Return a 4x4 matrix that rotates a given 3D point/vector about the y-axis by
// the given amount.
mat4 rotate_about_y(float theta)
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
return identity();
/////////////////////////////////////////////////////////////////////////////
}

11
src/1_translate.glsl Normal file
View file

@ -0,0 +1,11 @@
// Inputs:
// t 3D vector by which to translate
// Return a 4x4 matrix that translates and 3D point by the given 3D vector
mat4 translate(vec3 t)
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
return identity();
/////////////////////////////////////////////////////////////////////////////
}

12
src/1_uniform_scale.glsl Normal file
View file

@ -0,0 +1,12 @@
// Inputs:
// s amount to scale in all directions
// Return a 4x4 matrix that scales and input 3D position/vector by s in all 3
// directions.
mat4 uniform_scale(float s)
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
return identity();
/////////////////////////////////////////////////////////////////////////////
}

20
src/2_tessellate_5.tcs Normal file
View file

@ -0,0 +1,20 @@
// This is a tesselation control shader.
// Hard-code each tessellation level to 5.
// Tip: browse 'gl_TessLevelOuter' and 'gl_TessLevelInner'
// Layout:
layout (vertices = 3) out; // indicates that we're dealing with 3 vertices
// (triangles) as output
// Inputs:
in vec4 pos_cs_in[]; // 3-long array of this triangle's corners' 3D vertex positions
// Outputs:
out vec4 pos_es_in[]; // 3-long array of this triangle's corners' 3D vertex
// positions (should be set to input using gl_InvocationID)
void main()
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code:
pos_es_in[gl_InvocationID] = pos_cs_in[gl_InvocationID];
/////////////////////////////////////////////////////////////////////////////
}

45
src/3_snap_to_sphere.tes Normal file
View file

@ -0,0 +1,45 @@
// This is a tessellation evaluation shader. For some context:
// First, the vertex shader pass-through.vs wrote the 3D position of each vertex (in world space) to 'pos_cs_in'.
// Then after the tessellation control shader, all triangles were subdivided into more triangles,
// and the old 'pos_cs_in' was written into 'pos_es_in'.
// In this shader, we need to calculate the 3D position for the vertices newly created during tessallation.
// This is done for you, see 'interp_pos'.
// Now you need to project them to the screen ('gl_Position'), like in 1_model_view_projection.vs.
// Make it so that all vertices ('sphere_fs_in') lie on a sphere.
// Also recalculate the vertex normals ('view_pos_fs_in').
// Layout:
layout(triangles, equal_spacing, ccw) in; // Indicates that we're dealing with triangles in CCW order
// and using a sane spacing.
// Uniforms
uniform mat4 view;
uniform mat4 proj;
uniform float animation_seconds;
uniform bool is_moon;
// Inputs:
in vec4 pos_es_in[]; // array of 3 3D patch corner positions (from tessellation control shader)
// Outputs:
out vec3 sphere_fs_in; // 3D position _before_ applying model, view or projection
// transformations (e.g., point on unit sphere)
out vec4 view_pos_fs_in; // view and model transformed 3D position
out vec3 normal_fs_in; // view and model transformed 3D normal
out vec4 pos_fs_in; // projected, view, and model transformed 3D position
// expects: interpolate, model
void main()
{
vec4 interp_pos = interpolate(gl_TessCoord, pos_es_in[0], pos_es_in[1], pos_es_in[2]);
// interp_pos now contains the 3D position of the current vertex in world space
// (so before model, view, proj are applied)
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
gl_Position = interp_pos;
/////////////////////////////////////////////////////////////////////////////
}

31
src/4_blinn_phong.glsl Normal file
View file

@ -0,0 +1,31 @@
// Compute Blinn-Phong Shading given a material specification, a point on a
// surface and a light direction. Assume the light has a low
// ambient intensity of 0.1. To avoid confusion: this Blinn-Phong shading model
// should only consist of ambient, diffuse and specular lighting.
// Do not divide by r² (the squared distance to the point light).
// Inputs:
// ka rgb ambient color
// kd rgb diffuse color
// ks rgb specular color
// p specular exponent (shininess)
// n unit surface normal direction
// v unit direction from point on object to eye
// l unit light direction
// Returns rgb color
vec3 blinn_phong(
vec3 ka,
vec3 kd,
vec3 ks,
float p,
vec3 n,
vec3 v,
vec3 l)
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
return vec3(1,1,1);
/////////////////////////////////////////////////////////////////////////////
}

29
src/4_lit.fs Normal file
View file

@ -0,0 +1,29 @@
// Add (hard code) an orbiting point light to the scene. Light
// the scene using the Blinn-Phong Lighting Model.
// The point light should be 8 units above the planet and orbit with
// a radius of 10 units at a frequency of 1 revolution per 8 seconds.
// The base color of the planet and moon are the same as in 1_blue_and_gray.fs.
// For the specular exponent, pass p=1000 to blinn_phong().
// Since the light is white, ks=(1,1,1).
// Uniforms:
uniform mat4 view;
uniform mat4 proj;
uniform float animation_seconds;
uniform bool is_moon;
// Inputs:
in vec3 sphere_fs_in;
in vec3 normal_fs_in;
in vec4 pos_fs_in;
in vec4 view_pos_fs_in;
// Outputs:
out vec3 color;
// expects: PI, blinn_phong
void main()
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
color = vec3(1,1,1);
/////////////////////////////////////////////////////////////////////////////
}

16
src/5_perlin_noise.glsl Normal file
View file

@ -0,0 +1,16 @@
// Given a 3d position as a seed, compute an even smoother procedural noise
// value. "Improving Noise" [Perlin 2002].
//
// Inputs:
// st 3D seed
// Returns a smooth value between (-1,1)
//
// expects: random_direction, smooth_step
float perlin_noise( vec3 st)
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
return 0;
/////////////////////////////////////////////////////////////////////////////
}

45
src/5_procedural_color.fs Normal file
View file

@ -0,0 +1,45 @@
// This is just like lit.fs, but you need to multiply the base color
// of the planet and moon by 'marble_noise' to get a marble-like texture.
// Do this multiplication before passing the blue and grey colors to blinn_phong()
// Uniforms:
uniform mat4 view;
uniform mat4 proj;
uniform float animation_seconds;
uniform bool is_moon;
// Inputs:
in vec3 sphere_fs_in;
in vec3 normal_fs_in;
in vec4 pos_fs_in;
in vec4 view_pos_fs_in;
// Outputs:
out vec3 color; // rgb color of this pixel
// expects: blinn_phong, perlin_noise
void main()
{
// build up marble noise from layers of perlin noise with different scales
float s = sin(20*(sphere_fs_in.y + (1-0.5*float(is_moon))*perlin_noise( sphere_fs_in ))) *
(0.991+0.009*perlin_noise( (2.0 + 2*float(is_moon)) * sphere_fs_in));
float s2 = (
+0.25*perlin_noise( 1.0 * sphere_fs_in )
+0.25*perlin_noise( 4.0 * sphere_fs_in )
+0.25*perlin_noise( 8.0 * sphere_fs_in )
+0.25*perlin_noise(16.0 * sphere_fs_in ));
float s3 = max(s+0.4,0) *
pow(min(
(0.5+0.5*(
(0.2*sin(10*(sphere_fs_in.x + perlin_noise( 8*sphere_fs_in )))+
0.2*sin(15*(sphere_fs_in.z + perlin_noise( 8*sphere_fs_in )))
+ 0.2*perlin_noise(16*sphere_fs_in))
+ 0.6*perlin_noise(32*sphere_fs_in)
))
,1.0),2);
float marble_noise = 1-clamp( 0.1*pow(s*0.5+0.5,20) + 0.7*(0.5*s2+0.5) + 0.2*s3, 0,1) ;
marble_noise = clamp(marble_noise,0,1);
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
color = marble_noise * vec3(1,1,1);
/////////////////////////////////////////////////////////////////////////////
}

View file

@ -0,0 +1,15 @@
// Generate a pseudorandom unit 3D vector
//
// Inputs:
// seed 3D seed
// Returns psuedorandom, unit 3D vector drawn from uniform distribution over
// the unit sphere (assuming random2 is uniform over [0,1]²).
//
// expects: random2.glsl, PI.glsl
vec3 random_direction( vec3 seed)
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
return vec3(1,0,0);
/////////////////////////////////////////////////////////////////////////////
}

22
src/5_smooth_step.glsl Normal file
View file

@ -0,0 +1,22 @@
// Filter an input value to perform an even smoother step. This function should
// be a quintic polynomial with smooth_step(0) = 0,
// smooth_step(1) = 1, and zero first _and_ second derivatives at f=0
// and f=1. "Improving Noise" [Perlin 2002].
//
// Inputs:
// f input value
// Returns filtered output value
float smooth_step( float f)
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
return f;
/////////////////////////////////////////////////////////////////////////////
}
vec3 smooth_step( vec3 f)
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
return f;
/////////////////////////////////////////////////////////////////////////////
}

31
src/6_bump.fs Normal file
View file

@ -0,0 +1,31 @@
// This is just like lit.fs, but with some changes:
// 1. the color of the planet and moon need to be multiplied by 'terrain_color'.
// 2. the normal vector for the current pixel needs to be recalculated,
// to get the bump map effect. This needs to happen before passing the normal
// vector to blinn_phong().
// Uniforms:
uniform mat4 view;
uniform mat4 proj;
uniform float animation_seconds;
uniform bool is_moon;
// Inputs:
in vec3 sphere_fs_in;
in vec3 normal_fs_in;
in vec4 pos_fs_in;
in vec4 view_pos_fs_in;
// Outputs:
out vec3 color; // rgb color of this pixel
// expects: model, blinn_phong, bump_height, bump_position,
// perlin_noise, tangent
void main()
{
vec3 s = normalize(sphere_fs_in);
float b = bump_height(is_moon, s);
float terrain_color = clamp(1+5*b,0,1);
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
color = terrain_color * vec3(1,1,1);
/////////////////////////////////////////////////////////////////////////////
}

20
src/6_bump_position.glsl Normal file
View file

@ -0,0 +1,20 @@
// The end goal is to create a bumpy surface.
// In this function, you need to offset a point 'p' on the surface of a unit sphere
// by some offset, and along the normal 'n' of the sphere in 'p'.
// The offset (bump) should be the value of bump_height().
// Note: for a unit sphere object, s=p=n
//
// Inputs:
// is_moon whether we're looking at the moon or centre planet
// s 3D position of seed for noise generation, also assumed to be surface
// point on the unit sphere (and thus also equal to its normal)
// Returns 3D position of p adjusted along n by bump amount
//
// expects: bump_height
vec3 bump_position(bool is_moon , vec3 s)
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
return s;
/////////////////////////////////////////////////////////////////////////////
}

17
src/6_tangent.glsl Normal file
View file

@ -0,0 +1,17 @@
// Calculate the unit tangent vectors of N.
// In other words, calculate two vectors, T and B, so that
// N, T and B are all perpendicular to one another.
// Input:
// N 3D unit normal vector
// Outputs:
// T 3D unit tangent vector
// B 3D unit bitangent vector
void tangent(in vec3 N, out vec3 T, out vec3 B)
{
/////////////////////////////////////////////////////////////////////////////
// Replace with your code
T = vec3(1,0,0);
B = vec3(0,1,0);
/////////////////////////////////////////////////////////////////////////////
}

27
src/7_planet.fs Normal file
View file

@ -0,0 +1,27 @@
// Generate a procedural planet and orbiting moon. Use layers of (improved)
// Perlin noise to generate planetary features such as vegetation, gaseous
// clouds, mountains, valleys, ice caps, rivers, oceans. Don't forget about the
// moon. Use `animation_seconds` in your noise input to create (periodic)
// temporal effects.
//
// Uniforms:
uniform mat4 view;
uniform mat4 proj;
uniform float animation_seconds;
uniform bool is_moon;
// Inputs:
in vec3 sphere_fs_in;
in vec3 normal_fs_in;
in vec4 pos_fs_in;
in vec4 view_pos_fs_in;
// Outputs:
out vec3 color;
// expects: model, blinn_phong, bump_height, bump_position,
// perlin_noise, tangent
void main()
{
color = vec3(0,0,1);
}