Jump to content
  • Advertisement

Low poly terrain

thecheeselover

3513 views

Subscribe to our subreddit to get all the updates from the team!

Recently I've been tackling with more organic low poly terrains. The default way of creating indices for a 3D geometry is the following (credits) :

Image result for opengl shared vertices

 

A way to create simple differences that makes the geometry slightly more complicated and thus more organic is to vertically swap the indices of each adjacent quad. In other words, each adjacent quad to a centered quad is its vertical mirror.

MirrorIndices.thumb.png.adfc5555cdd3e9bff0595a830f893eb8.png

 

Finally, by not sharing the vertices and hence by creating two triangles per quad, this is the result with a coherent noise generator (joise) :

It is called flat shading.



15 Comments


Recommended Comments

I did something similar to this using a ridged-multifractal but I was subdividing roughly equilateral triangles because I was generating a planet based on an icosahedron.  It looked pretty cool from a distance but after ran to the top of mountains I noticed this problem.....When I generated a ridge line, the top would sometimes end up being jagged, like the teeth of a saw, depending on which way the ridge was running relative to the mesh. Subdividing further just made the saw-teeth smaller so it didn't really fix anything.  The way I solved it was as a final subdivision  I would check the values at each corner and the values at their midpoints of the edges and and do a special custom final subdivision based on this information.  If you run into this problem you could try it. 

Share this comment


Link to comment
10 hours ago, Gnollrunner said:

I did something similar to this using a ridged-multifractal but I was subdividing roughly equilateral triangles because I was generating a planet based on an icosahedron.  It looked pretty cool from a distance but after ran to the top of mountains I noticed this problem.....When I generated a ridge line, the top would sometimes end up being jagged, like the teeth of a saw, depending on which way the ridge was running relative to the mesh. Subdividing further just made the saw-teeth smaller so it didn't really fix anything.  The way I solved it was as a final subdivision  I would check the values at each corner and the values at their midpoints of the edges and and do a special custom final subdivision based on this information.  If you run into this problem you could try it. 

It's quite complicated, at least it seems so with words and the paragraph being in English. Any chance of you making a blog post about this so I could understand it more?

Share this comment


Link to comment

This terrain looks amazing. Fantastic work.

I am wondering how you will be resolving collisions for this type of terrain?

Share this comment


Link to comment
2 minutes ago, Scouting Ninja said:

This terrain looks amazing. Fantastic work.

I am wondering how you will be resolving collisions for this type of terrain?

Thanks! :)

 

Hmmm heightmap collision detection already exists, not with the heightmap geometry itself but with the heightmap data. However, it's true that it's for smoother terrain. I guess if you could use that algorithm and modify it to lerp correcly according to the geometry, it would work like a charm.

 

Also, if you have a lot of memory and CPU to spare, you could create triangles for the physics engine for each triangle in the geometry.

Share this comment


Link to comment
54 minutes ago, thecheeselover said:

it to lerp correcly according to the geometry, it would work like a charm.

I would really like to see the implementation when you do it. I do wonder if a character will be able to move safely on the terrain.

I would be really grateful if you can share your finds.

Share this comment


Link to comment
1 hour ago, thecheeselover said:

It's quite complicated, at least it seems so with words and the paragraph being in English. Any chance of you making a blog post about this so I could understand it more?

Here's what I was talking about, but now that I think about it It may not be such a big problem for you since you are using more of a standard mesh than I was.  If I remember when you subdivide, you split the triangles into two halfs and so you can have edges at any 45 degree angle if you subdivide enough.  I was subdividing into 4 parts to keep triangles equilateral.  However on the the final subdivision I had to do something special.  I've kind of abandoned height maps now and have gone to voxels so it's not a big deal any more.

Ridgeline problem.jpg

Share this comment


Link to comment

You should use sinus to get rid of the spikes.

I made terrain the same, with adjustable height only per vertex, not position, the texture will look a bit stretched.

I also made the collision code for the 2 triangles ( note that directX might handle this different then others with different triangle rotation ).

Try making a plane with only 1 vertex point higher then you can see 1 flat triangle and 1 not flat.

Share this comment


Link to comment
1 hour ago, thecheeselover said:

 

Also, if you have a lot of memory and CPU to spare, you could create triangles for the physics engine for each triangle in the geometry.

