Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file,...

30
Caravaggio Robert R. Snapp April 7, 2015 Contents 1 What is an org file? 2 1.1 Editing code blocks ................................................. 2 1.2 Weaving an org file ................................................. 2 1.2.1 Hints for weaving with L A T E X ....................................... 2 org_cs274.el .............................................. 3 1.3 Tangling ....................................................... 4 2 Installation 4 3 Project Outline 5 3.1 sphere.h ..................................................... 6 3.2 sphere.cpp ................................................... 8 3.2.1 File organization .............................................. 8 3.3 caravaggio.cpp ................................................ 12 3.3.1 File organization .............................................. 12 3.3.2 caution .................................................. 12 3.3.3 project_license ........................................... 12 3.3.4 headers .................................................. 13 3.3.5 globals .................................................. 13 3.3.6 tools ................................................... 14 loadAndCompileShader ....................................... 15 createVertexFragmentProgram .................................. 16 getUnusedFileName .......................................... 17 exportImageAsPNG .......................................... 18 exportWindowAsPNG .......................................... 19 3.3.7 initialization ............................................ 20 3.3.8 callbacks ................................................ 21 3.3.9 main .................................................... 25 3.4 Shader Files ..................................................... 27 3.4.1 The vertex shader: phong.vert ..................................... 27 3.4.2 The fragment shader: phong.frag ................................... 28 3.5 CMakeLists.txt ................................................ 28 3.6 Building the project. ................................................ 29 4 The Bibliography 29 Let there be light. Book of Genesis. 1

Transcript of Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file,...

Page 1: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

Caravaggio

Robert R. Snapp

April 7, 2015

Contents1 What is an org file? 2

1.1 Editing code blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.2 Weaving an org file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1.2.1 Hints for weaving with LATEX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2org_cs274.el . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

1.3 Tangling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2 Installation 4

3 Project Outline 53.1 sphere.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63.2 sphere.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

3.2.1 File organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83.3 caravaggio.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3.3.1 File organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123.3.2 caution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123.3.3 project_license . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123.3.4 headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133.3.5 globals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133.3.6 tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

loadAndCompileShader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15createVertexFragmentProgram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16getUnusedFileName . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17exportImageAsPNG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18exportWindowAsPNG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

3.3.7 initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203.3.8 callbacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213.3.9 main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

3.4 Shader Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.4.1 The vertex shader: phong.vert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.4.2 The fragment shader: phong.frag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

3.5 CMakeLists.txt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283.6 Building the project. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

4 The Bibliography 29

Let there be light.

Book of Genesis.

1

Page 2: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

1 What is an org file?An org file is a text file (e.g., ASCII or UTF-8) that conforms to a versatile set of conventions that support hierarchical sections(outlines), time management, spreadsheet operations, and literate programming. “Org” is short for “organization”, and conse-quently org files can be used to organize your life if not your work flow. Operationally, org files are created and modified withthe GNU Emacs text editor under the orgmode extension. (N.B., highlighted words in this document represent clickable linksin PDF readers and web browsers.) Orgmode was developed by Carsten Dominik. His 2008 presentation at Google introducesthe main features of orgmode. If you are new to Emacs, it is highly recommended to step through the Emacs Tutorial beforeediting or processing an org file. The website https://www.gnu.org/software/emacs/tour/ is a good place tostart.

This file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII file created andmaintained using the Emacs text editor. “Org” is short for organization, and org files can serve many functions: dynamicallyinteractive outlines, agendas, journals, and in the present context, literate programming. A literate program is a self-documentedcomputer program that explains in a literal style how the project is organized. Following Donald Knuth’s paradigm, two toolscan be applied to the source of a literate program: weave, which generates the documentation; and tangle, which generates thefiles containing code for compilation.

1.1 Editing code blocksAll the commands one uses to edit files in Emacs extend obviously to orgmode. An import new feature is the appearance ofcode blocks: lines delineated by #+begin_src . . . #+end_src delimiters. If the cursor falls within a code block, theEmacs command C-’ toggles in and out of a temporary window, where the syntax highlighting, and relevant language modeapply. (That’s Control-apostrophe, also known as Control-right-tick.)

1.2 Weaving an org fileThis file caravaggio.org can be “woven” into elegantly formatted documents (e.g., caravaggio.pdf or caravaggio.html)that describes the project. (Ideally, these documents are clearer than well-commented source code.) The generated documentsusually include every line of source contained in every project file. (With modest effort, documentation can also be generatedin Microsoft Word doc format.) An org file can be woven from within the Emacs text editor by executing the command,org-export-dispatch, which is by default bound to the key sequence C-c C-e. N.B., C denotes the Control key, soC-c C-e is entered by first pressing the Control key, then pressing and releasing the c key, then pressing and releasing thee key, then releasing the Control key. Phew! A menu is then presented for selecting the document format.

1.2.1 Hints for weaving with LATEX

Weaving caravaggio.org with LATEX (into a pdf) is tricky because of the use of the minted.sty package to print thesource code with syntax colorization. (N.B., the minted.sty package is part of the standard TEX/LATEX distribution: youshould not need to install it separately.) Here are some steps that I have found helpful.

1. I expect that you likely have a recent version of python installed on your computers. However, you might wish to ensureyou have a recent version. Even though Mac OS X includes python under /usr/bin/python, it is still in version 2.7.This should work fine for us, but you might wish to use a package manager to install the version 3+ branch.

2. Install the python package manager pip. (Again, it is easiest to let your OS package manager do the heavy lifting).

3. Once pip is installed, you can use it to install Pygments, a python tool that minted.sty uses to colorize the syntax for avariety of languages, providing yet another application for your cone cells. Execute sudo pip install Pygmentson the command line, and cross your fingers.

4. Ensure that when LATEX runs, the shell-escape option is enabled. I’ll show you below one way to do this. (Forsecurity reasons, it is by default not.) This option allows LATEX to execute external commands, namely pygments, whenit is running. Beware however of using this option on LATEX source that you do not trust, as this could put your system atrisk. In the following, I will show you one way to do this using a customization file for Emacs.

2

Page 3: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

There are many different ways to setup emacs. I used an OS package manager to install the latest version of GNU Emacs24.x. And then I installed the Emacs Prelude distribution, following the instructions at the link. Prelude creates asubdirectory ~/.emacs.d/personal/ to help you customize Emacs to your habits and needs. For example, I created a filecalled org_cs274.el that defines some settings that I like to use for org-mode. Let’s focus on a few items in this file.

org_cs274.el The org_cs274.el emacs customization file for org-mode, contains the following emacs-lisp com-mands that enables the shell-escape option, among other things. This file first ensures that some required files are loaded.The files that begin with ob, an abbreviation for “org babel”, include syntax rules for the indicated language, as well as commonmacros. Files beginning with ox, an abbreviation for “org export”, are needed for weaving into each output file format.

«org_cs274.el»=;;; org_cs274.el;;;;;; Commentary:;;; Place this file into an appropriate location so that it automatically loads when;;; you execute the GNU Emacs text editor, e.g., within ~/.emacs.d/personal/.;;;;;; Code:(require ’ox-latex)

;; Use minted.sty for displaying code in LaTeX(setq org-latex-listings ’minted)

;; Let pdflatex call pygmantize by activating the shell-escape mode.(setq org-latex-pdf-process

’("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f""bibtex %b""pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f""pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f" ))

The above commands specify that the minted.sty latex package should be used for code listings. (This package requiresthat Pygments be installed on your machine.) The last expression redefines the command that orgmode uses to convert an orgfile into a pdf document using LATEX. Note that we enable the -shell-escape option, so that org can invoke pygments as aremote process. Also note that pdflatex is invoked a total of three times, around a call to bibtex. The extra calls ensurethat references and links are properly constructed.

«org_cs274.el»+=;; Add a touch of style(setq org-latex-minted-options

’(("frame" "lines")("fontsize" "\\scriptsize")("bgcolor" "WhiteSmoke")))

The above expression defines some font attributes for code blocks rendered with minted. Each code block will be delineatedwith horizontal lines, the font size will be “scriptsize” (smaller than normal), and the background color will be “WhiteSmoke”.Each language used needs to be explicitly activated. Here we do so for C (which includes C++), emacs-lisp, latex, andsh (Bourne shell).

«org_cs274.el»+=;; Enable code blocks written in C, C++, tex, latex, orgmode, and sh.

(require ’ob)(require ’ob-C)(require ’ob-latex)(require ’ob-org)(require ’ob-sh)

(org-babel-do-load-languages’org-babel-load-languages’((C . t)

(emacs-lisp .t)(latex . t)(org . t)))

The next expression enables one to enter an org template for inserting source simply by typing “S > [tab]” where “[tab]” denotes

3

Page 4: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

the tab key.

