© B.J. Korites 2018
B.J. KoritesPython Graphicshttps://doi.org/10.1007/978-1-4842-3378-8_7

7. Shading

B. J. Korites1 
(1)
Duxbury, Massachusetts, USA
 

In this chapter, you’ll learn how to shade three-dimensional objects. Shading produces a much more realistic look and enhances the perception of three-dimensionality. The general idea is to first establish the direction of light rays impacting the object being illuminated and then determine the shading effect the light has on the object’s surface. In the case of a box, which I will discuss next, six flat planes comprise the box’s surface. The orientation of these planes relative to the direction of the light will determine the degree of shading on each plane. To simulate shading, the planes can be filled with dots or lines. Different intensities of shading can be obtained by changing the intensity of the color of the dots or lines and by color mixing.

Normally an object being plotted will appear on a white background. If a background color is used, such as in Figure 7-13, dots or lines may be used to paint the background. Recall from Chapter 1 that new dots overplot old dots and lines always overplot dots and old lines. This means that whether the object being shaded is constructed of dots or lines, they will overplot the background color if it is painted with dots. The disadvantage of using dots is it takes a lot of time to fill the background with dots. Lines are a better alternative in this regard and are preferred if the object can be constructed of lines. If you must use dots in your object, then you must use dots for your background color.

The heart of a shading program is the intensity function, which relates the shading intensity to the orientation of a plane relative to the incoming light direction. You do not specify the position of a light source; you define the direction of the light rays impacting the object from that source. For example, suppose the program calculates that the angle between a plane and the incoming light rays is 50 degrees. The intensity function converts this angle into a shading intensity, which is used to alter the color intensity of the lines or dots.

A considerable amount of research has been carried out on theories of shading in an effort to produce more lifelike computer-drawn images. These images often have a separate shading function for each primary color and take into account the reflectivity and physical characteristics of the surface material. Smooth surfaces will be highly reflective while rough, textured surfaces will scatter the incoming light, producing a higher degree of diffusivity. In your work here, you will keep it simple and use just one shading function and ignore the differences in surface features that can affect the surface’s reflectivity and diffusivity, although they could easily be introduced into the program. Also, you assume the shading of a surface is dependent on only the orientation of that surface relative to the light source and not on its orientation relative to the observer who, as usual, you take to be located in the -z direction.

7.1 Shading a Box

Figures 7-1 through 7-7 show samples of output from Listing 7-1. They show a box rotated to different orientations with shading on its surfaces. They are shaded in monochrome black at different intensities ranging from black to white.
../images/456962_1_En_7_Chapter/456962_1_En_7_Fig1_HTML.jpg
Figure 7-1

Shaded box produced by Listing 7-1, Io=.8

../images/456962_1_En_7_Chapter/456962_1_En_7_Fig2_HTML.jpg
Figure 7-2

Shaded box produced by Listing 7-1, Io=1.0

../images/456962_1_En_7_Chapter/456962_1_En_7_Fig3_HTML.jpg
Figure 7-3

Shaded box produced by Listing 7-1, Io=1.0

../images/456962_1_En_7_Chapter/456962_1_En_7_Fig4_HTML.jpg
Figure 7-4

Shaded box produced by Listing 7-1, Io=1.0

../images/456962_1_En_7_Chapter/456962_1_En_7_Fig5_HTML.jpg
Figure 7-5

Shaded box produced by Listing 7-1, Io=.8

../images/456962_1_En_7_Chapter/456962_1_En_7_Fig6_HTML.jpg
Figure 7-6

Shaded box produced by Listing 7-1, Io=.6

../images/456962_1_En_7_Chapter/456962_1_En_7_Fig7_HTML.jpg
Figure 7-7

Shaded box produced by Listing 7-1, Io=.4

