



Subject: Texturing, Bumpmapping of Spheres in Realtime

Date: 23 Apr 1995 20:27:07 GMT








FAST TEXTURING OF SPHERES 


Two weeks ago I posted an article that outlined a method for the

fast texture mapping of parallel projected spheres. In this article

I asked if this method is something new. I got two letters from

people who had developed their own algorithms for the fast texturing

of spheres, though both didn't publish them. There was no letter that

said that this algorithm has been published somewhere.


In this article I will try to give a better explanation of the

method I used, and I will describe some extensions, like

diffuse shading, bump mapping and specular shading of spheres in

realtime.


If you think that drawing spheres is a stupid thing to do,

read this article even though. Some of the techniques described can

be used in other algorithms as well.


All algorithms are scanline-orientated and require only

table-lookups, additions and some logical operations in the

interior loops. I implemented the algorithms in this article.


The basic idea of all algorithms is to store both points on the

sphere's surface and vectors in spherical coordinates, and to

convert this coordinates always into that spherical coordinate

system (SCS), in which the thing that you want to to is

particulary easy.

The conversion between different SCS can be done fast using

a lookup-table.


SPHERICAL COORDINATES (SC)


Sperical coordinates appear in a lot of math textbooks,

though I will give a short description, since SC are essential

for the algorithms below.


To describe a point in space using SC, you need two angles,