«org_cs274.el»+=;; This is a handy trick that generates the template for a code block by typing <S[TAB] where [TAB] stands;; for the TAB key.(add-to-list ’org-structure-template-alist

’("S" "#+name: ?\n#+begin_src \n\n#+end_src""<src id=\"?\" lang=\"\">\n\n</src>"))

The next statement ensures that the leading white space in every code block is preserved, when the org file is tangled.

«org_cs274.el»+=;; preserve the leading white space in every code block.(setq org-src-preserve-indentation t)

The next two emacs-lisp commands enable one to express unmatched quotation marks in a verbatim environment inorgmode.

«org_cs274.el»+=;; Allow one to display commas single, and double quotes in verbatim mode. Note the odd appearance of;; a left-tick (backquote) character in the second line, followed eventually by a comma. This peculiar;; line expands the second invocation of org-emphasis-alist to its current value, before invoking;; the first part of the command. See,;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Backquote.html#Backquote;; for more details.(setcar (nthcdr 2 org-emphasis-regexp-components) " \t\n")(custom-set-variables ‘(org-emphasis-alist ’,org-emphasis-alist))

(provide ’org_cs274.el)

;;; org_cs274.el ends here.

1.3 TanglingThis file can also be “tangled” into all of the project’s souce code. In this event a Bourne shell script, build_meshould be generated within the current directory, and the four files caravaggio.cpp, phong.vert, phong.frag, andCMakeLists.txt, should be generated within a subdirectory called build. An org file can be tangled from within theEmacs text editor by executing the command org-babel-tangle, which is by default bound to the key sequence C-cC-v t.

2 InstallationIf you would like to compile this org project, the following software tools should be installed on your system:

1. The GNU Emacs text editor, with a recent version of orgmode.

2. A C++ compiler with OpenGL version 3.2 or higher. This comes for free on Linux systems. Mac OSX users shouldinstall XCode, and activate the command line tools. I am told that Visual C++ might suffice for Windows. AnotherWindows system options are Cygwin and MinGW.

3. CMake: an open-source, cross-platform utility for generating makefiles for compiling projects in C, C++, and relatedlanguages.

4. GLM: the OpenGL Mathematics library implements many useful vector and matrix operations in C++ for OpenGLapplication and shader (GLSL) programs.

5. GLEW: the OpenGL Extension Wrangler is a cross-platform library that detects the presence of OpenGL extensions, andautomatically loads them.

6. GLFW: an open-source, cross-platform library that allows OpenGL applications to use operating-system resources of theresident operating system, including windows, keyboards, mice, and game pads.

4

Page 5: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

7. FreeImage: an open-source, cross-platform library for loading, manipulating, and saving digital images in a variety ofpopular formats, including png, tiff, and jpg.

8. An implementation of LATEX: e.g., TEXLive or MacTEX.

On Mac OS X you might choose to install Xcode, along with the command line tools. This will also install OpenGL 4.1 orhigher. The other components: CMake, GLM, GLEW, and GLFW can be obtained with a package installer, such as macports,fink, or homebrew.

3 Project OutlineThe Caravaggio project creates an OpenGL program that displays spheres illuminated by various Phong light sources.

Here is an sample view of the output.

The project illustrates some practical techniques for managing a list of graphical objects with an STL vector container,how to upload different sets of model vertex arrays to the GPU, and how to define and modify the color, orientation, scale, andposition of different objects using uniform shader variables.

5

Page 6: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

This project consists of five files:

1. sphere.h defines a C++ class that generates a model of a sphere that can rendered in OpenGL.

2. sphere.cpp implements the member functions declared in sphere.h

3. caravaggio.cpp is a C++ souce file that defines a pair of functions for loading, compiling, and linking the GLSLshader files, an initialization routine, various other functions, four callback functions, and the main function.

4. phong.vert defines a simple OpenGL vertex shader program.

5. phong.frag defines a simple OpenGL fragement shader program.

6. CMakeLists.txt is a cmake input file, that when compiled with cmake, generates a makefile that can be used tocompile and link the project. It is recommended to build the project within a dedicated subdirectory (e.g., ./build/).

7. build_me is a unix shell script that creates a build subdirectory, runs CMake to generate a system specific makefile,applies make to the makefile to generate caravaggio (the executable program), and finally launches caravaggio.

Each component is now described in sequence.

3.1 sphere.h

This header file defines a class that defines a spherical model for OpenGL applications. First we insert the preamble. Thepreprocessing directive "#pragma once" is not standard C++, but most compilers support it. It should prevent the compiler fromincluding this header file more than once in any file. (A standard alternative is to use "guard macros".) We also include someOpenGL headers to declare the OpenGL API. This includes two files that belong to the glm library.

«sphere.h»=//<<caution>>//////////////////////////////////////////////////////////////////////////////////// sphere.h//////////////////////////////////////////////////////////////////////////////////////<<project_license>>//

#pragma once

#include <GL/glew.h>#include <GLFW/glfw3.h>#include <glm/glm.hpp>#include <glm/gtc/matrix_transform.hpp>

<<sphere_class>>

Now we introduce the class declaration for a sphere. Note that variables declared as static in C++ are shared by everyclass object. Thus, static variables are used to store the references to the model arrays and buffers. In the following we prefaceeach data member name with an underscore (_).

«sphere__class»=

6

Page 7: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

class Sphere {private:

static const GLuint _primitive_restart_index{0xFFFF};static const GLuint _nSlices{60};static const GLuint _nWedges{60};static GLenum _mode[3];static GLsizei _count[3];static GLsizei _nElements;

static GLuint _base_offset[3];static GLuint _element_buffer_object;static GLuint _vertex_array_object;static GLuint _vertex_buffer;

// Shader Program Locationsstatic GLint _SL_AmbientRho;static GLint _SL_DiffuseRho;static GLint _SL_SpecularRho;static GLint _SL_Shininess;

In C++ only static variables that are declared as static const can be initialized within the class declaration. Thesevariables are essentially symbolic constants for use within the class functions. The remaining static variables must beexplicitly defined within a cpp file, to ensure that the C++ linker allocates memory for each. We will do this at the beginningof the file sphere.cpp below.

We now declare the data members that are specific to each class object. Note that each Sphere should have its own center,radius and material properties independent of the others.

«sphere__class»+glm::vec3 _center;GLfloat _radius;

glm::vec3 _ambient_rho; // Perhaps a bit redundant to maintain a color for each reflection mode.glm::vec3 _diffuse_rho;glm::vec3 _specular_rho;GLfloat _shininess;

glm::mat4 _model_transformation;glm::mat3 _vector_transformation; // should be set to the transpose of the inverse of the

// upper left three-by-three block of _model_transformation.

We now declare the public interface to the class. Functions that are declared as static in C++ belong to the class,not to any single object. We use them here to manage the non-constant static class variables declared above. The functioninitialize_class() only needs to be called once. It allocates the OpenGL resources required for rendering the basemodel of the sphere with the GPU, resetting the non constant static variables in the process.

«sphere__class»+public:

static void initialize_class();static void set_ambient_rho_SL(GLint sl);static void set_diffuse_rho_SL(GLint sl);static void set_specular_rho_SL(GLint sl);static void set_shininess_SL(GLint sl);

Next comes the constructor function which we will use whenever we want to create an new instance of the class, i.e., a newsphere in our world system. The default color is white

«sphere__class»+

7

Page 8: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

Sphere(glm::vec3 c = glm::vec3(0.0f, 0.0f, 0.0f), GLfloat r = 1.0f) :_center{c}, _radius{r},_ambient_rho{glm::vec3(0.10f, 0.10f, 0.10f)},_diffuse_rho{glm::vec3(1.00f, 1.00f, 1.00f)},_specular_rho{glm::vec3(0.10f, 0.10f, 0.10f)},_shininess{10.0f}{

// ensure that initialize_class() has been called.if (_element_buffer_object == 0) {

initialize_class();}

glm::mat4 identity = glm::mat4(1.0f);glm::mat4 smtx = glm::scale(identity, glm::vec3(_radius, _radius, _radius));_model_transformation = glm::translate(smtx, _center);_vector_transformation = glm::mat3(static_cast<GLfloat>(1.0f/_radius));

}

The all important function draw_elements() draws the sphere. The other functions are used to get and set private datamembers.

«sphere__class»+void draw_elements();glm::mat4 model_transformation();glm::mat3 vector_transformation();void set_model_transformation(glm::mat4 t);void set_vector_transformation(glm::mat3 t);

glm::vec3 ambient_rho();glm::vec3 diffuse_rho();glm::vec3 specular_rho();GLfloat shininess();

void set_ambient_rho(glm::vec3 rho);void set_diffuse_rho(glm::vec3 rho);void set_specular_rho(glm::vec3 rho);void set_shininess(GLfloat s);

};

3.2 sphere.cpp

3.2.1 File organization

«sphere.cpp»=//<<caution>>//----------// sphere.cpp//-----------//<<project_license>>//-----------#include "sphere.h"#include <cmath>