Figure 7-9 shows the model used by Listing 7-1. The light source is shown at the upper left. You do not explicitly state its location, only the direction of the light rays emanating from it. You do that by specifying lx,ly, and lz, the components of a unit vector $$ widehat{mathbf{l}} $$, which is aligned with the light rays. Keep in mind that $$ widehat{mathbf{l}} $$ is a unit vector so the following relation between its components must be observed:
$$ sqrt{l{x}^2+l{y}^2+l{z}^2}=1 $$
(7-1)

Looking at the top plane of the box defined by corners 0,1,2,3, you can see a unit normal vector $$ widehat{mathbf{n}} $$ at corner 0. This points outward from the plane. You shade the box by drawing lines, shown in blue, which extend across the width of the plane from B to E. These lines are drawn from edge 0,1 to 3,2 and then down the plane, thus shading it. The lines on each face will have an intensity that depends on the orientation of $$ widehat{mathbf{n}} $$ with $$ widehat{mathbf{l}} $$. You get this orientation by taking the dot product of $$ widehat{mathbf{n}} $$ with $$ widehat{mathbf{l}} $$. If $$ widehat{mathbf{n}} $$ is facing $$ widehat{mathbf{l}} $$, the dot product will be negative and the intensity of the lines will be less, which means the tone will be lighter; if $$ widehat{mathbf{n}} $$ is facing away from $$ widehat{mathbf{l}} $$, the dot product will be positive, the intensity will be greater, and the tone will be darker.

This is illustrated by Figure 7-10, which shows the shading intensity, I, vs. $$ widehat{mathbf{n}}cdot mathbf{l} $$. This is a linear relation. As you will see in the next section, better results can be obtained with a non-linear relation and by mixing (r,g,b) colors. You can get an equation for this linear intensity function by inspection:
$$ I=frac{Io}{2}+frac{Io}{2}widehat{mathrm{n}}cdot widehat{mathrm{l}} $$
(7-2)
$$ oxed{I=frac{Io}{2}left(1=widehat{mathrm{n}}cdot widehat{mathrm{l}}
ight)} $$
(7-3)
Note the parameter Io. It gives control over the degree of darkness in the shaded areas by increasing or decreasing the intensity of the color. The lines from B to E are plotted with the plt.plot() function, which includes the attribute alpha. By letting alpha=I you can control the intensity of the color. Higher values of alpha increase the intensity, making shaded areas appear darker; lower values of alpha decrease it, thus creating areas that appear lighter. Note that alpha may take on values from 0 to 1, hence I is limited to the same range of values. From Equation 7-3, this means that Io can have a maximum value of 1. Io=1 will give the darkest, most intense hues. To soften the image with more subtle hues, lower Io to something less than 1. To modify the function even more, the left side could be raised, which would darken the lights. If the function were horizontal, all shading would be uniform. To see the effect of Io on the shading, Figures 7-2 through 7-4 have Io=1.0. Figures 7-1, 7-5, 7-6, and 7-7 have Io=.8, .8, .6, and .4, respectively. Colors do not have to be black or primaries; they can be mixed. Figure 7-8 shows the result of using color=(r,g,b) with r=.5, g=0, b=.5,
$$ color=left(.5,,0,,.5
ight) $$
(7-4)
which is a purple mix of equal amount of red and blue. Recall that red, green, and blue in an (r,g,b) mix must each have values between 0 and 1.

You have been applying your shading intensity, I, to monochrome colors. Even if you use r,g,b color mixing, it is still a monochrome shade, although not a primary color. An extension of this method would be to apply separate intensities to each of the three primary colors. For example, when an artist paints a portrait, he/she might render the light side of the face a light pink. To darken the shaded side, he/she would normally add green, the compliment of red, to the mix. If you look closely at the portraits of an accomplished artist, you will see this is usually how it is done. Rarely would one add black to the mix to darken it. In fact, many painters do not even keep a black pigment on their pallet; they achieve darker colors by mixing the hues with their compliment. The compliment of red is green; of yellow it is violet. Color mixing in painting isn’t quite that simple, of course, but that is the fundamental idea. To accomplish this in your programming, suppose you are shading a red box using an (r,g,b) color mix. Rather than applying an intensity factor to the red to increase its intensity, thus simulating a darkening, you apply the intensity factor to the green, increasing its contribution in the r,g,b mix, thus darkening the red. For the present, in Listing 7-1 you will keep thing simple and simulate shading by increasing the intensity of the color in the dark areas rather than using color mixing. This works well with a monochrome black image, although it has limitations with colored objects.

