Friday, 12 April 2013

2D Gaussian blurs or 1D Gaussian blurs

When deciding between a 2D gaussian blur and two 1D gaussian blur passes, it is normally best to go two 1D gaussian blur passes. The reason for this is the difference in efficiency and outcome. Both gaussian do the same function, but differently. The primary difference is what they calculate and not necessarily the exact method as they both take the same approach.

A 2D Gaussian blur takes a particular fragment coordinates and blurs in all directions set by the user. This is nice but can quickly become inefficient. Think about it this way: what if you set your blur square to 5x5? This means your blurring 5^5 pixels, for every pixel on screen. While the end result doesn't look all that bad, it has many weaknesses. 2D Gaussian's are square like by nature and can handle flat, non-circular edges but in terms of anything round, it looks far less impressive. This is where two 1D Gaussians come in handy!


2D Blur, note the blocky edges

Two 1D Gaussian do almost the same thing but instead of sampling a set of 2D coords, it samples two sets of 1D coords. This is very interesting because it is not only more efficient but also looks better. Instead of producing a box type effect, we blur a particular pixel both in the X and Y direction, but not on any sort of angle. This way, we get a nice, smooth circular effect in which looks nice and is much more efficient then the previous style.

Nice and smooth two 1D passes

In conclusion, 1D is both faster and prettier!

The Accumulation buffer

Want to know something evil? It is called the glAccumulation buffer. This will allow you to achieve very basic motion blur, but at the same time it is not the best method of achieving this popular computer graphics effect. Why do we even want motion blur in the first place however? Well, if you swing your arm very fast, your eyes pick up on the movement but it is so fast it appears to be blurred. We can mimic this effect through the use of the glAccumulation buffer or through a shader.


So, what is the glAccumulation buffer exactly? Essentially, instead of doing a gpu based motion blur, the glAccumulation buffer allows us to do a rather simple blur. By placing the accumulation call after all of our primary draws, we can have our motion blur.


glAccum(GL_ACCUM,1.0);
glAccum(GL_RETURN,1.0);
glAccum(GL_MULT,0.45f);

glAccum obtains the R,G,B and A values from the buffer currently selected for reading. GL_RETRUN transfers accumulation buffer values to the color buffer(s) currently selected for writing. GL_MULT multiplies each R,G,B, and A in the accumulation buffer by a value and returns the value to its corresponding accumulation buffer location.



By simply writing those three lines of code after your primary draw, you have basic motion blur. 


Wednesday, 10 April 2013

Cross-Hatching Shader

Cross hatching is a fun way of adding cartoon like detail and shading to a scene. It details a scene by adding lines right-angles to create a mesh like appearance. While most often used in hand drawings or paintings, it can be used as a computer graphics effect. In OpenGL, a rather simple shader exists to create a similar kind of scene. All we need is a fragment shader to handle the cross hatching of our scene.

A hand-drawn cross hatching example
For cross hatching, we'll need a 2D texture that will tell us what fragment is being rendered and a luminace value to help determine whether or not our current fragment lies on a particular line. This will also make it easy to draw more lines in areas that need more shading.


uniform sampler2D Texture;
 float lum = length(texture2D(Texture, gl_TexCoord[0].xy).rgb);


First, we will want to draw the enter scene as black. The reason for this is that we'll later be adding in white to create the cross hatching effect.


gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);

Next is where the fun happens. By determining the strength of a shadow, we can take the mod of the fragment coordinates x and y, add or subtract them together and divide by ten. If it is equivalent to 0, we draw white. We can change the frequency of lines by adding or subtracting to the added value of the added fragment coordinates. Here is an example from learningwebgl.com


/*
    Straight port of code from
    http://learningwebgl.com/blog/?p=2858
*/


void main()
{
    float lum = length(texture2D(Texture, gl_TexCoord[0].xy).rgb);
     
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
     
    if (lum < 1.00) {
        if (mod(gl_FragCoord.x + gl_FragCoord.y, 10.0) == 0.0) {
            gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
        }
    }
     
    if (lum < 0.75) {
        if (mod(gl_FragCoord.x - gl_FragCoord.y, 10.0) == 0.0) {
            gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
        }
    }
     
    if (lum < 0.50) {
        if (mod(gl_FragCoord.x + gl_FragCoord.y - 5.0, 10.0) == 0.0) {
            gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
        }
    }
     
    if (lum < 0.3) {
        if (mod(gl_FragCoord.x - gl_FragCoord.y - 5.0, 10.0) == 0.0) {
            gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
        }
    }
}

   
And a resulting product I made using this exact code:


Monday, 8 April 2013

Graphics in Video Game: Ambient Occlusion



Ambient occlusion is the computer graphics effect of approximating how light radiates in real life, especially off what is considered non-reflective surfaces. Ambient occlusion, as the name suggests, is an ambient effect and globally illuminates. The soft appearance created by ambient occlusion is similar to an overcast day.

To calculate ambient occlusion, rays are calculated in every direction from the surface. If a ray collides with another surface, like a wall or another object in the scene, there is no significant change. However if that ray reaches the background, sky, or something else that was set by the programmer, the brightness of that particular surface will increase. As a result, points surrounded by a large amount of objects will appear dark, and objects in the open will be more white. As a final result, the ambient occlusion is then combined with the final textures of the scene to create the effect. The example below illustrates ambient occlusion that was done in Maya and implemented in an OpenGL program.




In this image, ambient occlusion is calculated and stored to a texture. This texture is then combined with the original texture in Photoshop and then added back to the original object. This way it provides realistic lighting to the object in question.

Screen space ambient occlusion

In video games, ambient occlusion, also known as screen space ambient occlusion (SSAO), is a method of calculating ambient occlusion in real-time! The first use of SSAO was for the game Crysis in 2007. Click here for a demonstration of SSAO in Crysis.

SSAO is calculated differently then most ambient occlusion methods simply because of how much potential calculation exists. With SSAO, the depth and surface normal are rendered to a texture in the first pass. In the second pass, a screen-size quad is rendered. In the pixel shader, samples are taken from the neighboring points in the scene. These points are then projected back to screen space to sample the depth by accessing to the texture in first pass. By doing this, we check if the depth sampled at the point is closer or further away than the depth of the sample point itself. The closer the depth sample, the darker the surface as something is covering it.

SSAO in Starcraft 2

Using this method of calculating SSAO could potentially be hundreds of calculations per pixel. With the more calculations done, the better quality the scene will look. To save on computation, some static objects can have a "baked" ambient occlusion texture while others can be constantly updated.

While SSAO is excellent for providing better quality, it has several flaws. One problem with SSAO is that it tends to include artifacts. For example, objects that are outside the screen do not contribute to the occlusion and the amount of occlusion depends on the camera position and viewing angle. Also, the higher the resolution, the more calculations that need to be done. Even small changes in resolution can have big problems.

To reduce artifacts, it is good to blur slightly as it will eliminate any noise left by the SSAO. A Gaussian blur will work for this. Then, upon adding lighting such as Phong, it will create a very nice looking object.





Conclusion

SSAO is an excellent method of calculating realistic lighting in a scene. Since first used in Crysis, it has seen use in many different games. In the future, perhaps we will use better looking and more intense uses of SSAO.