So last time, I had managed to add some code to create a height-map from a simplex noise function, using the space coordinates as input and getting a pseudo-random number between -1 and 1 as the output. For now I'm just coloring the vertices, black for lowest value and white for highest, but eventually I'd use the values to distort the sphere.
The only problem was that the noise function used a shuffled array to create the pattern of noise, so in order to create new patterns I had to reshuffle that array. Reshuffling the array presented a few problems, mostly caused by my reliance on the STL's rand() function, which is really quite terrible. As a result I had to search for a better function that could do the job I needed.
I eventually found an excellent paper on pseudo random number generators, by David Jones. I settled for the JLKISS64 generator, and after altering it to fit the framework I managed to create multiple height maps on demand. The problem, then, was how to display them.
Currently, my method for displaying models is to create a Vertex Buffer Array for each model, and a custom transform for each object. The different spheres where really all the same array with different matrices applied. But the height map was created as an additional coloring at each vertex, meaning I had to create new Arrays that still used the same buffers for vertex position, normal and color, but different heights.
This was fairly straightforward, thankfully. Essentially, the process involved the same steps as creating a vertex array from scratch, but skipping the buffer step, since the information had already been loaded to the video card.
In short order I had the new code running, and the space that was once filled with the same patterned spheres now had unique marbles floating all about.
Next up, I'm going to be trying to get text into the program, create a console of sorts.
Kian's Projects
Monday, January 16, 2012
Thursday, January 12, 2012
Holiday Hiatus Over
Game releases, coder's block and family obligations kept me pretty busy the past few weeks. However that is all behind us now, and I'm back in business. I've even done some progress, and should have a new demo on Monday. Nothing special, except that each and every asteroid is now unique. I'm looking into how to get writing in the game, now. That might not make it into the next release, but should fit in the following one for sure.
Monday, November 21, 2011
Tearing my hair out
Ok, I fixed the issue I was having. I'm not really sure what the issue was, but it appears to be gone after a few modifications to the offending code. I can't really say I'm happy about the outcome. I haven't really changed anything fundamental, and I don't even know what the problem was in the first place.
Since I'm sure you're riveted to your seats, wondering what the bug was, I'll tell you. Maybe you can tell me what the cause was. When I changed the path of the program, it crashed. That is, the compiled program would work in the directory I compiled it to, no issues. But change the name of the directory the executable was in, or copy the directory to a different location (keeping the relative paths of the resources intact) and it would fail. Madness.
I did do a couple of things in the program that weren't very nice. I had a nested for loop and I needed to keep track of how many times I run the inner loop, so I declared a variable outside of the loop and incremented it inside the loop, as an index into a dynamically allocated array, then used it again outside after increasing it a couple of times.
The big change I did was not to use it outside the loop. My best guess is I was incrementing it by one more than the size of the array outside the loop, and overwriting some bit of memory that I shouldn't have been messing with. How that could possibly cause the program to work in the compilation directory but not when I changed it's name, I may never know. But then, that's what happens when you play with arrays, and one of the reason to avoid them when possible.
Unfortunately, OpenGL calls want you to give them raw pointers to arrays. I am doing well enough using smart pointers instead, but until I wrap them up in safer structures, some dangers remain. I should look into using vector, I think it might help. I'm just not sure I can get the pointer to the memory, or that vector has to pack the information in a certain manner. Arrays are ugly, but at least I have control over the packing of the bits in there, and that's what I need.
Now that it's working, however, I'm can get to working on something more important. Currently I'm using a noise function that unfortunately I can't easily reinitialize. Basically, I need to add a 'shuffle' function to rearrange the values in an array, which serves as the seed of the noise generator. Shouldn't be too difficult.
EDIT - Curse you, Skyrim! I'm afraid I got slightly sidetracked this past week. Good thing this scheduling thing works.
Since I'm sure you're riveted to your seats, wondering what the bug was, I'll tell you. Maybe you can tell me what the cause was. When I changed the path of the program, it crashed. That is, the compiled program would work in the directory I compiled it to, no issues. But change the name of the directory the executable was in, or copy the directory to a different location (keeping the relative paths of the resources intact) and it would fail. Madness.
I did do a couple of things in the program that weren't very nice. I had a nested for loop and I needed to keep track of how many times I run the inner loop, so I declared a variable outside of the loop and incremented it inside the loop, as an index into a dynamically allocated array, then used it again outside after increasing it a couple of times.
The big change I did was not to use it outside the loop. My best guess is I was incrementing it by one more than the size of the array outside the loop, and overwriting some bit of memory that I shouldn't have been messing with. How that could possibly cause the program to work in the compilation directory but not when I changed it's name, I may never know. But then, that's what happens when you play with arrays, and one of the reason to avoid them when possible.
Unfortunately, OpenGL calls want you to give them raw pointers to arrays. I am doing well enough using smart pointers instead, but until I wrap them up in safer structures, some dangers remain. I should look into using vector, I think it might help. I'm just not sure I can get the pointer to the memory, or that vector has to pack the information in a certain manner. Arrays are ugly, but at least I have control over the packing of the bits in there, and that's what I need.
Now that it's working, however, I'm can get to working on something more important. Currently I'm using a noise function that unfortunately I can't easily reinitialize. Basically, I need to add a 'shuffle' function to rearrange the values in an array, which serves as the seed of the noise generator. Shouldn't be too difficult.
EDIT - Curse you, Skyrim! I'm afraid I got slightly sidetracked this past week. Good thing this scheduling thing works.
Monday, November 14, 2011
Giving shape to the world
I finally have an interesting playground to experiment in, so it's about time I focused on what I wanted to do in the first place. The main drive behind this project was procedural content, making use of algorithms to populate the world with variety instead of a fixed number of precomputed models. I've already been doing that, in fact. The distribution of spheres in the demo, for example, and their size, is all deterministically decided by the program at run time.
All I do is decide how many spheres I want, what the range of sizes should be, and the volume of space to use. The program then fills the space. Because the starting conditions are always the same, the program always builds the same scene. I could easily create a different distribution by changing the seed or any of the parameters. I want to go further, though. Right now, all these spheres are kind of boring. And not very asteroid like. To change this, I'll deform the spheres to create more interesting shapes.
My initial plan is to assign each sphere a height map. I'll then use the height map to raise or lower away from the center of the sphere each vertex. One difficulty I expect is how to adjust the normals as the terrain is reshaped, but I'll figure something out. On the height map end, I'll use the simplex noise algorithms I've talked about before to give each asteroid a distinct look. By seeding each asteroid with a distinct value, I ensure they'll all be unique.
For the height map, I only need it to match one pixel to a vertex. This way I can send the value to the vertex shader as an attribute. I already created the spheres using a gridlike pattern, so I can use the uv coordinates of each pixel for this purpose. This does mean that there will be more detail near the poles that along the equator, but because my source is going to be a 3d density function it should not result in any obvious distortions. The output will go into a Vertex Buffer Object, which will be fed to the video card and called when drawing.
Normally, height maps add a height to points on a grid, which is level with either the XZ or XY planes, meaning Z or Y is treated as height, and that value is replaced with the value of the height map. However, I have a sphere. On a sphere, height is defined as the distance from the center. To apply the height map to a sphere, I have to work a bit more.
The values I have will fall between -1 and 1. If I do straight multiplication of the surface point by the value, I'd have a mess. Instead, what I do is decide what I want the range of heights to be. If I want the highest points to jut out three times the radius of the sphere, for example, and the deepest valleys to reach half way to the center of the sphere, that gives me a range of (3 - 0.5 ) = 2.5 radius. The half point (where the height map is 0) would be 1.25 radius, so I could get the position of every point as ( (1+height)*1.25 + 0.5) * position.
The other complex bit is recalculating the normals. By changing the shape of the sphere, the surface normals all change as well and that impacts the light on each point. If I don't get them right, the lighting will look decidedly odd.
That's getting ahead of me a bit. First I need to be sure I can produce the information and get it into the shader. I had to add an extra attribute to the Model class to do it, but after a few short tweaks I got it up and running. What I'm doing right now is using the height value of each point as a color value, with -1 being black and 1 being white. So higher areas will look brighter.
It's working, and I'm pretty happy about that. I'm now struggling with an odd problem; when running the project from a pendrive it works, but copying the project to the hard drive and running it there doesn't. Fixing that is probably going to give me enough for another post itself.
All I do is decide how many spheres I want, what the range of sizes should be, and the volume of space to use. The program then fills the space. Because the starting conditions are always the same, the program always builds the same scene. I could easily create a different distribution by changing the seed or any of the parameters. I want to go further, though. Right now, all these spheres are kind of boring. And not very asteroid like. To change this, I'll deform the spheres to create more interesting shapes.
My initial plan is to assign each sphere a height map. I'll then use the height map to raise or lower away from the center of the sphere each vertex. One difficulty I expect is how to adjust the normals as the terrain is reshaped, but I'll figure something out. On the height map end, I'll use the simplex noise algorithms I've talked about before to give each asteroid a distinct look. By seeding each asteroid with a distinct value, I ensure they'll all be unique.
A sample of simplex noise |
For the height map, I only need it to match one pixel to a vertex. This way I can send the value to the vertex shader as an attribute. I already created the spheres using a gridlike pattern, so I can use the uv coordinates of each pixel for this purpose. This does mean that there will be more detail near the poles that along the equator, but because my source is going to be a 3d density function it should not result in any obvious distortions. The output will go into a Vertex Buffer Object, which will be fed to the video card and called when drawing.
Normally, height maps add a height to points on a grid, which is level with either the XZ or XY planes, meaning Z or Y is treated as height, and that value is replaced with the value of the height map. However, I have a sphere. On a sphere, height is defined as the distance from the center. To apply the height map to a sphere, I have to work a bit more.
The values I have will fall between -1 and 1. If I do straight multiplication of the surface point by the value, I'd have a mess. Instead, what I do is decide what I want the range of heights to be. If I want the highest points to jut out three times the radius of the sphere, for example, and the deepest valleys to reach half way to the center of the sphere, that gives me a range of (3 - 0.5 ) = 2.5 radius. The half point (where the height map is 0) would be 1.25 radius, so I could get the position of every point as ( (1+height)*1.25 + 0.5) * position.
The other complex bit is recalculating the normals. By changing the shape of the sphere, the surface normals all change as well and that impacts the light on each point. If I don't get them right, the lighting will look decidedly odd.
That's getting ahead of me a bit. First I need to be sure I can produce the information and get it into the shader. I had to add an extra attribute to the Model class to do it, but after a few short tweaks I got it up and running. What I'm doing right now is using the height value of each point as a color value, with -1 being black and 1 being white. So higher areas will look brighter.
Looks nicer than blue with red and green dots. |
It's working, and I'm pretty happy about that. I'm now struggling with an odd problem; when running the project from a pendrive it works, but copying the project to the hard drive and running it there doesn't. Fixing that is probably going to give me enough for another post itself.
Monday, November 7, 2011
Looking good. Looking real good.
Well, that was quite a bit of wait between updates, but lighting is done. Sort of.
It's helpful for me to see what I need to do to flesh the shaders out. Later on I'll need to add textures, and shadows, and multiple sources of light. The nice thing about space shooters, though, is that you generally only have one big source of light to worry about. Certainly, there is plenty of room for lighting effects all over, but what's going to be illuminating the planets, asteroids and ships for the most part is the big star in the middle.
A concern I'm having is that as I add features, the code is getting a more unwieldy. For the most part I've avoided a serious case of spaghetification, but I really should spend some time consolidating certain pieces of code. Before I do that, however, I want to have a better feel for what works and what doesn't in my design.
I've already identified some decisions that weren't the best. For example, my use of inheritance would in many cases be better suited to templates. I wanted to avoid templates, because they are somewhat arcane in their workings, but already my use of the standard and boost libraries has me working with them. I feel more confident now about using them, and it would simplify some things.
This means I'm going to have to do a bit of a rewrite of several big chunks. Rewrites aren't fun, but on the positive side I'm not going to be starting from zero. That seems like a good opportunity to reassess and redesign, so I want to get a bit more experience in before I undertake that particular task. It's a fragile balance I have to manage between having enough features in there that I know how to design it better and having so much that redesigning it is too daunting to do.
It doesn't help that work has picked up and I'm generally too burned out to focus on this when I get home. That's also part of why I've let the update schedule slip, for which I apologize.
In the interest of making things easier for everyone, I've switched back from 7z to zip files to share the program. 7z was a necessity when I was sharing the program through gmail. Mail programs don't like zipped executable files, and having people rename a file from .jpg to .exe to get around it was more trouble than convincing them to use 7zip. But with the files now hosted in Google Docs, I can go use zip files again.
As always, the folder is Framework Releases, and the project itself is Framework-0.0.8.
It's helpful for me to see what I need to do to flesh the shaders out. Later on I'll need to add textures, and shadows, and multiple sources of light. The nice thing about space shooters, though, is that you generally only have one big source of light to worry about. Certainly, there is plenty of room for lighting effects all over, but what's going to be illuminating the planets, asteroids and ships for the most part is the big star in the middle.
A concern I'm having is that as I add features, the code is getting a more unwieldy. For the most part I've avoided a serious case of spaghetification, but I really should spend some time consolidating certain pieces of code. Before I do that, however, I want to have a better feel for what works and what doesn't in my design.
I've already identified some decisions that weren't the best. For example, my use of inheritance would in many cases be better suited to templates. I wanted to avoid templates, because they are somewhat arcane in their workings, but already my use of the standard and boost libraries has me working with them. I feel more confident now about using them, and it would simplify some things.
This means I'm going to have to do a bit of a rewrite of several big chunks. Rewrites aren't fun, but on the positive side I'm not going to be starting from zero. That seems like a good opportunity to reassess and redesign, so I want to get a bit more experience in before I undertake that particular task. It's a fragile balance I have to manage between having enough features in there that I know how to design it better and having so much that redesigning it is too daunting to do.
It doesn't help that work has picked up and I'm generally too burned out to focus on this when I get home. That's also part of why I've let the update schedule slip, for which I apologize.
In the interest of making things easier for everyone, I've switched back from 7z to zip files to share the program. 7z was a necessity when I was sharing the program through gmail. Mail programs don't like zipped executable files, and having people rename a file from .jpg to .exe to get around it was more trouble than convincing them to use 7zip. But with the files now hosted in Google Docs, I can go use zip files again.
As always, the folder is Framework Releases, and the project itself is Framework-0.0.8.
Saturday, November 5, 2011
An apology and an explanation
I've spent the past week wrangling with an annoying bug in the code. Turned out to be a simple mistake, but since the lighting code was all new I had to go over it with a fine-toothed comb until I figured out what was wrong. Fixing it was simple enough, but it took me a while. It didn't help that I had a particularly busy month.
As the days passed and it seemed like I would miss my deadline, I had to make a choice. Either release what I had, knowing it was buggy, or let the week pass. I had the post already done, and I could fix the demo in the meantime. I had already slipped into posting on Wednesdays while doing the lighting bit, and I didn't want to completely miss the week.
However, the philosophy of releasing for a deadline, not when it is working, is one of the things I dislike about the programming industry. Seeing as this is something I'm doing for myself, I want it to be done right and follow my own ideals.
One of these ideals is releasing with no known bugs. No program more complex than 'Hello World' is ever bug free, but it's one thing for your users to find bugs, and a very different thing for you to release a bug you know about into the wild. Releasing with the expectation of a patch later is the reason why so much stuff works as badly as it does.
The downside is that if I'm planning to make a release and just missing the demo, a bug that takes too long to fix (an amount of time fundamentally unpredictable) will throw my schedule off and leave me with nothing to post. I do have to apologize for that. I should work on building a buffer of some sort, although that's a bit difficult as what I usually do is write about what I'm doing at the time.
On the other hand, planning what I'll work on with more time can only help avoid these issues.
The bug is fixed, the demo is working, and I'm now working on the focus for the next part. I have scheduled the post with the explanation of the demo to go up at 9:00 GMT on Monday, so as to go back to my regular release day. In the meantime, the demo is up in Google Docs already.
As always, the folder is Framework Releases, and the project itself is Framework-0.0.8. I switched to zip files to make things simpler for everyone.
As the days passed and it seemed like I would miss my deadline, I had to make a choice. Either release what I had, knowing it was buggy, or let the week pass. I had the post already done, and I could fix the demo in the meantime. I had already slipped into posting on Wednesdays while doing the lighting bit, and I didn't want to completely miss the week.
Really didn't want to be tardy |
One of these ideals is releasing with no known bugs. No program more complex than 'Hello World' is ever bug free, but it's one thing for your users to find bugs, and a very different thing for you to release a bug you know about into the wild. Releasing with the expectation of a patch later is the reason why so much stuff works as badly as it does.
The downside is that if I'm planning to make a release and just missing the demo, a bug that takes too long to fix (an amount of time fundamentally unpredictable) will throw my schedule off and leave me with nothing to post. I do have to apologize for that. I should work on building a buffer of some sort, although that's a bit difficult as what I usually do is write about what I'm doing at the time.
On the other hand, planning what I'll work on with more time can only help avoid these issues.
The bug is fixed, the demo is working, and I'm now working on the focus for the next part. I have scheduled the post with the explanation of the demo to go up at 9:00 GMT on Monday, so as to go back to my regular release day. In the meantime, the demo is up in Google Docs already.
As always, the folder is Framework Releases, and the project itself is Framework-0.0.8. I switched to zip files to make things simpler for everyone.
"I am NOT Tardy" Image by MidoriFlygon
Wednesday, October 26, 2011
Let there be light!
Now that I have a little playground with some basic physics, controls and the like, we can worry a little more about the downright dreadful appearance of the place. Everything looks flat, because I'm slapping solid colors with no shading at all.
Shading helps give a sense of depth, and even the most basic techniques would result in a much improved look and feel. Naturally then, we need to start thinking about lighting. What I'll be implementing for the following stretch is something called the ADS lighting model, which stands for Ambient, Diffuse and Specular.
According to this model, the color that we see an object have is the result of adding three contributions from the light that's lighting it up. These are the ambient light, the diffuse light and the specular light.
Ambient light is the result of light that has bounced several times between objects, with the result that there is no identifiable source. It results in a general brightening of an object, essentially a flat color boost to everything. If we only had ambient light, we'd get a picture much like what I already have.
Diffuse light is what produces the natural shading we are used to. This light comes from a source, and the angle between the source and the surface normal determines how brightly lit it is. A surface placed perpendicular to a light source will be illuminated more brightly than one placed at an angle, and as a result will have a brighter color. Surfaces parallel or looking away from the light source, on the other hand, are not illuminated at all.
Finally, specular light describes how shiny a surface is, creating the highlights caused by the light source. While Ambient and Difuse lighting depend solely on the position of the light source, Specular light also depends on the position of the observer.
Adding all these together would give the result we want. In order to add it I don't really have to mess much with the program code, which is fortunate. The heavy lifting is going to be done in the shader program. The actual program is just going to have to provide the necessary information.
Next week I should have a first implementation of it running.
What a solid red sphere would look like in the program |
According to this model, the color that we see an object have is the result of adding three contributions from the light that's lighting it up. These are the ambient light, the diffuse light and the specular light.
Ambient light is the result of light that has bounced several times between objects, with the result that there is no identifiable source. It results in a general brightening of an object, essentially a flat color boost to everything. If we only had ambient light, we'd get a picture much like what I already have.
Ambient light illuminating a red sphere |
Diffuse light. Paint doesn't have a gradient tool. Use your imagination. |
Finally, specular light describes how shiny a surface is, creating the highlights caused by the light source. While Ambient and Difuse lighting depend solely on the position of the light source, Specular light also depends on the position of the observer.
Specular lighting is highly focused. As the observer moves, the apparent position of the highlight can also move. |
Adding all these together would give the result we want. In order to add it I don't really have to mess much with the program code, which is fortunate. The heavy lifting is going to be done in the shader program. The actual program is just going to have to provide the necessary information.
Next week I should have a first implementation of it running.
Subscribe to:
Posts (Atom)