The definition of the box in Listing 7-1 is contained in the lists in lines 10, 11, and 12. Lines 14, 15, and 16 open lists for the global coordinates, which are returned by the rotation functions rotx, roty, and rotz. They have the same lengths as the x,y,z lists as specified by len(x).

A new function called shade() is defined in Listing 7-1, lines 54-84. The arguments received by shade() in line 54 are shown in Figure 7-11. When shade() is invoked for a specific plane, the box’s corners must follow the order shown in Figure 7-11. As an example, the ordering for plane 1,5,6,2 is shown in Figure 7-12. Some visual gymnastics can be required to orient the six planes of the box such that they conform to the ordering in Figure 7-11. Each of the six planes are drawn and shaded separately by six calls to function shade(). They are listed in lines 88-93. The arguments of the calls are the x,y,z coordinates of points a,b,c,d, respectively. Function shade() calculates the components of unit vector û in lines 55-61 and vˆ in lines 62-68. Components of unit vector $$ widehat{mathbf{n}} $$ are calculated in lines 69-71. The dot product on $$ widehat{mathbf{n}} $$ with the incoming light ray unit vector $$ widehat{mathbf{l}} $$, the components of which were specified in lines 23-25, is calculated in line 72 as ndotl; the shading intensity in line 73. If nz<=0 (i.e. $$ widehat{mathbf{n}} $$ is pointing toward the observer who is in the -z direction), the edges of the face are plotted in lines 75-78 and the face is shaded in loop 79-84. Line 79 ranges h, shown in Figure 7-11, from 0 to qad, the distance from corner a to d, which was calculated in line 58, in steps of 1. Lines 80-81 calculate the x and y coordinates of the beginning of the line; lines 82 and 83 get the coordinates of the end of the line. Line 84 plots the line. In line 84, alpha is equal to the intensity of the shading that was determined in line 73. The box’s color is equal to clr, which was specified in line 27; for example, color='k' will give a black box. An alternative would be to mix primary colors as shown in line 28. This produces the purple box shown in Figure 7-8. To get this color, just remove the # in line 28; otherwise, the shading will be done in black. I will discuss color mixing in more detail in the next section. The maximum intensity Io is specified in line 29. This can be anything between 0 and 1. If nz>0 (i.e. $$ widehat{mathbf{n}} $$ is pointing away from the observer), the face is not plotted. The remainder of Listing 7-1 should be familiar.
../images/456962_1_En_7_Chapter/456962_1_En_7_Fig8_HTML.jpg
Figure 7-8

Shaded box produced by Listing 7-1, (r,g,b)=(.5,0.,5) color mixing, Io=1.0

../images/456962_1_En_7_Chapter/456962_1_En_7_Fig9_HTML.jpg
Figure 7-9

Shading model used by Listing 7-1

../images/456962_1_En_7_Chapter/456962_1_En_7_Fig10_HTML.jpg
Figure 7-10

Shading function

../images/456962_1_En_7_Chapter/456962_1_En_7_Fig11_HTML.jpg
Figure 7-11

Model of a generic plane used in Listing 7-1

../images/456962_1_En_7_Chapter/456962_1_En_7_Fig12_HTML.jpg
Figure 7-12

Plane 1,5,6,2