With my old code I implemented JIT (Just in Time) Terrain for the physics. It' worked pretty well and I simply did ball to mesh collision.  I had to write it myself because I used the quadtree that the fractal functions placed everything in at run time.  I figure for any real game you will need it anyway because you need to hit trees and stuff.  Here's a very old screenshot. I just shaded it with a raw simplex nose that's implemented in HLSL. As such it has horrible aliasing problems, but I kind of fixed it later by playing with the amplitude as you go far away from the camera.  In any case this world was about 1000km in diameter and you could run all over it with these three collision balls, which where were a stand-in for a character....... Not that there was much to see since there was no real shading yet, but it was kind of cool that you could never hit a wall so you could go forever.  It did have a moon and the sun though, and everything orbited. I'm recording all this stuff now for my voxel engine.

NorthPoll-1.JPG

Share this comment


Link to comment
1 hour ago, Scouting Ninja said:

I would really like to see the implementation when you do it. I do wonder if a character will be able to move safely on the terrain.

I would be really grateful if you can share your finds.

I'll add this to my long term todo list.

 

17 minutes ago, the incredible smoker said:

You should use sinus to get rid of the spikes.

I made terrain the same, with adjustable height only per vertex, not position, the texture will look a bit stretched.

I also made the collision code for the 2 triangles ( note that directX might handle this different then others with different triangle rotation ).

Try making a plane with only 1 vertex point higher then you can see 1 flat triangle and 1 not flat.

It was intended to look like a crumpled piece of paper but thanks for the tip. By your last sentence, do you mean 1 shared vertex of a plane that is made of two triangles?

19 minutes ago, Gnollrunner said:

With my old code I implemented JIT (Just in Time) Terrain for the physics.

Do you mean that you added a physics library through JIT or that you created triangles for the terrain's physics on the fly?

 

Nice by the way :)

Share this comment


Link to comment
8 minutes ago, thecheeselover said:

Do you mean that you added a physics library through JIT or that you created triangles for the terrain's physics on the fly?

 

JIT Terrain is just what I called it. It's written in C++ and Direct X.  The thing is, there is no way to store planet sized mesh data on disk, it's just too big. So all the terrain is generated as you move by the fractal functions.  There are actually 2 different sets of data. One for the graphics and one for the physics. The reason is that the graphics model can update a lot slower, say every second or so. Note this is not the frame rate, which is fast because there was not much GPU stuff.  It just means the model of the terrain (i.e. Level of Detail).  If I tried to use it for collision there is the possibly that if something lags you would be building terrain under your feet which is bad news for collision. On the other hand I can update the physics terrain just right around the player and not worry about stuff that he can't touch, so I can do it very fast.   The way it's written there are no race conditions.  It has to update the terrain before you get there, so yes the collision is safe, albeit slightly complex to implement.  There is a system of nested bounding spheres.

Share this comment


Link to comment
5 minutes ago, Gnollrunner said:

On the other hand I can update the physics terrain just right around the player and not worry about stuff that he can't touch, so I can do it very fast.

Cool, I actually never thought that games could do this in a performant way.

Share this comment


Link to comment
2 minutes ago, thecheeselover said:

Cool, I actually never thought that games could do this in a performant way.

I'm not sure which games if any use run-time procedural generation. I assume some do, perhaps No Man's Sky, however from what I gather the planets aren't that large so maybe not.  But there are a lot of space games in the works and some may use it. 

Share this comment


Link to comment

@ Cheeselover : no, just that all planes exist of 2 triangles,

once you have the collision you can add plants and trees.

All very basic stuff

 

Make a GetY( x,y ); function to get the ground position to add things.

Also make a horizontal GetCollision function for if you going to add walls.

Make all level blocks in zones and handle zone changing, so you can have fast collision algorithm

remember to not add items you can grab close to the zone borders, items are also added per zone.

 

Also you can add a water plane for pools.

That is how i made the most basic thing.

 

Making a edittor for this also aint difficult.

I wanto improve it also with the ability to add bridges, very nice.

Only i gave up on all this ( i dont wanto demotivate you ),

it cost to much work while i like to release a simple game first, maybe later i will rewrite everything.

Share this comment


Link to comment

Here is the collision code to get the ground level ( y position )

You have to make 4 lists of weedfields :

Normal weedfield is totally flat, all vertices has the same Y position.

WeedfieldX is exactly the same Y position for all left vertices, also for all right vertices.

WeedfieldY is exactly the same Y position for all front vertices and same for all back vertices.

