From NURBS Curves to Surfaces

text only
 
progress indicator progress indicator progress indicator progress indicator

Implementing B-Spline Surfaces

The code for this chapter is on the CD in the \Code\Chapter08 - B-Spline Patches directory. As I said in Chapter 7, all of the real magic now happens in FillPatchBuffer . However, the magic requires a bit of setup. First, take a look at BSplinePatchApplication.h. There are now defined values for the order of both dimensions as well as the number of control points in each direction.

 #define NUM_U_POINTS 5 #define NUM_V_POINTS 5 #define PATCH_ORDER_U 3 #define PATCH_ORDER_V 3 

In this case, I have set the same number of points and the same order in both u and v, but that is not a requirement. I will use different values in later chapters, but you should also feel free to experiment. Just remember that the order should not exceed the number of control points.

A few lines down in the class definition, you will see that there are now two arrays for the knot vectors, two arrays for precomputed basis values, and two arrays of precomputed values for the derivatives.

 float       m_KnotVectorU[NUM_U_POINTS + PATCH_ORDER_U];        float       m_KnotVectorV[NUM_V_POINTS + PATCH_ORDER_V];        float       m_BasisFunctionsU[NUM_PATCH_VERTICES]                                     [NUM_U_POINTS][PATCH_ORDER_U];        float       m_DerivativeBasisU[NUM_PATCH_VERTICES]                                      [NUM_U_POINTS][PATCH_ORDER_U];        float       m_BasisFunctionsV[NUM_PATCH_VERTICES]                                     [NUM_V_POINTS][PATCH_ORDER_V];        float       m_DerivativeBasisV[NUM_PATCH_VERTICES]                                      [NUM_V_POINTS][PATCH_ORDER_V]; 

Conceptually, these are the same arrays you saw in Chapter 4. There are now two sets because there are two parametric directions.

If you look at BSplinePatchApplication.cpp, you will see code for the setup functions SetKnotVectors and DefineBasisFunctions . I have not included the code here in the text because it is basically identical to the code you saw in Chapter 4. The only real difference is that SetKnotVectors now sets two different knot vectors and DefineBasisFunctions sets the values for both the u and v direction. This doubles the amount of code, but the computation is exactly the same as what you saw in Chapter 4.

