Thread: 2D Fractal Terrain

  1. #1
    Registered User
    Join Date
    Jan 2010
    Posts
    1

    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....

  2. #2
    Registered User C_ntua's Avatar
    Join Date
    Jun 2008
    Posts
    1,853
    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
    Code:
    i<=points.size()-2
    so the max value is points.size() -2. Why would you need to check
    Code:
    i >= points.size()-1
    and break??

  3. #3
    Registered User
    Join Date
    Apr 2009
    Posts
    45
    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.

  4. #4
    Registered User
    Join Date
    Apr 2009
    Posts
    45
    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
    Last edited by Flaug; 01-11-2010 at 07:44 PM.

  5. #5
    Me
    Join Date
    Jul 2006
    Posts
    71
    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.
    Last edited by relyt_123; 01-11-2010 at 10:48 PM.

  6. #6
    Registered User
    Join Date
    Apr 2009
    Posts
    45

    Talking

    That would help... Thanks for pointing that out. That explains a lot.
    The output still looks pretty crappy though.

  7. #7
    Registered User rogster001's Avatar
    Join Date
    Aug 2006
    Location
    Liverpool UK
    Posts
    1,472
    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
    Last edited by rogster001; 01-12-2010 at 02:02 PM.

  8. #8
    Registered User
    Join Date
    Apr 2009
    Posts
    45
    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...
    Last edited by Flaug; 01-12-2010 at 06:22 PM.

  9. #9
    Registered User rogster001's Avatar
    Join Date
    Aug 2006
    Location
    Liverpool UK
    Posts
    1,472
    perhaps your starting line segment values can be altered then? to give more scope for variation?

  10. #10
    Registered User
    Join Date
    Apr 2009
    Posts
    45
    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!

  11. #11
    Registered User
    Join Date
    Apr 2009
    Posts
    45
    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
    Last edited by Flaug; 01-14-2010 at 04:37 PM.

  12. #12
    Registered User rogster001's Avatar
    Join Date
    Aug 2006
    Location
    Liverpool UK
    Posts
    1,472
    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?

  13. #13
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,332
    I kinda liked the first output. Reminds me of Coyote and Roadrunner.
    Mainframe assembler programmer by trade. C coder when I can.

  14. #14
    Registered User
    Join Date
    Apr 2009
    Posts
    45
    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.

  15. #15
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    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.
    Last edited by VirtualAce; 01-15-2010 at 02:00 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Generating random 2D mountains/terrain
    By Flaug in forum C++ Programming
    Replies: 7
    Last Post: 04-12-2009, 02:49 PM
  2. Continous LOD Terrain Source... Released!
    By Perspective in forum Game Programming
    Replies: 13
    Last Post: 04-17-2006, 11:21 PM
  3. New terrain engine
    By VirtualAce in forum Game Programming
    Replies: 16
    Last Post: 03-16-2006, 02:47 AM
  4. Terrain algos
    By VirtualAce in forum Game Programming
    Replies: 1
    Last Post: 04-10-2004, 02:50 PM
  5. OpenGL terrain demo (EDITOR)
    By Jeremy G in forum Game Programming
    Replies: 2
    Last Post: 03-30-2003, 08:11 PM