WeedfieldZ has 3 or 4 vertices with different Y positions.

Once you have this sorted you can use this code :

 

	
//-----------------------------------------------------------------------------------------
float LevelMesh::getGroundLevel( float x , float z )
{
// WeedField ground level
sWeedField*nextwf = firstwf;
while( nextwf )
     {
     if( x >= nextwf->l && x <= nextwf->r &&
         z >= nextwf->f && z <= nextwf->b )
       {
       return nextwf->y;
       }
     
     nextwf = nextwf->next;
     }
	// WeedFieldX ground level
sWeedFieldX*nextwfx = firstwfx;
while( nextwfx )
     {
     if( x >= nextwfx->l && x <= nextwfx->r &&
         z >= nextwfx->f && z <= nextwfx->b )
       {
       float fTempBalance = ( ( x - nextwfx->r ) - ( nextwfx->l - nextwfx->r ) ) * nextwfx->fAbsRScale;
       return XFade( nextwfx->y1 , nextwfx->y2 , fTempBalance );
       }
     
     nextwfx = nextwfx->next;
     }
	// WeedFieldY ground level
sWeedFieldY*nextwfy = firstwfy;
while( nextwfy )
     {
     if( x >= nextwfy->l && x <= nextwfy->r &&
         z >= nextwfy->f && z <= nextwfy->b )
       {
       float fTempBalance = ( ( z - nextwfy->b ) - ( nextwfy->f - nextwfy->b ) ) * nextwfy->fAbsBScale;
       return XFade( nextwfy->y1 , nextwfy->y2 , fTempBalance );
       }
     
     nextwfy = nextwfy->next;
     }
	// WeedFieldZ ground level
sWeedFieldZ*nextwfz = firstwfz;
while( nextwfz )
     {
     if( x >= nextwfz->l && x <= nextwfz->r &&
         z >= nextwfz->f && z <= nextwfz->b )
       {
       float fTempBalanceFB = ( ( z - nextwfz->b ) - ( nextwfz->f - nextwfz->b ) ) * nextwfz->fAbsBScale;
       float fTempBalanceLR = ( ( x - nextwfz->r ) - ( nextwfz->l - nextwfz->r ) ) * nextwfz->fAbsRScale;
       
       if( fTempBalanceFB > fTempBalanceLR ) // Driehoek 1
         {
         float y1 = nextwfz->y3 - ( nextwfz->y4 - nextwfz->y2 );
         
         float fXFadeLFB = XFade( nextwfz->y2 , nextwfz->y4 , fTempBalanceFB );
         float fXFadeRFB = XFade( y1 , nextwfz->y3 , fTempBalanceFB );
         
         return XFade( fXFadeLFB , fXFadeRFB , fTempBalanceLR );
         }else // Driehoek 2
         {
         float y4 = nextwfz->y2 - ( nextwfz->y1 - nextwfz->y3 );
         
         float fXFadeFLR = XFade( nextwfz->y2 , nextwfz->y1 , fTempBalanceLR );
         float fXFadeBLR = XFade( y4 , nextwfz->y3 , fTempBalanceLR );
         
         return XFade( fXFadeFLR, fXFadeBLR , fTempBalanceFB );
         }
       
       }
     
     nextwfz = nextwfz->next;
     }
	return fEndOfLevel; // end of level
}
	 
	

Xfade is something like a + fade * ( b - a );

Driehoek = dutch for triangle.

Share this comment


Link to comment
3 hours ago, the incredible smoker said:

Only i gave up on all this ( i dont wanto demotivate you ),

it cost to much work while i like to release a simple game first, maybe later i will rewrite everything.

This is actually an old blog post from me that was reordered because of a problem with gamedev.net. I'm a actually making a game with a friend. Even though it is ambitious, we try to simplify it down so it can be achieved with the both of us.

 

We're not trying to make an engine, just a rogue-lite procedural vaporwave game. Consider looking at our subreddit if you're interested.

 