Once everything is set up, the render loop calls FillPatchBuffer to actually compute the positions of the vertices before rendering. These vertices are rendered using the code outlined in Chapter 7.

 BOOL CBSplinePatchApplication::FillPatchBuffer() { 

I have omitted the preliminary code that is an exact duplicate of the code shown in Chapter 7. The omitted code locks the vertex buffer, creates a uniform control grid, animates some of the control points, and creates the lighting variables . Once that has been done, the actual B-spline computation begins. As you saw in Chapter 7, the code loops and computes the position of each vertex for a given u and v value. The following code is the interior of that loop. Please see the code on the CD for the complete listing.

The loop defines a u and v value as U and V. First, you need to map that 2D value to the equivalent index in the 1D vertex buffer. Once you have that, the first order of business is to initialize all values to zero to facilitate summing the effects of the control points.

 long Current = (NUM_U_POINTS * NUM_V_POINTS) + (U * NUM_PATCH_VERTICES) + V; pVertices[Current].x = 0.0f; pVertices[Current].y = 0.0f; pVertices[Current].z = 0.0f; memset(&dPdU, 0, sizeof(D3DXVECTOR3)); memset(&dPdV, 0, sizeof(D3DXVECTOR3)); 

The values of U and V are between 0 and NUM_PATCH_VERTICES . Here, I'm mapping them to values between 0 and 1. This will allow me to compare the current u and v values to the values in the knot vector. The knot vector determines the range of influence for each control point. I can optimize the calculations by ignoring the effects of control points that have no effect on this particular point. I will go into more detail about this below.

 float CurrentU = (float)U / (float)(NUM_PATCH_VERTICES - 1); float CurrentV = (float)V / (float)(NUM_PATCH_VERTICES - 1); 

The following loops step through each control point in order to sum the influences.

 for (long UStep = 0; UStep < NUM_U_POINTS; UStep++) {        for (long VStep = 0; VStep < NUM_V_POINTS; VStep++)        { 

This if statement is the key to the very simple (but potentially powerful) optimization. It checks the knot vector values to see if the current (u, v) point is affected by the current ( UStep , VStep ) control point. If the current point falls outside of the range of influence, the control point is skipped entirely. This can save very many calculations in cases where the order of the surface is much lower than the number of control points.

 if (m_KnotVectorU[UStep] <= CurrentU &&                  CurrentU <= m_KnotVectorU[UStep + PATCH_ORDER_U] &&                  m_KnotVectorV[VStep] <= CurrentV &&                  CurrentV <= m_KnotVectorV[VStep + PATCH_ORDER_V])              { 

The vertex position is set using the 2D equivalent to the code you saw in Chapter 4. This is essentially the code for Equation 8.1.

 pVertices[Current].x += pVertices[UStep *                                             NUM_V_POINTS + VStep].x *                                             m_BasisFunctionsU[U][UStep]                                             [PATCH_ORDER_U - 1] *                                             m_BasisFunctionsV[V][VStep]                                             [PATCH_ORDER_V - 1];                     pVertices[Current].y += pVertices[UStep *                                             NUM_V_POINTS + VStep].y *                                             m_BasisFunctionsU[U][UStep]                                             [PATCH_ORDER_U - 1] *                                             m_BasisFunctionsV[V][VStep]                                             [PATCH_ORDER_V - 1];                     pVertices[Current].z += pVertices[UStep *                                             NUM_V_POINTS + VStep].z *                                             m_BasisFunctionsU[U][UStep]                                             [PATCH_ORDER_U - 1] *                                             m_BasisFunctionsV[V][VStep]                                             [PATCH_ORDER_V - 1]; 

The tangent vectors are found using the 2D equivalent to the slope code you saw in Chapter 4. This is basically the same code as I showed in Chapter 7, only this time the B-spline derivatives are used in place of the Bezier derivatives.

 dPdU.x += pVertices[UStep * NUM_V_POINTS + VStep].x *                               m_DerivativeBasisU[U][UStep][PATCH_ORDER_U - 1] *                               m_BasisFunctionsV[V][VStep][PATCH_ORDER_V - 1];                     dPdU.y += pVertices[UStep * NUM_V_POINTS + VStep].y *                               m_DerivativeBasisU[U][UStep][PATCH_ORDER_U - 1] *                               m_BasisFunctionsV[V][VStep][PATCH_ORDER_V - 1];                     dPdU.z += pVertices[UStep * NUM_V_POINTS + VStep].z *                               m_DerivativeBasisU[U][UStep][PATCH_ORDER_U - 1] *                               m_BasisFunctionsV[V][VStep][PATCH_ORDER_V - 1];                     dPdV.x += pVertices[UStep * NUM_V_POINTS + VStep].x *                               m_BasisFunctionsU[U][UStep][PATCH_ORDER_U - 1] *                               m_DerivativeBasisV[V][VStep][PATCH_ORDER_V - 1];                     dPdV.y += pVertices[UStep * NUM_V_POINTS + VStep].y *                               m_BasisFunctionsU[U][UStep][PATCH_ORDER_U - 1] *                               m_DerivativeBasisV[V][VStep][PATCH_ORDER_V - 1];                     dPdV.z += pVertices[UStep * NUM_V_POINTS + VStep].z *                               m_BasisFunctionsU[U][UStep][PATCH_ORDER_U - 1] *                               m_DerivativeBasisV[V][VStep][PATCH_ORDER_V - 1];              }       } } 

After the position and tangents are computed, the normal vector is computed just as it was in Chapter 7, and the lighting value is set as the vertex color . Figure 8.1 shows a screenshot of the resulting surface.


Figure 8.1: A simple B-spline surface.

In addition to showing the surface, Figure 8.1 also shows another feature of this application. I have added code that draws the basis functions for both u and v. I have not included the code here because it's very similar to the code shown in the curve chapters, but you can find it in the source code on the CD.

Figure 8.1 doesn't really demonstrate why B-spline surfaces are better than Bezier surfaces. Figure 8.2 shows another screenshot, only this time the number of control points in the u direction has been greatly increased.


Figure 8.2: A B-spline surface with more control points.

The surface in Figure 8.2 has much more flexibility than most Bezier curves. Also, notice how the ranges shown on the basis function graphs correspond to the shape of the bulge in the surface. You could explicitly set the values in the knot vector to control the way each of the control points affects the surface. Figure 8.3 shows the same surface, only this time the knot vector has been changed (as you can see from the graphs of the basis functions).


Figure 8.3: A B-spline surface with nonuniform knot vectors.

In the next chapter, you will see more examples of the level of control you can achieve with B-spline surfaces when I demonstrate different shapes with NURBS.

progress indicator progress indicator progress indicator progress indicator


Focus on Curves and Surfaces
Focus On Curves and Surfaces (Focus on Game Development)
ISBN: 159200007X
EAN: 2147483647
Year: 2003
Pages: 104
Authors: Kelly Dempski

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net