170 likes | 500 Views
Fractal Terrain Generation . Fault Formation. Algorithm – Fault Formation . Starting with an empty height field Draw a random line through it with an offset value dHeight on one side of the line. Keep drawing new lines which decreases the dHeight. Linearly Decreasing.
E N D
Fractal Terrain Generation Fault Formation
Algorithm – Fault Formation • Starting with an empty height field • Draw a random line through it with an offset value dHeight on one side of the line. • Keep drawing new lines which decreases the dHeight
Linearly Decreasing • Linear decrease in the dHeight in each iteration. • Value of dHeight at ith iteration: dHeight_i=dHeight_0+(i/n)(dHeight_n-dHeight_0).
How to Generate Random Lines • l – line created with two points p1 and p2. • vp – vector in the direction p1->p2. • 0-point in the height field. • V0- vector in the direction p1->0 • Vx = vp × v0, vx.z>0 point o on left side, if vx.z<0 point o lies on the other side of the line, if vx.z equals 0 , point o is on the line.
Erosions • Fault formation creates : difference in neighboring cells height fields. • Low or high iterations will result in unrealistic terrain. • Solution: Pass the height field through a low pass image filter.
Converts the sequence of x_0,x_1,……x_n to y_0,y_1,…y_n Y_i=ky_i-1+(1-k)x_i, k is the filtering constant lies between 0 and 1. Low k means low erosion, high k is more erosion. Apply the filter across the rows and coloumns on both side results a nice landscape. FIR- Finite Impulse Response
4.18 Fractal Terrain Generation-Midpoint Displacement Jason Shankel One Dimension Take midpoint C, where C is between -dHeight/2 and +dHeight/2 Recursively generate additional stages until an adequate detail level is achieved. At every iteration multiply dHeight by 2-r . r is a roughness factor. r>1 generates smoother terrain
Simulates mountains ranges like the Rockies. Midpoint Displacement in Two Dimensions – Diamond Square
When using this algorithm a square height field with width of 2n is recommended, so that the rectangle will still have integer values at each iteration.
4.19 Fractal Terrain Generation – Particle DepositionJason Shankel • Simulates volcanic mountain ranges. Drop random particles to build height field
Creating the caldera (volcanic crater) • Invert the mountain peak • Invert the height field values above a certain altitude about the horizontal plane defined by that altitude. If there are multiple peaks, then the caldera line for one peak can interfere with other peaks • A flood fill technique can be used to implement the caldera inversion process • Start from an initial point, invert the point and check its neighbors • For each neighbor above the line. Invert it and check all its neighbors. • Continue until there are no more neighbors
4.16 Real-Time Realistic Terrain Generation • A grid based terrain can be easily generated by simply assigning each grid space a random value. This, however, can result in a very unnatural appearance. • Smoothing algorithms can be applied to decrease the unnatural appearance created by purely random value assignments. Below: Pattern immergence in a grid populated with a poorly designed random function. Below: Terrain smoothed with grid smoothing algorithm.
for ( int square_size = width; square_size > 1; square_size /= 2) { int random_range = square_size; for ( int x1 = row_offset; x1 < width; x1 += square_size ) { for ( int y1 = row_offset; y1 < width; y1 += square_size ) { // Calculate the four corner offsets int x2 = (x1 + square_size) % width; int y2 = (y1 + square_size) % width; // Get the values int i1 = this->terrain[x1][y1]; int i2 = this->terrain[x2][y1]; int i3 = this->terrain[x1][y2]; int i4 = this->terrain[x2][y2]; // Create wieghted averages int p1 = ((i1 * 9) + (i2 * 3) + (i3 * 3) + (i4)) / 16; int p2 = ((i1 * 3) + (i2 * 9) + (i3) + (i4 * 3)) / 16; int p3 = ((i1 * 3) + (i2) + (i3 * 9) + (i4 * 3)) / 16; int p4 = ((i1) + (i2 * 3) + (i3 * 3) + (i4 * 9)) / 16; // Calculate the center points of each quadrant int x3 = (x1 + square_size/4) % width; int y3 = (y1 + square_size/4) & width; x2 = (x3 + square_size/2) % width; y2 = (y3 + square_size/2) % width; // Set the points to the averages calculated above this->terrain[x3][y3] = p1; this->terrain[x2][y3] = p2; this->terrain[x3][y2] = p3; this->terrain[x2][y2] = p4; } } // For the next row, move in slightly row_offset = square_size/4; } step = 4; for y = 0; y < 100; y = y + step { for x = 0; x < 100; x = x + step { total = 0; for y_local = y; y_local <= y + step; y_local = y_local + 1 { for x_local = x; x_local <= x + step; x_local = x_local + 1 { total = total + map[x_local,y_local]; } } average = total / (step * step); for y_local = y; y_local <= y + step; y_local = y_local + 1 { for x_local = x; x_local <= x + stepp; x_local = x_local +1 { map[x_local,y_local] = average; } } } } Below: Grid Smoothing algorithm. Divide the terrain into larger grid spaces, sum up the values of the spaces inside a particular grid and then set all spaces inside to the average value for that grid section. Right: Corner Sampling Smoothing algorithm. A modification of the Grid Smoothing algorithm. For a given grid take the average of the corner spaces and set only the center spaces for each quarter to that average.
Random lines: A different approach to populating a grid with random values. Select two grid points and then draw a line in the grid from one point to the other. To vary the values make each grid value based on a function using distance from current point to one of the end points. i.e. Sine function. A smoothing algorithm can then be applied. do { this->terrain[(int)x_start][(int)y_start] = nCurrentRandomValue; x_start = x_start + x_diff; y_start = y_start + y_diff; // Apply a sine function oscillating between 0 and 255 // The sin function should be called with from // -pi/2 to pi/2 if (x_diff < y_diff) { nCurrentRandomValue = (sin(x_start) * 128) + 128; } else { nCurrentRandomValue = (sin(y_start) * 128) + 128; } } while (((y_start < (float)this->terrain_width) && (y_start > 0.0)) && ((x_start < (float)this->terrain_height) && (x_start > 0.0)));
Random Generation of Cityscape Simple cityscapes can be modeled by subdivision of a grid. First randomly draw horizontal and vertical lines that span across the whole grid. Then find the most narrow paths (least distance between any two vertical or horizontal lines) to be marked as streets. Non-street areas to be treated as buildings. To generate rooms inside one of these buildings first draw a random horizontal line to cut the building in half, then subdivide each half with a random vertical line, and continue cut each new half up to a desired room size. Doors could then be randomly placed on walls to allow a player to navigate between rooms as well as from buildings to streets.
void AddLetters( char * szWord, unsigned long ulTable[28][28]) { int nWordLength, nFirstLetter, nLastLetter, nLetter; // Decapitalise the word for (nLetter = 0; nLetter < (int) strlen(szWord)-1;nLetter++) tolower(szWord[nLetter]); // Add the first, and last to the table nWordLength = (int)strlen(szWord); nFirstLetter = (szWord[0] – ‘a’) + 1; nLastLetter = (szWordLength-1] – ‘a’) + 1; ulTable[0][nFirstLetter]++; // Space followed by letter ulTable[nLastLetter][27]++; // Letter follow by space for (nLetter = 0; nLetter < nWordLength-2; nLetter++) { nFirstLetter = (szWord[nLetter] – ‘a’) + 1; nLastLetter = (szWord[nLetter+1] – ‘a’) + 1; ulTable[nFirstLetter][nLastLetter]++; } } Random Name Generation The simplest algorithm to generate names would be to simply assign a random letter to each letter space in a word, however such simple generation would lead to unrealistic words such as “ddtwmmq”. A more sophisticated approach is to generate a heuristic for pairings of letters, such that for a given letter the next letter to be generated would be based on the current letter. This requires first generating a heuristic and then the actual creation of random words. To create the heuristic a series of example words must be used to seed the heuristic creator. Each word is parsed into the heuristic creator and updates the heuristic with what letters follow a given letter. This is done with a 28x28 grid of integers. Rows and columns from 1 to 27 represent letters a through z. The 0 row/column and 27th row/column represent the space before a word and the space after, respectively. The heuristic is read as follows: for a given letter x there is 26 possible letters to follow it where heuristic[x][y] is the weighted value of that pair. To randomly generate a word one would select a letter from heuristic[0][y] and then the next letter would be heuristic[x][y] where x was the last letter generated. The ending letter should satisfy two cases, such that it is in heuristic[x][y] and that any letter from that set would satisfy heuristic[x][0]. These two cases check to make certain the last letter is both a letter that would follow the current letter and also a letter that would end a word. int GetLetterPosition(unsigned long ulWordTable[28][28], int nPrevious) { int nCounter; unsigned long ulFrequencyTotal, ulFrequencyRunningTotal, ulRandomLetter; ulFrequencyTotal = 0; // Get the frequencies for (nCounter = 1; nCounter < 27; nCounter++) { ulFrequencyTotal = ulFrequencyTotal + ulWordTable[nPrevious][nCounter]; } // Choose a ‘target’ frequency ulRandomLetter = rand() % (ulFrequencyTotal); // Move through the table until we hit the ‘target’ frequency ulFrequencyRuuningTotal = 0; do { ulFrequencyRunningTotal = ulFrequencyRunningTotal + ulWordTable[nPrevious][nCounter]; nCounter++; } while (ulFrequnecyRunningTotal < ulRandomLetter); return nCounter; }
word[0] = GetLetterPosition(word_table,0); x = 1; while (x < 6) { word[x] = ((GetLetterPosition(word_table,word[x-1] - ’a’)) – 1) + ‘a’; x = x + 1; } int GetEndLetter (unsigned long ulWordTable[28][28], int nPrevious) { int nCounter; unsigned long ulFrequencyAdjacentTotal, ulFrequencyRunningTotal, ulRandomLetter, ulFrequencyEndingTotal; ulFrequencyAdjacentTotal = 0; ulFrequencyEndingTotal = 0; // Get the frequencies for (nCounter = 1; nCounter < 27; nCounter++) { ulFrequencyAdjacentTotal = ulFrequencyAdjacentTotal + ulWordTable[nPrevious][nCounter]; ulFrequencyEndingTotal = ulFrequencyEndingTotal + ulWordTable[27][nCounter]; } // choose a ‘target’ frequency ulRandomLetter = rand() % ulFrequencyAdjacentTotal; // Move through the table until we hit the ‘target’ frequency ulFrequencyRunningTotal = 0; do { ulFrequencyRunningTotal = ulFrequencyRunningTotal + ulWordTable[nPrevious][nCounter]; nCounter++; if (ulFrequncyEndingTotal > 0) if((ulFrequencyRunningTotal >= ulRandomLetter) && (ulWordTable[27][nCounter] != 0)) break; else if(ulFrequencyRunningTotal >= ulRandomLetter) break; } while (true); return nCounter; } An improvement to be made would be to check for multiples of the same letters in a row and omit them. The code snippet below modifies the above code to do so. word[0] = GetLetterPosition(word_table,0); x = 1; while(x < 6) { word[x] = ((GetLetterPosition(word_table,word[x-1] – ‘a’)) -1) + ‘a’; x = RemoveChain(word,x); } int RemoveChain( char word[MAX_LENGTH], int letter_position ) { int nPos = 0; int nOccurences = 0; while (nPos < strlen(word)) { if (word[nPos] == word[letter_position]) nOccurences++; } if (nOccurnences > 2) return letter_position – 1; return letter_position + 1; }