Anyway, generation wise, we're doing ok but thank you for the tips :)

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • Blog Entries

  • Similar Content

    • By blesseddisciple
      So I have a decent amount of JavaScript experience now and decided I was gonna lower my head and start cranking out some 2d games, partly to learn, partly to have fun. Afterall, HTML5 canvas is such an easy and enticing medium. I love the JavaScript implementation of it. But after literally struggling for a week to get basic game functionality working I have had enough of the little stupid bugs that pop up with JavaScript. Don't get me wrong, I still love the language for scripting. I'm just not going to spend 20 mins coding and 5 hours debugging just because the language is crap.
      I've decided to return to my previous endeavor, Java. I like Java a lot and the only reason I haven't pursued more in the way of game development is just for the fact that Java is limited to mobile or PC apps that may never see the light of day unless it's hosted on some obscure Java game hosting website that is populated with 2,000 half developed games that no one will ever care about. BUT, still, I enjoy hand coding and I know C# but don't feel like using Visual studio and I really don't wanna hand code C# on the .Net or whatever. I use Visual Studio for business apps (ASP.NET) but I don't wanna build a game with it.
      So, does anyone have any points to share about why moving to Java for game development is not smart? Besides the whole, "Java is slow" thing. I mean things that might make it harder in JAva to make games vs. in other languages. Please share your thoughts. 
    • By komires
      We are pleased to announce the release of Matali Physics 4.4. The latest version introduces comprehensive support for Android 9.0 Pie, iOS 12.x and macOS Mojave (version 10.14.x). Latest version also introduces Matali Render 3.4 add-on with normal mapping and parallax mapping based on the distance from the observer as well as other improvements and fixes.
      What is Matali Physics?
      Matali Physics is an advanced, multi-platform, high-performance 3d physics engine intended for games, virtual reality and physics-based simulations. Matali Physics and add-ons form physics environment which provides complex physical simulation and physics-based modeling of objects both real and imagined.
      Main benefits of using Matali Physics:
       Stable, high-performance solution supplied together with the rich set of add-ons for all major mobile and desktop platforms (both 32 and 64 bit)  Advanced samples ready to use in your own games  New features on request  Dedicated technical support  Regular updates and fixes
      You can find out more information on www.mataliphysics.com

      View full story
    • By komires
      We are pleased to announce the release of Matali Physics 4.4. The latest version introduces comprehensive support for Android 9.0 Pie, iOS 12.x and macOS Mojave (version 10.14.x). Latest version also introduces Matali Render 3.4 add-on with normal mapping and parallax mapping based on the distance from the observer as well as other improvements and fixes.
      What is Matali Physics?
      Matali Physics is an advanced, multi-platform, high-performance 3d physics engine intended for games, virtual reality and physics-based simulations. Matali Physics and add-ons form physics environment which provides complex physical simulation and physics-based modeling of objects both real and imagined.
      Main benefits of using Matali Physics:
       Stable, high-performance solution supplied together with the rich set of add-ons for all major mobile and desktop platforms (both 32 and 64 bit)  Advanced samples ready to use in your own games  New features on request  Dedicated technical support  Regular updates and fixes
      You can find out more information on www.mataliphysics.com
    • By TheMode
      Before starting: I'm looking for a Java developer who want to help me improving my game engine and then, create a game that I will describe 
      My goal is to create a "fight arena" multiplayer game similar to xblaster (only how they managed arena, I do not want robot stuff) 
      For people who do not know this game, let me explain how I'm inspired by it
      You log in the game, you can enter an arena at any time, in the arena, there are 4 portals where you can go and enter another arena, there can be a maximum of 4players in the same arena, they have to fight each other in order to get money to improve their characters. 
      I won't describe it any longer, I've much more ideas about the game. 
      I already done the server architecture, I have a Game Engine (the client side), but there are still things to do on it, that's why I'm looking for another developer to help me if the game also look interesting for you
      Here the version of the engine:
      https://github.com/TheMode911/ProneusEngineV2/blob/master/src/main/java/fr/proneus/engine/demo/DemoState.java
      Discord: TheMode#3487
    • By babaliaris
      Hello!
      My texture problems just don't want to stop keep coming...
      After a lot of discussions here with you guys, I've learned a lot about textures and digital images and I fixed my bugs. But right now I'm making an animation system and this happened.
       
      Now if you see, the first animation (bomb) is ok. But the second and the third (which are arrows changing direction) are being render weird (They get the GL_REPEAT effect).
      In order to be sure, I only rendered (without using my animation system or anything else i created in my project, just using simple opengl rendering code) the textures that are causing this issue and this is the result (all these textures have exactly 115x93 resolution)

      I will attach all the images which I'm using.
      giphy-27 and giphy-28 are rendering just fine.
      All the others not.They give me an effect like GL_REPEAT which I use in my code. This is why I'm getting this result? But my texture coordinates are inside the range of -1 and 1 so why?
      My Texture Code:
      #include "Texture.h" #include "STB_IMAGE/stb_image.h" #include "GLCall.h" #include "EngineError.h" #include "Logger.h" Texture::Texture(std::string path, int unit) { //Try to load the image. unsigned char *data = stbi_load(path.c_str(), &m_width, &m_height, &m_channels, 0); //Image loaded successfully. if (data) { //Generate the texture and bind it. GLCall(glGenTextures(1, &m_id)); GLCall(glActiveTexture(GL_TEXTURE0 + unit)); GLCall(glBindTexture(GL_TEXTURE_2D, m_id)); //Not Transparent texture. if (m_channels == 3) { GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_width, m_height, 0, GL_RGB, GL_UNSIGNED_BYTE, data)); } //Transparent texture. else if (m_channels == 4) { GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data)); } //This image is not supported. else { std::string err = "The Image: " + path; err += " , is using " + m_channels; err += " channels which are not supported."; throw VampEngine::EngineError(err); } //Texture Filters. GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)); GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)); GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST)); GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); //Generate mipmaps. GLCall(glGenerateMipmap(GL_TEXTURE_2D)); } //Loading Failed. else throw VampEngine::EngineError("There was an error loading image \ (Myabe the image format is not supported): " + path); //Unbind the texture. GLCall(glBindTexture(GL_TEXTURE_2D, 0)); //Free the image data. stbi_image_free(data); } Texture::~Texture() { GLCall(glDeleteTextures(1, &m_id)); } void Texture::Bind(int unit) { GLCall(glActiveTexture(GL_TEXTURE0 + unit)); GLCall(glBindTexture(GL_TEXTURE_2D, m_id)); }  
      My Render Code:
      #include "Renderer.h" #include "glcall.h" #include "shader.h" Renderer::Renderer() { //Vertices. float vertices[] = { //Positions Texture Coordinates. 0.0f, 0.0f, 0.0f, 0.0f, //Left Bottom. 0.0f, 1.0f, 0.0f, 1.0f, //Left Top. 1.0f, 1.0f, 1.0f, 1.0f, //Right Top. 1.0f, 0.0f, 1.0f, 0.0f //Right Bottom. }; //Indices. unsigned int indices[] = { 0, 1, 2, //Left Up Triangle. 0, 3, 2 //Right Down Triangle. }; //Create and bind a Vertex Array. GLCall(glGenVertexArrays(1, &VAO)); GLCall(glBindVertexArray(VAO)); //Create and bind a Vertex Buffer. GLCall(glGenBuffers(1, &VBO)); GLCall(glBindBuffer(GL_ARRAY_BUFFER, VBO)); //Create and bind an Index Buffer. GLCall(glGenBuffers(1, &EBO)); GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)); //Transfer the data to the VBO and EBO. GLCall(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW)); GLCall(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW)); //Enable and create the attribute for both Positions and Texture Coordinates. GLCall(glEnableVertexAttribArray(0)); GLCall(glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void *)0)); //Create the shader program. m_shader = new Shader("Shaders/sprite_vertex.glsl", "Shaders/sprite_fragment.glsl"); } Renderer::~Renderer() { //Clean Up. GLCall(glDeleteVertexArrays(1, &VAO)); GLCall(glDeleteBuffers(1, &VBO)); GLCall(glDeleteBuffers(1, &EBO)); delete m_shader; } void Renderer::RenderElements(glm::mat4 model) { //Create the projection matrix. glm::mat4 proj = glm::ortho(0.0f, 600.0f, 600.0f, 0.0f, -1.0f, 1.0f); //Set the texture unit to be used. m_shader->SetUniform1i("diffuse", 0); //Set the transformation matrices. m_shader->SetUniformMat4f("model", model); m_shader->SetUniformMat4f("proj", proj); //Draw Call. GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL)); }  
      Vertex Shader:
      #version 330 core layout(location = 0) in vec4 aData; uniform mat4 model; uniform mat4 proj; out vec2 TexCoord; void main() { gl_Position = proj * model * vec4(aData.xy, 0.0f, 1.0); TexCoord = aData.zw; }  
      Fragment Shader:
      #version 330 core out vec4 Color; in vec2 TexCoord; uniform sampler2D diffuse; void main() { Color = texture(diffuse, TexCoord); }  






×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!