3

I am trying to render circles around each point of a 3d curves. Basically trying to create a tube like structure for the curve. But the orientation of the circle is wrong as shown in the image. Below are my calculations for the Model matrix of the circle object after calculating the frenet frame. Where am I going wrong? For reference, green line is the tangent, blue is the normal and red is the binormal.

view 1 view 2

Frenet Frame Calculations:

glm::vec3 pointback = curve_points[i-1];
glm::vec3 pointmid = curve_points[i];
glm::vec3 pointforward = curve_points[i+1];

glm::vec3 forward_tangent_vector =  glm::vec3(glm::normalize(pointforward - pointmid)) ;
glm::vec3 backward_tangent_vector = glm::vec3(glm::normalize(pointmid - pointback)) ;

glm::vec3 second_order_tangent = glm::normalize(forward_tangent_vector - backward_tangent_vector);

glm::vec3 binormal = glm::normalize(glm::cross(forward_tangent_vector, second_order_tangent));

glm::vec3 normal = glm::normalize(glm::cross(binormal, forward_tangent_vector));

Model Matrix for Circle calculations

glm::mat3 tbn = glm::mat3(forward_tangent_vector,binormal,normal);

glm::vec3 normal_axis = glm::vec3(0, 1, 0);
//normal_axis = forward_tangent_vector;

glm::vec3 circleNormal = glm::normalize(tbn * normal_axis);
glm::vec3 rotationAxis = glm::cross(normal_axis, circleNormal);
float rotationAngle = glm::acos(glm::dot(normal_axis, circleNormal));

R = glm::rotate(R, glm::degrees(rotationAngle), rotationAxis);

T = glm::translate(T, pointmid);

glm::mat4 Model = T*R;
genpfault
  • 51,148
  • 11
  • 85
  • 139
jaykumarark
  • 2,359
  • 6
  • 35
  • 53
  • 2
    The circles must be built from the normal and binormal vectors. –  Oct 28 '15 at 21:50
  • I am not sure if I am following you. Can you please elaborate? – jaykumarark Oct 28 '15 at 21:50
  • It seems that currently they are built from the tangent and normal vectors. –  Oct 28 '15 at 21:51
  • Currently they are built using this in a loop `float x = 0.1f*cos(dt * i);` `float y = 0.f;` `float z = 0.1f*sin(dt * i);` – jaykumarark Oct 28 '15 at 21:57
  • 1
    Then swap two of the coordinates. –  Oct 28 '15 at 21:58
  • That did the trick! Thanks so much! – jaykumarark Oct 28 '15 at 22:09
  • Possible duplicate of [Smoothly connecting circle centers](http://stackoverflow.com/questions/25178181/smoothly-connecting-circle-centers) – Spektre Oct 29 '15 at 07:00
  • I remember an old paper about this from the original Graphics Gems book. It's online in pdf format: Calculating Reference Frames Along a Curve: http://webhome.cs.uvic.ca/~blob/courses/305/notes/pdf/ref-frames.pdf – Robinson Oct 29 '15 at 09:04

1 Answers1

4

The easiest way to do this is by using Frenet-Serret frames, more commonly known as TBN frames or a TBN matrix. Here's how:

  1. Sample two points on the curve. Let's call them "current" and "next".
  2. Construct the Frenet frame as follows:

    vec3 T = normalize( next - current );
    vec3 B = normalize( cross( T, next + current ) );
    vec3 N = normalize( cross( B, T ) );
    
  3. Calculate your 2D circle, similar to this:

    float x = cos( angle );
    float y = sin( angle );
    
  4. Now, use the Frenet frame to calculate the proper orientation:

    vec3 tangent = T;
    vec3 normal = normalize( B * x + N * y );
    vec3 vertex = current + B * x + N * y; // note: not normalized!
    

An easy to follow explanation can be found here: http://www.blackpawn.com/texts/pqtorus/

Paul Houx
  • 1,984
  • 1
  • 14
  • 16
  • 1
    Thanks! Your explanation and the accompanying link are great! Just the math I needed to understand this better. – jaykumarark Oct 29 '15 at 19:18
  • 1
    If you have an analytic curve, be it a spline curve or some other kind of mathematical function, I think it's much better to calculate the direction vector analytically. Taking the difference between two points is just an approximation. If you have a parametric representation of the curve, the direction vector is simply given by the derivatives of the parametric functions for the coordinates. – Reto Koradi Oct 30 '15 at 04:24
  • @RetoKoradi: you are right in that an analytical solution is more accurate, but I'd argue that it is not necessarily better. If you're constructing a mesh, for example, and want to apply a normal mapping shader to it, you'd want your tangents to run along the surface. The algorithm in this post will make sure this is the case. More accurate tangents and normals would not make a huge visual difference for the lighting calculations, but you might see normal mapping artifacts. So it depends on your use case whether or not the analytical approach is better. – Paul Houx Nov 13 '15 at 17:35