Advanced Shading Methods

Wednesday | Friday
Geometry Shaders

Reading

Geometry Shader Specifications
Example Applications at LearnOpenGL.com
Silhouette extraction
Hedgehog Plots

Examples updates

Let's fetch this week's upstream changes.

$ cd ~/cs40/examples
$ git fetch upstream
$ git merge -X ours upstream/master

After this step, you will need to manually edit the top level CMakeLists.txt to add the new subdirectories.

$ tail ~/cs40/examples/CMakeLists.txt
if(CMAKE_CUDA_COMPILER)
  add_subdirectory(w07-cuda-pt1)
  add_subdirectory(w07-cuda-pt2)
  add_subdirectory(w08-cuda-patterns)
endif()

add_subdirectory(w09-particles)
add_subdirectory(w10-noise)
add_subdirectory(w11-noise2)
#add the line below
add_subdirectory(w12-gshader)

No symlink this week.

If everything went well, you should be able to go into your build directory and run make -j8 to compile the week 10 examples.

cd build
make -j8
cd w12-gshader
./gshader
X,Y,Z will rotate the scene. P toggles polygon mode, C toggles culling. N toggles drawing normals.

Exercise: Exploding Triangles

Let's modify the geometry shader to translate each triangle in the geometry along its planar normal. Note this is a bit different than moving each vertex along its normal. What effect would moving each vertex have with the sphere geometry in this example?

First let's modify the main in the geometry shader to be a bit more modular.

void drawNormals() {
  makeNormalLine(0);
  makeNormalLine(1);
  makeNormalLine(2);
}

void main() {
  drawNormals();
  //drawExplode();
}

Next, add two helper functions for computing the planar normal and moving a vertex index along a given normal by a specified length.

vec3 triNormal() {
  vec3 a = vec3(gl_in[1].gl_Position) - vec3(gl_in[0].gl_Position);
  vec3 b = vec3(gl_in[2].gl_Position) - vec3(gl_in[1].gl_Position);
  return normalize(cross(a, b));
}

vec4 explode(int index, vec3 normal, float magnitude) {
  mat4 mvp = projection * camera * model;
  vec3 direction = normal * ((cos(time) + 1.0) / 2.0) * magnitude;
  return mvp * (gl_in[index].gl_Position + vec4(direction, 0.0));
}

This example uses a new uniform time that we can later modify to animate the explosion effect. Add the uniform to the list of uniforms at the top of your shader.

uniform float time=0;

Complete the implementation of drawExplode below and uncomment the drawExplode call in main when you are ready to test.

void drawExplode() {
  vec3 norm = triNormal();
  float magnitude = 0.3;
  color = vec4(1, 1, 1, 1);
  //TODO: complete implementation
  //TODO: you will need to change the output type of the shader at
  //the top of your program. What is it now? What should it be?
}

If you want to animate, one easy way to do this is to add two member variables to your MyPanelOpenGL class.

QTimer m_timer;
QTime m_time;
and a new slot
public slots:
  void step();

Connect the Timer to the step slot in initializeGL in your mypanelopengl.cpp file. The step slot can just call update as shown below.

void MyPanelOpenGL::initializeGL() {
  ...
  connect(&m_timer, &QTimer::timeout, this, &MyPanelOpenGL::step);
  m_timer.start(30); /*fire every 30ms */
}

void MyPanelOpenGL::step() { update(); }

The m_time variable, not m_timer will set the value for the time uniform in our shader. Initialize m_time in the constructor.

m_time.start();
You can fetch the result in MyPanelOpenGL::setUniforms.
  if(m_drawNormals){
    float t = 3. * m_time.elapsed() / 1000.;
    m_shaderPrograms[prog]->setUniformValue("time", t);
  }