alpha e [0..2Pi[ and beta e [-Pi/2..Pi/2[, and the distance r

of the point to the origin. Since I use only points on spheres

and normalized vectors, r is always constant, and I don't have

to regard it any further.

A exemplary SCS is that of the earth, with the longitude as the

first angle alpha, and the latitude as the second angle beta.

Every SCS has a special axis, the polar axis. The polar axis is

the line were beta = -Pi/2 or beta = Pi/2, e.g. the polar axis

of the earth's SCS just connects the two poles of the earth.

To convert SC given in a SCS with the y-axis as polar-axis into

kartesian coordinates, take the following formula:

x = r*cos(alpha)*cos(beta)

y = r*sin(beta)

z = r*sin(alpha)*cos(beta)


For my purposes, the most important property of SCS's is, that

it is very easy to rotate a point given in SC around the polar

axis of the SCS. You just have to add the desired angle to the

alpha-coordinate of the point. Nevertheless, it is very difficult

to rotate a point in SC around any other axis.

But if you want to move a texture or anything else into an arbitrary

position, you have to rotate it at least around 3 axis. Why this is so

is discussed in many physic textbooks (any rigid body with a single

fixed point has three degrees of freedom).


To solve this problem, I convert the SC from the first SCS into a

second SCS with a polar axis identical to the second axis of rotation.

In physics the second axis of rotation is choosen perpendicular to the

first. I will do the same.


Our problem at the moment is the conversion between the two SCS's:

We have a fixed point in a first SCS, and want to compute the SC of

that point in a second SCS.

I will derive the formula that does this for a first SCS with the

y-axis as polar axis and a second SCS with the z-axis as polar axis: 


First, I convert the point from the first SCS into kartesian

coordinates:

x = cos(alpha1)*cos(beta1)

y = sin(beta1)

z = sin(alpha1)*cos(beta1)

Then, I convert the point from kartesian coordinates into the

second SCS:

beta2 = arcsin(z)

w = x/cos(beta2)

alpha2 = arccos(w)

if (y/cos(beta2) < 0) alpha2 = Pi*2 - alpha2

This is just the inverse of the mapping above, with y and z exchanged.


To compute this formula during rendering, I store the mapping in

a large 2D-lookuptable. How this is done is described below.


Now I can rotate the point in this second SCS. But I still have

to rotate the point around a third axis. To do this, I go in

a third SCS, that's polar axis is perpendicular to the second axis.

I can use the same mapping for the conversion from the second

SCS to the third SCS as I used for the conversion from the first to

the second SCS. Note, that the third SCS is different from the first!

For illustrations, what happens to a 3D-object when it is rotated

in this way, see a physics textbook that deals with 'rigid bodies' or

'euler angles'.


DOING IT FAST


To do this stuff fast during rendering, you first have to convert the

spherical coordinates into unsigned integers.

I do this in a straighforward way:

alpha_int = alpha/(2*pi)*S

beta_int = (beta+Pi/2)/Pi*S

where 0..S is the range of the integer-SC

(0 <= alpha_int < S, 0 <= beta_int < S)

I will store the two SC in a single integer, using the first

n bits for alpha_int, and the second n bits for beta_int (n = log2(S)):

alpha_beta_int = alpha + beta*S.

If you choose S=256, 16 bit are sufficent for storing a SC.


The lookuptable for the converting between the two SCS's in then a

array of short int's of the size S*S.


Here's pseudo-code, how to compute it:

for alpha_int1 = 0 to S-1

 for beta_int1 = 0 to S-1

  (alpha1,beta1) = convert_to_double(alpha_int1,beta_int1)

  (x,y,z) = convert_to_kartesian(alpha1,beta1)     (1) 

  (alpha2,beta2) = convert_to_spherical(x,y,z)     (2) 

  (alpha_int2,beta_int2) = convert_to_int(alpha2,beta2)

  LOOKUPTABLE[alpha_int1 + beta_int1*S] = alpha_int2 + beta_int2*S

 next

next


The function in line (1) uses a SCS with the x-axis as polar axis,

while the function in line (2) uses a SCS with z-axis as polar axis.


TEXTURING


The parallel projected textured sphere is drawn scanline by scanline

and pixel by pixel.

In every scanline I first have to compute the width of the sphere

in that scanline (scanconvert the contour of the projected sphere).

Then, compute the color of every pixel in this horizonal span that

is covered by the sphere.

I will use a kartesian koordinate system with the z-axis

perpendicular to the screen, and that has a origin that lies in the

center of the sphere.


At first, I have to map each pixel (x,y) onto the sphere, and

convert this point on the sphere into the SC (alpha,beta).

For this purpose, I use a initial SCS, so that this mapping is

particulary easy and I can do both, the mapping onto the sphere and

the conversion into SC, in a single step.

The SCS that fulfills this requirements has a polar axis that is

identical to the y-axis. (again, if you take the SCS of the earth,

you would look towards the equator).

The formula to do the mapping with a sphere of radius r is :

beta1 = asin(y/r)

alpha1 = acos(x/sqrt(r^2-y^2))

(2*sqrt(r^2-y^2) is the width of the sphere in scanline y)

This formula is computed during rendering using lookuptables.


Now I can rotate this point around the three axis, as described above.

The texture is stored also in SC so that the only thing remaining

is to look up the color. (the texture is an array of color-values

of the size S*S).


Here's the algorithm in pseudo-code:


for x = -r to r

 for y = -sqrt(r*r-y*y) to sqrt(r*r-y*y)

   beta1 = ARCSIN_TABLE[x*S/(r*2)+S/2]

   alpha1 = ARCCOS_TABLE[y*S/(2*sqrt(r*r-y*y))+S/2] + phi

   alpha_beta_2 = LOOKUPTABLE[alpha1 + beta1*S] + theta

   alpha_beta_3 = LOOKUPTABLE[alpha_beta_2] + psi

   putpixel(x,y,TEXTURE[alpha_beta_3])

 next

next


where

 ARCSIN_TABLE[i] = arcsin(i*2/S-1)*S/PI+S/2 , (i=0..S)

 ARCCOS_TABLE[i] = arccos(i*2/S-1)*S/(PI*2) , (i=0..S)

 phi, theta and psi are the angles used in the three rotations

  

In this pseudo-code I ignored the problem, that e.g. alpha_beta_2

may become greater than S*S, but the 2D-lookuptable has only the

size S*S.

To solve this problem, you can either use masking-operations, or

use a 2D-lookuptable and a texture of the size S*S+S.

(LOOKUPTABLE[S*S+i] = LOOKUPTABLE[i] , i = 0..S)

You may have noticed, that the programm contains still squareroots

and multiplications/divisions. It isn't too hard to remove these

using another lookuptable and fixed-point forward-differencing.


DIFFUSE SHADING


The intensity-distribution on a sphere that is lit by a lightsource

is also a texture. So, you can use the techniques in the chapter

above for diffuse shading of spheres. The only thing you have to

adapt is the preprocessing.

Here's a pseudocode to compute a intensity-texture :


for alpha = 0 to S

 for beta = 0 to S

  (x,y,z) = convertSCtokartesian(alpha,beta)

  INTENSITY_TEXTURE[alpha+beta*S] = shade_sphere(x,y,z)

 next

next


This method can be used to draw spheres that are lit by more than

one lightsource.


BUMP-MAPPING


A bump-map is similiar to a texture, with the difference that it does

not map color-values to points on the sphere, but surface-normals.

If you use bump-maps, you use the normal you found in the bump-map to

shade a given point on the sphere, and not the true normal of the

sphere at this point.

I store surface-normals in SC, too. So, I use a array of short int's

of the size S*S to store the bump-map.

Once I got the surface-normal I have to compute the intensity.

If the incident light is parallel, this can be done using

intensity-textures, too. Note, that a intensity-texture then can be

seen as a mapping from surface-normals to intensities.

You can rotate surface-normals in the same way as you rotated points

on the sphere.


a pseudo-code for bump-mapping follows :


for each pixel (x,y)

 (alpha1,beta1) = converttoSC(x,y)

 alpha1 = alpha1 + phi1

 alpha_beta2 = LOOKUPTABLE[alpha1+beta1*S]+theta1

 alpha_beta3 = LOOKUPTABLE[alpha_beta2]+psi1

 normal1 = BUMPMAP[alpha_beta3]+phi2

 normal2 = LOOKUPTABLE[normal1]+theta2

 normal3 = LOOKUPTABLE[noraml2]+psi2

 putpixel(x,y,INTENSITY_TEXTURE[normal3])

next


Note, that this algorithm involves 6 rotations:

first, I rotate the bump-map, using 3 rotations, then

I rotate the light-vector/surface-normal, using again 3 rotations.

The first few lines in this pseudo-code would be identical to

the code for the texture-mapping, so I abbreviated them.


SPECULAR SHADING


Specular shading is harder to do than diffuse shading.

For specular shading, I have to compute the direction of the ray

coming from the eye of the viewer, after it has been reflected on the

spheres surface. To do this, I go into that SCS, where the computing

of the reflected ray is particulary easy. This is the

SCS with the polar axis parallel to the direction of view.

In this SCS, the direction of the reflected ray can be computed by

multiplying the (integer-) beta-coordinate of a given

point by 2 (draw it!!!).

Again, all incident light should be parallel, so that the intensity

of the point depends only on the direction of the reflected ray.

The actual computation of the intensity is done using a

intensity-texture, that contains a intensity-value for every direction

of the ray.


here's the pseudo-code for specular shading:


for each pixel (x,y)

  (alpha1,beta1) = converttoSC(x,y)

  alpha_beta2 = LOOKUPTABLE[alpha1+beta1*S]           (1)

  alpha2 = alpha_beta2 and (S-1)                      (2)

  beta2 =  alpha_beta2/S

  beta2 = beta2*2

  alpha2 = alpha2 + phi

  alpha_beta3 = LOOKUPTABLE[alpha2+beta2*S] + theta

  alpha_beta4 = LOOKUPTABLE[alpha_beta3] + psi

  putpixel(x,y,INTENSITY_TEXURE[alpha_beta4]

next


The 'and' in line (2) is a bitwise and, that is used

to extract alpha2 out of alpha_beta2.

In line (1), LOOKUPTABLE[] is used to convert SC from

a SCS with the y-axis as polar axis to a SCS with the z-axis as

polar axis.

This has to be regared while initializing the 2d-lookuptable.


To compute the intensity-texture during preprocessing, use

the formula for specular shading that can be found e.g. in

Foley/VanDam:

I = I0*cos(delta)^e

'delta' is the angle between the reflected ray and the incident light.

'e' is a constant (e.g. 12).

Again, you can use this method to texture spheres that are lit by

more than one lightsource. 


Note, that this algorithm does not test, if the light does really

reach a point on the sphere. This is a serious flaw of this

algorithm, which has the effect that a lightsource behind

the sphere causes a lit circle at the margin of the sphere.

At least if you use only one lightsource, there should be

a efficent way to do a appropriate test.



THINGS I DIDN'T DO


The things in this chapter were neither tested nor implemented.


If a sphere is illuminated by a single lightsource, you can

use the symetry of the intensity-distribution to

reduce either the number of table-lookups or the size of the

intensity-texture (a one-dimensional texture is then sufficent).


The use of spherical coordinates can be fruitful for other realtime-

graphic-algorithms as well. In the chapter 'Specular Shading'

I computed the direction of a reflected ray without multiplications.

It should be possible to do other calculations with normalized vectors

in spherical coordinates and without multiplications as well,

e.g. the computing of normalized vector-products or dot-products.

Remember the basic idea of the algorithms in this article :

To do something, go into that SCS, in which the thing you want to

do is particulary easy.


The method I used to bump-map spheres can be used to bump-map

polygons as well.


If you want to texture planets in a flight-simulator, the

rotation that has to be applied to the planets before drawing is given

in the form of an orthogonal matrix. But the texturing-algorithm

uses 3 angles to describe such a rotation. So, you have to

convert the orthogonal matrix into the three angles.



MISCELLANEOUS


Using assembler language the programs above can be optimized highly.

I achieved 17 fps at texturing a sphere of 100 pixel diameter

on a 286/12Mhz.


Of course, the algorithms described can be used free of royalities.

This article may be redistributed.


Here's a short summary of some of the mail I got about the first

article:

Joe Lee and Mike Currington have developed their own algorithms for

the fast texture mapping of spheres with diffuse shading.

Diana Gruber send me some suggestions for improving the

C++-source. My code wasn't really well optimized.

Steve Metke suggested the extension of this algorithm to

ellipsoids. I agree, that this should be possible, though it

will not be easy.


Here's a list of books that might help you to implement or

understand the algorithms:

* Foley/vanDam (shading, introduction to 3D-graphics)

* PCGPE, Mark Feldmann et al.

  (fixed-point math, assembler coding, shading,

   introduction to 3D-graphics, planar texture mapping)

  (the PCGPE can be found on a lot of ftp-servers)

* Classical Mechanics, H.Goldstein, (euler angles)


I will post a source for Watcom/Turbo C++ that does the things

above to comp.sys.ibm.pc.demos.


Please, don't ask for executables. Maybe I make a demo about

spheres in the next few months and upload it to a ftp-server.


I am a student of computer science at the university in

Erlangen/Germany.


Thanks for reading this, 


Hans Kopp,  4.22.1995

  