GLuint Sphere::_base_offset[3] = {0,0,0};GLuint Sphere::_element_buffer_object = 0;GLuint Sphere::_vertex_array_object = 0;GLuint Sphere::_vertex_buffer = 0;GLint Sphere::_SL_AmbientRho = 0;GLint Sphere::_SL_DiffuseRho = 0;GLint Sphere::_SL_SpecularRho = 0;GLint Sphere::_SL_Shininess = 0;GLenum Sphere::_mode[3] = {GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN};GLsizei Sphere::_count[3] = {_nWedges + 2, (_nSlices - 1)*(2*(_nWedges + 1) + 1) - 1, _nWedges + 2};GLsizei Sphere::_nElements{_nSlices*(2*_nWedges + 3)};

<<sphere_initialize_class>><<sphere_draw_elements>><<sphere_model_transformation>><<sphere_vector_transformation>><<sphere_ambient_rho>><<sphere_diffuse_rho>><<sphere_specular_rho>><<sphere_shininess>>

8

Page 9: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

The initialize_class() function defines the geometry of the sphere and allocates and initializes all static attributeswith the exception of the four shader program locations. The sphere is defined by north and south triangle fans, and a circum-ferential waist consisting of a sequence of triangle strips.

«sphere_initialize_class»=

9

Page 10: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

void Sphere::initialize_class() {_base_offset[0] = 0;_base_offset[1] = _count[0];_base_offset[2] = _base_offset[1] + _count[1];GLushort element[_nElements];

const GLuint nVertices = _nSlices*_nWedges + 2;double dTheta = M_PI/(_nSlices + 1);double dPhi = 2*M_PI/_nWedges;

GLfloat position[nVertices][3];

position[0][0] = 0.0f; // north poleposition[0][1] = 0.0f;position[0][2] = 1.0f;

int index = 1;for (int slice = 0; slice < _nSlices; slice++) {

double theta = (slice + 1)*dTheta;for (int wedge = 0; wedge < _nWedges; wedge++) {

double phi = wedge*dPhi;position[index][0] = std::cos(phi) * std::sin(theta);position[index][1] = std::sin(phi) * std::sin(theta);position[index++][2] = std::cos(theta);

}}// index shoud now equal nSphereVertices - 1position[index][0] = 0.0f; // south poleposition[index][1] = 0.0f;position[index][2] = -1.0f;

int element_index = 0;

// Insert the element indices for the triangle fan at the north pole.for (int i = 0; i < _nWedges + 1; i++) {

element[element_index++] = i;}element[element_index++] = 1;

// Insert the element indicies for the nSlices - 1 triangle strips that// constitute the waist of the sphere.int lower_offset;int upper_offset= 1;for (int slice = 0; slice < _nSlices - 1; slice++) {

lower_offset = upper_offset;upper_offset += _nWedges;for(int wedge = 0; wedge < _nWedges; wedge++) {

element[element_index++] = wedge + lower_offset;element[element_index++] = wedge + upper_offset;

}element[element_index++] = lower_offset;element[element_index++] = upper_offset;if (slice < _nSlices - 2) {

element[element_index++] = _primitive_restart_index;}

}

// Insert the element indices for the triangle fan at the south pole.element[element_index++] = nVertices - 1;for(int i = 0; i < _nWedges; i++) {

element[element_index++] = i + upper_offset;}element[element_index] = upper_offset;

// Allocate GPU address space.glGenBuffers(1, &_element_buffer_object);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _element_buffer_object);glBufferData(GL_ELEMENT_ARRAY_BUFFER,

sizeof(element), element, GL_STATIC_DRAW);

glGenVertexArrays(1, &_vertex_array_object);glBindVertexArray(_vertex_array_object);

glGenBuffers(1, &_vertex_buffer);glBindBuffer(GL_ARRAY_BUFFER, _vertex_buffer);

// Here we will pack the vertex positions, and the normal vectors of each vertex into// the same vertex array buffer, with the position in location 0, and the normal in// position 1. Note that for a unit sphere, the position and normal vectors are equal.

glBufferData(GL_ARRAY_BUFFER, 2*sizeof(position), NULL, GL_STATIC_DRAW);// For the position of each vetex.glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(position), position);

// For the normal vector to each vertexglBufferSubData(GL_ARRAY_BUFFER, sizeof(position), sizeof(position), position);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid *) sizeof(position));

glEnableVertexAttribArray(0);glEnableVertexAttribArray(1);

}

10

Page 11: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

The function draw_elements() passes the material constants to the shader program, and then draws each of the threeelement list.

«sphere_draw_elements»=void Sphere::draw_elements() {

glUniform3fv(_SL_AmbientRho, 1, &_ambient_rho[0]);glUniform3fv(_SL_DiffuseRho, 1, &_diffuse_rho[0]);glUniform3fv(_SL_SpecularRho, 1, &_specular_rho[0]);glUniform1f(_SL_Shininess, _shininess);

glEnable(GL_PRIMITIVE_RESTART);glPrimitiveRestartIndex(_primitive_restart_index);glBindVertexArray(_vertex_array_object);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _element_buffer_object);for (int i = 0; i < 3; i++) {

glDrawElements(Sphere::_mode[i], Sphere::_count[i], GL_UNSIGNED_SHORT,(const GLvoid *) (Sphere::_base_offset[i]*sizeof(GLushort)));

}}

«sphere_model_transformation»=glm::mat4 Sphere::model_transformation() {

return _model_transformation;}

«sphere_vector_transformation»=glm::mat3 Sphere::vector_transformation() {

return _vector_transformation;}

«sphere_ambient_rho»=glm::vec3 Sphere::ambient_rho() {

return _ambient_rho;}

void Sphere::set_ambient_rho(glm::vec3 rho) {_ambient_rho = rho;

}

void Sphere::set_ambient_rho_SL(GLint sl) {_SL_AmbientRho = sl;

}

«sphere_diffuse_rho»=glm::vec3 Sphere::diffuse_rho() {

return _diffuse_rho;}

void Sphere::set_diffuse_rho(glm::vec3 rho) {_diffuse_rho = rho;

}

void Sphere::set_diffuse_rho_SL(GLint sl) {_SL_DiffuseRho = sl;

}

«sphere_specular_rho»=glm::vec3 Sphere::specular_rho() {

return _specular_rho;}

void Sphere::set_specular_rho(glm::vec3 rho) {_specular_rho = rho;

}

void Sphere::set_specular_rho_SL(GLint sl) {_SL_SpecularRho = sl;

}

11

Page 12: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

«sphere_shininess»=GLfloat Sphere::shininess() {

return _shininess;}

void Sphere::set_shininess(GLfloat s) {_shininess = s;

}

void Sphere::set_shininess_SL(GLint sl) {_SL_Shininess = sl;

}

3.3 caravaggio.cpp

3.3.1 File organization

This C++ file can be partitioned into seven sections. When tangled, the following source block generates caravaggio.cpp:each name surrounded by double angled brackets (<< label >>) expands to a labeled code block, called a “chunk”, definedelsewhere in this file. (The C++ comment characters to the left of <<project_licenses>> result in like comment char-acters appearing at the beginning of each line of this particular code block. Pretty neat!) You should view the content ofcaravaggio.org to see how this magic is actually performed.

The first chunk defines the file caravaggio.cpp:

«caravaggio.cpp»=//<<caution>>//////////////////////////////////////////////////////////////////////////////////// caravaggio.cpp//////////////////////////////////////////////////////////////////////////////////////<<project_license>>//<<headers>><<globals>><<tools>><<initialization>><<callbacks>><<main>>// end of caravaggio.cpp

We now describe each code block in sequence.

3.3.2 caution

The following cautionary note should be inserted as a comment at the beginning of each file that is automatically generated bythis org document.

«caution»=CAUTION: The content of this file is automatically generated by Emacs orgmodefrom the file caravaggio.org that should either be in this, or the parentdirectory. Consequently, any modifications made to this file will likely beephemeral. Please edit caravaggio.org instead.

3.3.3 project_license

«project_license»=

12

Page 13: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

================================================================================This file is part of project caravaggio, a simple demonstration of the OpenGLAPI, that generates several polyhedra, each decorated with a texture mapthat is derived from an input digital image.

caravaggio is free software: you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation, either version 3 of the License, or(at your option) any later version.

caravaggio is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.

You should have received a copy of the GNU General Public Licensealong with union-find.org. If not, see <http://www.gnu.org/licenses/>.

Copyright (C) 2014, 2015 Robert R. Snapp.================================================================================This file was automatically generated using an org-babel tangle operation withthe file caravaggio.org. Thus, the latter file should be edited instead ofthis file.================================================================================

3.3.4 headers

As C++ is a strongly typed language, every function needs to be declared before it is invoked. It is convenient to declare thetypes of the standard C++ library functions by including the corresponing header files. Our program will also some functionsfor some commonly used libraries that extend the functionality of OpenGL. As noted above, you will need to install theselibraries, preferably by means of an automatic package manager. Doing so, will also install the corresponding header files. Allthree graphics libraries will be useful in our course.

