The main part of this homework is to get your machine configured, and to get a basic understanding of geometry shaders and write a fragment shader that will compute a Gaussian texture centered in the image. The next homework will use this as a paint brush for a simple 2D paint packagel. You will also port your backdrop shaders to a C++ framework.
Below are a list of files to get you started. These provide a complete OpenGL 3.1 core (GLSL 1.3) solution to either set the background to a solid color or a mutli-colored (ugly) backdrop. You will need to download and use (locally) GLEW and FreeGLUT. The specific tasks to get you started are:
Follow the steps below to get a paint brush texture ready. Here is a sample of what your result should look like:
A Gauassian function has a maximum of one at zero and slowly and smoothly decreases to zero at infinity. It has the form:
For this image, we want the value at the edges to be insignificant (aka less than 1/256). You can solve for sigma by letting r=1 at the edge and taking the natural logarithm of 1/256 or some other small number (I think I used 1/512). |
Step 0
Set the viewport to be the entire window and change the background clear color or use one of your background shaders!
Step 1
Create a fragment shader that computes this value as the intensity. Use a vertex shader that passes an arbitrary color from the vertex shader to the fragment shader (This will be used to understand passing data through the geometry shader later). Also pass a Uniform value to the fragment shader to specify the size of your window / texture. No need to do anything fancy, you can fix it at 256 or whatever. Play with resizing the window and watch the center point move. After playing put the Window size to the value of the uniform to have a consistent view.
Step 2
Now, create a simple pass-thru geometry shader, PassThru.geom, that looks like this:
#version 330 core |
This geometry shader simply walks thru each vertex that is passed in using the data structure gl_in, and emits the vertex without changing its value or position. No other data is passed through. The layout attributes tell OpenGL what is expected of this shader. A good reference for this is Chapter 11 of the 5th Edition of the OpenGL SuperBible. It is available on-line with Safari. |
In your main.cpp, let's first change initShaders to createShader that takes as input the filenames for the vertex, fragment and geometry shaders and returns the unsigned int (GUID) of the shader program, like so:
unsigned int createShader(const char* vertexShader, const char* fragmentShader, const char* geometryShader = 0)
I set a default value for the geometryShader so that this routine can be used with only two arguements (no geometry shader) or three. Simply test if geometryShader !=0 and then load, compile and attach the geometry shader. Make these changes, and test that the program runs as in Step 1 without the geometry shader. Now, try to run it with the geometry shader. You will (or should) get black, as the color from the vertex shader is not making it to the fragment shader anymore. Actually, let's add a routine to print out any link errors. Very similar to the printShaderInfo routine, but change shader/Shader everywhere to program/Program. Call this after the link and/or inquiring about the Link status (again change Shader to Program and GL_COMPILE_STATUS to GL_LINK_STATUS. Changing your fragment shader such that it does not use the color (passed in from the vertex shader) should produce a result for you.
Now, let's look at how to get data from the vertex shader through the geometry shader to the fragment shader. For this we need the specify the input variable coming from the vertex shader as an array since we will have access to all of the vertices for this primitive. We will output a variable of the same type only not as an array. What happens is that everytime EmitVertex is called, the out data is set for that vertex. This avoids you trying to change vertex one's color while you are working on emitting vertex 2. So, if my variable in the vertex shader is:
out vec4 baseColor;
Then in the geometry shader I would have:
in vec4 baseColor[];
out vec4 color;
The fragment shader would then have:
in vec4 color;
The geometry shader also needs to set the color variable, assuming it varied every vertex, then we would say:
color = baseColor[i];
just before the EmitVertex call in the for loop.
Make sure you have: