Monday, January 16, 2012

Improved interaction between rigidbodies and fluid

I fixed a lot of bugs in my simulation framework. Here is a video showing my progress.



Bugs:

  • I wasn't properly scaling the positions for the rigidbody simulation to match the fluid simulation.
  • Some of my quaternion calculations were not correct when updating the rotation of the rigid body.
  • The coefficients for the interaction forces weren't calculated correctly.  I found a detailed formulation for the dampening coefficient in [1].
Features:

  • I refactored the rendering code so you can switch rendering techniques on the fly.
  • The test program has run-time configurable mass and dimensions for rigid bodies being added.
  • Velocity visualization can be turned on/off during run-time.
My plans now are to push my changes into the blender version that Ian already created. Once I have done that I will create some examples and work on writing up my Master's thesis. From then, I will return to working on integrating the library into Bullet. Hopefully, I will get a chance to work on some visualization as well.

[1] Cleary, P. W., & Prakash, M. (2004). Discrete-element modelling and smoothed particle hydrodynamics: potential in the environmental sciences. hilosophical Transactions of the Royal Society - Series A: Mathematical, Physical and Engineering Sciences362(1822), 2003-2030. The Royal Society. Retrieved from http://www.ncbi.nlm.nih.gov/pubmed/15306427

Saturday, January 14, 2012

Geometry Shaders for visualizing velocity.

A couple months ago, I posted a blog with my updated rigidbody/fluid interaction. I mentioned I would discuss how I draw the velocity vectors efficiently. In the simulation, all the information for each particle is held on the gpu. Therefore, to render lines in the standard approach I would need to transfer to vectors from the gpu to the host, the position and the vector. Then I would have to loop through to create a third vector which held the position plus the vector. And then draw lines for each set of vertices.

Here is the video.

Geometry shaders (GS) give us the ability to take in one primitive type (points, lines, triangles,..) and output a different primitive type. That makes GSs perfect for our simulation. We already have the position and vector, we just need to calculate the second vertex from this information and then draw a line between the two points. Therefore, we can define the vertices for a line for each vector as follows:

$v_{i,start}=pos_{i}$
$v_{i,end}=pos_{i}+vector_{i}$

I then define the color for the vector as:

$col_{i}=\frac{|vector_{i}|}{||vector_{i}||}$

I chose this color representation primarily for its simplicity. Essentially this encodes direction into the color of each vector. Thus, red represents the x direction, green represents the y direction, and blue represents the z direction. The absolute value is necessary because negative colors don't exist (In OpenGL it chooses black automatically if any component is negative).

Now onto the code.

C code: 

//Tell opengl the vector vbo is the color vector.
        glBindBuffer(GL_ARRAY_BUFFER, vecVBO);
        glColorPointer(4, GL_FLOAT, 0, 0);

        glBindBuffer(GL_ARRAY_BUFFER, posVBO);
        glVertexPointer(4, GL_FLOAT, 0, 0);

        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_COLOR_ARRAY);

        glUseProgram(m_shaderLibrary.shaders["vectorShader"].getProgram());
        glUniform1f(glGetUniformLocation(m_shaderLibrary.shaders["vectorShader"].getProgram(), "scale"),scale);
        glDrawArrays(GL_POINTS, 0, num);
        glUseProgram(0);

        glDisableClientState(GL_COLOR_ARRAY);
        glDisableClientState(GL_VERTEX_ARRAY);

The variable m_shaderLibrary.shaders["vectorShader"].getProgram() is specific to my library. If you want to use this in your own code you should pass in the program(GLuint) which is the compiled shader program from the vector shaders provided below.
Vertex Shader: 

#version 120
varying vec4 vector;
varying out vec4 color;

void main() 
{
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    //We must project the vector as well.
    vector = gl_ModelViewProjectionMatrix * gl_Color;
    //normalize the vector and take the absolute value.
    color = vec4(abs(normalize(gl_Color.rgb)),1.0);
}


Geometry Shader: 

#version 150
//#geometry shader

layout(points) in;
layout(line_strip, max_vertices=2) out;
in vec4 vector[1];
in vec4 color[1];
uniform float scale;

void main() 
{
    //First we emit the position vertex with the color calculated
    //in the vertex shader.
    vec4 p = gl_in[0].gl_Position;
    gl_Position = p; 
    gl_FrontColor = color[0];
    EmitVertex();

    //Second we emit the vertex which is the xyz position plus a
    //scaled version of the vector we want to visualize. Also, the
    //color is the same as calculated in the vertex shader.
    gl_Position = vec4(p.rgb+(scale*vector[0].rgb),p.a);
    gl_FrontColor = color[0];
    EmitVertex();
    EndPrimitive();
}

Fragment Shader: 

//simple pass through fragment shader.
void main(void) {
    gl_FragColor = gl_Color;
}