Burning Man Shade Structure

I just got back from Burning Man this year. It was an awesome time. I went with 7 friends and we decided to DIY our own shade structure. We followed Jon Starbuck’s design (archive.org link) which uses a 20’x20′ aluminet tarp stretched between four 7′ tall 2×4 corner posts, with a 10′ tall center post, plus a 20’x10′ sloper on the east side.

The total cost was around $480 dollars (about $390 without the sloper). It easily fit four tents plus plenty of room in the middle as a kitchen / sitting area. Total prep time was about 2 hours plus a practice run to set it up once. In the desert, it took five people somewhere between one and two hours to set up. It provided pretty good shade from sunrise until about 3:30pm. After around 5:30pm, our tents provided good shade from the west.

Camp Starstruck in the AEZ and its amazing shade structure

Here’s a photo from Jon Starbuck’s flickr of his original shade structure.

What follows is essentially a list of modifications, tips, and suggestions that we discovered when building our structure.

Parts List

ItemPriceNotes
20'x20' aluminet$200with nylon webbing and pre-sewn grommets
20'x10' aluminet$90with nylon webbing and pre-sewn grommets
100' static rope$15We got cheap rope. It worked, but it was pretty shot afterwards.
8x ratchet straps$35We got two packs of four, 16' long. These only need to go from the top of the pole to the bottom, with about a ~30 degree angle, but 16' was no more expensive than 10'.
4x 8' 2x4s$13We had the staff at Home Depot cut these exactly in half to make a total of 8 four foot sections. These are for the corner poles.
3x 5' 2x4s$5We bought one 10' board and had it cut in half, then scrounged another spare 5' section from scrap. These are for the center pole.
4x 3/8" x 6" eyebolts$5These are where the rope will pass. We got all hardware in 3/8" diameter. Pick whatever (as long as it's not tiny), but be consistent!
18x 3/8" x 4" hex head cap screws$15We got 18 bolts (3 for each corner post and 6 for the center post). Make sure to get them long enough to go through two 2x4s. Also, especially if you suck at drilling like me, having threads only on the last inch of the bolt is very nice.
100x 3/8" washers$11We bought a ton of washers. It was way easier than figuring out exactly how many we'd need.
100x 3/8" nuts$10Ditto.
100x black zipties$15The tarps will be zip-tied to the rope. Also, I used a ton of these things to fix random stuff that broke on the Playa. Get decent ones. I'm not including a link because the ones that I bought broke a lot. They worked, but I'd have much rather had better ones.
100' paracord$15We also used this for a lot of repairs and tie-downs. I suggest getting some reflective paracord as well, because it's much easier to see in the night.
14x 3/8"x3' rebar$27We got 2' rebar. Get 3'. Also, get more than you think you need. Don't forget your tents.
14x tennis balls$15You put the tennis balls over your rebar, or you will eventually injure yourself by stepping on it.
8x 1/4"-5/8" hose clamps$6These are part of a system for attaching to the rebar. Get extras!
8x 1/2" fender washers$3These are part of a system for attaching to the rebar. Get extras!

We made a number of modifications to Jon’s design:

  • We were transporting our structure in three cars, none of which had a roof rack. Because we didn’t have a truck or rack, we had a maximum length limit of about five feet (5′). None of our poles could be longer than 5′, so we cut the corner poles into two 4′ sections, with a one foot overlap. The center pole was made of three 5′ sections, also overlapping by one foot.
  • Our aluminet came with two inches of static nylon webbing sewn into the edge, with grommets added into that webbing. Jon’s original design relies significantly on the stretch of the aluminet, so we had to lengthen the center pole to make up for less tension on the edges.
  • We added a 20’x10′ sloper on the east side to help keep the sun off in the morning. You could add one on the west side (in addition, or instead) for better afternoon shade.
  • We were unable to bend the rebar. “Candy caning” it is actually counterproductive, because it makes it very hard to hammer into the ground. So we worked out another system for attaching to the rebar. See below.

Preparation

Total preparation time took two people around four hours, not counting shopping or our test of raising the structure. Most of that time went into drilling holes in the corner poles and ensuring they assembled. If you can transport 8′ length poles, it would be much easier.

