Woodgrain Example

The following program will generate and write out a 3-D texture to simulate woodgrain:

 1from direct.directbase.DirectStart import *
 2from panda3d.core import *
 3import math
 4
 5# These constants define the RGB colors of the light and dark bands in
 6# the woodgrain.
 7lightGrain = (0.72, 0.72, 0.45)
 8darkGrain = (0.49, 0.33, 0.11)
 9
10
11def chooseGrain(p, xi, yi, radius):
12    """ Applies the appropriate color to pixel (xi, yi), based on
13    radius, the computed distance from the center of the trunk. """
14
15    # Get the fractional part of radius.
16    t = radius - math.floor(radius)
17
18    # Now t ranges from 0 to 1.  Make it see-saw from 0 to 1 and back.
19    t = abs(t - 0.5) * 2
20
21    # Now interpolate colors.
22    p.setXel(xi, yi,
23             lightGrain[0] + t * (darkGrain[0] - lightGrain[0]),
24             lightGrain[1] + t * (darkGrain[1] - lightGrain[1]),
25             lightGrain[2] + t * (darkGrain[2] - lightGrain[2]))
26
27
28def calcRadius(xn, yn, x, y, z, noiseAmp):
29    """ Calculates radius, the distance from the center of the trunk,
30    for the 3-d point (x, y, z).  The point is perturbed with noise to
31    make the woodgrain seem more organic. """
32
33    xp = x + xn.noise(x, y, z) * noiseAmp
34    yp = y + yn.noise(x, y, z) * noiseAmp
35
36    return math.sqrt(xp * xp + yp * yp)
37
38
39def makeWoodgrain(texSize, texZSize, noiseScale, noiseZScale,
40                  noiseAmp, ringScale):
41
42    """ Generate a 3-D texture of size texSize x texSize x texZSize
43    that suggests woodgrain, with the grain running along the Z (W)
44    direction.  Since there is not as much detail parallel to the
45    grain as across it, the texture does not need to be as large in
46    the Z dimension as in the other two dimensions.
47
48    The woodgrain shape is perturbed with Perlin noise to make it more
49    organic.  The parameters noiseScale and noiseZScale controls the
50    frequency of the noise; larger numbers make smoother rings.  The
51    parameter noiseAmp controls the effect of the noise; larger
52    numbers make more dramatic distortions.
53
54    ringScale controls the number of rings visible in the cross
55    section of the texture.  A larger number makes more, denser rings.
56    """
57
58    # First, create the two PerlinNoise objects to perturb the rings
59    # in two dimensions.  This class is defined in Panda3D.
60    xn = PerlinNoise3(noiseScale, noiseScale, noiseZScale)
61    yn = PerlinNoise3(noiseScale, noiseScale, noiseZScale)
62
63    # Start by creating a empty 3-D texture.
64    tex = Texture('woodgrain')
65    tex.setup3dTexture()
66
67    for zi in range(texZSize):
68        z = float(zi) / float(texZSize - 1) - 0.5
69
70        # Walk through the Z slices of the texture one at a time.  For
71        # each slice, we create a PNMImage, very much as if we were
72        # reading the texture from disk.
73        print(zi)
74        p = PNMImage(texSize, texSize)
75
76        # But instead of reading the PNMImage, we fill it in with the
77        # ring pattern.
78        for yi in range(texSize):
79            y = float(yi) / float(texSize - 1) - 0.5
80            for xi in range(texSize):
81                x = float(xi) / float(texSize - 1) - 0.5
82
83                radius = calcRadius(xn, yn, x, y, z, noiseAmp)
84                chooseGrain(p, xi, yi, radius * ringScale)
85
86        # Now load the current slice into the texture.
87        tex.load(p, zi, 0)
88
89    return tex
90
91
92# Create a 3-D texture.
93tex = makeWoodgrain(texSize=256, texZSize=8, noiseScale=0.4, noiseZScale=0.8,
94                    noiseAmp=0.12, ringScale=40)
95
96# Write out the texture.  This will generate woodgrain_0.png,
97# woodgrain_1.png, and so on, in the current directory.
98tex.write(Filename('woodgrain-#.png'), 0, 0, True, False)

The resulting images look like this:

Woodgrain_0.jpg Woodgrain_1.jpg Woodgrain_2.jpg Woodgrain_3.jpg Woodgrain_4.jpg Woodgrain_5.jpg Woodgrain_6.jpg Woodgrain_7.jpg

To get consistent (over multiple runs of the program) values from the PerlinNoise functions a seed value other than 0 has to be used, as specifying a seed value of 0 tells the noise function to use a random seed.

The table_size is 256 by default.

PerlinNoise3( scaleX, scaleY, scaleZ, table_size, seed)

PerlinNoise2( scaleX, scaleY, table_size, seed)

PerlinNoise( table_size, seed )