1   """
2   SHADEBOX
3   """
4
5   import numpy as np
6   import matplotlib.pyplot as plt
7   from math import sin, cos, radians, sqrt
8
9   #—————————————————————————lists
10  x=[-20,20,20,-20,-20,20,20,-20]
11  y=[-10,-10,-10,-10,10,10,10,10]
12  z=[5,5,-5,-5,5,5,-5,-5]
13
14  xg=[0]*len(x)
15  yg=[0]*len(x)
16  zg=[0]*len(x)
17
18  #———————————————————————parameters
19  xc=75 #———center coordinates
20  yc=50
21  zc=50
22
23  lx=.707 #———light ray unit vector components
24  ly=.707
25  lz=0
26
27  clr='k' #———use this for black monochrome images, or use another color
28  #clr=(.5,0,.5) #———use this to mix colors, this mix produces purple
29  Io=.8 #———max intensity, must be 0 < 1
30
31  #=====================================================define rotation functions
32  def rotx(xc,yc,zc,xp,yp,zp,Rx):
33       xpp=xp
34       ypp=yp*cos(Rx)-zp*sin(Rx)
35       zpp=yp*sin(Rx)+zp*cos(Rx)
36       [xg,yg,zg]=[xpp+xc,ypp+yc,zpp+zc]
37       return[xg,yg,zg]
38
39  def roty(xc,yc,zc,xp,yp,zp,Ry):
40       xpp=xp*cos(Ry)+zp*sin(Ry)
41       ypp=yp
42       zpp=-xp*sin(Ry)+zp*cos(Ry)
43       [xg,yg,zg]=[xpp+xc,ypp+yc,zpp+zc]
44       return[xg,yg,zg]
45
46  def rotz(xc,yc,zc,xp,yp,zp,Rz):
47       xpp=xp*cos(Rz)-yp*sin(Rz)
48       ypp=xp*sin(Rz)+yp*cos(Rz)
49       zpp=zp
50       [xg,yg,zg]=[xpp+xc,ypp+yc,zpp+zc]
51       return[xg,yg,zg]
52
53  #==============================================================shading
54  def shade(ax,ay,az,bx,by,bz,cx,cy,cz,dx,dy,dz):
55       a=dx-ax
56       b=dy-ay
57       c=dz-az
58       qad=sqrt(a*a+b*b+c*c)
59       ux=a/qad
60       uy=b/qad
61       uz=c/qad
62       a=bx-ax
63       b=by-ay
64       c=bz-az
65       qab=sqrt(a*a+b*b+c*c)
66       vx=a/qab
67       vy=b/qab
68       vz=c/qab
69       nx=uy*vz-uz*vy
70       ny=uz*vx-ux*vz
71       nz=ux*vy-uy*vx
72       ndotl=nx*lx+ny*ly+nz*lz
73       I=.5*Io*(1+ndotl)
74       if nz<=0:
75            plt.plot([ax,bx],[ay,by],color='k',linewidth=1)
76            plt.plot([bx,cx],[by,cy],color='k',linewidth=1)
77            plt.plot([cx,dx],[cy,dy],color='k',linewidth=1)
78            plt.plot([dx,ax],[dy,ay],color='k',linewidth=1)
79            for h in np.arange(0,qad,1):
80                 xls=ax+h*ux
81                 yls=ay+h*uy
82                 xle=bx+h*ux
83                 yle=by+h*uy
84                 plt.plot([xls,xle],[yls,yle],linewidth=2,alpha=I,color=clr)
85
86  #=============================================================
87  def plotbox(xg,yg,zg):
88       shade(xg[0],yg[0],zg[0],xg[1],yg[1],zg[1],xg[2],yg[2],zg[2],xg[3],yg[3],zg[3])
89       shade(xg[7],yg[7],zg[7],xg[6],yg[6],zg[6],xg[5],yg[5],zg[5],xg[4],yg[4],zg[4])
90       shade(xg[0],yg[0],zg[0],xg[3],yg[3],zg[3],xg[7],yg[7],zg[7],xg[4],yg[4],zg[4])
91       shade(xg[1],yg[1],zg[1],xg[5],yg[5],zg[5],xg[6],yg[6],zg[6],xg[2],yg[2],zg[2])
92       shade(xg[3],yg[3],zg[3],xg[2],yg[2],zg[2],xg[6],yg[6],zg[6],xg[7],yg[7],zg[7])
93       shade(xg[4],yg[4],zg[4],xg[5],yg[5],zg[5],xg[1],yg[1],zg[1],xg[0],yg[0],zg[0])
94
95       plt.axis([0,150,100,0]) #———plot axes and grid
96       plt.axis('off')
97       plt.grid(False)
98       plt.show() #———plot latest rotation
99
100 #============================================================
101 def plotboxx(xc,yc,zc,Rx): #——————transform and plot Rx
102      for i in range(len(x)):
103           [xg[i],yg[i],zg[i]]=rotx(xc,yc,zc,x[i],y[i],z[i],Rx)
104           [x[i],y[i],z[i]]=[xg[i]-xc,yg[i]-yc,zg[i]-zc]
105
106           plotbox(xg,yg,zg) #—————plot
107
108 def plotboxy(xc,yc,zc,Ry):
109      for i in range(len(x)): #——————transform and plot Ry
110           [xg[i],yg[i],zg[i]]=roty(xc,yc,zc,x[i],y[i],z[i],Ry)
111           [x[i],y[i],z[i]]=[xg[i]-xc,yg[i]-yc,zg[i]-zc]
112
113           plotbox(xg,yg,zg)
114
115 def plotboxz(xc,yc,zc,Rz):
116      for i in range(len(x)): #——————transform and plot Rz
117           [xg[i],yg[i],zg[i]]=rotz(xc,yc,zc,x[i],y[i],z[i],Rz)
118           [x[i],y[i],z[i]]=[xg[i]-xc,yg[i]-yc,zg[i]-zc]
119
120           plotbox(xg,yg,zg)
121
122 #————————————————————————-input
123 while True:
124      axis=input('x, y or z?: ') #———input axis of rotation (lower case)
125      if axis == 'x': #–if x axis
126           Rx=radians(float(input('Rx Degrees?: '))) #———input degrees
127           plotboxx(xc,yc,zc,Rx) #———call function plotboxx
128      if axis == 'y':
129           Ry=radians(float(input('Ry Degrees?: '))) #———input degrees
130           plotboxy(xc,yc,zc,Ry)
131      if axis == 'z':
132           Rz=radians(float(input('Rz Degrees?: '))) #———input degrees
133           plotboxz(xc,yc,zc,Rz)
134      if axis == ":
135           break
Listing 7-1