Poles

  • Cut all the two-by-fours to their correct lengths. You’ll want 8 four-foot segments (for the corner poles) and 3 five-foot segments (for the center pole).
  • Drill three holes in a roughly triangular pattern into one segment, covering about a foot.
  • Lay a second segment under that (end-to-end) and clamp together. Using a smaller drill bit, make a quick start through the top board into the bottom one (using the top as a template).
  • Remove the top board, switch to the bigger drill bit, and drill the three holes into the second board.
  • Mark the insides, and number the boards! So you should have “1-IN” on two sections, and when the “IN” parts touch, it should be possible to put bolts through and assemble the board.
  • For each corner pole, choose one piece to be the “top”. Drill a hole about 4″ from the top of the board in the center and pass an eye-bolt through there. Secure it with washers and nuts on both sides.
  • For the center pole, one section should have holes in both ends. This will be the middle section. Ideally, the other two sections should go on the same side of the board (so the load is more up-and-down), but we messed this up and it worked fine.
  • Even if you don’t test anything else, at least test assembling the boards at home. Probably bring your drill and bits as well. Use washers.

Rebar

We didn’t have a vice to allow us to bend the rebar, so we worked out another way to keep the ratchet straps attached. Our straps had a hook at the end, as well as a closed ring. On each piece of rebar, we placed a 1/2″ fender washer. Above that, we attached a hose clamp and tightened it down. The hole in the washer could slide over the rebar but not the hose clamp. The washer was too big for the ratchet strap to slide over it. This kept the ratchet straps strongly in place without bending anything.

Rebar, washer, and hose clamp. The tennis ball saves your feet.

Rebar, washer, and hose clamp. The tennis ball saves your feet.

Center Pole

Because we weren’t sure how much stretch we needed in the tarp, we planned to cut the center pole to length when we got to the desert. We eyeballed the final length, but it was pretty close to 13′. Starting too big and using a handsaw is a pretty good strategy. It doesn’t take long at all to cut through a 2×4.

Erecting the Structure

Erecting the structure took around 2 hours when we got to Black Rock City. The approach we used required at least five people. Essentially, the rope is used to connect the poles and placed under tension. Then the aluminet is raised over the top and ziptied on. Finally, the center pole is raised to support the middle.

  1. Assemble the four corner poles with three bolts each. Use washers.
  2. Assemble all three sections of the center pole, and attach something soft/smooth to the top. We used a metal bowl and a ton of duck tape. Others have used a towel or rag.
  3. Lay out the main tarp on the ground.
  4. One person should stand at each corner, holding a corner pole.
  5. Run the rope through the eyebolts at the top of each corner pole.
  6. Stand up all of the poles, then lean them in a bit.
  7. Pull the rope tight and tie a knot.
  8. Stand up all of the poles. This provides the tension in the rope. It should be pretty tight.
  9. Attach the long bits of two ratchet straps to the top of each corner pole.
  10. Attach the short, ratcheting pieces of the strap to two different pieces of rebar, spaced about 90° apart and about 6′ away from the base of the pole.
  11. Ratchet the two straps down until they’re taut. This replaces the person holding up that pole.
  12. Once all four poles are tight, walk around and tighten them all up a bit more. Make sure to leave some room in the ratchets to tighten more later if things shift.
  13. Raise the aluminet and walk it over the tops of the poles.
  14. Zip-tie the aluminet to the rope at each gromet. At the corners, zip-tie to the eyebolt. Use a piece of paracord tied in a fairly tight loop to back up the zip-tie. The corners experience the greatest stress.
  15. Try raising the center pole. If it’s too long, use your handsaw to cut off a foot from one end and try again. Repeat until the tarp is pretty tight but doesn’t look like it’s in danger of breaking.
  16. Put in two or three pieces of rebar at the bottom of the pole and zip-tie/duct-tape/lash the center pole to the rebar. This should keep the bottom of it in place. Throw something heavy around it as well, like your water or batteries.
  17. Add the sloper to one side (if you want) by zip-tying it to the rope. Put in two or three pieces of rebar at the bottom and tie the corners and/or center of the sloper to them. It doesn’t need to hold much load, but make it tight so it won’t flap.

Because of the length of the center pole, the force of the wind on the top of it exerts pretty extreme leverage on the bottom of the board. The rebar will keep the bottom from moving around, but it won’t stop the pole from leaning pretty heavily. To address this, we added a single guy wire (made of paracord) pulling against the direction of prevailing winds (about south by southwest) to counter the tilt.

