Thursday, February 10, 2011

Senior Project Screenshots

Lately classes have been keeping me super busy, and as a result I haven't really had the time to post anything new in quite a while. But before another semester flies by without another post I thought I'd share some screenshots of our year-long group senior project that I've been working on.

The game is called Unchained, and it's a 3rd-person action adventure game where you play as the captain of an airship who has just inherited her father's shipping business. As you try to make shipping runs to further your profits and shipping empire, you are constantly confronted with challenges and obstacles: navigational challenges, airship system failures, and most of all, pirate attacks which you must fend off. The game is set in a steampunk-inspired world where airships have become the primary mode of travel.

A few of my responsibilities on the project (that you can actually see in the screenshots) are the 3D clouds with soft particles, silhouette outlining, and camera controls, although you can't really get a feel for the camera from just the screenshot. The silhouette outlining I already talked about in my previous post, and I'll talk about the cloud system with soft particles (along with code, of course!) when I finally have some spare time...which probably means it'll be awhile. =)




Thursday, September 23, 2010

Cel Shader

I've recently had to dabble in non-photorealistic rendering (NPR) techniques, specifically cel or toon shading and silhouette edge rendering, and thought I'd share a simple cel shader that I implemented.

Toon shading is a rendering style that gives everything a "cartoony" look. It can have a good deal of variation applied to it, but at its simplest it applies solid colors with distinct color boundaries to objects, akin to what you'd see in cartoons. The cartoon feel can be further enhanced by applying some type of black outlining around significant features of the object, i.e. some type of silhouette edge rendering.

Below is a composite screenshot taken from my demo showing the difference between an NPR-rendered and non-NPR-rendered teapot. The teapot on the left uses classic Phong shading, while the teapot on the right uses cel shading with silhouette edge rendering.


The cel shading is accomplished by first calculating classic Phong shading values, then mapping them to their final color based on the computed light intensity. My particular implementation uses an ambient layer, three diffuse layers, and one specular layer. To make the color transitions slightly smooth (instead of a hard boundary) we use the HLSL instinsic smoothstep which bleeds each subsequent layer into the previous one. There are other more efficient techniques for cel shading (with varying results) but this is what I chose to go with. The HLSL 4.0 pixel shader code is shown below.


// layerXStart: Percent of light intensity at which to start each diffuse layer. Subsequent layers
// should be greater than the previous ones. Valid range is [0,1].
static float layer1Start = 0.0;
static float layer2Start = 0.1;
static float layer3Start = 0.25;

// Range, at the beginning of each layer, to bleed into the previous one. Valid range is (0,1].
static float layerBleed = 0.025;

// layerXColor: Percent of diffuse material color for each diffuse layer. Valid range is [0,1].
static float layer1Color = 0.35;
static float layer2Color = 0.7;
static float layer3Color = 1.0;

// Specular modifiers have the same meanings as above.
static float specStart = 0.35;
static float specBleed = 0.2;
static float specColor = 0.7;

float4 PS_CelShade(PS_INPUT input) : SV_TARGET
{
input.normal = normalize(input.normal);
input.viewDir = normalize(input.viewDir);
vector reflection = reflect(lightDir * -1.0, input.normal);

float diffuseIntensity = saturate(dot(lightDir, input.normal));
float specIntensity = pow(saturate(dot(input.viewDir, reflection)), potSpecPower) * ceil(diffuseIntensity);

float toonIntensity = 0.0;
toonIntensity += smoothstep(layer1Start, layer1Start + layerBleed, diffuseIntensity) * layer1Color;
toonIntensity += smoothstep(layer2Start, layer2Start + layerBleed, diffuseIntensity) * (layer2Color - layer1Color);
toonIntensity += smoothstep(layer3Start, layer3Start + layerBleed, diffuseIntensity) * (layer3Color - layer2Color);

float4 outputColor = 0.0;
outputColor += potKs * (lightKs * smoothstep(specStart, specStart + specBleed, specIntensity) * specColor);
outputColor += potKd * (lightKd * toonIntensity);
outputColor += potKa * lightKa;
outputColor.a = 1.0;

return outputColor;
}

Silhouette rendering is accomplished through a bit of rendering trickery known as the shell extension method. After the cel shading is performed, the object is rendered again with front-face culling turned on, i.e. the back-facing triangles will be visible instead of the front. Each vertex is then extented slightly along the vertex normal in the vertex shader, proportional to the distance from the camera (to create a uniform extension as viewed from the camera). The new vertex positions are then sent to the pixel shader, which just outputs black as the color, resulting in a black outline around the object wherever the back-facing pixels are visible.

Of course, this particular method of silhouette rendering only works if an object's vertices all use shared, averaged vertex normals. For example, this method wouldn't work on a cube whose vertices all used different vertex normals; it would leave clear gaps in the outline around the cube.

Here's the complete vertex and pixel shader code for silhouette edge rendering.


// This controls the thickness of the outline.
static float thickness = 0.0035;

