Thursday, 13 March 2014

GDC 14 Lecture Resources

I am making this blog as place with extra resources for a talk I will be giving at GDC on Tuesday next week (14.20 at the Narrative Summit). The talk is called "Making Storytelling a Fundamental Part of the Gameplay Experience" and will be about a new approach to narrative design called 4-Layers. I only have 25 minutes to give the talk and there is a lot of stuff I cannot explain at proper depth; this page is a way to make up for that. I will also post a link to the cleaned up script of the talk here when I get back.

If you are planning on attending this talk, these links should also serve as some warm up to some of the
concepts I will cover!

Here comes resources:

Terminology
This is a huge stumbling block when discussing narrative in games, as story means so many things to a lot of people. Here I have tried to give a basic overview on what I mean with the different words. (Also check comments for further discussions).
http://frictionalgames.blogspot.se/2013/05/nailing-down-terminology.html

Core Elements of Interactive Storytelling
This is more in-depth information on the core elements that I discuss in the lecture:
http://frictionalgames.blogspot.se/2013/08/5-core-elements-of-interactive.html

http://frictionalgames.blogspot.se/2013/07/thoughts-on-last-of-us.html
My post on Last Of Us go over some more examples of when a game manages to really shine with storytelling, as well as the moments when it does not.

http://unbirthgame.com/TheSelfPresenceStorytelling.pdf
This is a longer essay that goes over the idea that the core of a game is made up in the player's mind.

Puzzles
When designing puzzles, it is important to not just see them as some intrinsically interesting element, they must serve some purpose to the narrative. This means there is a lot of things you need to think about when designing one. Some of these things should also apply to non-puzzle gameplay.

http://frictionalgames.blogspot.se/2012/12/introduction-i-recently-started-to-play.html
Here I go over how the high level design of most adventure games is flawed from a narrative perspective.

http://frictionalgames.blogspot.se/2013/02/puzzles-what-are-good-for.html
When are puzzles useful?

http://frictionalgames.blogspot.se/2013/03/puzzles-and-causal-histories.html
Thinking of puzzles as a way for the designer to make the player do certain actions but still feel agency.



Friday, 7 March 2014

People of Frictional: Mikael Hedberg

Who am I?

I am Mikael Hedberg and I’m the writer. I write all sorts of text that shows up in the game. I give actors lines to say and then ask them to scream a few times, because their character will most likely die at some point.


Background

I’ve been a gamer for as long as I can remember. I played everything I could get my hands on, board games, RPGs, and of course computer games. I can’t remember a brand or anything, but the very first computer I owned was a green keyboard with rubber keys. It didn’t have any memory to actually save things, so you had to program every game you wanted to play. I had a few sheets of paper filled with code which you painstakingly had to type before you could play. And what riveting games I got to play. Oh, the thrills of guessing that number between 1 and 100 or dodging those falling letters. Those were the days.
I did eventually upgrade to an Amiga which was the first time I encountered real story in games. LucasArts and Sierra titles quickly became my addiction. I liked all of them, but more than any other I would say Gabriel Knight: Sins of the Fathers really made me go, “Oh, these can get pretty dark and tell real stories.”

I was kind of late to the computer RPG party, because playing a lot of tabletop RPGs made me think they were pretty bad in comparison. I did start liking them around the release of Fallout 2 and played a bunch of the RPGs released around that time. I still consider Arcanum: Of Steamworks and Magick Obscura to be one of the best computer games I’ve ever played. You see, the thing about being a tabletop RPG player is that I’ve played and hosted easily over a hundred different stories in this very free and responsive type of play. Going to computer games, it’s really difficult to get excited by the storytelling because it’s rarely that interesting. Not because it doesn’t tell the right stories, but because I never feel like I’m really doing anything. I feel like I’m slowly moving a movie forward. It’s like all I do is power the projector by pushing keys. People often get dumbstruck when I don’t go nuts over games like the Last of Us. They are great at story, they tell me. Yes, they really are, but we have always had good writing and conventional storytelling. Look at the Sierra games from the 90s. They are absolutely fantastic in that respect.