Musings

  • You’ll be out in the desert for a week, with no way to fix broken stuff except for what you brought. So bring useful things:
    • Make sure to bring a ton of zip ties and some paracord. We brought ~100′ of good paracord that I’d used before, plus 100′ of some cheap reflective paracord that I thought might be a bit dodgy. It turned out to hold up okay, but was definitely weaker than the regular black stuff.
    • Having two hammers is nice for setup and teardown. It’s kind of a waste the rest of the time, and you might be able to ask to borrow a neighbor’s hammer if you get there later. Make sure to have at least one fairly large (mini-sledge or bigger) hammer, though.
    • Vice grips. Those things are the most amazing tool.
    • Duck tape. We went through two rolls. Get the good 3M stuff, not the “duck” brand. Gorilla tape or gaffer’s tape could work as well.
  • The wind comes from about South by Southwest. Park your cars between the wind and your shade structure and tents.
  • Tents can also make good shade, especially in the late evening when the sun is low. In exchange, they’ll get hot during that time.
  • Bring extra of every part, especially small ones.
  • Once you set up the poles, you can move their bases a bit just by picking them up.

More Images

 

Cell Noise

 

What is Cell Noise?

Cell noise (sometimes called Worley noise) is a is a procedural texture primitive like Perlin Noise. Despite its numerous applications there are few good implementations and explanations available online. Perhaps the best way to be introduced to cell noise  is with a demo. By the end of this blog post you will understand all of the options available in the demo 🙂

Install Microsoft Silverlight

Theory

The evaluated value of cell noise at a given position is simply the distance to the nearest feature point. Feature points are points placed randomly throughout space. The image below illustrates this.

In the above image the circled green dot (point A) is the position at which the noise function is being evaluated and all the red dots are the feature points.  Feature point D is the closest point to the evaluation point, A. The noise function evaluated at point A is the distance between point A and point D (0.49 in this case).

Naive Implementation

The obvious implementation of cell noise is to:

  1. Generate and store a bunch of random feature points
  2. Evaluate a point by looking through all feature points and finding the closest point.

There are several major drawbacks to this naive implementation including storage requirements and computational cost making the naive implementation impractical. In 1996, Steven Worley devised an efficient algorithm for  evaluating cell noise. The rest of this blog post will detail his algorithm.

Efficient Implementation

Evaluating a point using Steven Worley’s algorithm consist of the following steps

  1. Determine which cube the evaluation point is in
  2. Generate a reproducible random number generator for the cube
  3. Determine how many feature points are in the cube
  4. Randomly place the feature points in the cube
  5. Find the feature point closest to the evaluation point
  6. Check the neighboring cubes to ensure their are no closer evaluation points.

1. Determine which cube the evaluation point is in

Steven Worley’s algorithm divides space into a regular grid of cubes whose locations are at integer locations. In the image below the evaluation point is the green dot, point A. Point A is located at ( 3.45, 2.65) in the cube ( 3, 2).

2. Generate a random number generator for the cube

We need to generate a reproducible random number generator uniquely seeded for the cube. It is critical that the seed is the same each time a point is evaluated within the cube. The seed can be created by hashing the integer coordinates of the cube. There are many hash functions suitable for this purpose. In the demo and sample code I used the FNV hashing algorithm. Once we have a seed unique to the cube we can generate random numbers using a LCG random number generator.

3. Determine how many feature points are in the cube

In order to determine how many feature points are in the cube we must consider how feature points are distributed in space. The simplest distribution is probably the Poisson distribution, feature points are placed randomly and independently of each other. The only parameter the Poisson distribution requires is the average number of feature points per unit space. Using the discrete Poisson distribution function we can calculate the probability of having 0,1,2, or more points in a cube. By generating a random number between zero and one (using the generator from step 2) and comparing its value with the calculated probabilities we can determine the number of feature points in a specific cube. For performance reasons, which will be discuss later, the number of points in a cube will be clamped between one and nine.

4. Randomly place the feature points in the cube

Using the random number generator initialized in step 2, we generate coordinates for each feature point are inserted in the cube.

5. Find the feature point closest to the evaluation point

As we place the feature points into the cube we do not store them. Rather, we simply keep track of the distance to the closest feature point inserted thus far. When we are done placing all the feature points we have found the closest feature point.

6. Check the neighboring cubes to ensure their are no closer evaluation points.

The closest feature point to the evaluation point may not be in the same cube as the evaluation point. The image below is an example of such a case.

