🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Spherical Billboard of a Line Strip with Geometry Shader in OpenGL

Started by
0 comments, last by padkanel 2 years, 8 months ago

Hello everyone! I'm new to the forums here and kinda new to OpenGL programming. I'm going to document my progress so far, so it's going to be a bit lengthy post, but please bear with me! Here goes:

I'm interested in rendering hair in real time using OpenGL, so I studied Nvidia's presentation from SIGGRAPH 2010. In it, they suggest rendering each line segment as camera facing quad instead of the GL_LINE_STRIP primitive. Here is the full quote from the presentation:


The other option instead of rendering hair directly as lines is to expand the line segments into camera facing quads. That is, for each line segment, we expand it into two triangles that are facing the camera.

This expansion is pretty easy, and can be done for example in the Geometry Shader. We specify the input primitive for the Geometry Shader to be a line (two vertices), from these we can figure out the tangent, and taking the cross product of that with the eye vector gives us the two axis to expand the quad.”

My camera is built following the tutorial found here: https://learnopengl.com/Getting-started/Camera

The camera class is:

class Camera{
public:
   // camera Attributes
   glm::vec3 Position;
   glm::vec3 Front;
   glm::vec3 Up;
   glm::vec3 Right;
   glm::vec3 WorldUp;
   // euler Angles
   float Yaw;
   float Pitch;
   // camera options
   float MovementSpeed;
   float MouseSensitivity;
   float Zoom;
   
   // ... (camera functions) ...
}

For the “eye vector”, I use the Front vector of the camera class. The line coordinates are passed in a VBO. Here is the Veretx Shader:

guide_vertex_shader.glsl

#version 430

layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in float aTransparency;
layout (location = 3) in float aSegmentIndex;
layout (location = 4) in float aThickness;

uniform vec3 camera_position;


uniform mat4 model;
uniform mat4 view;

out vec3 color;
out float transparency;

out VS_OUT{

float thickness;
vec3 eye_vector;
vec4 point;

}vs_out;

void main()
{
   gl_Position =  view* model * vec4(aPos, 1.0);

color = aColor;

vs_out.eye_vector = normalize(camera_position - gl_Position.xyz);

vs_out.thickness = aThickness;
vs_out.point = gl_Position;

transparency = aTransparency;
}

Each line has two points and each point has its own struct called vsout. In it, I store the normalized distance from the camera to the point (eye vector), and the thickness value. Each vertex is multiplied by the model_view matrix (view * model). The Geometry Shader is as follows:

guide_geometry_shader.glsl

#version 430

layout (lines) in;
layout (triangle_strip, max_vertices = 4) out;

uniform mat4 projection;

in VS_OUT{

float thickness;
vec3 eye_vector;
vec4 point;

}gs_in[];


void main(){

float radius = gs_in[0].thickness / 2;

vec4 p1 =  gs_in[0].point;
vec4 p2 =  gs_in[1].point;

vec3 tangent = normalize(p1.xyz - p2.xyz);

vec3 expand_direction1 = normalize(cross(gs_in[0].eye_vector,tangent));
vec3 expand_direction2 = normalize(cross(gs_in[1].eye_vector,tangent));

vec3 Pos;

Pos = p1.xyz + expand_direction1 * radius;
gl_Position = projection * vec4(Pos, 1.0);
EmitVertex();

Pos = p1.xyz - expand_direction1 * radius;
gl_Position =  projection * vec4(Pos, 1.0);
EmitVertex();

Pos = p2.xyz + expand_direction2 * radius;
gl_Position =  projection * vec4(Pos, 1.0);
EmitVertex();

Pos = p2.xyz - expand_direction2 * radius;
gl_Position = projection * vec4(Pos, 1.0);
EmitVertex();

EndPrimitive();
}

First I compute the tangent as suggested in the paper by Nvidia. Then, I compute the cross product of the eye vector and the tangent for each point of the line. This cross product is the expand direction. Then, I compute the ammount of expansion for each point and multiply the result with the projection matrix (I use perspective projection).

The result seems to be fine from a distance as seen in the following images:

Please ignore the color for now (Also, I'm rendering a subset of the hair called "guides" and not the complete hairstyle. I will use tessellation to fill up the rest of the scalp later).

The white cube is just a rotating light bulb ?

Even though it looks good from a distance, there are some issues when I zoom in. There seem to be some discontinuities in each line segment, as if each segments rotates differently. All quads should face the camera, and the bottom of one quad should align with the top of the next (if that makes sense ).

P.S: I know that there are faster methods to create billboards instead of using geometry shaders, but I want to use geometry shaders to measure performance and because it's easier to implement and more intuitive to me.

Thank you for taking time to read all this! I would appreciate any input on how to solve this problem ?

Advertisement

This topic is closed to new replies.

Advertisement