Program SHADEBOX

7.2 Shading a Sphere

In the previous section, you shaded a box using a simple linear relation for the shading function where the intensity of the shading, I, was linearly related to the dot product $$ widehat{mathbf{n}}cdot widehat{mathbf{l}} $$. In this section, you will be mixing the three primary colors and controlling the intensity of each with a non-linear shading function. Results are shown in Figure 7-13, which was produced by Listing 7-2.
../images/456962_1_En_7_Chapter/456962_1_En_7_Fig13_HTML.jpg
Figure 7-13

Shading a sphere by color mixing with a non-linear intensity function (produced by Listing 7-2)

Nonlinear shading functions are shown as the red, green, and blue curves in Figure 7-14; the linear one is in black. The non-linear functions give more control over the shading and can produce more realistic effects. They allow you to control the shading by amplifying and extending the lighter shaded areas while more rapidly increasing the transition of intensity into the darker areas. The linear shading function is similar to the one used in Listing 7-1, except that it now starts at I=IA where IA may be greater than zero. The curves begin at I=IA and terminate at I=IB where $$ widehat{mathbf{n}}cdot widehat{mathbf{l}} $$=+1. IA and IB are parameters that can be adjusted in Listing 7-2. IA>0 will darken the lights. This is sometimes necessary since the tones, when I=0 or close to it, may not transition well to higher regions of I; discontinuities can sometimes be observed. To correct this, start the intensity function at some small value of IA greater than 0. Increasing IA can also be a technique for reducing the brightness of light areas.