In the above image the closest feature point to the evaluation point A is obviously the feature point B which is not in the same cube as the evaluation point A.

Because the closest feature point may not be in the same cube as the evaluation point all the neighboring cubes must be checked to see if they contain the closest feature point. This is done by repeating steps 2 through 5 for each neighboring cube.

In step 3 we clamped the number of feature points in a cube between 1 and 9. The lower bound of 1 guarantees that the closest feature point is either in the same cube as the evaluation point or in an immediately neighboring cube. If we do not have this lower bound we may have to check many neighbors and performance would suffer. In the image below each cube is not required to have at least one feature point. The closest feature point to the evaluation point A is feature point E. Feature point E is not in the same cube as the evaluation cube or in an immediately neighboring cube.

Extensions

Combining the N closest feature points

So far we have only concerned ourselves with finding the distance to the closest feature point. If we find the distances to the closest several feature points we can combine their distances to achieve some cool effects. For example, in the image below the value at the evaluation point is the distance to the second closest feature point minus the distance to the closest feature point.

Keeping track of the closest n feature points requires a simple modification to our algorithm. Instead of only keeping track of the distance to the closest point (for step 5), we keep track of an array of the distances to the closest n feature points. To determine if a feature point is in the closest n feature points we simply insert the feature point’s distance from the evaluation point into the array using insertion sort.

Alternative Distance Functions

To this point we have been measuring distance using the the euclidean distance metric. There are other distance metrics which will produce very different result. Look at the images below for examples of different distance metrics.

Code

The commented critical code from the demo is listed below. The full source for the demo is hosted at github and can be found at https://github.com/freethenation/CellNoiseDemo. The source code below is not optimized for performance but rather written to be easily understood. I may post an optimized version at some later time.

