Well, we are now at the point where we can make surfaces that are plots of a function, like: Z = F(X,Y):
    Private Function F(ByVal X As Single, ByVal Y As Single) As Single
        'Try out your own functions of X and Y.
        'Return 0.25F * X * X  'Z = 1/4 (X²)... parabolic sheet of paper.
        'Return 0.25F * X * Y   'Origami?  Looks like the beginning of a paper bird.
        'Return 0.25F * X * X + 0.25F * Y * Y  'A hammock with an invisible person sitting in it.
        'Return Convert.ToSingle(Math.Sin(X * PiHalf))  'This looks more sinusoidal at a higher resolution.
        'Return 0.25F * X * X - 0.25F * Y * Y  'Saddle paper.
        Return Convert.ToSingle(Math.Cos(X * PiHalf)) * Convert.ToSingle(Math.Sin(Y * PiHalf))  'Eggshell bed comforter.


    End Function
Other functions that I've tried are commented out in the above function. The function is used in our example over the range of (-3,-3) to (+3, +3). First, we can get the graph set up for a flat version - example of early stages of the graph environment:
        'Now, we are going to have a contiguous arrangement of tiles.
        'Since only the vertices of a triangle are lit from a DX9 light,
        'the more vertices we have the better.
        'The actual sides are just linear interpolations of the end point brightnesses.
        'So, to start things off, we'll just have a little plane that goes from -3 to 3
        'composed of more than the two triangles necessary.
        'The formula for the number of triangles needed is below (and is how we determined the number of vertices
        'to use at first:
        'Triangle Number: (2 for each square on the plane) 2 * dX * dY = 2 * (3 - -3) * (3 - -3) = 2 * 6 * 6 = 72
        'Vertex Number: 3 * Triangle number = 3 * 72 = 216

        For LY = -3.0F To 2.0F
            For LX = -3.0F To 2.0F
                'triangle 1:
                vs(Ctr) = New CustomVertex.PositionNormal(LX, LY, 0.0F, 0.0F, 0.0F, 0.0F)
                vs(Ctr + 1) = New CustomVertex.PositionNormal(LX + 1, LY, 0.0F, 0.0F, 0.0F, 0.0F)
                vs(Ctr + 2) = New CustomVertex.PositionNormal(LX, LY + 1, 0.0F, 0.0F, 0.0F, 0.0F)
                'triangle 2:
                vs(Ctr + 3) = New CustomVertex.PositionNormal(LX + 1, LY + 1, 0.0F, 0.0F, 0.0F, 0.0F)
                vs(Ctr + 4) = vs(Ctr + 2)
                vs(Ctr + 5) = vs(Ctr + 1)
                Ctr += 6
            Next
        Next
That's good - now, let's have the Z value correspond to a function's values (a.k.a. F(X,Y) function).
        'Note, that now we have values of X and Y going through a set of possible values,
        'We can set Z to be a function of X and Y, and put that right into the Z argument.
        'Z = F(x, y)
        'For kicks, I put a function F right below.

        For LY = -3.0F To 2.0F
            For LX = -3.0F To 2.0F
                'triangle 1:
                vs(Ctr) = New CustomVertex.PositionNormal(LX, LY, F(LX, LY), 0.0F, 0.0F, 0.0F)
                vs(Ctr + 1) = New CustomVertex.PositionNormal(LX + 1, LY, F(LX + 1, LY), 0.0F, 0.0F, 0.0F)
                vs(Ctr + 2) = New CustomVertex.PositionNormal(LX, LY + 1, F(LX, LY + 1), 0.0F, 0.0F, 0.0F)
                'triangle 2:
                vs(Ctr + 3) = New CustomVertex.PositionNormal(LX + 1, LY + 1, F(LX + 1, LY + 1), 0.0F, 0.0F, 0.0F)
                vs(Ctr + 4) = vs(Ctr + 2)
                vs(Ctr + 5) = vs(Ctr + 1)
                Ctr += 6
            Next
        Next
So, there's a simple way of having the graph - but for more graph resolution (plus, neater lighting effects), we need more vertices.
        'We can also increase the triangle density.
        'This means more triangles and thusly, more vertices.
        'This makes the new formula:
        'Triangle number: 2 * P dx * Q dy, P is the number of squares in one unit in the X direction, and
        'Q is the number of squares in one unit in the Y direction.
        'Again, Vertex Number = 3 * Triangle number
        'For example, if we expanded the above (-3 To 3, -3 To 3) with one square per unit (we had 36 squares before)
        'and instead, we had 4 squares per unit (16 squares in each unit square)... 
        'Our triangle number jumps to: 2 * 4 * (3 - -3) * 4 * (3 - -3) = 2 * 4 * 6 * 4 * 6 = 1152 :o
        'Number of vertices: 3 * 1152 = 3456 (pretty number :) )

        For LY = -3.0F To 2.999F Step 0.25F
            For LX = -3.0F To 2.999F Step 0.25F
                'triangle 1:
                vs(Ctr) = New CustomVertex.PositionNormal(LX, LY, F(LX, LY), 0.0F, 0.0F, 0.0F)
                vs(Ctr + 1) = New CustomVertex.PositionNormal(LX + 0.25F, LY, F(LX + 0.25F, LY), 0.0F, 0.0F, 0.0F)
                vs(Ctr + 2) = New CustomVertex.PositionNormal(LX, LY + 0.25F, F(LX, LY + 0.25F), 0.0F, 0.0F, 0.0F)
                'triangle 2:
                vs(Ctr + 3) = New CustomVertex.PositionNormal(LX + 0.25F, LY + 0.25F, F(LX + 0.25F, LY + 0.25F), 0.0F, 0.0F, 0.0F)
                vs(Ctr + 4) = vs(Ctr + 2)
                vs(Ctr + 5) = vs(Ctr + 1)
                Ctr += 6
            Next
        Next
Now, fortified with 3456 vertices, we alter the VertexCount to reflect this number:
            VertexCount = 3456
            PrT = PrimitiveType.TriangleList
            VxB = New VertexBuffer(GetType(CustomVertex.PositionNormal), VertexCount, D9, Usage.WriteOnly, CustomVertex.PositionNormal.Format, Pool.Managed)
            'That is a LOT of non-trivial parameters.

            SetVertexBufferData(Vertices, VxB, D9)
And we'll alter culling to properly reflect the top of the graph which consists of triangles with upward pointing normals.
            D9.RenderState.CullMode = Cull.Clockwise
            D9.RenderState.ZBufferEnable = True
            D9.RenderState.Lighting = True
            D9.RenderState.Ambient = Color.DarkGray        'When lighting is enabled and materials have been laid, this is the color of darkness.

.vb file