// Vertex shader that uses the shell extension method to create a silhouette outline.
float4 VS_ShellExtension(VS_INPUT input) : SV_POSITION
{
input.position.w = 1.0;
input.normal.w = 0.0;

// Put everything into world-space first.
input.position = mul(input.position, world);
input.normal = mul(input.normal, world);

// Offset the vertex along the normal, proportional to the distance from the camera.
float distance = min(length(input.position - cameraPos), 20.0);
input.position += input.normal * distance * thickness;
input.position = mul(input.position, viewProj);

return input.position;
}

// Simply outputs black.
float4 PS_Black(float4 position : SV_POSITION) : SV_TARGET
{
return float4(0.0, 0.0, 0.0, 1.0);
}

When calculating the distance from the camera you may have noticed the upper bound placed upon it. Without this, the outline would maintain a uniform thickness regardless of the size of the model, which is exactly what we want as long as the model is visibly large enough that the outlining doesn't dwarf the actual geometry. As the model gets farther and farther away from the camera, however, the outline begins to overcome the geometry until eventually all you see is a black blob. Placing an upper limit on the distance forces the outline to start scaling down with the model at that distance, thus avoiding the problem of the "black blob".

The source code for the demo is given below, along with an .exe-only version for those who just want to see it in action. Again, a DX10-capable GPU is required. Enjoy!

CelShader Source.zip (full source code/project)
CelShader Demo.zip (.exe only)

Thursday, September 2, 2010

HTD Update 2

Three months since my last post! The summer flew by fast. Unfortunately, the HTD project revamp isn't really any further along since my last update. Almost immediately another higher-priority project came up, and then another after that, and so on...and now fall classes have begun. So the HTD revamp was pushed aside and has remained there ever since.

However, I said I'd post the source code at some point, so before my classes completely consume me I thought I'd go ahead and post at least the code for the water flow simulation. The hydraulic erosion part is still buggy and not working, so I stripped out any code related to it. I'm also posting just the executable for those who want to see the sim in action without having to build it first. The readme file explains how to run/control it. A DX10-capable GPU is required (sorry XP users!)

Water Flow Source.zip (full source code/project)
Water Flow Demo.zip (.exe only)

As for the explanation of the implementation and flow equations, I refer the interested reader to the paper that my project is based upon, the Symposium on Computer Animation 2008 paper titled Interactive Terrain Modeling Using Hydraulic Erosion by St'ava et al.

I do hope to one day return to this and finish it, but for now it will have to continue to sit on the back burner - classes are going to keep me extremely busy for the next few months. In the meantime, enjoy the water flow simulation!

Thursday, June 10, 2010

HTD Update

The other day I realized I hadn't posted anything new in nearly two months, so I figured I'd better give an update on the Hydraulic Terrain Deformation (HTD) project I was/am working on.

Of course the semester is long over with by now, and the project submitted long ago, but I've been cleaning up the messy and sometimes hackish code that inevitably made it into the project as the deadline approached. And in the process of the cleanup, I also decided to give my personal code libraries an overhaul and add new functionality, which means breaking and changing the way my project worked beforehand. The hope is to release a nice, polished, and properly-commented program for public consumption when I'm done.

So in short, I'm still "working" on it. It'll probably be a while longer before I'm done, but I will get it out the door...eventually. =)

Friday, April 16, 2010

Hydraulic Terrain Deformation Preview

Here's a quick couple of screenshots and videos of my hydraulic terrain deformation project I'm working on. So far I have water flowing over arbitrary terrain, and pretty much all the underlying code and systems necessary to start deforming the terrain via hydraulic erosion. The simulation runs in real-time entirely on the GPU, i.e. no data is transferred between the CPU and GPU while it's running - all data calculations are done with GPGPU methods.

I'll give a more thorough explanation when I have more time (probably when I'm done with the project) and I'll probably post the source code as well.





Tuesday, April 13, 2010

GPU FTL....Again

Having one GPU fail on you is rare as it is, but having the second just-replaced-less-than-a-month-ago GPU fail also??? What rotten luck I must have. Either that, or the GPU seriously disagrees with my shader code and is willing to destroy itself so that it doesn't have to be subjected to my cruel instructions anymore. I think it's a toss-up.

Once again I had a replacement shipped out under warranty, and again the response was prompt, which made the loss of the GPU about as painless as it could be. I take a small bit of comfort in knowing that when this GPU fails it'll only take a couple days to get replaced.

Friday, March 26, 2010

The Joys of DirectX 10

For the last couple of weeks I've been working on my final project for my physically-based modeling and simulation course, which is going to be a real-time, interactive terrain modeling system using hydraulic erosion (more on that at a later date). Instead of using OpenGL like we have all semester I decided to delve into DirectX 10, since I most likely will be using DirectX out in industry and therefore need to start getting experienced with it. Yup, this is my first DirectX adventure, and what an adventure it's turning out to be.

I'm finding that good online DX10 resources are few and far between. Sure, there's MSDN that lists all the API functions and whatnot, but it doesn't really clearly tell you what they're used for or exactly how you would use them. Most of the documentation is fairly vague, and there aren't any code examples of everything in action except for a few basic tutorials that come with the SDK. For someone new to DirectX, the journey is frequently rough and slow-going. I guess it's time to find a good book on the subject.

As soon as I have something to show from my project I'll get it posted.