«headers»=// Standard C++ libraries#include <algorithm> // for max and min#include <iostream>#include <stdexcept>#include <cmath>#include <climits>#include <ctime>#include <sstream>#include <fstream>#include <string>#include <cstdlib> // for rand(), srand()#include <random> // for the Mersenne Twister using in caravaggioImageFrame.#include <vector>

// Graphics libraries that extend OpenGL#include <GL/glew.h>#include <GLFW/glfw3.h>#include <glm/glm.hpp>#include <glm/gtc/matrix_transform.hpp>

// FreeImage headers, for digital image format support.#include <FreeImage.h>

// Headers for our models#include "sphere.h"

3.3.5 globals

The purpose of Caravaggio is to demonstrate how one can create a three dimension scene composed of illuminated spheres,each with distinct material properties.

A C++ STL vector container called SphereVector will be used to maintain the set of figures that will be displayed.This list will be initialized within the init() procedure. The display() procedure will iterate through the entire array,rendering each figure in sequence onto the display.

«globals»=

13

Page 14: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

std::vector<Sphere> SphereVector;

We also will define our light source globally. This will allow us to modify it interactively if we desire. A more professionalimplementation would define a light class, encapsulating some of the variables, but our casual approach should work for thenonce.

«globals»=glm::vec3 TheAmbientIntensity;glm::vec3 TheLightIntensity;glm::vec3 TheLightPosition;

The next chunk defines some a few global variables that communicate information from the application to the display()callback function. Normally, adopting an object-oriented language allows one to forego the undesirable use of global variables.However, since OpenGL is designed to work with both C and C++, it is not possible to overload, or override the display callbackfunction. Consequently, we will often need to define a few global variables in each OpenGL application. Another option wouldbe to create an Application class, which would contain the global variables as member data, and then define the display()callback as a member function of the same class. This would have the desirable effect of hiding the variables from functionsoutside of the Application class. We might adopt this strategy in the future, but for now, we will keep things simple.

In the following GLuint maps to a 32-bit unsigned integer. Also glm defines the namespace of the glm library, a templatelibrary that defines many useful matrix and vector operations for OpenGL. Likewise, GLFWwindow is a type defined by theGLFW library that I believe is easier to use than the older GLUT library.

«globals»+=GLFWwindow* gWindow = NULL; // pointer to the graphics window goverened by the OSconst glm::vec2 CANVAS(800, 800); // Our window and viewport dimensions.

The next constant, MAX_FILE_MATCH_ATTEMPTS sets a limit on the number times the function getImageFilenameis allowed to test that an indexed string is not currently in use in the current directory.

«globals»+=const size_t MAX_FILE_MATCH_ATTEMPTS = 1000;

The vertex shader for caravaggio employs two variables having a uniform GLSL qualifier. From an examination ofthe OpenGL and GLSL documentation, one learns that a uniform variable allows an algorithm to apply the same value inevery shader process. The following two statements create handles (shader locations) for the GLSL variables color andtransformation, so that this C++ program (running on the CPU) can modify their values as needed. Here we simplydeclare them within a global scope. They are each initialized in the init() procedure, and are repeatedly evaluated in everyinvocation of display().

«globals»+=GLint SL_ModelViewTransformation;GLint SL_NormalVectorTransformation;GLint SL_ModelViewPerspectiveTransformation;GLint SL_AmbientIntensity;GLint SL_LightIntensity;GLint SL_LightPosition;GLint SL_EyeDirection;

The location of the camera (view origin) a point on a sphere. Here we initialize these variables, which are modified bypressing on the keys.

«globals»+=double Theta = M_PI/90.0;double Phi = 0.0;double Rho = 8.0;

3.3.6 tools

One of the themes of computer graphics involves the accelerated development of powerful graphics hardware. In order to exploitthe computational power of graphics processing units (GPUs), which are now integrated into nearly every smartphone, laptop,

14

Page 15: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

and desktop computer, the OpenGL API has evolved to enable (or rather require) programmers to develop code that targetsGPUs. The programs, called graphics shaders, play different roles in the graphics pipeline. The current application defines twodifferent shaders: a vertex shader, and a fragment shader. These shader programs are written in the OpenGL Shading Language(GLSL) which shares syntactic elements with C. Each shader program must be compiled and linked by the application programduring runtime using the glCompileShader and glLinkShader OpenGL functions. Since these tasks will be performedfrequently (indeed, within every OpenGL program we will run this semester), a couple general purpose utility functions areprovided below as tools.

«tools»=<<loadAndCompileShader>><<createVertexFragmentProgram>><<getUnusedFileName>><<exportImageAsPNG>><<exportWindowAsPNG>>

loadAndCompileShader The first tool, loadAndCompileShader accepts two arguments. The first is constant oftype GLenum, which also represents an existing shader type. Legal values are GL_VERTEX_SHADER, GL_GEOMETRY_SHADER,GL_TESS_EVALUATION_SHADER, GL_TESS_CONTROL_SHADER, GL_FRAGMENT_SHADER, orGL_COMPUTE_SHADER. The second argument is a reference to a C++ string that defines the path of the shader file.

«loadAndCompileShader»=GLuint loadAndCompileShader(GLenum shaderType, const std::string& path) {

std::ifstream f;f.open(path.c_str(), std::ios::in | std::ios::binary);if (!f.is_open()) {

throw std::runtime_error(std::string("Can’t open shader file ") + path);}

An input file stream f is declared. An attempt is them made to bind it to the path defined by the second argument. Anexception is thrown if the file cannot be opened.

«loadAndCompileShader»+=// read the shader program from the file into the bufferstd::stringstream buffer;buffer << f.rdbuf();

The contents of the file referenced by path are then read into a buffer using the standard C++ stream library.

«loadAndCompileShader»+=GLuint shader = glCreateShader(shaderType);if (! shader) {

throw std::runtime_error(std::string("Can’t create shader for file ") + path);}

We then “create” a shader of the kind specified by the first argument to loadAndCompileShader. In the event that theinput argument is out of range, (e.g., the type of shader might not be supported by the current version of OpenGL), an exceptionis thrown.

«loadAndCompileShader»+=// tricky conversion from a stringstream buffer to a C (null terminated) string.const std::string& bufferAsString = buffer.str();const GLchar* shaderCode = bufferAsString.c_str();const GLchar* codeArray[] = { shaderCode };GLint size = strlen(shaderCode);glShaderSource(shader, 1, codeArray, NULL);glCompileShader(shader);

Converting the std::stringstream buffer into a character array required by glCompileShader requires a smallmachination: buffer is first copied into bufferAsString as a C++ string, and then subsequently converted on thenonce into a C character array referenced by shaderCode. (Most C++ implementations perform this operation quicklywithout significant memory modification.) A single element codeArray is declared (N.B., this is an array of characterpointers: no significant memory allocation is required). The number of characters is shaderCode is then computed and stored

15

Page 16: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

in size. The OpenGL function glShaderSource binds the pointer codeArray to shader, which is then compiled byglCompileShader. Note that all of the steps occur within the application and CPU, not the GPU.

«loadAndCompileShader»+=GLint status;glGetShaderiv(shader, GL_COMPILE_STATUS, &status);if (! status) {

std::cerr << "Compilation error in shader file " << path << std::endl;GLint logLen;glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLen);if (logLen > 0) {

char *log = new char[logLen];GLsizei written;glGetShaderInfoLog(shader, logLen, &written, log);std::cerr << "Shader log: " << std::endl;std::cerr << log << std::endl;delete [] log;

}throw std::runtime_error(

std::string("Can’t compile the shader defined in file ") + path);}return shader;

}

It is always a good idea to check that the shader compiled without an error. If there was a problem, the most likely indicationwould be an incomprehensible blank window. A status variable is declared, and it is set to GL_FALSE (a.k.a., 0) in the eventof an error. In this event a log message, which is usually informative, is sent to the console, before the runtime exception isthrown. (These steps were inspired by a recipe in the OpenGL 4 Shading Language Cookbook, 2nd edition, by David Wolff,Packt Publishing Ltd., 2013.) If all went well, OpenGL has allocated an internal buffer containing a version of the shader codedfor the GPU, and the value of shader, the ID referencing that code, is returned.

createVertexFragmentProgram This second tool invokes loadAndCompileShader twice to compile and linka GLSL program that contains a vertex shader and fragment shader, each specified by corresponding paths as arguments. Ifsuccessful, an integer-valued reference to the linked program is returned. An example of the usage of this function appears atthe end of the initialization function init below.

First we load and compile the vertex and fragment shaders.

