-
2D Fractal Terrain
Hello-
I'm trying to generate some random fractal terrain, but what I've come up with looks horrible. I'm basing my terrain generation method off of this: Generating Random Fractal Terrain
Here is my code:
Code:
//January 10, 2010
#include <windows.h>
#include <ddraw.h>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <string>
#include <iostream>
#include <vector>
HDC _hOffscreenDC;
HBITMAP _hOffscreenBitmap;
void BuildSky(HDC hDC, HWND hWnd, RECT windowrect)
{
int endx = windowrect.right;
int endy = windowrect.bottom;
int startx = 0;
int starty = 0;
float r1 = 0;
float g1 = 0;
float b1 = 10;
float r2 = 0;
float g2 = 2;
float b2 = 30;
float rs = r2/endy;
float gs = g2/endy;
float bs = b2/endy;
for (int i=0; i<=endy; i++)
{
//rs = r2/endy;
//gs = g2/endy;
//bs = b2/endy;
r1 = r1 + rs;
g1 = g1 + gs;
b1 = b1 + bs;
HPEN pen = CreatePen(PS_SOLID, 1, RGB(r1, g1, b1));
SelectObject(hDC, pen);
MoveToEx(hDC, 0, i, NULL);
LineTo(hDC, endx, i);
SelectObject(hDC, pen);
DeleteObject(pen);
}
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
using namespace std;
struct Point
{
int x,y;
};
//Point points(5);
vector<Point> points;
void SetupMountains(RECT windowrect)
{
Point p;
/*for(int i=1; i<=20; i++)
{
p.x = i*25;
p.y = rand() % 150 + 50;
points.push_back(p);
}*/
p.x = 0;
p.y = 0;
points.push_back(p);
p.x = 1080;
p.y = 0;
points.push_back(p);
}
void Complex()
{
if (points.size()-1 >= 1000) //Quit so that it doesn't freeze!
return;
int tx,ty,tx1,ty1,midx,midy,miny;
float Complexity = 1;
Point tp;
float range = 1;
int mass = 500;
if(true) //(points.size() > 2)
{
for(int i=0; i<=points.size()-2; i++)
{
tx = points[i].x;
ty = points[i].y;
tx1 = points[i+1].x;
ty1 = points[i+1].x;
midx = (tx + tx1)/2; //Midpoint of x
midy = (ty + ty1)/2; //Midpoitn of y
miny = ty; //Minimum point of y
tp.x = midx;
tp.y = miny + (rand() % (int)floor(range*mass*2+1)); //- range*mass*0.5; //Displace the y value by a random amount
if(abs(tp.y - miny) > (mass*range*0.7)) //Smoothing?
tp.y = tp.y * 0.4;
points.insert(points.begin()+i+1,tp); //Break the segment into two. (aka Add a new point in between)
range *= 0.3; //Deplete the range by half
//if(range <= 0.001)
// range = 0.001;
i += 1;
if(i >= points.size()-1)
break;
}
} else {
int i=0;
tx = points[i].x;
ty = points[i].y;
tx1 = points[i+1].x;
ty1 = points[i+1].x;
midx = (tx + tx1)/2;
midy = (ty + ty1)/2;
miny = ty;
tp.x = midx;
tp.y = miny + rand() % (int)(range*mass) + range*mass;
points.insert(points.begin()+i+1,tp);
//range = range*0.7;
}
}
void DrawMountains(HDC hDC, HWND hWnd, RECT windowrect)
{
HPEN pen = CreatePen(PS_SOLID, 1, RGB(255,20,20));
SelectObject(hDC, pen);
for(int i=0; i<=points.size()-2; i++)
{
MoveToEx(hDC, points[i].x, 1080 - points[i].y, NULL);
LineTo(hDC, points[i+1].x, 1080 - points[i+1].y);
}
for(int i=0; i<=points.size()-1; i++)//Mark points or elbows of mountain
{
SetPixel(hDC, points[i].x, 1080 - points[i].y, RGB(255,255,100));
}
SelectObject(hDC, pen);
DeleteObject(pen);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT nMsg, WPARAM wParam,
LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPreInst,
LPSTR lpszCmdLine, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASSEX wc;
//fill the WNDCLASSEX structure with the appropriate values
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW; //CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon(NULL, IDI_EXCLAMATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = "wndClass";
wc.hIconSm = LoadIcon(NULL, IDI_EXCLAMATION);
//register the new class
RegisterClassEx(&wc);
//create a window
hWnd = CreateWindowEx(
NULL,
"wndClass",
"Fractal Terrain",
WS_POPUP | WS_VISIBLE,
0, //Position x
0, //Position y
1920, //Size x
1080, //Size y
NULL,
NULL,
hInst,
NULL
);
//event loop - handle all messages
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//standard return value
return (msg.wParam);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT nMsg, WPARAM wParam,
LPARAM lParam)
{
HDC hDC;
HBRUSH brush;
RECT windowrect;
srand((int)time(0));
switch(nMsg)
{
case WM_CREATE:
hDC = GetDC(hWnd);
GetClientRect(hWnd, &windowrect);
srand(time(0));
SetCursor(NULL);
SetTimer(hWnd, 1, 250, NULL);
SetupMountains(windowrect);
ReleaseDC(hWnd, hDC);
break;
case WM_PAINT:
PAINTSTRUCT ps;
hDC = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &windowrect);
//BuildEarth(hDC, hWnd, windowrect);
DrawMountains(hDC, hWnd, windowrect);
EndPaint(hWnd, &ps);
break;
case WM_MOUSEMOVE:
SetCursor(NULL);
break;
case WM_TIMER:
hDC = GetDC(hWnd);
brush = CreateSolidBrush(RGB(0,10,25));
GetClientRect(hWnd, &windowrect);
RECT temp;
temp.left = 0;
temp.top = 0;
temp.right = windowrect.right;
temp.bottom = windowrect.bottom;
_hOffscreenDC = CreateCompatibleDC(GetDC(hWnd));
_hOffscreenBitmap = CreateCompatibleBitmap(GetDC(hWnd),windowrect.right,windowrect.bottom);
SelectObject(_hOffscreenDC, _hOffscreenBitmap);
BuildSky(_hOffscreenDC, hWnd, windowrect);
DrawMountains(_hOffscreenDC, hWnd, windowrect);
BitBlt(hDC, 0, 0, windowrect.right, windowrect.bottom, _hOffscreenDC, 0, 0, SRCCOPY);
DeleteObject(_hOffscreenBitmap);
DeleteDC(_hOffscreenDC);
SelectObject(hDC, brush);
DeleteObject(brush);
ReleaseDC(hWnd, hDC);
break;
case WM_DESTROY:
KillTimer(hWnd, 1);
PostQuitMessage(0);
break;
case WM_KEYUP:
switch (wParam)
{
case VK_ESCAPE:
//destroy the timer
KillTimer(hWnd, 1);
//end the program
PostQuitMessage(0);
break;
case VK_UP:
Complex(); //Make the terrain more complex
break;
}
break;
default:
//let Windows handle every other message
return(DefWindowProc(hWnd, nMsg, wParam, lParam));
}
return 0;
}
The main thing that needs to be focused on is the Complex() function....
Can anyone help me make the terrain look a little more realistic?
Thanks
My resolution is set at 1920 x 1080, so you might have to play around with the settings....
-
As an advice, most people don't read large codes. Thankefully, some blessed ones. But you should try to post small codes. Its ok, since you hint people to look at the Complex() function. But reading it you have something like:
Code:
if(true) {
...
i += 1;
if(i >= points.size()-1)
break;
}
} else {
From the code someone can understand that you changed it, that is why you left if(true), but why add more code and discourage people when you post it? I suggest you edit.
The i += 1 is really not needed. You could put i+=2 on the for loop if you wanted this. I guess you have put this in order to comment and uncomment it. But still, it doesn't look nice.
The if part above, is not needed at all. You will check for
so the max value is points.size() -2. Why would you need to check
Code:
i >= points.size()-1
and break??
-
Thanks for the reply!
I'll try to work on the stuff you pointed out.
A lot of that is the result of editing and not removing excess code...
I wasn't sure if I should post just the function or the whole code so that people can run the program. I now see that I should have just posted the function. I'll edit the rest of the code out in a minute.
Still, if anyone wants the code in its entirety to test the program, just ask me.
Optimizing my code is good, but my problem remains. I still need help with my so-called "algorithm" to generate random fractal terrain. If anyone has experience in this area, that would be great.
-
Turns out, I can't edit it? I'll post just the code snippet that I need help with.
The problem with it is the same as stated above, that said, here it is:
Code:
using namespace std;
struct Point
{
int x,y;
};
//Point points(5);
vector<Point> points;
void SetupMountains(RECT windowrect) //Setup the first two points (of the fractal terrain) to make a line
{
Point p;
p.x = 0;
p.y = 0;
points.push_back(p);
p.x = 1080;
p.y = 0;
points.push_back(p);
}
void Complex() ////////////////////////////////////////////////!!!!!!!!!!!!!!!!! I need help with this function! My algorithm makes the terrain look horrible. I need it to look more realistic! Help?
{
if (points.size()-1 >= 1000) //Quit so that it doesn't freeze!
return;
int tx,ty,tx1,ty1,midx,midy,miny;
float Complexity = 1;
Point tp;
float range = 1;
int mass = 500;
for(int i=0; i<=points.size()-2; i+=2)
{
tx = points[i].x;
ty = points[i].y;
tx1 = points[i+1].x;
ty1 = points[i+1].x;
midx = (tx + tx1)/2; //Midpoint of x
midy = (ty + ty1)/2; //Midpoitn of y
miny = ty; //Minimum point of y
tp.x = midx;
tp.y = miny + (rand() % (int)floor(range*mass*2+1)); //- range*mass*0.5; //Displace the y value by a random amount
if(abs(tp.y - miny) > (mass*range*0.7)) //Smoothing?
tp.y = tp.y * 0.4;
points.insert(points.begin()+i+1,tp); //Break the segment into two. (aka Add a new point in between)
range *= 0.3; //Deplete the range by half
//if(range <= 0.001)
// range = 0.001;
}
}
This is what the output of the program looks like (after running the Complex() function several times):
Program Output
-
One thing I see very quickly which would screw it up royally is this:
Code:
...
for(int i=0; i<=points.size()-2; i+=2)
{
tx = points[i].x;
ty = points[i].y;
tx1 = points[i+1].x;
ty1 = points[i+1].x;
...
The line in red. You're setting ty1 equal to the x value from points[i+1], rather than the y value.
-
That would help... Thanks for pointing that out. That explains a lot.
The output still looks pretty crappy though.
-
This type of fractal is for me another spin on the chaos game, you have set your range to be halved, that is why it is less complex, according to the article it should be smaller for more variation
-
Thank you for pointing that out... But I have already changed the range around a lot.
When the range is set to deplete at about .20 the fractal just turns into a spikey mess.... when the range is set to deplete at maybe .80 it just gets flat-ish...
-
perhaps your starting line segment values can be altered then? to give more scope for variation?
-
Whoops! Turns out there were two reasons why it wasn't working.
#1 - I kept resetting the value of "range" and other important variables whenever the "Complex()" function was ran. To fix this, I just declared the variables outside the function so that their value wouldn't get reset and that made the program work.
#2 - range *= 0.5 should have been outside of the "for" loop.
Now the program works, but still looks a little weird... I'm much happier with it though.
If you want to see the full source, just ask and I'll supply the new version.
I could still use some help in improving my algorithm to make the "terrain" look better...
Thank you all for helping!
-
Here is what the output of the program looks like:
Mountain
And here is a nicer photo with a filled in mountain and a sky gradient:
Filled in mountain
-
hehe, its nice when you spot some simple changes like that to fix a problem, that output looks really good there, next step i suppose is to render the mountain area, or perhaps move on to the actual diamond/square step algorithm and do a wireframe. this is the introductorymidpoint-displacement one?
-
I kinda liked the first output. Reminds me of Coyote and Roadrunner.
-
Yes, this is the midpoint-displacement one.... I would like to dive into the diamond/square algorithm as well, but I think I'm going to work on a fill function for the mountain first like you mentioned.
-
You do not displace the y value by a totally random amount nor by a constant or your terrain will not look right. You displace the y values by a random value within a range that continually decreases with each LOD.
This 'range' determines the bumpiness or smoothness of the terrain. Virtual Terrain Project has tons of links to examples and other algorithms. Diamond-square cannot be done in 2D. The midpoint displacement algo can be extended to 3D quite easily and diamond-square uses the midpoint-displacement algorithm for the square portion of the algo.
However one can just as easily generate a greyscale bitmap with other algorithms such as the fault algorithm which also generates nice terrains. You can also take a random noise texture in GIMP,smooth it a bit, and then bump map it to get a very nice grey scale terrain.