Monday 4 May 2015

Good News or Bad News?



The past week has been... tumultuous.

On a scale of 1 ~10, I've had a really good moment that brought the week up to 8, only to be followed really bad things that drop it down to 2. At the end of it all, I am currently sitting on a solid 5, a very half-hearted "meh". If you'd be willing, o reader, I shall regail to you the sad sorry tale that is my life so far. Starting with the good news, since I'm such a cheerful optimist.



The Good News

I finally managed to get a God Rays post-processing shader working in Unity, using nothing but the tools and functions available in Unity 4 Free Edition. In the beginning I thought it would be a simple task; simply render the scene to a render texture (a feature that very much exists in Unity), apply a hand-written shader effect to it using Unity's built-in custom shaders, and voila! Pretty visual effects! Of course, I'd failed to read the fine print that stated all this was only possible in Unity Pro. Therefore, I had to get creative.


1. Capture the entire screen display into a texture

 This process is, conceptually, identical to rendering the scene into a texture. The key difference is in the method; 'capture the screen display', not 'render the scene'. Because render textures were unavailable, I was forced to perform another function that produced similar results: ReadPixels. I talked about how this functions works previously, and how expensive it is... No kidding, I'm forcing an extra draw call to be performed entirely by the CPU. I managed to optimise it by running the function in a sub-routine thread, but using this technique still dropped the FPS from 120 to about 50. It pained me, but it was necessary.


The worst part is nothing has seemingly changed (-____-)


2. Run Shader Pass A: 'Occlusion'

Once I'd managed to somehow cobble together an image I could work with, I began my work on actually writing a shader to edit this screenshot texture. The good news here is that Unity's shader language, Shader Lab, supports multiple render passes in a single draw call. This made it much easier for me to layer the separate effects I needed to achieve my end goal of God Rays. Now, the first thing one needs to create a God Ray shader is to create an occlusion pass; essentially, draw every object in the scene as solid black. this allows us to represent everything that will be blocking (occluding) the light as it shines through.

Achieving this in a shader is, to be honest, a cakewalk. Simply sample the base texture, and for every pixel that has an alpha value, render black.


Finally, some visible changes!


This method relies on the fact that the screenshot / render texture will, when drawing the scene, only modify a pixel if it sees that an object occupies that pixel space. If an object exists, it will be rendered to the texture (alpha = 1.0); if not, the render texture will not edit the pixel in any way and leaves it blank (alpha = 0.0). Therefore, if the sampled pixel has an alpha value it must mean there is an occluding object occupying that pixel space.


3. Run Shader Pass B: 'Lighting'

Once the occlusion pass was in, I wrote the next block of shader code: lighting. This is just the simple 3D directional lighting technique used in all languages, so I won't go into too much detail about it here.It should be noted though, that I wrote the standard vertex-based lighting model from scratch. Since Unity already has the phong shading and blinn shading models built-in, it may have been easier to use those instead of writing a model myself. However, as with all pre-built code I dislike not knowing how the logic flows internally. As such, I felt more comfortable writing one I knew would work than trusting Unity.


I see the light! Hallelujah! 


4. Run Shader Pass C: 'Blur'

Last but not least, the radial blur. In order for to simulate the God Rays effect, a radial blur is applied to the whole texture with the blur originating from the light source. Or more accurately, where the light's position is in screen space.  This involves passing in the light's position in-game and translating that into screen space, disregarding the z-pos since we are working with 2D textures. Once the light position is found, we set that as the origin for a radial blur that decays in strength the further from the origin. Something along the lines of:

void main()
{
 pixel_position = texCoord[0];
 delta_pixel_position = pixel_position - light position;
 color = renderTexture.sample(pixel_position);
 
 for(int i=0; i < NUM_SAMPLES ; i++)
 {
    pixel_position -= delta_pixel_position;
    new_sample = renderTexture.sample(pixel_position);
    sample *= godray_strength_decay;
    color += sample;
 }
 
 color out 
}

This site also helped.

 ...It's... it's so pretty...!


I'm rather proud of the results to be honest, and I'll more than likely use the technique again in future.... Actually, no I won't. In future I'll just use an engine that has render textures to begin with, save myself the numerous headaches. I briefly talked about some of these headaches previously, so I won't devolve into another rant here. Needless to say, I still very mush dislike working with Unity. if it weren't for its layout and the relatively intuitive component system, I'd never look at it again.



The Bad News

...Aaaand here comes the downside. So as you can imagine, when I had completed this wonderful project of mine I was ecstatic. I was cheering, I was laughing, I even did a little dance. Best of all, this tech that I had managed to complete was to be displayed to the public in a Games Exhibit! I could see my work proudly displayed, and people would see me for the genius, coding savant that I am!

"This calls for celebration!" I thought, "break out the liquor!"


...Yeah.

Two shots of Tequila and half a bottle of Whiskey later, I awoke to find that the whole world was constantly spinning and my legs had transformed to jelly overnight. I could barely walk without feeling nauseated, let alone making it to an exhibit on the other side of town.

Sigh.

It really is my own stupid fault, and that's why I'm feeling rather blue at the moment. I had a wonderful opportunity to see my own work on display, and I miss out on it being a drunken idiot. I can only hope that the exhibit went well and I didn't screw everyone else over by not being present for the preparations. My apologies if I did.


I think I'll quit drinking.

No comments:

Post a Comment