Since surfaces are frequently subdivided into triangles, raytracing a group of triangles is a common practice, although it is usually very slow. Two types of triangles are supported by POVRAY, namely triangle {} and smooth_triangle {}. Since triangles and smooth triangles do not have inside and outside (i.e., interior and exterior), they cannot be used with Boolean operations intersection and difference; but, they can be used with union for simplifying modeling process.
triangle { vertex-1, // in vector form vertex-2, // in vector form vertex-3 // in vector form // other specifications such as pigment, finish, ... } smooth_triangle { vertex-1, normal-1, vertex-2, normal-2, vertex-3, normal-3 // other specifications }
Both forms require three vectors indicating the vertices of a triangle; but the smooth triangle also requires the normal vector at each vertex. Note that the normal vector does not have to be the true normal vector at that vertex. This is because in many cases the true normal vector is not available and must be approximated. Moreover, this normal vector is for rendering purpose and as a result one can use an arbitrary vector for generating special effect. The reason of using smooth_triangle {} is for rendering purpose. With a normal vector at each vertex, POVRAY is able to interpolate a color for each pixel so that the resulting triangle looks smooth (i.e., part of a curvilinear surface).
The above left triangle is generated with vertices < 1, 0, 0 >, < 0, 1, 0 > and < 0, 0, 1 >:
while the above right triangle is generated withtriangle { x, y, z }
where vertices x, y and z have normal vectors x, y and z, respectively. Note that this triangle looks smooth although it is actually flat.smooth_triangle { x, x, y, y, z, z }
If the parametric equation of a surface is given, generating triangles would be very easy. For example, if the domain is a rectangle [a,b] x [c,d]. Then, one can subdivide [a, b] into m segments a0=a, a1, ..., am=b and subdivide [c, d] into n segments c0=c, c1, ..., cn=d. Thus, for each rectangle defined by four neighboring points in the domain, ai, ai+1, ci and ci+1, we have two triangles with vertices (ai,ci), (ai+1,ci) and (ai,ci+1) and (ai,ci+1), (ai+1,ci+1) and (ai+1,ci). These triangles in the domain are mapped to the surface by the parametric equations becoming triangles on the surface. Note that only the vertices of these triangles lie on the surface and the triangles themselves are not. But, at least, one can generate the vertices and normal vectors for raytracing.
A sphere has the following parametric equation:
f(u,v) = ( a + rcos(u)cos(v), b + rsin(u)cos(v), c + rsin(v) )where (a, b, c) and r are the center and radius and both parameters u and v are in the range of 0 and 2*PI.
The above figures show the effects of different numbers of subdivisions. Each figure contains three spheres. The left one is given by sphere { < 0, 0, 0 >, 1 }, the middle one uses smooth_triangle {} and the right one uses triangle {}. The first figure shows two subdivisions in each direction. More precisely, [0,2*PI] is subdivided into [0,PI] and [PI,2*PI]. Therefore, the domain [0,2*PI] x [0, 2*PI] is subdivided into four smaller squares each of which is further subdivided into two triangles. Therefore, there are eight triangles in total. As you can see from the figure, both smooth_triangle {} and triangle {} do not look like spheres.
The next figure shows the effect of subdividing the domain into 16 squares and hence 32 triangles. The third one has 32 squares and hence 64 triangles. The fourth one has 64 squares and hence 128 triangles. Even with 128 triangles, the green "sphere" that is generated with triangle {} is not yet a sphere. But the yellow one that is generated with smooth_triangle {} looks very smooth and close to a sphere.
Note that these triangles are generated by a C program which outputs a union like the following:
// Polygonalized smooth sphere with 16 subdivisions #declare SMOOTH_TRIANGLE = union { smooth_triangle { < 0.000000, 1.000000, 0.000000 >, < 0.000000, 1.000000, 0.000000 >, < 0.382683, 0.923880, 0.000000 >, < 0.382683, 0.923880, 0.000000 >, < 0.353553, 0.923880, 0.146447 >, < 0.353553, 0.923880, 0.146447 > } smooth_triangle { < 0.000000, 1.000000, 0.000000 >, < 0.000000, 1.000000, 0.000000 >, < 0.353553, 0.923880, 0.146447 >, < 0.353553, 0.923880, 0.146447 >, < 0.270598, 0.923880, 0.270598 >, < 0.270598, 0.923880, 0.270598 > } smooth_triangle { < 0.000000, 1.000000, 0.000000 >, < 0.000000, 1.000000, 0.000000 >, < 0.270598, 0.923880, 0.270598 >, < 0.270598, 0.923880, 0.270598 >, < 0.146447, 0.923880, 0.353553 >, < 0.146447, 0.923880, 0.353553 > } // other smooth triangles smooth_triangle { < 0.000000, -1.000000, 0.000000 >, < 0.000000, -1.000000, 0.000000 >, < 0.353553, -0.923880, -0.146447 >, < 0.353553, -0.923880, -0.146447 >, < 0.382683, -0.923880, 0.000000 >, < 0.382683, -0.923880, 0.000000 > } }
f(u,v) = ( (a + bcos(v))cos(u), csin(v), (a + bcos(v))sin(u) )where u and v are in the range of 0 and 2*PI and a = 8, b = 3 and c = 7. This surface is generated by revolving an ellipse about the y-axis. The following figure is rendered with smooth triangles by subdividing both directions into 20 segments and hence the domain [0,2PI] x [0,2PI] into 400 squares (800 triangles).
A typical way of generating these triangles is done with a function similar to the following:
Each call to SmoothTriangle() outputs a smooth triangle. Function OneRow() prepares one row of division points for triangle generation. To see the complete detail, please click here to download a copy of this file. The following is part of the the output from this program. You can save this output to a file and then #include it into the scene file.void GridGen(void) { POINT3D *r; NORM3D *n; int j; r1 = Row1; n1 = Norm1; r2 = Row2; n2 = Norm2; u = u1; OneRow(r1, n1); for (u = u1+DeltaU; u <= u2; u += DeltaU) { OneRow(r2, n2); for (v = v1, j = 0; v < v2; j++, v += DeltaV) { SmoothTriangle(r1[j], r2[j], r1[j+1], n1[j], n2[j], n1[j+1]); SmoothTriangle(r2[j], r1[j+1], r2[j+1], n2[j], n1[j+1], n2[j+1]); } SWAP(r1, r2, r); SWAP(n1, n2, n); } }
#declare TRIANGULATED_TORUS = union { smooth_triangle { < 11.000000, 0.000000, 0.000000 >, < -1.000000, 0.000000, 0.000000 >, < 10.461622, 0.000000, 3.399187 >, < -0.951057, 0.000000, -0.309017 >, < 10.853170, 2.163119, 0.000000 >, < -0.990443, -0.137921, 0.000000 > } smooth_triangle { < 10.461622, 0.000000, 3.399187 >, < -0.951057, 0.000000, -0.309017 >, < 10.853170, 2.163119, 0.000000 >, < -0.990443, -0.137921, 0.000000 >, < 10.321978, 2.163119, 3.353814 >, < -0.941968, -0.137921, -0.306064 > } // other smooth triangles smooth_triangle { < 10.853170, -2.163119, 0.000000 >, < -0.990443, 0.137921, 0.000000 >, < 10.461622, 0.000000, -3.399187 >, < -0.951057, 0.000000, 0.309017 >, < 11.000000, 0.000000, 0.000000 >, < -1.000000, 0.000000, 0.000000 > } }