Saturday, October 23, 2010

Texture select added

I can't believe it took me this long to finally decide to implement the more important parts of Voxelcraft. But now I can use multiple textures with Voxelcraft.


The pictures you see on the bottom left show which textures for each face are being used for the current cube. All of the textures are packed into a single file called the "texture atlas". The user can use the QWERTY keys to change the textures for each face. When one of those keys are pressed, a picture of the entire texture atlas appears, and the user presses the arrow keys to choose the texture.

  • Q - Front
  • W - Back
  • E - Left
  • R - Right
  • T - Up
  • Y - Down
Also, the user can press the U key to assign one texture to all sides of the cube. Very handy.

One of the problems I've run into was that the Wrap address mode for texture sampling didn't work as I thought. I thought I could wrap a portion of a texture (I'm such a noob at this :-P). So I couldn't wrap tiles anymore. Sucks. I had to think of an alternative. And I've come up with several:

I  could probably get away with using multiple vertices to emulate wrapping. But this would be very expensive if I wanted to, say, draw a 100x100 plane. That'd be 10,000 vertices (20000 primitives)! That's a lot more compared to 4 vertices (2 primitives), which I used to get before I found out wrapping wouldn't work.

So I thought I could separate the textures into different texture files. And then for each texture, I would draw each cube face that corresponds to that. And I'd switch to the next texture for the next draw call. But this'll amount to too many draw calls. I'd like to keep the number of draw calls low.

Eventually, I settled with what I thought was a really crappy method of wrapping. But it works. Originally, I had a texture atlas sized 64x64, each texture being 8x8. I decided since my textures are so simple, I could expand my texture atlas to 512x512, and have each texture that's supposed to wrap repeat itself 8 times both ways! And when I resize my cube, it'll refer to the next 8x8 tile over it, emulating a tiling effect.

This is loosely based off my "many vertices" idea, except not as many vertices. Because each wrapped texture repeats 8 times, I'll have to make more cubes to expand the wrapping effect. Going back to my 100x100 plane scenario, that'd be about 13 smaller planes both ways, and because the smaller planes aren't really connected to each other and are using it's own vertices, that would be 676 vertices (338 primitives), but it's SO MUCH better than the previous 10,000 vertices solution.


I also ran into the problem of flickering lines at the edges of each cube that has another cube adjacent to it. I'm pretty sure even professionals run into this problem a lot, but I have the problem worse because Voxelcraft is based on cubes. Not even POINT sampling will save me, which I was using in the first place.

If I couldn't solve the problem, I decided I'd hide it. I made a 256x256 image of repeating cubes.


Each cube represents a "pixel". The effect I'm getting from applying this to my textures is that each "pixel"  in the texture atlas kind of sticks out. It's similar to 3D Dot Game Hero's style of art. I was trying to avoid this so I could have a cleaner sort of look, but it looks this is the only way to solve this problem. 

I noticed that there were some strange artifacts at certain distances where this texture overlay would form strange shapes of it's own (if you can see it in the picture up top). "Duh!" I thought. "I haven't made use of mip-mapping!" Honestly, I thought mip-mapping didn't make much of a difference in graphics. But I was SO wrong. Mip-mapping cleans up a lot of aliasing, and it can make scenes render faster, since it refers to a smaller texture within the texture's mip-map "chain".

I should probably use mip-mapping for my actual texture atlas, but I hear it's impossible to do without merging pixels from neighboring textures, but I could probably just add a border instead.

Monday, October 18, 2010

War of the 300 Errors

Tonight, I dine in hell.

Today, I decided I wanted to get with the times and upgrade to the new XNA 4.0. For these reasons:

  • It's so much easier to use. I hate having to write so much code for the simplest of things.
  • I noticed while I was implementing the moon that the glow was fading to black around the edges, even though it was white when I made it. It's because alpha wasn't pre-multiplied, apparently (or something to that extent), and XNA 4.0 does the alpha correctly. I could've written a custom importer to help me with this while using XNA 3.1, but I'd rather not get into very complicated code right now.
  • XNA Creators Club won't accept 3.1 games after a while. They will only accept 4.0.
  • Reach/HiDef profiles. One's used for low-end systems (Reach), and the other for high end systems (HiDef). I can use Reach if I want to develop a game using a "limited API", which will make it easy for me to develop for older, less powerful computers. Or, I can use HiDef for the full API, and make higher quality games for XBox 360 or a powerful PC. I plan on using both, if possible.
  • I can develop for Windows Phone 7. Don't know how that would work out, but it'll be a neat experiment, at the very least.
So I got all the new stuff now, and converted Voxelcraft to use the XNA 4.0, and I started the program... only to be faced with 300 errors. As it turns out, the XNA crew did a lot of changes to the new framework. Some of these changes are very convenient, I like the new replacements for RenderState, and now I'm just able to set vertices and indices and draw them, and I don't have to deal with VertexDeclaration. They also took out a couple of things, though, most of which are no big deal (as far as I'm concerned), and since they've made so many changes to how stuff works in XNA now, that brought up those 300 errors. Their errors have blocked out the sun...

Then I shall code in the shade. Just me against the Microsoft Empire. THIS.... IS....

...not as bad as I thought. I've already gotten rid of the errors, most were pretty simple. I ended up having to take out normal maps (for cubes, not ocean) and I never use those normal maps anyways. It wasn't getting the tangent for vertices for some reason, but it always worked for XNA 3.1. But I'm glad the new XNA picked up on this error, because it's possible if someone tried to use this program on another computer, it would crash. 

Most things are working, except for water. They took out ClipPlane, which I needed for reflection/refraction. This should be easy to fix, though. I could probably use the clip() intrinsic in the HLSL code to replace it.

Saturday, October 16, 2010

Ocean

Bit.Trip Runner is probably the happiest game I've ever played. Seriously, what other game have you played where you run around collecting gold while leaving behind freaking rainbows in an atari-like world? Not that it has anything to do with this post, of course.

But implementing oceans into my engine was so much easier than I thought. It took me one day to do it. Probably because I was following Riemer's tutorials, and I suggest that you do the same thing (www.riemers.net)  I figured that the water he's done was simple enough for me to implement quickly, and just the right type of water for this type of project.

Think of water as a plane. A HUGE plane that stretches over the playing field of the game... with waves and reflections and refractions and stuff. Sounds complicated, but as far as geometry goes, it's just a single plane.

I didn't have any wave maps on hand, so I started with mirroring and basic dirty color. Dirty color is what we'll call the base color of the ocean.


At first, when you want mirror, all you need is a "reflection map". To create the reflection map, you first must create the clipping plane. The plane must be a horizontal plane set a little bit below the ocean. There should be a little offset, because once waves are implemented, it may reflect incorrectly. Next, you render your scene as normal with everything below the clipping plane culled away. But instead of rendering the scene from the view of the camera, you must render from below the ocean. To do this, you take the camera's y coordinate, and flip it over the ocean. For example, if it's y coordinate is 4 and the ocean height is 1, the reflected y coordinate is -2.

By the way, if you want to save a couple of milliseconds, you could probably get away with making the reflection map 1/2 or 1/4 the size of the screen, so it would render faster. This will result in a pixelated reflection, but blurring can be applied to the map afterwards.

Then you take the reflection map, and in the ocean shader make it sample from the map using the reflected camera's view matrix as projected onto the screen.

Adding the dirty color is easy. Simply add it into the color you get from the reflection map.

Of course, what's water without waves?


I thought I was finished at this point, since this requires making normal maps, but then I found a Normal Map plug-in for GIMP here (http://registry.gimp.org/node/69) By rendering a tiled turbulence cloud in GIMP, followed by using the normal map plugin, I got this picture:


If you're wondering what a normal map is, a normal map contains data for which way light will reflect. The rgb values of the picture represents xyz values of rays going from point (0, 0, 0) to (x, y, z). These values are in the range of [-1, 1], but a picture contains values ranging from 0 to 255. So normal maps have 0 = -1, 127 = 0, and 255 = 1. This is why this image is mostly blue, by default a texture should reflect light forward (perpendicular from it's orientation), so that value is (0, 0, 1), with the normal map equivalent being (r = 127, g = 127, b = 255).

This normal map won't be used just for lighting, though. It's used also to displace pixels in the reflection map. To do that you would first find the color of the normal map at the specified coordinates. When you find that, you must get its "perturbation", or the offset, by using that color. Multiply by the height of the wave, and you have the offset of the reflection of the map. Now when you find the coordinates of the pixel to sample from in the reflection map, just add the perturbation to that coordinate.

All that's left is refraction.



To do this, I made a refraction map the same way I did with the reflection map, only this time I'm rendering from the camera's view and not the reflected camera's. I will also cull away everything above the water. I'll get the color of the refraction the same way I get the color of the reflection, too. But this time, I'll need to know how much of the reflection color and how much of the refraction color to use. Something called the Fresnel term will be used to find this out. To get the fresnel term, you find the dot product of the normal vector (from the wave normal map) and the camera-to-pixel vector. I get the correct color value by "lerping" between the refractive and reflective colors with the fresnel term.

Lerping's a weird word. But it's so useful. I also created a texture for the ocean to replace having a single dirty color. I sample the texture using the coordinates from the bump map, and blend that in as the "dirty color", by lerping the refract/reflect color and the dirty color with the alpha of the dirty color.


And so with that, and the specular highlight of the water, I'm done. I think I'll enjoy the pixelated look to my water texture. I still have the sun to add in, which I'll probably draw into my sky cylinder. Lens flare, maybe?

Thursday, October 7, 2010

Converting from Deferred to Light Pre-Pass, and Sky Cylinder creation

I'm sick. Not only sick of this sore throat I have, but also sick of these light blobs!

But I should probably introduce my program first before I talk about that. I've been developing a program called Voxelcraft for the Starhunter Engine. Voxelcraft creates worlds in cubes, which is simple enough for what I have in mind. The game I would make with it would be reminiscent of old-school games. I'll still be using many different present-time effects such as shadowing, high-dynamic range, depth blur, and such. It's being built with Microsoft XNA.




Previously, I had a deferred renderer working beautifully on my program. Deferred rendering works by seperating the colors, normals, depth, and any other information into different render targets. Combined, this would make up the G-Buffer (the G is for geometry!) I would use the normal and depth buffers to make a buffer that contains all lighting information. Then I would lay that on top of the color buffer, and call that the final render. There were some disadvantages, notably memory bandwidth, but the main advantage of this was that it is perfect for many small lights, because for normal rendering, your number of draws was (number of objects) * (number of lights). With deferred rendering, it's (buffer creation + number of lights).  I liked it, until the part where I had to add in the sky.

My game rendered like:

Render G-Buffer-----\
|  Color/Specular   |------------------------\
|  Normal/SpecPower |----->Light buffer-------> Final Scene
|  Depth            |----/
\-------------------/

I would render the sky as a cylinder. Then I would add that into the color buffer of my G-Buffer during the initialization of it, and then render everything else in. Whenever I moved close to any light, a random blob of light would move across the sky! This is coming from the light buffer. I never had this problem before I put in the sky. I think it had to do with how I was calculating the specular for everything. So I messed around in the lighting effect, and got nothing out of it.

There is a way to draw the sky much later by using the stencil buffer from the G-Buffer draw call. But this is very hard to do in XNA, because XNA clears its stencil buffer every time the render target has been switched! I hear I can fix this by having the render target "preserve contents", but that can't work on XBox 360, apparently. And with the new XNA 4.0, I'm even more screwed because the DepthStencilBuffer has been removed completely, and has been incorporated into RenderTarget2D instead!

So stenciling in the sky is not an option. But I've been looking into a method of lighting called "pre-pass lighting". (http://www.bungie.net/images/Inside/publications/siggraph/Engel/LightPrePass.ppt) It's basically like deferred rendering, but without the color buffer (which has been giving me problems). And since there's no color buffer, the process won't take up as much memory bandwidth for the GPU. I'd still render light into its own buffer, but I would apply it by re rendering the geometry instead. This way, it knows where it should use the light buffer. This gets rid of my "random light blob" problem because it's only been happening in the sky where there's no geometry being rendered.

So now this is my rendering:
----Render sky (without light buffer)-----\
                                          |         
Render-------------\                      |    
|  Normal/Specular |------> Light Buffer  |
|  Depth           |-----/       |        |
\------------------/             |--------/
                                 V
                          Re-render scene
                          geometry w/
                          Light Buffer


Now everything is working, and the random light blob doesn't appear! But I looked into the light buffer afterwards, and saw that the light blob doesn't appear at all, and I thought that it would anyways. Hmm, must have been something with the color buffer, because I was using that in the original light buffer creation to refer to specular. Oh well, either way, the problem's fixed, and I've implemented a lighting procedure that will (hopefully) run faster!




Creating the sky itself was easy. All it is is a cylinder with a texture of 2:1 ratio, and a bottom and top texture. I read about it on http://blogs.msdn.com/b/shawnhar/archive/2008/11/11/the-sky-s-the-limit.aspx. It works for me, because since I'm using sprites for all characters anyways. (and it's incredibly awkward to display a sprite from above anyways, it'll appear as flat). I rendered the geometry in code using Cos() and Sin() functions, instead of having a cylinder model. I'm pleased with it, but the resolution (512 x 256) is too small in the pic above, so I'll probably increase that to (1024 x 512).

Next, I'll work on sun and moon placement, and water. I want to work on high dynamic range (HDR) at some point, though.