Joining Bezier Curves

text only
 
progress indicator progress indicator progress indicator progress indicator

Implementing NURBS Surfaces

The code for this chapter is on the CD in the \Code\Chapter09 -NURBS Patches directory. The code is in many ways very similar to the code from Chapter 8. Therefore, I am only going to highlight the changes.

The first change is the inclusion of a 2D weight vector. The weight vector contains a weight value for each of the control points in the 2D control grid.

 float  m_Weights[NUM_U_POINTS][NUM_V_POINTS]; 

The source code also includes a function that sets the weight values for each of these points. Currently, those values are set to 1.0 but feel free to experiment with different values. I chose not to highlight the effects of changing weights because the principles are the same as you saw in the curves chapters.

As you can see in Equations 9.1 and 9.2, the weight values are multiplied with different combinations of basis functions and their derivatives depending on the situation. Therefore, I do not include weight values when I precompute the basis functions. In fact, the code in DefineBasisFunctions is exactly the same as in Chapter 8. In the previous chapters, I pointed out that all the magic happens in FillPatchBuffer . This is especially true in this chapter because there are more calculations that need to be done to incorporate the weight values. I will walk you through the code for FillPatchBuffer and you'll see that the bulk of the code is basically the NURBS equations in C++ form.

 BOOL CNURBSPatchApplication::FillPatchBuffer() { 

For the sake of brevity, I have chosen to omit the first several lines that lock the vertex buffer and set the positions of the control points. See the code on the CD for the complete listing and feel free to change the control point positions to see the effects on the surface. The following code is focused on the actual computation of the points on the NURBS surface. There is a main loop that loops through each of the vertices and computes the values based on the precached basis values, the weight values, and the control point positions . The following code is taken from the inside of those nested loops.

In order to make the calculations more readable, I have divided the equations into numerators and denominators for both the position calculations and the derivative calculations. The code will compute each piece separately and then put them together at the end.

 float NumeratorMultiplier = 0.0f; float Denominator         = 0.0f; float DenominatorDU       = 0.0f; float DenominatorDV       = 0.0f; 

Figure 9.2 demonstrates how each of the values map to components of Equations 9.1 and 9.2.


Figure 9.2: Mapping equations to variables .

This loop is used to apply the influences of each control point to the current vertex.

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

This conditional statement ensures that the NURBS computations are only applied if this particular control point actually affects this particular vertex. The savings here are more substantial than in Chapter 8 because there are more calculations with NURBS.

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

This first denominator value is the denominator of Equation 9.1. Here I am summing the products of the weight of the control points and their "influence" as determined by the precomputed basis function values.

 Denominator += m_Weights[UStep][VStep] *                             m_BasisFunctionsU[U][UStep][PATCH_ORDER_U - 1] *                             m_BasisFunctionsV[V][VStep][PATCH_ORDER_V - 1]; 

This second denominator value is the denominator of the partial derivative with respect to u, as shown in Figure 9.2.

 DenominatorDU += m_Weights[UStep][VStep] *                             m_DerivativeBasisU[U][UStep][PATCH_ORDER_U - 1] *                             m_BasisFunctionsV[V][VStep][PATCH_ORDER_V - 1]; 

The final denominator value is the denominator of the partial derivative with respect to v. The three denominators are not actually used until after all the control points have been accounted for.

 DenominatorDV += m_Weights[UStep][VStep] *                             m_BasisFunctionsU[U][UStep][PATCH_ORDER_U - 1] *                             m_DerivativeBasisV[V][VStep][PATCH_ORDER_V - 1]; 

The numerator multiplier variable is actually reused to compute several different numerators. First, I compute the numerator for Equation 9.1.

 NumeratorMultiplier = m_Weights[UStep][VStep] *                           m_BasisFunctionsU[U][UStep][PATCH_ORDER_U - 1] *                           m_BasisFunctionsV[V][VStep][PATCH_ORDER_V - 1]; 

The control index is the index of this particular control point.

 long ControlIndex = UStep * NUM_V_POINTS + VStep; 

The position calculation is partially based on the product of the numerator multiplier value and the position of the control point. The following code is not the complete position calculation (you still need to divide by the denominator), but you can conveniently store the interim value in the vertex.

 pVertices[Current].x += NumeratorMultiplier *                                              pVertices[ControlIndex].x;                      pVertices[Current].y += NumeratorMultiplier *                                              pVertices[ControlIndex].y;                      pVertices[Current].z += NumeratorMultiplier *                                              pVertices[ControlIndex].z; 

Now the numerator multiplier is used to store the scaling factor of the partial derivative with respect to u. This scales the effect of this control point on the tangent in the u direction.

 NumeratorMultiplier = m_Weights[UStep][VStep] *                           m_DerivativeBasisU[U][UStep][PATCH_ORDER_U - 1] *                           m_BasisFunctionsV[V][VStep][PATCH_ORDER_V - 1];                     dPdU.x += NumeratorMultiplier*                     pVertices[ControlIndex].x;                     dPdU.y += NumeratorMultiplier*                     pVertices[ControlIndex].y;                     dPdU.z += NumeratorMultiplier*                     pVertices[ControlIndex].z; 

Finally, the numerator multiplier is used to scale the effect of this control point on the tangent in the v direction.

 NumeratorMultiplier= m_Weights[UStep][VStep] *                           m_BasisFunctionsU[U][UStep][PATCH_ORDER_U - 1] *                           m_DerivativeBasisV[V][VStep][PATCH_ORDER_V - 1];                     dPdV.x += NumeratorMultiplier*                     pVertices[ControlIndex].x;                     dPdV.y += NumeratorMultiplier*                     pVertices[ControlIndex].y;                     dPdV.z += NumeratorMultiplier*                     pVertices[ControlIndex].z;              }        } } 

At this point, the code has looped through all of the control points and summed the influences into several intermediate variables. The following code brings all the factors together to generate the tangent vectors and the vertex position. The code completes the computations described by Equation 9.2.

 dPdU.x = ((dPdU.x / Denominator) - (pVertices[Current].x *          DenominatorDU / (Denominator * Denominator))); dPdV.x = ((dPdV.x / Denominator) - (pVertices[Current].x *          DenominatorDV / (Denominator * Denominator))); dPdU.y = ((dPdU.y / Denominator) - (pVertices[Current].y *          DenominatorDU / (Denominator * Denominator))); dPdV.y = ((dPdV.y / Denominator) - (pVertices[Current].y *          DenominatorDV / (Denominator * Denominator))); dPdU.z = ((dPdU.z / Denominator) - (pVertices[Current].z *          DenominatorDU / (Denominator * Denominator))); dPdV.z = ((dPdV.z / Denominator) - (pVertices[Current].z *          DenominatorDV / (Denominator * Denominator))); 

The vertex already contains the results of all of the numerator calculations. The following lines factor in the denominator to complete the calculations shown in Equation 9.1.

 pVertices[Current].x /= Denominator; pVertices[Current].y /= Denominator; pVertices[Current].z /= Denominator; 

At this point, the calculations for position and tangent vectors are complete. The remainder of the loop applies the lighting calculations to compute the vertex color before moving on to the next vertex.

Figure 9.3 shows a screenshot of the application in action.


Figure 9.3: Screenshot of a simple NURBS patch.

This particular patch could have been modeled with a B-spline patch, but I wanted to keep things very simple. Feel free to experiment with the weighting values to create different shapes and effects.

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