Note the difference between $$ widehat{mathbf{n}}cdot widehat{mathbf{l}}p $$ and $$ widehat{mathbf{n}}cdot widehat{mathbf{l}} $$ in Figure 7-14. To get a relation for I vs. $$ widehat{mathbf{n}}cdot widehat{mathbf{l}} $$, you let the function be of the form
$$ I={C}_1+{C}_2{left(widehat{mathrm{n}}cdot widehat{mathrm{l}}p
ight)}^n $$
(7-5)
where C1 and C2 are constants and n is a parameter. n can be changed in the program. Noting that I=IA at $$ widehat{mathbf{n}}cdot widehat{mathbf{l}}p=0 $$,
$$ IA={C}_1+{C}_2{(0)}^n $$
(7-6)
$$ {C}_1= Ia $$
(7-7)
At $$ widehat{mathbf{n}}cdot widehat{mathbf{l}}p $$ = +2, ($$ widehat{mathbf{n}}cdot widehat{mathbf{l}} $$ = +1), I=IB,
$$ IB= IA+{C}_2{(2)}^n $$
(7-8)
$$ {C}_2=frac{IB- IA}{2^n} $$
(7-9)
With $$ widehat{mathbf{n}}cdot widehat{mathbf{l}}p=widehat{mathbf{n}}cdot widehat{mathbf{l}}+mathbf{1}, $$
$$ I= IA+left( IB- IA
ight){left(frac{widehat{mathrm{n}}cdot widehat{mathrm{l}}+1}{2}
ight)}^n $$
(7-10)

Equation 7-10 is your intensity function, $$ mathrm{I}left(widehat{mathbf{n}}cdot widehat{mathbf{l}}
ight) $$. You thus have three parameters with which to adjust I: IA, which regulates the intensity of the lightest areas; IB, which adjusts the darkest areas; and n, which adjusts the transition from light to dark. Higher values of n will produce a more rapid transition. Figure 7-14 shows curves for n=1, 2, 3, and 4. When n=1, the curve becomes linear. There are no definite values for n, IA, and IB; they should be adjusted by trial and error to give visually appealing results.

Regarding colors, the background shown in Figure 7-13 is 'midnightblue'. A good source for color samples is #https://matplotlib.org/examples/color/named_colors.html.

Listing 7-2 creates a sphere by plotting longitudes and latitudes as you did in Listing 6-4. In Listing 6-4, these were spaced six degrees apart. To carry out the shading in Listing 7-2, you will space the longitudes and latitudes closer together, two degrees apart, and adjust their plotting intensity depending on the angle between a local unit vector normal to the surface $$ widehat{mathbf{n}} $$ and the light source unit vector $$ widehat{mathbf{l}} $$ at each point on the surface. This will then be used to control the relative r,g,b contributions to the color mix. As before, you establish this relation by taking the dot product $$ widehat{mathbf{n}}cdot widehat{mathbf{l}} $$. $$ widehat{mathbf{n}} $$ at each point is determined quite simply by obtaining a vector from the sphere’s center to the point in question on the sphere’s surface and then dividing by the sphere’s radius, rs. For example, suppose you are at a point p on the sphere’s surface with coordinates xp,yp,zp. A vector Vp from the sphere’s center at xc,yc,zc to p is
$$ mathbf{Vp}=left( xp- xc
ight)widehat{mathbf{i}}+left( yp- yc
ight)widehat{mathbf{j}}+left( zp- zc
ight)widehat{mathbf{k}} $$
(7-11)
Vp is normal to the surface at p. A unit normal vector, $$ widehat{mathbf{n}} $$, is then
$$ widehat{mathbf{n}}=left(frac{xp- xc}{rs}
ight)widehat{mathbf{i}}+left(frac{yp- yc}{rs}
ight)widehat{mathbf{j}}+left(frac{zp- zc}{rs}
ight)widehat{mathbf{k}} $$
(7-12)
where rs is the sphere’s radius. Taking the dot product of $$ widehat{mathbf{n}} $$ in Equation 7-12 with the incoming light unit vector $$ widehat{mathbf{l}} $$ gives $$ widehat{mathbf{n}}cdot widehat{mathbf{l}} $$, which you need to determine I from Equation 7-10.