I do get more excited with games that either try to give me that sense of responsibility like the Fallout games or games that try to merge gameplay and story like Heavy Rain. If you mess up, you don’t just fail, you actually fail someone in the game, you change the story. That’s when story matters, that’s when it gets interesting to me.
I’ve always wanted to tell a really good story in a game where it makes you feel like I do when I play tabletop RPGs. A game where you are concerned about what you are doing, not because you might fail some gameplay, but because it may take the story somewhere you don’t want it to go. Where your actions affect the lives of the characters you meet in a meaningful way and their well-being is reflected back on you. Basically to be human, but in another world.


My "corner office"

I started out my career at a pretty big company in Stockholm. It was a very educational experience concerning how the business works. However, it was very frustrating trying to get anything done. In the end I felt like I put maybe 5% of my time into the writing and 95% into company politics, talking to people and trying to get everyone on board. The one thing I learned about being a game writer is: You are responsible for the story, but you don’t have mandate to actually control the story. Impossible situation, you say? I thought so, so I kind of ran away to Japan for a while.

One day as I sat there in my house on a snowy peak in Japan there came a knocking on my door. It was Jens, an old friend of mine from school.
“Gotham needs you,” he said.
“What?”
“Sorry, wrong house. Oh, hi Mike. You want to do some writing for Frictional Games?”
“No, man. I’m done with game writing. My life is all about crochet now. Wanna see?”
“Wow, that’s like really bad…”
“Well, I just started.”
“No, I mean, like really really bad…”
All right, you made your point. I’ll take the writing gig.”
“What is this even supposed to be?!”
“I said I’d do it already!”

That’s how I ended up at Frictional Games. And there’s at least five words of truth to that story.
SIDE – Voice recording studio
Home away from home.

What do I do?

Explaining what I do and the amount of control I have over the narrative is very difficult, but I’ll give it try. Unlike what many seem to think I don’t set up the game like a screenwriter would set up a movie. I don’t precede the making of the game like a screenwriter, I write the game while in production. What happens in preproduction is more of setting up what the game will be rather than the story and the plot itself. Thomas, my boss, will explain what he wants thematically and roughly what events he wants the player to experience. It often sounds like story, but it really isn’t, even though it sometimes lends itself to something looking like a narrative. This preproduction talk mostly limits the story through theme and game structure. In the case of the Frictional Games titles I’ve worked on they all go with real-time linear progression of levels with a horror theme. This setup could make a thousand different stories if not more, but it also makes thousands of stories impossible to tell. After preproduction I should have an idea of what the game could be, but also what it absolutely can’t be.

Thomas then starts designing levels, providing a procedural journey through the levels with some suggestions for story content that could or sometime absolutely should be included. When I get to do the real work, actually writing the text you read or the dialog you hear in game, all the people in the company are already doing their thing, working from the level design. So, when I start writing there is already a level in production, gameplay designed, and a good sense of what the player will be doing. To make this work I can’t just start writing what I think would make a good story, but rather what would make the game work - and if there’s enough room, try to make it interesting beyond just being functional.

Step one in my work is always to have whatever the player is doing make some sense. Before I can get into shaping interesting characters or explain the plot, I need to hit that mark.

Take one of the very first puzzles in Amnesia. The door is covered in fleshy goo (the Shadow). The player is to create a concoction to dissolve it to continue.

