How to visually match the perspective projection of a Bézier curve to the original, only projecting its control points?

StackOverflow https://stackoverflow.com/questions/18921962

Pergunta

I am coding a cubic Bézier curve editor that is designed to let you create, select and modify Bézier curves in a 3D world.

I am currently working on the selection/picking interface. I would like to be able to pick the curve based on how far (in terms of pixel count) the user clicked from the actual projected representation of the curve in screen space.

My first approach was to do a brute force numerical analysis in 3D using the segment of the picking ray cast from the camera origin that intersects with the bounding box of the curve cubic segment. I used a dichotomy search to find an approximation of the closest point to the ray on the curve. It worked, but lacked robustness in use cases and also required the selection distance setting to be scaled according to the distance between the camera and the point on the curve. Also it was not an elegant-enough implementation to me. ;)

My current idea is to project the curve segment manually on the near plane, to get a projected Bézier curve that I could perform my numerical analysis on. Also the previous picking ray parameter would now be reduced to a single point: where the user clicked on the viewport.

Unfortunately what I feared before prototyping revealed itself to be true: the projection of a Bézier curve on a plane is not exactly the curve obtained by the projection of its control points. After further reading on the subject (I am terrible at maths in general), it seems like the projected curve somehow degenerates to a rational Bézier curve which is expressed differently. The visual representation of the new curve does not match the original, it looks fairly close but I need a pixel-perfect match.

I have other options to explore for my problem, like projecting a picking cone in the scene, or projecting the curve segment once discretized as a poly-line. I would like to know though, whether it exists a quick and easy solution to my current problem through re-parameterization or changes to the projected curve using math tricks I am unable to figure out myself.

Thanks in advance.

edit example projection image: http://i.imgur.com/1XawRof.png

Foi útil?

Solução

Projecting the control points of a Bezier curve onto the near plane by using the appropriate view matrix should yield a rational Bezier curve that is the same as what you see in the viewport.

The problem could stem from a number of issues. If it were my code, here is what I would try:

  • Make sure that the projection to the plane is done properly. You need to use the world to near plane perspective projection matrix, rather than just projecting the input bezier control points to the view plane. The perspective projection part of the matrix is what makes polynomial control points turn rational.

  • You could create a 3d polyline and project the polyline to your near plane via the perspective matrix. Your polyline points should be rational, and should exactly match what you see visually (although for a polyine, rational points don't change the rendering at all). This is a good way to check that your projection is working properly.

  • Make sure that your rational curve evaluation is working properly. A good way to do this is to render a rational circular arc in 2d and make sure that the output points are exactly circular. I'd do this numerically to avoid any confusion with projection/viewing.


So, there are a few different ways you can evaluate a rational Bezier curve. My favorite way is to evaluate using the simple equation, but on a homogeneous point type, but I've seen people who sum the weights separately, or who evaluate by multiplying the control points by a basis matrix. Whatever works, I guess.

Here's a good test for 2d rational curve evaluation. Let's start with an arc, centered on the origin. We'll use a degree-2 rational Bezier. The points will be stored as [xw, yw, w].

The three points are:

P0 = [ 1, 0, 1 ], P1 = [ 0.707107, 0.707107, 0.707107 ], P2 = [ 0, 1, 1 ]

where 0.707107 is actually sqrt(2)/2

If we evaluate this at t=0.5, we get P = [ 0.603553, 0.603553, 0.853553 ]

If we divide out w, then we get P = [ 0.707107, 0.707107, 1 ]

Hope that helps.. :)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top