«createVertexFragmentProgram»=GLuint createVertexFragmentProgram(const std::string& vertex_shader_path,

const std::string& fragment_shader_path) {GLuint vertexShader = loadAndCompileShader(GL_VERTEX_SHADER, vertex_shader_path);GLuint fragmentShader = loadAndCompileShader(GL_FRAGMENT_SHADER, fragment_shader_path);

Then we ask OpenGL the resources required for defining a new shader program, and wisely test for success: glCreateProgramreturns a null value if the program could not be created.

«createVertexFragmentProgram»+=GLuint program = glCreateProgram();if (! program) {

throw std::runtime_error("Can’t create GLSL program.");}

After binding our shader programs, referenced by vertexShader and fragmentShader to our new program, we linkit with a call to glLinkProgram.

«createVertexFragmentProgram»+=glAttachShader(program, vertexShader);glAttachShader(program, fragmentShader);glLinkProgram(program);

Next, we repeat the recipe found in the OpenGL 4 Shading Language Cookbook, 2nd edition, to test for any unfortunatesurprises.

«createVertexFragmentProgram»+=

16

Page 17: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

GLint status;glGetProgramiv(program, GL_LINK_STATUS, &status);if (! status) {

std::cerr << "Linking error in shader program!" << std::endl;GLint logLen;glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLen);if (logLen > 0) {

char *log = new char[logLen];GLsizei written;glGetProgramInfoLog(program, logLen, &written, log);std::cerr << "Shader log: " << std::endl;std::cerr << log << std::endl;delete [] log;

}throw std::runtime_error("Can’t link shader program.");

}return program;

}

getUnusedFileName We will now create a function that will attempt to construct an image file name that does notconflict with any existing files in the current directory. The name will be constructed by concatenating a common prefix (e.g.,the application name) to an integer time stamp, which will be followed by an integer index, followed by the specified extension.The index will be incremented until no conflict occurs. Just to be safe, a ceiling will be placed on the index, in the unlikelyevent that the current process does not have permission to read any files from the current directory. If no file name can be found,getUnusedFileName returns a null pointer. Otherwise, the file name is returned as a STL string.

«getUnusedFileName»=std::string getUnusedFileName(const std::string &prefix, const std::string &ext) {

std::string imageFileName(""); // initial value is returned in search fails.

// load the current date and time in theTime.time_t theTime;time(&theTime);

// convert the date time into the current locale.struct tm *theTimeInfo;theTimeInfo = localtime(&theTime);

The <ctime> header included near the top of caravaggio.cpp defines a numeric type, time_t, which can representlong time intervals. (See Stroustrup, Section 43.6.) The header also defines the struct tm that has nine different fields,representing hours, minutes, seconds, and other calendar measures. The function time computes the number of seconds thathave elapsed since 00:00:00, January 1, 1970 (UTC), and stores that value in the memory address indicated by its argument.(N.B. &theTime evaluates to the address of the variable theTime.) The localtime function converts the numeric timepointed to by its argument into the various calendar units in accordance with the local time zone. This information is passedinto the tm struct. The above four commands is pretty much the standard way to answer the question “What time is it?” inC or C++.

«getUnusedFileName»+=// represent the date and time in the format "150117184509" for 2015 January 17,// 18 hours, 45 minutes, 09 seconds.const int TIME_STRING_SIZE = 16; // must be at least 13.char timeString[TIME_STRING_SIZE];strftime(timeString, TIME_STRING_SIZE, "%y%m%d%H%M%S", theTimeInfo);

We then declare and define a C character array. The function strftime is originally part of the C time library. It acceptsfour arguments. First comes the address of the character buffer that will receive the formatted results. Second comes the sizeof the buffer. Third comes a string that dictates how the character string is formatted. We will write a four digit year, followedby a two digit month, followed by a two-digit date, followed by a 2 digit hour (with respect to a 24 hour clock), followed by atwo-digit minute, followed by a two digit second. If you want to try other options, study the table on page 1263 of Stroustrup[2013]. Finally, the fourth argument is a tm structure that has already broken the moment of interest into its salient components.

We will now use this information to compose a unique filename in the current directory. First we introduce three newvariables.

«getUnusedFileName»+=

17

Page 18: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

std::stringstream filename;std::ifstream fs;int index =0;

The C++ Standard Template Library defines a plethora of useful data structures with matching algorithms. A std::stringstreamobject is a string buffer that will automatically expand and contract as needed, and supports most of the functionality of a filedevice. In the following we will construct a name for our file by writing formatted values to filename. For each candidatefilename that we generate, we will probe the current directory in order to verify that filename does not already exist. We willuse std::ifstream fs as our probe. A std::ifstream is used to read an existing file, the leading i stands for input,the f for file. The third variable is an integer index initialized to 0. We will iteratively increment i until we obtain a uniquefile name.

«getUnusedFileName»+=// find a new filename of the form <filenameprefix>_<timeString>_<index>.<ext>, where// <index> is incremented as needed.do {

// reset the stringstream filenamefilename.str(std::string()); // clear the buffer contentfilename.clear(); // reset the state flags

// propose an available (unused) filename.filename << prefix << "_" << timeString << "_" << index++ << ext;

// Ensure the ifstream fs is closed.if (fs.is_open()) fs.close();

// If file does not exist, it can’t be opened in input mode.fs.open(filename.str(), std::ios_base::in);

} while (fs.is_open() && index < MAX_FILE_MATCH_ATTEMPTS);

In the above loop, first the filename stringstream is reset. Then a formatted character string is written into thestringstream buffer as if it were a C++ output device. The content of the prefix is followed by an underscore, the formattedtimeString computed above, another underscore, the current index, and finally the requested extension ext. (N.B., the firstcharacter of ext should probably be a period.) Also note that here we increment the value of index by 1, in the event that weneed to try again. An attempt is then made to open a file with the constructed name for reading. We attempt to close fs, in theevent it was opened during the previous iteration, then we attempt to open the file. Here failure is a success. A failed attemptindicates that a file with that name does not exist. So we found our unused file name, and exit from the loop. However, if fs isopen, then a file with that name exists. If index is less than the value of MAX_FILE_MATCH_ATTEMPTS, defined as a globalconstant, then the iterations continue. Imposing an upper limit on index is probably pedantic and unnecessary: no directorycan have an infinite number of files. (You are free to modify it if you like.)

Note that a do { <<body>> } while (<<test>>); loop construct is employed whenever we must execute<<body>> at least once.

«getUnusedFileName»+=// Test to see if a unique name was actually found.if (fs.is_open() && index >= MAX_FILE_MATCH_ATTEMPTS) {

fs.close(); // failure: there are too many files with the same time stamp.} else { // N.B. fs must be closed on this branch, right?

imageFileName = filename.str(); // success!}return imageFileName;

}

If fs is open, and the value of index exceeds its predetermined limit, then fs is closed and the function returns an emptystring to the calling function. Otherwise, the function returns the successful filename as a string.

exportImageAsPNG «exportImageAsPNG»=This little utility copies a tightly packed OpenGL RGB image buffer to a PNG image file with a unused file name that

begins with the content of filename_prefix. The first argument points to the image buffer, the second and third indicatethe image dimensions, and the fourth determines the file’s name prefix. Note that the FreeImage library performs the heavylifting.

18

Page 19: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

void exportImageAsPNG(GLubyte* image, GLuint width, GLuint height, const std::string& filename_prefix) {// The following variables are introduced to clarify the call signature of// FreeImage_ConvertFromRawBits, which will convert the GLubyte pixel buffer// into a FreeImage buffer.const unsigned int red_mask = 0xFF0000;const unsigned int green_mask = 0x00FF00;const unsigned int blue_mask = 0x0000FF;const unsigned int bits_per_pixel = 24;const bool top_left_pixel_appears_first = false;

unsigned int bytes_per_row = 3 * width;

FIBITMAP* fi_image = FreeImage_ConvertFromRawBits(image, width, height, bytes_per_row,bits_per_pixel, red_mask, green_mask,blue_mask, top_left_pixel_appears_first);

FreeImage_ConvertFromRawBits creates a new FITBITMAP data structure that holds a copy of the input image.Before we can export the contents of fi_image to a file, we need to construct a unique file name that begins with the prefixspecified in filename_prefix. The utility getUnusedFileName defined above, constructs a file name that does notappear in the current directory, that begins with filename_prefix, followed by a numeric time stamp, followed by asequence index, followed by the extension string, here .png.

«exportImageAsPNG»+=std::string imagefilename = getUnusedFileName(filename_prefix, std::string(".png"));if (imagefilename.length() == 0) {

throw std::runtime_error("Can’t find an available png file name.");}

Having found the file name, we utilize the FreeImage library to create the PNG file. The routine we use isFreeImage_Save. The first argument FIF_PNG specifies the file format as PNG. (Other format options are describedin the FreeImage documentation.) The last argument (here 0, the default option) is used to specify optional behavior, e.g.,alternate compression schemes. «exportImageAsPNG»+=

FreeImage_Save(FIF_PNG, fi_image, imagefilename.c_str(), 0); // Save fi_image as a PNG file.

Mission accomplished! Before returning to the calling program, we need to release the fi_image structure that wasrecently allocated. Note that the STL string, imagefilename will be automatically released once the scope changes.

«exportImageAsPNG»+=// Free allocated memoryFreeImage_Unload(fi_image);return;

}