The door itself and the goo in front of it takes care of itself. The player understands that this is not like other doors and something needs to be done to proceed. The first step is to introduce the solution, the recipe to dissolve the goo. Now this is pretty strange for someone to have lying around, so it needs to be justified somehow. I could start with saying Alexander’s plumbing is clogging up and that he purposefully created the recipe. Problem solved. Now that it is justified, I look at it. Can it be improved? Can I say something else about the world and the characters within it?
“This is my third attempt to produce artificial Vitae. The former compounds lacked the potency I need, but I sense I'm close. Calamine and Orpiment are a given and the Cuprite binds them well. This time I will attempt Aqua Regia instead of Aqua Fortis in hope it will produce a more even solution.The experiment was unsuccessful. The solution is highly acidic and proves impractical to put to any use except as a detergent. Organic tissue reacts especially violently to the solution and should be handled with the greatest care. I might be able to use the recipe, but I'm losing hope that I will find an alchemic solution to my predicament.”
Yes. Let’s make it one of Alexander’s experiments to create the vitae he wants so badly, but a failed one that has the effect the player wants. Now suddenly the note justifying the puzzle just told us about Alexander and further detailed his journey. Also the kind of throw away attitude Alexander has towards the concoction gives the player a sense of “Even though Alexander didn’t, I can put that to use”. It’s not just Daniel blindly following Alexander’s footsteps. Daniel actually finds a use to something that Alexander didn’t, which creates a nice distance between the two characters. So what started as a strictly functional solution ends up helping me talk about these two characters and enrich the story and reveal Alexander’s motives.

Justification followed by building story is the foundation of my work.

Pretty early on I know what I want the overall story to be and I map out what I need to tell that story in my head. To accommodate for changes and things that others, mostly Thomas want to include, I need to keep this plan pretty flexible. I try to keep this in mind when writing early texts as well, so I don’t paint myself into a corner. The further we get into the game, the more I dare to nail things down as the rest of the team has subconsciously started to take this as the obviously best version of the narrative.

Illustration time! Let’s say I have three pieces of information I need to get into the game so the story works the way I want it to. Let’s call them Transform, Conflict, and Payoff.
"Oh, boy! Don’t they look great together? 
And then we put the game and its structure on top of that:
"You bastards! Look what you did to my words!"

Then we make the best of the situation:

"I suppose Change is kind of like Transform, so if I just shuffle them around a little. There, they totally fit."
"— Mike, we cut another level."
"Muddafu—!"
Maybe not the best of illustrations, but it’s sort of what I do. I try keep an idea of the overall information I need to put into the game to tell the story I’m aiming for, and then keep it flexible enough so I can tell that story in a range of different ways. It’s not that I come up with thousands of versions, but instead remain opportunistic enough to fit the information I need where ever it will fit.

What’s probably most surprising to people is that I don’t get to boss people around and claim that the story demands this or that. Many would probably equate this to the story taking a backseat and is more of an afterthought, but that would be missing the point about the process. Yes, I don’t get to plan and direct the story, but I kind of get to cultivate it. Over time shaping a nice little bonsai tree out of the chaos that feeds it. And since we started on SOMA, Thomas has started to show more interest in storytelling which takes a much more transformative form as he has the power to actually tell the team to rework things. Thomas and I more openly discuss where we think the story should go this time around and what we would both like to see, so it’s easier getting a good framework down to work from. Funnily enough, I consider Thomas my most powerful ally as well as the most destructive force on the team, since he could suddenly decide to reshape a level to make it fit the narrative better or on the other hand decide to cut a critical scene due to gameplay flow. Ultimately I need to trust him to make the game he wants. Because if I cultivate a little story bonsai tree, I’m sure Thomas is worried about a whole garden of different fauna, like graphical coherency, technical efficiency and engaging gameplay.

As always I just need to roll with the punches.


Thursday, 20 February 2014

LevelEditor Feature: Poser EditMode

So I'm back with another HPL LevelEditor feature update, the Poser EditMode.