public static class CellNoise
{
	/// <summary>
	/// Generates Cell Noise
	/// </summary>
	/// <param name="input">The location at which the cell noise function should be evaluated at.</param>
	/// <param name="seed">The seed for the noise function</param>
	/// <param name="distanceFunc">The function used to calculate the distance between two points. Several of these are defined as statics on the WorleyNoise class.</param>
	/// <param name="distanceArray">An array which will store the distances computed by the Worley noise function. 
	/// The length of the array determines how many distances will be computed.</param>
	/// <param name="combineDistancesFunc">The function used to color the location. The color takes the populated distanceArray
	/// param and returns a float which is the greyscale value outputed by the worley noise function.</param>
	/// <returns>The color worley noise returns at the input position</returns>
	public static Vector4 CellNoiseFunc(this Vector3 input, int seed, Func<Vector3, Vector3, float> distanceFunc, ref float[] distanceArray, Func<float&#91;&#93;, float> combineDistancesFunc)
	{
		//Declare some values for later use
		uint lastRandom, numberFeaturePoints;
		Vector3 randomDiff, featurePoint;
		int cubeX, cubeY, cubeZ;

		//Initialize values in distance array to large values
		for (int i = 0; i < distanceArray.Length; i++)
			distanceArray&#91;i&#93; = 6666;

		//1. Determine which cube the evaluation point is in
		int evalCubeX = (int)Math.Floor(input.X);
		int evalCubeY = (int)Math.Floor(input.Y);
		int evalCubeZ = (int)Math.Floor(input.Z);

		for (int i = -1; i < 2; ++i)
			for (int j = -1; j < 2; ++j)
				for (int k = -1; k < 2; ++k)
				{
					cubeX = evalCubeX + i;
					cubeY = evalCubeY + j;
					cubeZ = evalCubeZ + k;

					//2. Generate a reproducible random number generator for the cube
					lastRandom = lcgRandom(hash((uint)(cubeX + seed), (uint)(cubeY), (uint)(cubeZ)));
					//3. Determine how many feature points are in the cube
					numberFeaturePoints = probLookup(lastRandom);
					//4. Randomly place the feature points in the cube
					for (uint l = 0; l < numberFeaturePoints; ++l)
					{
						lastRandom = lcgRandom(lastRandom);
						randomDiff.X = (float)lastRandom / 0x100000000;

						lastRandom = lcgRandom(lastRandom);
						randomDiff.Y = (float)lastRandom / 0x100000000;

						lastRandom = lcgRandom(lastRandom);
						randomDiff.Z = (float)lastRandom / 0x100000000;

						featurePoint = new Vector3(randomDiff.X + (float)cubeX, randomDiff.Y + (float)cubeY, randomDiff.Z + (float)cubeZ);

						//5. Find the feature point closest to the evaluation point. 
						//This is done by inserting the distances to the feature points into a sorted list
						insert(distanceArray, distanceFunc(input, featurePoint));
					}
					//6. Check the neighboring cubes to ensure their are no closer evaluation points.
					// This is done by repeating steps 1 through 5 above for each neighboring cube
				}

		float color = combineDistancesFunc(distanceArray);
		if(color < 0) color = 0;
		if(color > 1) color = 1;
		return new Vector4(color, color, color, 1);
	}

	public static float EuclidianDistanceFunc(Vector3 p1, Vector3 p2)
	{
		return (p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y) + (p1.Z - p2.Z) * (p1.Z - p2.Z);
	}

	public static float ManhattanDistanceFunc(Vector3 p1, Vector3 p2)
	{
		return Math.Abs(p1.X - p2.X) + Math.Abs(p1.Y - p2.Y) + Math.Abs(p1.Z - p2.Z);
	}

	public static float ChebyshevDistanceFunc(Vector3 p1, Vector3 p2)
	{
		Vector3 diff = p1 - p2;
		return Math.Max(Math.Max(Math.Abs(diff.X), Math.Abs(diff.Y)), Math.Abs(diff.Z));
	}

	/// <summary>
	/// Given a uniformly distributed random number this function returns the number of feature points in a given cube.
	/// </summary>
	/// <param name="value">a uniformly distributed random number</param>
	/// <returns>The number of feature points in a cube.</returns>
	// Generated using mathmatica with "AccountingForm[N[Table[CDF[PoissonDistribution[4], i], {i, 1, 9}], 20]*2^32]"
	private static uint probLookup(uint value)
	{
		if (value < 393325350) return 1;
		if (value < 1022645910) return 2;
		if (value < 1861739990) return 3;
		if (value < 2700834071) return 4;
		if (value < 3372109335) return 5;
		if (value < 3819626178) return 6;
		if (value < 4075350088) return 7;
		if (value < 4203212043) return 8;
		return 9;
	}

	/// <summary>
	/// Inserts value into array using insertion sort. If the value is greater than the largest value in the array
	/// it will not be added to the array.
	/// </summary>
	/// <param name="arr">The array to insert the value into.</param>
	/// <param name="value">The value to insert into the array.</param>
	private static void insert(float[] arr, float value)
	{
		float temp;
		for (int i = arr.Length - 1; i >= 0; i--)
		{
			if (value > arr[i]) break;
			temp = arr[i];
			arr[i] = value;
			if (i + 1 < arr.Length) arr&#91;i + 1&#93; = temp;
		}
	}

	/// <summary>
	/// LCG Random Number Generator.
	/// LCG: http://en.wikipedia.org/wiki/Linear_congruential_generator
	/// </summary>
	/// <param name="lastValue">The last value calculated by the lcg or a seed</param>
	/// <returns>A new random number</returns>
	private static uint lcgRandom(uint lastValue)
	{
		return (uint)((1103515245u * lastValue + 12345u) % 0x100000000u);
	}


	/// <summary>
	/// Constant used in FNV hash function.
	/// FNV hash: http://isthe.com/chongo/tech/comp/fnv/#FNV-source
	/// </summary>
	private const uint OFFSET_BASIS = 2166136261;
	/// <summary>
	/// Constant used in FNV hash function
	/// FNV hash: http://isthe.com/chongo/tech/comp/fnv/#FNV-source
	/// </summary>
	private const uint FNV_PRIME = 16777619;
	/// <summary>
	/// Hashes three integers into a single integer using FNV hash.
	/// FNV hash: http://isthe.com/chongo/tech/comp/fnv/#FNV-source
	/// </summary>
	/// <returns>hash value</returns>
	private static uint hash(uint i, uint j, uint k)
	{
		return (uint)((((((OFFSET_BASIS ^ (uint)i) * FNV_PRIME) ^ (uint)j) * FNV_PRIME) ^ (uint)k) * FNV_PRIME);
	}
}
	

Conclusion

I hope this post has provided you with an understanding of what cell noise is and how it can be efficiently implemented. If you are confused about anything above leave a comment or read Steven Worley’s paper on cell noise.