exportWindowAsPNG This function exportWindowAsPNG uses the FreeImage library to save the contents of the spec-ified graphics window to a PNG file. exportWindowAsPNG requires two arguments, a pointer to the active GLFWwindow,and a string prefix that will be the beginning of the file name.

«exportWindowAsPNG»+=void exportWindowAsPNG(GLFWwindow* window, const std::string &prefix) {

// Recover window dimensions from glfwint width;int height;glfwGetFramebufferSize(window, &width, &height);

The function glfwGetFramebufferSize should return the dimension of the framebuffer of the active window inpixels. We will use this information to allocate a binary vector for storing the frame buffer, using 3 bytes per pixel. Using thesedimensions, we allocate a sufficiently large buffer to hold all the image data, assuming an RGB color model with 1 byte percolor channel.

«exportWindowAsPNG»+=

19

Page 20: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

// allocate a buffer to store all of the pixels in the frame buffer.GLubyte* pixels = new GLubyte [width * height * sizeof(GLubyte)* 3];if (! pixels) {

throw std::runtime_error(std::string("Can’t allocate pixel array for image of size ")+ std::to_string(width) + std::string(" by ") + std::to_string(height)+ std::string(" pixels."));

}

Here we use the C++ new operator, a common source of memory leaks. We must remember to delete pixels beforewe exit this function. GLubyte is a typedef for an unsigned standard byte. You can see we requesting a contiguousone-dimensional vector of three of these for every pixel in the frame buffer. In the event new fails (e.g., we have run out ofmemory), pixels is set to 0. In this case, ! 0 returns true and an exception is thrown. It is probably clearer to writepixels == NULL for this test, but I like the simplicity of !.

«exportWindowAsPNG»+=glPixelStorei(GL_PACK_ALIGNMENT, 1); // Align data by bytes so the framebuffer data will fit.glReadBuffer(GL_FRONT); // The front color buffer is the visible one.glReadPixels(0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, pixels);

You can read about the previous three commands in the Redbook Shreiner et al. [2013]. (Commands that generally beginwith a gl belong to OpenGL.) The workhorse is glReadPixels which will copy and tightly pack the entire framebuffer intovector pixels. Choosing GL_BGR over the other options, requires a modicum of thought.

We now rely on the previously defined function, exportImageAsPNG to create the output PNG image file, named withthe specified prefix, that contains the image represented in pixels. «exportWindowAsPNG»+=

exportImageAsPNG(pixels, width, height, prefix);

Before exiting, we explicitly free up the memory used by image and pixels. Note that STL data structures, likeimagefilename perform their own memory management.

«exportWindowAsPNG»+=delete [] pixels;return;

}

3.3.7 initialization

Within the initialization chunk the application constructs the graphical model (in this case a three-by-three array of spheres,each of unit diameter. Shreiner et al. [2013].

«initialization»=void init() {

Sphere::initialize_class();

Now we create the scene: a 3 x 3 array of 9 spheres, using the Sphere class defined above. Each sphere has differentdifferent material properties.

«initialization»+=

20

Page 21: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

for (int i = 0; i < 3; i++) {GLfloat tx = 2.0f*(i - 1);for(int j = 0; j < 3; j++) {

GLfloat ty = 2.0*(j - 1);GLfloat tz = 0.0f;GLfloat rhoRed = 0.9 - i*0.1;GLfloat rhoGreen = 0.9 - j*0.1;GLfloat rhoBlue = 0.9 - (i + j)*0.1;Sphere *s = new Sphere(glm::vec3(tx, ty, tz), 0.5f);s->set_ambient_rho(glm::vec3(rhoRed, rhoGreen, rhoBlue));s->set_diffuse_rho(glm::vec3(rhoRed, rhoGreen, rhoBlue));s->set_specular_rho(glm::vec3(rhoRed, rhoGreen, rhoBlue));s->set_shininess(30.0f);SphereVector.push_back(*s);delete s;

}}

Now we initialize the light source and its position. We will give it a yellow hue.

«initialization»+=TheAmbientIntensity = glm::vec3(0.1f, 0.1f, 0.08f);TheLightIntensity = glm::vec3(1.0f, 1.0f, 0.8f); // a yellow tint.TheLightPosition = glm::vec3(0.0f, 0.0f, 30.0f);

The next step is to compile, link, and upload the shader program to the GPU. Most of the work is performed by our utilityfunction, createVertexFragmentProgram. The upload is performed by glUseProgram.

«intialization»+=GLuint shader_program = createVertexFragmentProgram(std::string("phong.vert"),

std::string("phong.frag"));glUseProgram(shader_program);

«intialization»+=Once the shader program has been defined, we can query OpenGL for the locations for the uniform variables required by

the vertex shader (phong.vert).Sphere::set_ambient_rho_SL(glGetUniformLocation(shader_program, "AmbientRho"));Sphere::set_diffuse_rho_SL(glGetUniformLocation(shader_program, "DiffuseRho"));Sphere::set_specular_rho_SL(glGetUniformLocation(shader_program, "SpecularRho"));Sphere::set_shininess_SL(glGetUniformLocation(shader_program, "Shininess"));

SL_ModelViewTransformation = glGetUniformLocation(shader_program, "ModelViewTransformation");SL_NormalVectorTransformation = glGetUniformLocation(shader_program, "NormalVectorTransformation");SL_ModelViewPerspectiveTransformation

= glGetUniformLocation(shader_program, "ModelViewPerspectiveTransformation");SL_AmbientIntensity = glGetUniformLocation(shader_program, "AmbientIntensity");SL_LightIntensity = glGetUniformLocation(shader_program, "LightIntensity");SL_LightPosition = glGetUniformLocation(shader_program, "LightPosition");SL_EyeDirection = glGetUniformLocation(shader_program, "EyeDirection");

}

3.3.8 callbacks

Four call back functions are used:

«callbacks»=<<display_callback>><<keyboard_callback>><<GLFW_error_callback>><<FreeImage_error_callback>>

Each will now be described in sequence.

1. display_callback

The display callback, display(), is technically not a callback function in this application because it is explicitlyinvoked main below.. However, the purpose of this function, to redraw the graphics content of the framebuffer is imple-mented as a callback function in many OpenGL applications, (e.g., those implemented using GLUT or freeGlut. Normally

21

Page 22: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

this function is called whenever the content of the graphics window needs to be drawn, for example, following eventsthat correspond to the window being resized, or becoming unobstructed by another window that was just minimized.

«display_callback»=

//------------------------------------------------------------------------------//// display() should be called whenever the canvas needs to be refreshed. Here it// redraws the content of the Figs vector,

void display(void) {glClearColor(0.25,0.25,0.25,1); // grayglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glEnable(GL_DEPTH_TEST);

glm::vec3 eye = glm::vec3(static_cast<GLfloat>(Rho*std::cos(Phi)*std::sin(Theta)),static_cast<GLfloat>(Rho*std::sin(Phi)*std::sin(Theta)),static_cast<GLfloat>(Rho*std::cos(Theta)));

glm::vec3 ctr = glm::vec3(0.0f, 0.0f, 0.0f);glm::vec3 up = glm::vec3(0.0f, 0.0f, 1.0f);glm::mat4 vmtx = glm::lookAt(eye, ctr, up);glm::mat4 pmtx = glm::perspective(glm::radians(50.0f), CANVAS.x/CANVAS.y, 0.1f, 15.0f);

glm::vec3 eyeDirection = glm::normalize(eye);glUniform3fv(SL_EyeDirection, 1, &eyeDirection[0]);glUniform3fv(SL_AmbientIntensity, 1, &TheAmbientIntensity[0]);glUniform3fv(SL_LightIntensity, 1, &TheLightIntensity[0]);

glm::vec3 transformedLightPosition = glm::vec3(vmtx * glm::vec4(TheLightPosition, 1.0f));glUniform3fv(SL_LightPosition, 1, &transformedLightPosition[0]);

for (std::vector<Sphere>::iterator iter = SphereVector.begin();iter != SphereVector.end(); iter++) {

glm::mat4 modelViewTransformation = vmtx * iter->model_transformation();glm::mat3 vectorTransformation = glm::mat3(vmtx) * iter->vector_transformation();glm::mat4 modelViewPerspectiveTransformation = pmtx * modelViewTransformation;

glUniformMatrix4fv(SL_ModelViewTransformation, 1, GL_FALSE, &modelViewTransformation[0][0]);glUniformMatrix3fv(SL_NormalVectorTransformation, 1, GL_FALSE, &vectorTransformation[0][0]);glUniformMatrix4fv(SL_ModelViewPerspectiveTransformation,

1, GL_FALSE, &modelViewPerspectiveTransformation[0][0]);

iter->draw_elements();}

glfwSwapBuffers(gWindow);}

Usually, display will redraw the entire framebuffer. We begin by clearing the display: here setting every pixel to gray.(Using a background color other than white or black can sometimes help discover the common error that occurs whenthe color of the foreground and background colors match.) Since the call to glClearColor does not need to be calledrepeatedly, it could be moved into our init function. The function glClear performs the actual clearing.

The next two lines enable alpha blending to occur: see pages 166-169 of the OpenGL Redbook [Shreiner et al., 2013].Then for each figure in the Figs global vector, we bind the vertex shader’s uniform color variable to the figure color, itsuniform theTransformation variable to the figure’s transformation. The call to glBindVertexArray select’sthe vertex array that corresponds to the figure’s base model (triangle, square or circle).

The function glDrawArrays then renders the base model using the appropriate “primitive mode” (see page 90 of theRedbook). Here the globally defined array N_Vertices retains the number of vertices in the current base model.

All of the drawing takes place off-screen. The call to glfwSwapBuffers informs OpenGL to exchange the newversion of the framebuffer with the old one in the graphics window, gWindow, a global variable that is defined in themain function below.

2. keyboard_callback

The second callback function responds to keyboard events and is managed by GLFW. The first parameter of keyboard,window, identifies the window that was active when the keyboard event was detected. the integer keycode identifiesthe key that was pressed (see the GLFW keycode reference). The scanCode is a code that is platform dependent. The

22

Page 23: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

action reveals if the key was pressed, repeated, or released. The modifier integer represents a bit array that indicatesthe combination of modifier keys (shift, control, alt, or super) that were pressed, if any.

In the function below, the detection of a lowercase p triggers a message on the console, and the creation of a PNG imagefile that duplicates the contents of the active framebuffer. If the user presses a lowercase q, then GLFW will receive awindow close event. This will in turn result in the termination of the while loop in main; consequently, the programwill exit.

«keyboard_callback»=

23

Page 24: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

void keyboard(GLFWwindow* window, int keyCode, int scanCode, int action, int modifiers) {switch (keyCode) {

case GLFW_KEY_E: // eastif (action == GLFW_PRESS) {

if (modifiers == 0x0000) {Phi += M_PI/90.0f;

} else {Phi += M_PI/9.0f;

}std::cout << "Phi = " << Phi << std::endl;

}break;

case GLFW_KEY_H: // homeif (action == GLFW_PRESS && modifiers == 0x0000) {

Theta = M_PI/2.0f;Phi = 0.0f;Rho = 1.0f;std::cout << "Setting (Rho, Theta, Phi) = ("

<< Rho << ", " << Theta << ", " << Phi << ")" << std::endl;}break;

case GLFW_KEY_N: // northif (action == GLFW_PRESS) {

if (modifiers == 0x0000) {Theta -= M_PI/90.0f;

} else {Theta -= M_PI/9.0f;

}Theta = std::max(M_PI/180.0f, Theta);std::cout << "Theta = " << Theta << std::endl;

}break;

case GLFW_KEY_P: // printif (action == GLFW_PRESS && modifiers == 0x0000) {

std::cout << "Pressed a lower-case p, scanCode = " << scanCode << std::endl;exportWindowAsPNG(window, std::string("caravaggio"));

}break;

