{{FULL_COURSE}} Homework 4 - OpenGL Fun


Overview ----- You will practice working with basic OpenGL constructs and functions by writing code to construct a cube out of vertex buffer objects. You will also program portions of OpenGL's graphics pipeline by writing a few different vertex and fragment shaders to apply different coloration effects to the surfaces of 3D models. Supplied Code --------- Click here to access the homework's Github repository. We will provide you with a basic Qt GUI that draws scenes using OpenGL, along with a user interface that allows you to switch between different shaders with which to color the surface of the object. Conceptual Questions (Due Monday, September 30 at 11:59 PM) ------------- Before you begin the programming portion of this homework assignment, read and answer the following conceptual questions. Your answers should be submitted as a plaintext (`.txt`) file. * (5 pts) In the OpenGL Shading Language (GLSL), what is a uniform variable? What is an "in" variable? What is an "out" variable? How does a vertex shader pass data to a fragment shader? * (5 pts) The SurfaceShader class has several member variables of type int, such as attrPos and unifModel. What do these variables represent? How are they given values in the first place? Help Log (5 points) ------- Maintain a log of all help you receive and resources you use. Make sure the date and time, the names of everyone you work with or get help from, and every URL you use, except as noted in the collaboration policy. Also briefly log your question, bug or the topic you were looking up/discussing. Ideally, you should also the answer to your question or solution to your bug. This will help you learn and provide a useful reference for future assignments and exams. This also helps us know if there is a topic that people are finding difficult. If you did not use external resources or otherwise receive help, please submit a help log that states you did not receive external help. You may submit your help log as an ASCII (plain) text file or as a PDF. Refer to the Policies section of the course web site for more specifications. Code Requirements (Due Wednesday, October 2 at 11:59 PM) ------- For the sections that require you to modify shader files, you can find the shaders under `Resources/glsl.qrc/glsl/surface` in Qt Creator's project file browser. Note that we have provided you with a working Lambertian shader filled with comments explaining each shader program element. You can find this program in `lambert.vert.glsl` and `lambert.frag.glsl`. ### Cube Vertex Buffers (25 points) ### In the `Mesh` class, which can be found in `scene/mesh.h`, a function called `createCube` has been declared and partially defined. In the body of this function, write code that fills `std::vector`s with vertex data for the following attributes of a cube that spans the range [-1, 1] in the X, Y, and Z axes (in world space): * Position (`glm::vec4`) * Normal (`glm::vec4`) * UV (`glm::vec2`) To make coding your cube a little easier, we recommend writing the vertex attributes on a per-face basis, i.e. front quad, right quad, back quad, etc. Once you have made your collections of vertex data, you will have to triangulate your cube by making a `std::vector` of `GLuint`s (unsigned integers that are guaranteed to be 32 bits) and storing triangle indices. Make sure your cube's faces' surface normals are correct; this means you will need to create duplicate vertex positions in order to maintain a 1:1 ratio of position:normal. You may set the UVs of each cube face to span the range (0,0) to (1,1), like the UVs of the example square. We have already written code in `createCube` that sets up the vertex buffer objects for a square. Take this code and expand it to create a cube with the specifications above. You can display your cube by changing the Model dropdown menu to `Cube`. ### `SurfaceShader` Uniform Handle (5 points) ### Add an integer member variable to the `SurfaceShader` class to act as a handle to a `uniform vec3` in a surface shader that represents your camera's position in world space. Use this variable in at least the Blinn-Phong shader to compute the lighting on the surface. Set this handle to your shader variable in `SurfaceShader::setupMemberVariables`, and update its value in `MyGL::mouseMoveEvent`, which is defined in `cameracontrols.cpp`. You might want to write a helper function in the style of `SurfaceShasder::setModelMatrix` to update your camera position variable in your GPU. We want to use this uniform instead of the current implementation involving the inverse of our view matrix because invoking the inverse function is costly. ### Blinn-Phong Reflection Shader (10 points) ### Add code to the shader files `blinnPhong.vert.glsl` and `blinnPhong.frag.glsl` in order to implement Blinn-Phong reflection on the surface of the models provided. Blinn-Phong reflection is a method of emulating the specular highlights one sees on materials like plastic. To compute this shading effect, one needs to know the following information for a given pixel fragment: * The vector from the fragment's world-space position to the camera, i.e. the view vector * The vector from the fragment's world-space position to the light source (assuming a point light source) * The surface normal at the fragment Given these values, one can compute the intensity of the specular highlight for the current fragment using the following formula: `specularIntensity = max(pow(dot(H, N), exp), 0)`, where `H` is the average of the view vector and the light vector and `N` is the surface normal. As always, both vectors should be normalized. The `exp` value is any number greater than 1. The higher the `exp`, the smaller and brighter the specular highlight. Once you have the specular highlight intensity, you can add it to a basic Lambertian shading calculation, and achieve an effect like this: Drawing ### Matcap Reflection Shader (10 points) ### Add code to the shader files `matcap.vert.glsl` and `matcap.frag.glsl` in order to implement the "matcap" method of shading surfaces on the models provided. Matcap shading is most commonly used to give 3D models the appearance of a complex material with dynamic lighting without having to perform expensive lighting calculations. The implementation of the matcap technique is actually quite simple, yet with the right textures it can look photorealistic. Given a 2D texture like this: Drawing you can make an entire model appear to be made of reddish clay: Drawing All that needs to be done to achieve this effect is to map the (x, y) coordinates of the surface normal of a fragment to a point in the circular matcap texture. So, if the (x, y) is (-1, 0), that would map to the leftmost point of the circle. Sample the texture at this point to find the color of your surface. ### Iridescent Shader (10 points) ### Add code to the shader files `gradient.vert.glsl` and `gradient.frag.glsl` in order to color your model using a prodedurally-generated color palette based on cosine curves with different periods. Following the example discussed [here](http://www.iquilezles.org/www/articles/palettes/palettes.htm), create your own custom color palette. Then, map this palette to the surface of the provided models by using the Lambertian dot product as the `t` value in this formula: `color(t) = a + b * cos(2 * PI * (c * t + d))`. Your models will look similar to this image (but may be different due to your chosen palette or `t` mapping): Drawing ### Custom vertex deformation shader (30 points) ### Modify `deform.vert.glsl` and `deform.frag.glsl` so that the shader non-uniformly modifies the positions of the provided models' vertices as a function of time, and also non-uniformly modifies the fragments' colors as a function of time. As shown in class, one inspiration for your shader might be to use a cosine-curve palette as a color basis, and alter your fragment color based on world-space surface normal orientation. Then, you could modify the vertex shader to interpolate between some "fixed" form, like a sphere, and the model's normal form. You can access the current time through the `u_Time` uniform, which is passed an ever-increasing value in `paintGL`. We encourage you to look up the different functions available to you in GLSL. There are more convenience functions than you might think. For instance, there is a function called `mix`, which linearly interpolates between two values. There is also a similar function called `smoothstep`, which interpolates between two values along a Hermite curve (effectively, it "eases in" and "eases out" near the extremes of the interpolation). Have fun playing around with different combinations of functions; we want you to be creative! For a more "procedural" appearance, you could even use a deterministic noise function as the basis for color or vertex offset. For example, the following code produces a "random" float with values between 0 and 1 given an input vec3, but always produces the same output for the same input: >
float mod289(float x){return x - floor(x * (1.0 / 289.0)) * 289.0;}
vec4 mod289(vec4 x){return x - floor(x * (1.0 / 289.0)) * 289.0;}
vec4 perm(vec4 x){return mod289(((x * 34.0) + 1.0) * x);}