Back in the Amnesia days, whenever we wanted to add details which implied a unique skeletal model with different poses, we had to go all the way back to the modelling tool, set them up there and save them as a different mesh files. So we pretty much ended up with lots of replicated and redundant data (the corpse piles come to mind as I'm writing this), plus the burden of having to prepare everything outside the editor.

So, how to fix this? This is where the poser mode comes in. In a nutshell, it takes a skeletal mesh and exposes its bones to be translated and rotated in whatever way you fancy. This is useful not only for organic and creature geometry, but to create details like cables, piping and anything you can add a skeleton to.

There's not much to be added, so see it in action in this little video.





Friday, 24 January 2014

People of Frictional: Rasmus Gunnarsson

Fifth post in the series. You can read the other posts here.

Who am I?

Hello! My name Rasmus Gunnarsson and I started working for Frictional Games as a freelance artist back in 2010 to help out with the final stages of Amnesia's development. Later, I became an intern, and that landed me full-time employment as a concept artist / 3D artist / level artist / whatever’s needed artist.                                   
I work from an elevated desk where I stand all day. 
Background
I've always drawn a lot since I was a child. I never took it that seriously, which is something I regret, but then again perhaps if I had I wouldn't have spent most of my free time playing games. I used to buy those magazines that came with demo disks, and then use all my savings to either get a cool game from the budget bin or save it up for a larger release. I played everything from Megaman to Silent Hill to Counter Strike to Fallout to Starcraft to Monkey Island.

The time comes when you look at your life and where you are going; soon you'll be out of school and in need of a job. I asked myself what I wanted to do. If I was going to have to work for most of my life, I’d love it to be something that I cared about and enjoyed. This started a long and hard journey to where I am today.

I started out with all the resources I could find on the internet and practised all day instead of just playing - I was drawing, painting, reading tutorials and watching educational materials. When I did play games or watch movies, I tried to be more observant of things art-related. But just working on my own wasn't really going to cut it; even when you improve with lots of practice, you won't have any real practical tests of your skill and it’s easy to fall into patterns that feel comfortable.

I enrolled in a school for Industrial Design in my home town. I'd read that many successful concept artists had started out there, and it was the best way I could find of getting practical experience - or so I thought at the time. It turned out that it wasn’t a good idea at all. All it offered were some practical opportunities for that specific field, and no real meaty general design courses regarding aesthetics and so on -  we were left to figure that out on our own. As I was already doing that myself, I just quit, and looked for a course that might give me a chance for real practical experience.

This search took me to The Game Assembly, a school with close links to the games industry. So not only were there many more practical opportunities to be had, but there was also a chance to meet people in the industry. I didn't expect to be spoon-fed anything and still focused on the work and practice I did on my own.

I got to work on many small games during the course, but the big turning point was when I just got lucky. One of the guys I befriended happened to be doing ex for Frictional. Due to the busy schedule at the school, he had trouble keeping up with everything he needed to do and asked me to help out. While I was quite busy myself, I wouldn't pass up on this opportunity.


Painting made in my spare time.
A personal painting much inspired by Beksinski.
When I started working on Amnesia I had no idea what Frictional had done in the past, so of course I decided to check it out. After playing Penumbra, I realised quite how good Frictional were, and felt under pressure to perform well. I really enjoyed Penumbra and thought it did a lot of new things with old gaming ideas that I loved in a way that got me really excited. During school I hadn't wanted to end up working on AAA games as I felt they didn't manage to create new things that could rival many of the old great classic games. But after playing Penumbra I knew that I'd do anything to keep working with Frictional.


Concept art for the Justine suitor enemy.

After completing the freelance work for Amnesia I got an internship at Frictional where I, among other things, worked on the Justine expansion. After the internship I got full employment.


What do I do?
I create concept art and sketches for objects and environments. I also create 3D models and work on levels like a normal 3D artist when that takes a higher priority.

Sometimes an area might need a redesign, and doing a paintover is the quickest way of evaluating what needs to be changed and to set a clear goal; at other times we might need to fix a bunch of objects both technically and aesthetically.

I really love being able to have a finger in everything, making sure that the concepts are translated correctly, and then having the freedom to explore and tweak things, making every area or object as good as it can be instead of simply contributing art and hoping that it becomes something good in the end.

I use Photoshop and a Wacom for drawing and Modo for creating 3D assets.

My normal workflow is to interpret the initial design doc by Thomas (the Lead Designer), using his description and simple layout to try and capture the right mood and give the artists building the level something to go on.

Concepts for the teaser.
Above is an example from the teaser video we recently released. We chose "hero shots" of the environment where there is something special going on, so you get the most mileage out of a single sketch.

Another thing that happens in the process of designing an area is that the initial sketches tend to act as a visual playtest. Something that might've been problematic to communicate visually can easily slip through when you are only designing something in your head or on paper. In the end, nothing is final and will always evolve in each stage it goes through.


Wednesday, 22 January 2014

New Screenshot and Intercepted Radio Message

Click to view large version.


Transcription of radio message intercepted at XX/XX/XXX.

UNKNOWN: Look, this is the short version. -- [CORRUPT DATA] -- gone rogue. We're not sure what it’s up to, but it looks like it’s messing with the help. Are you hurt in any way, are you guys okay?

AMY: -- [CORRUPT DATA] -- are turning on us! We need to get out of here.

UNKNOWN: Hold on, don't panic. Carl, tell Amy to calm down. You guys can’t have much left to do before evac.

CARL: Calm down, Amy.

AMY: You calm down!

An alarm is heard in UNKNOWN’s mic.

UNKNOWN: Shit. I got to go. Stay the course, ok? Stay the --

Static outburst after which the signal is lost.


Thursday, 16 January 2014

Tech Feature: SSAO and Temporal Blur


Screen space ambient occlusion (SSAO) is the standard solution for approximating ambient occlusion in video games. Ambient occlusion is used to represent how exposed each point is to the indirect lighting from the scene. Direct lighting is light emitted from a light source, such as a lamp or a fire. The direct light then illuminates objects in the scene. These illuminated objects make up the indirect lighting. Making each object in the scene cast indirect lighting is very expensive. Ambient occlusion is a way to approximate this by using a light source with constant color and information from nearby geometry to determine how dark a part of an object should be. The idea behind SSAO is to get geometry information from the depth buffer.

There are many publicised algorithms for high quality SSAO. This tech feature will instead focus on improvements that can be made after the SSAO has been generated.



SSAO Algorithm
SOMA uses a fast and straightforward algorithm for generating medium frequency AO. The algorithm runs at half resolution which greatly increases the performance. Running at half resolution doesn’t reduce the quality by much, since the final result is blurred.

For each pixel on the screen, the shader calculates the position of the pixel in view space and then compares that position with the view space position of nearby pixels. How occluded the pixel gets is based on how close the points are to each other and if the nearby point is in front of the surface normal. The occlusion for each nearby pixel is then added together for the final result. 

SOMA uses a radius of 1.5m to look for nearby points that might occlude. Sampling points that are outside of the 1.5m range is a waste of resources, since they will not contribute to the AO. Our algorithm samples 16 points in a growing circle around the main pixel. The size of the circle is determined by how close the main pixel is to the camera and how large the search radius is. For pixels that are far away from the camera, a radius of just a few pixels can be used. The closer the point gets to the camera the more the circle grows - it can grow up to half a screen. Using only 16 samples to select from half a screen of pixels results in a grainy result that flickers when the camera is moving.
Grainy result from the SSAO algorithm
Bilateral Blur
Blurring can be used to remove the grainy look of the SSAO. Blur combines the value of a large number of neighboring pixels. The further away a neighboring pixel is, the less the impact it will have on the final result. Blur is run in two passes, first in the horizontal direction and then in the vertical direction.

The issue with blurring SSAO this way quickly becomes apparent. AO from different geometry leaks between boundaries causing a bright halo around objects. Bilateral weighting can be used to fix the leaks between objects. It works by comparing the depth of the main pixel to the depth of the neighboring pixel. If the distance between the depth of the main and the neighbor is outside of a limit the pixel will be skipped. In SOMA this limit is set to 2cm.
To get good-looking blur the number of neighboring pixels to sample needs to be large. Getting rid of the grainy artifacts requires over 17x17 pixels to be sampled at full resolution.

Temporal Filtering 
Temporal Filtering is a method for reducing the flickering caused by the low number of samples. The result from the previous frame is blended with the current frame to create smooth transitions. Blending the images directly would lead to a motion-blur-like effect. Temporal Filtering removes the motion blur effect by reverse reprojecting the view space position of a pixel to the view space position it had the previous frame and then using that to sample the result. The SSAO algorithm runs on screen space data but AO is applied on world geometry. An object that is visible in one frame may not be seen in the next frame, either because it has moved or because the view has been blocked by another object. When this happens the result from the previous frame has to be discarded. The distance between the points in world space determines how much of the result from the previous frame should be used.

Explanation of Reverse Reprojection used in Frostbite 2 [2]
Temporal Filtering introduces a new artifact. When dynamic objects move close to static objects they leave a trail of AO behind. Frostbite 2’s implementation of Temporal Filtering solves this by disabling the Temporal Filter for stable surfaces that don’t get flickering artifacts. I found another way to remove the trailing while keeping Temporal Filter for all pixels.


Shows the trailing effect that happens when a dynamic object is moved. The Temporal Blur algorithm is then applied and most of the trailing is removed.

Temporal Blur 

(A) Implementation of Temporal Filtered SSAO (B) Temporal Blur implementation 
I came up with a new way to use Temporal Filtering when trying to remove the trailing artifacts. By combining two passes of cheap blur with Temporal Filtering all flickering and grainy artifacts can be removed without leaving any trailing. 

When the SSAO has been rendered, a cheap 5x5 bilateral blur pass is run on the result. Then the blurred result from the previous frame is applied using Temporal Filtering. A 5x5 bilateral blur is then applied to the image. In addition to using geometry data to calculate the blending amount for the Temporal Filtering the difference in SSAO between the frames is used, removing all trailing artifacts. 

Applying a blur before and after the Temporal Filtering and using the blurred image from the previous frame results in a very smooth image that becomes more blurred for each frame, it also removes any flickering. Even a 5x5 blur will cause the resulting image to look as smooth as a 64x64 blur after a few frames.

Because the image gets so smooth the upsampling can be moved to after the blur. This leads to Temporal Blur being faster, since running four 5x5 blur passes in half resolution is faster than running two 17x17 passes in full resolution. 

Upsampling
All of the previous steps are performed in half resolution. To get the final result it has to be scaled up to full resolution. Stretching the half resolution image to twice its size will not look good. Near the edges of geometry there will be visible bleeding; non-occluded objects will have a bright pixel halo around them. This can be solved using the same idea as the bilateral blurring. Normal linear filtering is combined with a weight calculated by comparing the distance in depth between the main pixel and the depth value of the four closest half resolution pixels.

Summary 
Combining SSAO with the Temporal Blur algorithm produces high quality results for a large search radius at a low cost. The total cost of the algoritm is 1.1ms (1920x1080 AMD 5870). This is more than twice as fast as a normal SSAO implementation.

SOMA uses high frequency AO baked into the diffuse texture in addition to the medium frequency AO generated by the SSAO.

Temporal Blur could be used to improve many other post effects that need to produce smooth-looking results.

Ambient Occlusion is only one part of the rendering pipeline, and it should be combined with other lighting techniques to give the final look.


References
  1. http://gfx.cs.princeton.edu/pubs/Nehab_2007_ARS/NehEtAl07.pdf 
  2. http://dice.se/wp-content/uploads/GDC12_Stable_SSAO_In_BF3_With_STF.pdf 

 // SSAO Main loop

//Scale the radius based on how close to the camera it is
 float fStepSize = afStepSizeMax * afRadius / vPos.z;
 float fStepSizePart = 0.5 * fStepSize / ((2 + 16.0));    

 for(float d = 0.0; d < 16.0; d+=4.0)
 {
        //////////////
        // Sample four points at the same time

        vec4 vOffset = (d + vec4(2, 3, 4, 5))* fStepSizePart;
        
        //////////////////////
        // Rotate the samples

        vec2 vUV1 = mtxRot * vUV0;
        vUV0 = mtxRot * vUV1;

        vec3 vDelta0 = GetViewPosition(gl_FragCoord.xy + vUV1 * vOffset.x) - vPos;
        vec3 vDelta1 = GetViewPosition(gl_FragCoord.xy - vUV1 * vOffset.y) - vPos;
        vec3 vDelta2 = GetViewPosition(gl_FragCoord.xy + vUV0 * vOffset.z) - vPos;
        vec3 vDelta3 = GetViewPosition(gl_FragCoord.xy - vUV0 * vOffset.w) - vPos;

        vec4 vDistanceSqr = vec4(dot(vDelta0, vDelta0),
                                 dot(vDelta1, vDelta1),
                                 dot(vDelta2, vDelta2),
                                 dot(vDelta3, vDelta3));

        vec4 vInvertedLength = inversesqrt(vDistanceSqr);

        vec4 vFalloff = vec4(1.0) + vDistanceSqr * vInvertedLength * fNegInvRadius;

        vec4 vAngle = vec4(dot(vNormal, vDelta0),
                            dot(vNormal, vDelta1),
                            dot(vNormal, vDelta2),
                            dot(vNormal, vDelta3)) * vInvertedLength;


        ////////////////////
        // Calculates the sum based on the angle to the normal and distance from point

        fAO += dot(max(vec4(0.0), vAngle), max(vec4(0.0), vFalloff));
}

//////////////////////////////////
// Get the final AO by multiplying by number of samples
fAO = max(0, 1.0 - fAO / 16.0);

------------------------------------------------------------------------------ 

// Upsample Code
 
vec2 vClosest = floor(gl_FragCoord.xy / 2.0);
vec2 vBilinearWeight = vec2(1.0) - fract(gl_FragCoord.xy / 2.0);

float fTotalAO = 0.0;
float fTotalWeight = 0.0;

for(float x = 0.0; x < 2.0; ++x)
for(float y = 0.0; y < 2.0; ++y)
{
       // Sample depth (stored in meters) and AO for the half resolution 
       float fSampleDepth = textureRect(aHalfResDepth, vClosest + vec2(x,y));
       float fSampleAO = textureRect(aHalfResAO, vClosest + vec2(x,y));

       // Calculate bilinear weight
       float fBilinearWeight = (x-vBilinearWeight .x) * (y-vBilinearWeight .y);
       // Calculate upsample weight based on how close the depth is to the main depth
       float fUpsampleWeight = max(0.00001, 0.1 - abs(fSampleDepth – fMainDepth)) * 30.0;

       // Apply weight and add to total sum
       fTotalAO += (fBilinearWeight + fUpsampleWeight) * fSampleAO;
       fTotalWeight += (fBilinearWeight + fUpsampleWeight);
}

// Divide by total sum to get final AO
float fAO = fTotalAO / fTotalWeight;

-------------------------------------------------------------------------------------

// Temporal Blur Code

//////////////////
// Get current frame depth and AO

vec2 vScreenPos = floor(gl_FragCoord.xy) + vec2(0.5);
float fAO = textureRect(aHalfResAO, vScreenPos.xy);
float fMainDepth = textureRect(aHalfResDepth, vScreenPos.xy);   

//////////////////
// Convert to view space position
vec3 vPos = ScreenCoordToViewPos(vScreenPos, fMainDepth);

/////////////////////////
// Convert the current view position to the view position it 
// would represent the last frame and get the screen coords
vPos = (a_mtxPrevFrameView * (a_mtxView
Inv * vec4(vPos, 1.0))).xyz;

vec2 vTemporalCoords = ViewPosToScreenCoord(vPos);

       
//////////////
// Get the AO from the last frame

float fPrevFrameAO = textureRect(aPrevFrameAO, vTemporalCoords.xy);

float f
PrevFrameDepth = textureRect(aPrevFrameDepth, vTemporalCoords.xy);

/////////////////
// Get to view space position of temporal coords

vec3 vTemporalPos =
ScreenCoordToViewPos(vTemporalCoords.xy, fPrevFrameDepth);
       

///////
// Get weight based on distance to last frame position (removes ghosting artifact)

float fWeight = distance(vTemporalPos, vPos) * 9.0;

////////////////////////////////
// And weight based on how different the amount of AO is (removes trailing artifact)
// Only works if both fAO and fPrevFrameAO is blurred
fWeight += abs(
fPrevFrameAO - fAO ) * 5.0;

////////////////
// Clamp to make sure atleast 1.0 / FPS of a frame is blended

fWeight = clamp(fWeight, afFrameTime, 1.0);       
fAO = mix(fPrevFrameAO , fAO , fWeight);
   
------------------------------------------------------------------------------



SiteMeter