case GLFW_KEY_Q: // quitif (action == GLFW_PRESS && modifiers == 0x0000) {

std::cout << "Pressed a lower-case q, scanCode = " << scanCode << std::endl;glfwSetWindowShouldClose(window, GL_TRUE);

}break;

case GLFW_KEY_R: // rhoif (action == GLFW_PRESS) {

if (modifiers == 0x0000) {Rho += 0.2f;

} else {Rho -= 0.2f;

}Rho = std::max(0.1, std::min(10.0, Rho));std::cout << "Rho = " << Rho << std::endl;

}break;

case GLFW_KEY_S: // southif (action == GLFW_PRESS) {

if (modifiers == 0x0000) {Theta += M_PI/90.0f;

} else {Theta += M_PI/9.0f;

}Theta = std::min(M_PI*89.0f/90.0f, Theta);std::cout << "Theta = " << Theta << std::endl;

}break;

case GLFW_KEY_W: // westif (action == GLFW_PRESS) {

if (modifiers == 0x0000) {Phi -= M_PI/90.0f;

} else {Phi -= M_PI/9.0f;

}std::cout << "Phi = " << Phi << std::endl;

}break;

}return;

}

24

Page 25: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

3. GLFW_error_callback

The next callback is used by GLFW, in the event that an error occurs. Hopefully, it will not need to be called.

«GLFW_error_callback»=

//------------------------------------------------------------------------------//// error_callback is used by GLFW.

void GLFW_error_callback(int errorCode, const char* msg) {throw std::runtime_error(msg);

}

GLFW_error_callback simply throws a C++ runtime exception, which will abort the application, since we are notattempting to catch it.

4. FreeImage_error_callback

Likewise, we define a callback function for the errors discovered by the FreeImage library:

«FreeImage_error_callback»=

//------------------------------------------------------------------------------//// free image error callbackvoid fi_error_callback(FREE_IMAGE_FORMAT fif, const char* msg) {

if (fif != FIF_UNKNOWN) {std:: cerr << FreeImage_GetFormatFromFIF(fif) << " format." << std:: endl;

}throw std::runtime_error(msg);

}

And now for the main function, which drives all of the above.

3.3.9 main

«main»=int main(int argc, char** argv) {

// initialize GLFWglfwSetErrorCallback(GLFW_error_callback);if (!glfwInit())

throw std::runtime_error("glfwInit failed!");

Here we are using the third party GLFW library to act as an interface between our application and the window managerof the host system. We will see below, and in subsequent examples, how GLFW can be used to create an OpenGL context,enable a stereo display, respond to keyboard and mouse events, and measure time increments. The above code simply registersGLFW_error_callback(), defined above, as the error callback function, and initializes the GLFW subsystem. In the eventthat the initialization fails, a runtime exception is thrown.

«main»+=// Create the main windowglfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

The above snippet informs GLFW that we desire our current application to be compatible with all future versions ofOpenGL, starting with OpenGL 3.2. Declaring the GLFW_OPENGL_CORE_PROFILE indicates that our application will useshaders, instead of the older direct draw paradigm. We finally tell GLFW that we prefer drawing windows with fixed dimension:it will not be possible for the user to change the size of the window by dragging the edges or corners; nor will it be possible tomaximize the window to cover the entire display.

«main»+=

25

Page 26: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

int glfw_major;int glfw_minor;int glfw_rev;glfwGetVersion(&glfw_major, &glfw_minor, &glfw_rev);std::cout << "GLFW version: " << glfw_major << "."

<< glfw_minor << "." << glfw_rev << std::endl;

The above prints the version of our GLFW library on the console.

«main»+=gWindow = glfwCreateWindow((int) CANVAS.x, (int) CANVAS.y, argv[0], NULL, NULL);if (! gWindow)

throw std::runtime_error("Can’t create a glfw window!");

glfwMakeContextCurrent(gWindow);

The glfwCreateWindow asks GLFW to obtain a GUI window (with appropriate decorations) from the window managerof the host operating system with the specified horizontal and vertical dimensions, and title. For the latter, we elect to useargv[0], the name of the application. By setting the fourth argument to NULL we indicate that we want to use an applicationwindow, instead of the full screen. By setting the final argument to NULL we indicate that we will not be sharing the contextthis window with a previously created one.

«main»+=glfwSetKeyCallback(gWindow, keyboard);

Here we bind keyboard as a key callback function for the graphics window, gWindow.

«main»+=glewExperimental = GL_TRUE; // Prevents a segmentation fault on Mac OSXif (glewInit() != GLEW_OK)

throw std::runtime_error("Can’t initialize glewInit!");

Our application also employs GLEW the OpenGL Extension Wrangler, a third-party toolkit that orchestrates miscellaneousOpenGL extensions: tools written by third parties that extend the capabilities of OpenGL. Normally, the host OpenGL systemkeeps track of the extensions that have been installed. By activating the glewExperimental mode in the line above, weinstruct the toolkit to access those OpenGL extensions that might be present, but are not reported by the host OpenGL system.This setting must be applied before GLEW is initialized.

«main»+=std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl;std::cout << "GLSL version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;std::cout << "Vendor: " << glGetString(GL_VENDOR) << std::endl;std::cout << "Graphics engine: " << glGetString(GL_RENDERER) << std::endl;

if (! GLEW_VERSION_3_2)throw std::runtime_error("OpenGL 3.2 is not supported!");

The above lines print important information about the version of OpenGL that is running on your system, and informationabout the active graphics hardware. Since we are using shader programs, we ensure that the version of OpenGL running is 3.2or greater.

We now initialize the FreeImage software package. This open source library (available for Linux, MacOS X and Windows),provides a simple interface for loading, modifying, and writing to digital image files in a variety of popular formats (e.g., png,tiff and jpg).

«main»+=// Initializing FreeImage:FreeImage_Initialise(TRUE); // only load local plugins.FreeImage_SetOutputMessage(fi_error_callback);

// Let’s see the version of FreeImage:std::cout << "FreeImage version = " << FreeImage_GetVersion() << std::endl;

«main»+=

26

Page 27: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

init(); // Initialize the model.

int update_count = 0;while(! glfwWindowShouldClose(gWindow)) {

update_count++;display();glfwWaitEvents(); // or, replace with glfwPollEvents();

}

The

call to init() of course initializes our vertex models, and compiles and links the vertex and fragment shader programs. Thesubsequent while loop performs the main display loop; glfwWindowShouldClose(gWindow) returns true if the indi-cated window has received a closed event, which occurs within the keyboard-callback function when the user presses a q.The number of times this loop is iterated is counted and ultimately printed on the console. The function glfwWaitEvents()allows the application to take a short nap between events (window exposure, keyboard taps, mouse movements, etc.) while theapplication has the focus of the window manager.

«main»+=FreeImage_DeInitialise();glfwTerminate();std::cout << argv[0] << " gracefully exits after "

<< update_count << " window updates." << std::endl;return 0; // Can be safely omitted, but the application will still return 0.

}