float noise(vec3 p) {
    vec3 a = floor(p);
    vec3 d = p - a;
    d = d * d * (3.0 - 2.0 * d);

    vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0);
    vec4 k1 = perm(b.xyxy);
    vec4 k2 = perm(k1.xyxy + b.zzww);

    vec4 c = k2 + a.zzzz;
    vec4 k3 = perm(c);
    vec4 k4 = perm(c + 1.0);

    vec4 o1 = fract(k3 * (1.0 / 41.0));
    vec4 o2 = fract(k4 * (1.0 / 41.0));

    vec4 o3 = o2 * d.z + o1 * (1.0 - d.z);
    vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x);

    return o4.y * d.y + o4.x * (1.0 - d.y);
}
[Code source](https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83) Here is a recording of what we created by playing around with functions: Drawing Coding Style (10 points) ------- We will provide you with feedback on the organization and clarity of the code you have written for this assignment. Please refer to our course style guide as you implement your shaders and VBOs. Extra Credit (Maximum 15 points) --------- We will grant you extra credit if we deem your custom surface deformation shader to be particularly complex or interesting. Take time to experiment with different functions, both in your vertex shader and fragment shader, to produce visually interesting results. If you want to spend the time, you could even hard-code some sort of artistically-driven animation of your mesh based on the `u_Time` variable that all your shaders support. If you want to experiment with several different shader effects, you can add more `.glsl` files to your project. Add additional options to the surface shader drop-down menu in `shadercontrols.ui`, add your new `.glsl` files to `glsl.qrc`, and add code to `MyGL::createShaders` to add your new shader to `MyGL`'s list of shader programs. Submission -------- We will grade the code you have pushed to your GitHub repository, so make sure that you have correctly committed all of your files! Once you have pushed your finished project to GitHub, submit a link to your commit through the course dashboard. If you click on the Commits tab of your repository on Github, you will be brought to a list of commits you've made. Simply click on the one you wish for us to grade, then copy and paste the URL of the page into your text file.