In Listing 7-2, lines 22-24 set the components of the incoming light’s unit vector. Lines 26-28 set the intensity function parameters. These values produce Figure 7-13. Lines 37-39 paint the background with dots. Lines 61-101 plot the longitudes. Note in lines 69 and 70 that dalpha and dphi have been added to alpha2 and phi2 since roundoff errors in the np.arange() function can sometimes fail to close the sphere; this assures it closes. Lines 86-92 determine the components of the $$ widehat{mathbf{n}} $$ at the current values of alpha and phi. Line 93 calculates the dot product $$ widehat{mathbf{n}}cdot widehat{mathbf{l}} $$; line 94 calculates the intensity.

In line 99, the attribute linewidth has been increased to 4. When combined with the angular spacing of two degrees in lines 63 and 67, this insures there are no gaps in the surface. Also in line 99 the color statement shows red at 100 percent, green at 80 percent, and blue at 40. The (I-1) factor reflects the impact of the shading function. Recall that when the color mix is (0,0,0), black is produced; conversely, when the mix is (1,1,1), white is produced. Since you want darks where I is close to or equal to 1 (facing away from the light source), the (I-1) factor accomplishes this since it equals 0 when I=1 producing black. If you did not include the (I-1) factor, the mix (1,.8,.45) would simply produce an unshaded round rusty orange disc.
../images/456962_1_En_7_Chapter/456962_1_En_7_Fig14_HTML.jpg
Figure 7-14

Nonlinear shading function

