[:arrow_backward:](dilation.md) [:arrow_double_up:](../README.md) [:arrow_up_small:](#) [:arrow_down_small:](#copyright) [:arrow_forward:](lookup-table.md) # 3D Game Shaders For Beginners ## Film Grain <p align="center"> <img src="https://i.imgur.com/ct7mTv5.gif" alt="Film Grain" title="Film Grain"> </p> Film grain (when applied in subtle doses, unlike here) can add a bit of realism you don't notice until it's removed. Typically, it's the imperfections that make a digitally generated image more believable. In terms of the shader graph, film grain is usually the last effect applied before the game is put on screen. ### Amount ```c // ... float amount = 0.1; // ... ``` The `amount` controls how noticeable the film grain is. Crank it up for a snowy picture. ### Random Intensity ```c // ... uniform float osg_FrameTime; //... float toRadians = 3.14 / 180; //... float randomIntensity = fract ( 10000 * sin ( ( gl_FragCoord.x + gl_FragCoord.y * osg_FrameTime ) * toRadians ) ); // ... ``` This snippet calculates the random intensity needed to adjust the amount. ```c Time Since F1 = 00 01 02 03 04 05 06 07 08 09 10 Frame Number = F1 F3 F4 F5 F6 osg_FrameTime = 00 02 04 07 08 ``` `osg_FrameTime` is [provided](https://github.com/panda3d/panda3d/blob/daa57733cb9b4ccdb23e28153585e8e20b5ccdb5/panda/src/display/graphicsStateGuardian.cxx#L930) by Panda3D. The frame time is a timestamp of how many seconds have passed since the first frame. The example code uses this to animate the film grain as `osg_FrameTime` will always be different each frame. ```c // ... ( gl_FragCoord.x + gl_FragCoord.y * 8009 // Large number here. // ... ``` For static film grain, replace `osg_FrameTime` with a large number. You may have to try different numbers to avoid seeing any patterns. <p align="center"> <img src="https://i.imgur.com/xqSIMCb.gif" alt="Horizontal, vertical, and diagonal lines." title="Horizontal, vertical, and diagonal lines."> </p> ```c // ... * sin ( ( gl_FragCoord.x + gl_FragCoord.y * someNumber // ... ``` Both the x and y coordinate are used to create points or specs of film grain. If only x was used, there would only be vertical lines. Similarly, if only y was used, there would be only horizontal lines. The reason the snippet multiplies one coordinate by some number is to break up the diagonal symmetry. <p align="center"> <img src="https://i.imgur.com/4UXllmS.gif" alt="Rain" title="Rain"> </p> You can of course remove the coordinate multiplier for a somewhat decent looking rain effect. To animate the rain effect, multiply the output of `sin` by `osg_FrameTime`. ```c // ... ( gl_FragCoord.x + gl_FragCoord.y // ... ``` Play around with the x and y coordinate to try and get the rain to change directions. Keep only the x coordinate for a straight downpour. ```c input = (gl_FragCoord.x + gl_FragCoord.y * osg_FrameTime) * toRadians frame(10000 * sin(input)) = fract(10000 * sin(6.977777777777778)) = fract(10000 * 0.6400723818964882) = ``` `sin` is used as a hashing function. The fragment's coordinates are hashed to some output of `sin`. This has the nice property that no matter the input (big or small), the output range is negative one to one. ```c fract(10000 * sin(6.977777777777778)) = fract(10000 * 0.6400723818964882) = fract(6400.723818964882) = 0.723818964882 ``` `sin` is also used as a pseudo random number generator when combined with `fract`. ```python >>> [floor(fract(4 * sin(x * toRadians)) * 10) for x in range(0, 10)] [0, 0, 1, 2, 2, 3, 4, 4, 5, 6] >>> [floor(fract(10000 * sin(x * toRadians)) * 10) for x in range(0, 10)] [0, 4, 8, 0, 2, 1, 7, 0, 0, 5] ``` Take a look at the first sequence of numbers and then the second. Each sequence is deterministic but the second sequence has less of a pattern than the first. So while the output of `fract(10000 * sin(...))` is deterministic, it doesn't have much of a discernible pattern. <p align="center"> <img src="https://i.imgur.com/Mtt8BNg.gif" alt="Increasing the pseudo randomness." title="Increasing the pseudo randomness."> </p> Here you see the `sin` multiplier going from 1, to 10, to 100, and then to 1000. As you increase the `sin` output multiplier, you get less and less of a pattern. This is the reason the snippet multiplies `sin` by 10,000. ### Fragment Color ```c // ... vec2 texSize = textureSize(colorTexture, 0).xy; vec2 texCoord = gl_FragCoord.xy / texSize; vec4 color = texture(colorTexture, texCoord); // ... ``` Convert the fragment's coordinates to UV coordinates. Using these UV coordinates, look up the texture color for this fragment. ```c // ... amount *= randomIntensity; color.rgb += amount; // ... ``` Adjust the amount by the random intensity and add this to the color. ```c // ... fragColor = color; // ... ``` Set the fragment color and you're done. ### Source - [main.cxx](../demonstration/src/main.cxx) - [basic.vert](../demonstration/shaders/vertex/basic.vert) - [film-grain.frag](../demonstration/shaders/fragment/film-grain.frag) ## Copyright (C) 2019 David Lettier <br> [lettier.com](https://www.lettier.com) [:arrow_backward:](dilation.md) [:arrow_double_up:](../README.md) [:arrow_up_small:](#) [:arrow_down_small:](#copyright) [:arrow_forward:](lookup-table.md)