It is important to invoke both FreeImage_Deintialize() and glfwTerminate() before terminating the applica-tion, in order to release resources, avoiding memory leaks that the window manager might not catch. Since main is of typeint, we return 0 indicating successful termination.

3.4 Shader FilesEvery modern OpenGL application that adheres to the core profile, is required to provide a vertex and fragment shader program.These programs are written in a language that is called GLSL, (short for OpenGL Shading Language). GLSL looks a lot likeC. Here we borrow many ideas from Chapter 3 of the OpenGL 4 Shading Language Cookbook, second edition.

3.4.1 The vertex shader: phong.vert

The vertex shader is invoked a the beginning of the graphics pipeline. Every shape that is rendered by OpenGL is delimited byvertices, which can possess numerous attributes. Here we restrict these to position and texture coordinate.

«phong.vert»=#version 330

//<<caution>>

layout (location=0) in vec3 vertexPosition;layout (location=1) in vec3 vertexNormal;

// Transformation matricesuniform mat4 ModelViewTransformation;uniform mat3 NormalVectorTransformation;uniform mat4 ModelViewPerspectiveTransformation;

out vec3 NormalVector;out vec4 VertexPosition;

void main() {NormalVector = normalize(NormalVectorTransformation * vertexNormal);VertexPosition = ModelViewTransformation * vec4(vertexPosition, 1.0f);gl_Position = ModelViewPerspectiveTransformation * vec4(vertexPosition, 1.0f);

}

Every shader is required to specify its version in “preprocesser” syntax, i.e., with a hash symbol. Here #version 330specifies GLSL version 3.30 or higher. The next lines define the vertexPosition and vertexNormal channels for the vertexshader: the spatial coordinates of each vertex as a three-dimensional floating point vector (in vec3), for the x, y, and zcomponents.

27

Page 28: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

The body of the shader appears as a short C program. It is important to adhere to this signature: main is a function ofzero arguments that returns void. This particular vertex shader is rather trivial; it is often called a pass-through shader, as thetexture coordinates are passed along to the fragment shader without modification. The transformation is applied to each vertex,resulting in a transformed gl_Position for each.

3.4.2 The fragment shader: phong.frag

The fragment shader will assign a color value fragmentColor in accordance with the the Phong-Blinn reflectance model.The following represents a slight variation on the point-source lighting shader in Example 7.4, which uses a single sourceof light specified by a uniform three-dimensional vector LightPosition. For additional flexibility, we define two sets ofcolors, each also defined by a three-dimensional vector: AmbientIntesity and LightColor.

«phong.frag»=#version 330

//<<caution>>

// Material parametersuniform vec3 AmbientRho;uniform vec3 DiffuseRho;uniform vec3 SpecularRho;uniform float Shininess;

uniform vec3 EyeDirection;uniform vec3 AmbientIntensity;uniform vec3 LightIntensity;uniform vec3 LightPosition;

in vec3 NormalVector;in vec4 VertexPosition;

out vec4 fragmentColor;

void main() {vec3 lightDirection = normalize(LightPosition - vec3(VertexPosition));

float diffuse = max(0.0, dot(NormalVector, lightDirection));float specular = 0.0;if (diffuse > 0.0) {// vec3 halfVector = normalize(lightDirection + EyeDirection);// specular = pow( max(0.0, dot(NormalVector, halfVector)), Shininess);vec3 viewDirection = - normalize(vec3(VertexPosition));vec3 reflectedDirection = 2*dot(lightDirection, NormalVector)*NormalVector - lightDirection;specular = pow( max(0.0, dot(reflectedDirection, viewDirection)), Shininess);

}

vec3 scatteredLight = AmbientIntensity * AmbientRho + LightIntensity * DiffuseRho * diffuse;vec3 reflectedLight = LightIntensity * SpecularRho * specular;vec3 rgb = min(scatteredLight + reflectedLight , vec3(1.0));fragmentColor = vec4(rgb, 1.0);

}

3.5 CMakeLists.txt

For optimal portability, this application is designed to be compiled and linked using CMake. The following CMake file identifiesthe source code dependencies and library locations. Note that the reader will likely need to modify some items defined below,especially those specified by set.

«CMakeLists.txt»=

28

Page 29: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

#<<caution>>

cmake_minimum_required(VERSION 3.0.2)project (caravaggio)add_executable(caravaggio caravaggio.cpp sphere.cpp)# target_compile_features(caravaggio PRIVATE cxx_generalized_initializers)

find_package(OpenGL REQUIRED)find_package(GLEW REQUIRED)set(CMAKE_CXX_FLAGS "-std=c++1y ${CMAKE_CXX_FLAGS} -g")

## Very likely you will need to adjust the following paths to match your installation.#

set(GLM_INCLUDE_DIR "/opt/local/include" CACHE PATH "" FORCE)set(GLFW_LIBRARIES "/opt/local/lib/libglfw.dylib" CACHE FILEPATH "" FORCE)set(GLFW_INCLUDE_DIR "/opt/local/include" CACHE PATH "" FORCE)set(FREEIMAGE_INCLUDE_DIR "/opt/local/include" CACHE PATH "" FORCE)set(FREEIMAGE_LIBRARIES "/opt/local/lib/libfreeimage.dylib" CACHE FILEPATH "" FORCE)

include_directories(${CMAKE_CURRENT_BINARY_DIR})include_directories(${GLFW_INCLUDE_DIR})include_directories(${GLEW_INCLUDE_DIRS})include_directories(${GLM_INCLUDE_DIR})include_directories(${OPENGL_INCLUDE_DIR})include_directories(${FREEIMAGE_INCLUDE_DIR})target_link_libraries(caravaggio ${OPENGL_LIBRARIES})target_link_libraries(caravaggio ${GLFW_LIBRARIES})target_link_libraries(caravaggio ${GLEW_LIBRARIES})target_link_libraries(caravaggio ${FREEIMAGE_LIBRARIES})

The above reflects a little laziness on my part. Really, I should create a FindGLFW.cmake, FindFreeImage.cmake, etc.Instead, I am guilty of kludging.

3.6 Building the project.The following Bourne shell can be used to build the project from within a command-line shell. Simply, enter ./build_mefrom the command line. Good luck.

«build_me»=#!/bin/sh##<<caution>>#mkdir -p build/wscd build/wscmake ..make allcd ..ws/caravaggio ../rrs.jpg

4 The BibliographyThe BibTeX references are exported to the file caravaggio.bib when the project is tangled. Consequently, it is best toreweave the project in LATEX after the tangling caravaggio.org.

29

Page 30: Caravaggio - cs.uvm.edursnapp/teaching/cs274/src/caravaggio/caravaggio.pdfThis file, caravaggio.org, is an example of an org file (hence the file’s extension). It is an ASCII

% caravaggio.bib

@Book{Redbook2013,author={Dave Shreiner and Graham Sellers and John Kessenich and Bill Licea-Kane},title ={OpenGL Programming Guide},edition={8th},publisher={Addison-Wesley},address={Upper Saddle River, NJ},year = 2013,

}

@Book{Stroustrup2013,author={Bjarne Stroustrup},title ={The C++ Programming Language},edition={4thn},publisher={Addison-Wesley},address={Upper Saddle River, NJ},year=2013,

}

ReferencesDave Shreiner, Graham Sellers, John Kessenich, and Bill Licea-Kane. OpenGL Programming Guide. Addison-Wesley, Upper

Saddle River, NJ, 8th edition, 2013.

Bjarne Stroustrup. The C++ Programming Language. Addison-Wesley, Upper Saddle River, NJ, 4thn edition, 2013.

30