1   """
2   SHADESPHERE
3   """
4
5   import numpy as np
6   import matplotlib.pyplot as plt
7   from math import sin, cos, radians, sqrt
8
9   plt.axis([0,150,100,0])
10  plt.axis('off')
11  plt.grid(False)
12
13  #—————————————————————————lists
14  g=[0]*3
15
16  #———————————————————————parameters
17  xc=80 #———sphere center
18  yc=50
19  zc=0
20  rs=35 #———sphere radius
21
22  lx=.707 #———light ray unit vector components
23  ly=.707
24  lz=0
25
26  IA=.01 #———define curve
27  IB=1
28  n=2.0
29
30  clrbg='midnightblue' #———background color
31
32  Rx=radians(-15) #———sphere angles of rotation
33  Ry=radians(0)
34  Rz=radians(30)
35
36  #———————————————————paint background color
37  for x in np.arange(0,150,1):
38       for y in np.arange(0,100,1):
39            plt.scatter(x,y,s=10,color='clrbg')
40
41  #============================================================rotation functions
42  def rotx(xc,yc,zc,xp,yp,zp,Rx):
43       g[0]=xp+xc
44       g[1]=yp*cos(Rx)-zp*sin(Rx)+yc
45       g[2]=yp*sin(Rx)+zp*cos(Rx)+zc
46       return[g]
47
48  def roty(xc,yc,zc,xp,yp,zp,Ry):
49       g[0]=xp*cos(Ry)+zp*sin(Ry)+xc
50       g[1]=yp+yc
51       g[2]=-xp*sin(Ry)+zp*cos(Ry)+zc
52       return[g]
53
53  def rotz(xc,yc,zc,xp,yp,zp,Rz):
55       g[0]=xp*cos(Rz)-yp*sin(Rz)+xc
56       g[1]=xp*sin(Rz)+yp*cos(Rz)+yc
57       g[2]=zp+zc
58       return[g]
59
60  #————————————————————longitudes
61  phi1=radians(-90)
62  phi2=radians(90)
63  dphi=radians(2)
64
65  alpha1=radians(0)
66  alpha2=radians(360)
67  dalpha=radians(2)
68
69  for alpha in np.arange(alpha1,alpha2+dalpha,dalpha):
70       for phi in np.arange(phi1,phi2+dphi,dphi):
71            xp=rs*cos(phi)*cos(alpha)
72            yp=rs*sin(phi)
73            zp=-rs*cos(phi)*sin(alpha)
74            rotx(xc,yc,zc,xp,yp,zp,Rx)
75            xp=g[0]-xc
76            yp=g[1]-yc
77            zp=g[2]-zc
78            roty(xc,yc,zc,xp,yp,zp,Ry)
79            xp=g[0]-xc
80            yp=g[1]-yc
81            zp=g[2]-zc
82            rotz(xc,yc,zc,xp,yp,zp,Rz)
83            xpg=g[0]
84            ypg=g[1]
85            zpg=g[2]
86            a=xpg-xc
87            b=ypg-yc
88            c=zpg-zc
89            qp=sqrt(a*a+b*b+c*c)
90            nx=a/qp
91            ny=b/qp
92            nz=c/qp
93            ndotl=nx*lx+ny*ly+nz*lz
94            I=IA+(IB-IA)*((1+ndotl)/2)**n
95            if phi == phi1:
96                xpglast=xpg
97                ypglast=ypg
98            if nz < 0:
99                plt.plot([xpglast,xpg],[ypglast,ypg],linewidth=4,color=((1-I),.8*(1-I),.45*(1-I))
100               xpglast=xpg
101               ypglast=ypg
102
103 #————————————————————latitudes
104 for phi in np.arange(phi1,phi2+dphi,dphi):
105      r=rs*cos(phi)
106      for alpha in np.arange(alpha1,alpha2+dalpha,dalpha):
107            xp=r*cos(alpha)
108            yp=rs*sin(phi)
109            zp=-rs*cos(phi)*sin(alpha)
110            rotx(xc,yc,zc,xp,yp,zp,Rx)
111            xp=g[0]-xc
112            yp=g[1]-yc
113            zp=g[2]-zc
114            roty(xc,yc,zc,xp,yp,zp,Ry)
115            xp=g[0]-xc
116            yp=g[1]-yc
117            zp=g[2]-zc
118            rotz(xc,yc,zc,xp,yp,zp,Rz)
119            xpg=g[0]
120            ypg=g[1]
121            zpg=g[2]
122            a=xpg-xc
123            b=ypg-yc
124            c=zpg-zc
125            qp=sqrt(a*a+b*b+c*c)
126            nx=a/qp
127            ny=b/qp
128            nz=c/qp
129            ndotl=nx*lx+ny*ly+nz*lz
130            textbfI=IA+(IB-IA)*((1+ndotl)/2)**n
131            if alpha == alpha1:
132                xpglast=xpg
133                ypglast=ypg
134            if nz < 0:
135                plt.plot([xpglast,xpg],[ypglast,ypg],linewidth=4,color=((1-I),.8*(1-I),.45*(1-I)))
136                xpglast=xpg
137                ypglast=ypg
138
139 plt.show()
Listing 7-2

Program SHADESPHERE

7.3 Summary

While adding a background color can greatly enhance the visual appearance of an object, shading can also be quite effective. In this chapter, you learned techniques for shading an object. Shading implies the presence of an illuminating light source. In your model, you used the direction of the light rays coming from a source but you did not specify the position of the source. In Listing 7-1, you explored the concept of a shading function as shown in Figure 7-10 and how it determines the intensity of shading on a plane. This depends on the orientation of the plane relative to the direction of the incoming light rays, which is determined by taking the dot product of a unit vector normal to the surface, $$ widehat{mathbf{n}}, $$ with a unit vector pointing in the direction of the light rays, $$ widehat{mathbf{l}} $$. In Listing 7-2, you performed the same shading operations on a sphere. However, you improved on the shading function. Whereas in Listing 7-1 you used a simple linear relation between the shading intensity and the dot product $$ widehat{mathbf{n}}cdot widehat{mathbf{l}}, $$ in Listing 7-2 you used a nonlinear relation, as shown in Figure 7-14. This greatly improves the appearance of the shading.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.62.105