s&box
RustCounter-Strike 2s&boxDeadlock
Wiki
Get s&box on Steam
Browse
OverviewGetting StartedCreating GamesEngine & ToolsDocumentationCommunity GamesResources
s&boxDocumentationRenderingPost ProcessingEffectsFilmgrain
SOURCE 2 · C#
Build and play games with s&box.

Facepunch's game engine and creation platform — make games in C#, or jump straight into community games.

Get s&box on Steam

s&box Wiki

  • Overview
  • Getting Started
  • Creating Games
  • Engine & Tools
  • Documentation
  • Community Games
  • Resources

Tools & Community

  • Getting Started
  • Creating Games
  • Engine & Tools
  • Resources

Platforms

  • RustBattle
  • CSBattle
  • Rust Game Wiki
  • CS2 Skins Wiki
  • Deadlock Wiki

About

  • Official Site
  • Developer Wiki
  • GitHub
  • Get on Steam
NOT AFFILIATED WITH FACEPUNCH STUDIOS · © 2026 s&box Wiki (community)
Docs/Rendering
Rendering

FilmGrain

The FilmGrain component adds simulated film-style grain to the camera output. It's purely visual, it does not affect gameplay or lighting, and is intended to add texture, grit or stylistic noise to the final image.

PropertyDescription
IntensityOverall visibility of the grain. 0 = off, 1 = heavy.
ResponseHow much the grain reacts to brightness. Higher values = less grain in darker areas.
namespace Sandbox;

/// <summary>
/// Applies a film grain effect to the camera
/// </summary>
[Title( "FilmGrain" )]
[Category( "Post Processing" )]
[Icon( "grain" )]
public sealed class FilmGrain : BasePostProcess<FilmGrain>
{
	[Range( 0, 1 )]
	[Property] public float Intensity { get; set; } = 0.1f;

	[Range( 0, 1 )]
	[Property] public float Response { get; set; } = 0.5f;

	public override void Render()
	{
		float intensity = GetWeighted( x => x.Intensity );
		if ( intensity.AlmostEqual( 0.0f ) ) return;

		float response = GetWeighted( x => x.Response, 1 );

		Attributes.Set( "intensity", intensity );
		Attributes.Set( "response", GetWeighted( x => x.Response, 1 ) );

		var shader = Material.FromShader( "shaders/postprocess/pp_filmgrain.shader" );
		var blit = BlitMode.WithBackbuffer( shader, Stage.AfterPostProcess, 200, false );
		Blit( blit, "FilmGrain" );
	}
}
HEADER
{
    DevShader = true;
}

MODES
{
    Default();
    Forward();
}

COMMON
{
    #include "postprocess/shared.hlsl"
}

struct VertexInput
{
    float3 vPositionOs : POSITION < Semantic( PosXyz ); >;
    float2 vTexCoord : TEXCOORD0 < Semantic( LowPrecisionUv ); >;
};

struct PixelInput
{
    float2 uv : TEXCOORD0;

	// VS only
	#if ( PROGRAM == VFX_PROGRAM_VS )
		float4 vPositionPs		: SV_Position;
	#endif

	// PS only
	#if ( ( PROGRAM == VFX_PROGRAM_PS ) )
		float4 vPositionSs		: SV_Position;
	#endif
};

VS
{
    PixelInput MainVs( VertexInput i )
    {
        PixelInput o;
        
        o.vPositionPs = float4(i.vPositionOs.xy, 0.0f, 1.0f);
        o.uv = i.vTexCoord;
        return o;
    }
}

PS
{
    #include "postprocess/common.hlsl"
    #include "postprocess/functions.hlsl"
    #include "procedural.hlsl"

    #include "common/classes/Depth.hlsl"

    Texture2D g_tColorBuffer < Attribute( "ColorBuffer" ); SrgbRead( true ); >;

    float grainIntensity< Attribute("intensity"); >;
    float grainResponse< Attribute("response"); >;

    Texture2D g_tBlueNoise < Attribute( "BlueNoise" ); >;

    float3 FilmGrain( float3 vColor, float3 flSampledGrain, float intensity, float flResponse )
    {
        // Remap grain to a -1 -> 1 range
        flSampledGrain.rgb = flSampledGrain.b;

        // Grab our luminence and rescale based on response
        float lum = 1 - (GetLuminance( vColor.rgb ) * 5.0f);
        intensity *= saturate( lerp( 1.0f, saturate( lum ), flResponse ) );

        float3 fullGrain = saturate( (flSampledGrain - 0.1) * 2 ) - 0.7;

        vColor = lerp( vColor, GetLuminance( vColor.rgb ) * 0.1, intensity );

        return lerp( vColor, fullGrain, intensity );
    }

    float4 MainPs( PixelInput i ) : SV_Target0
    {
        float2 vScreenUv = CalculateViewportUv( i.vPositionSs.xy );
        float4 color = g_tColorBuffer.SampleLevel( g_sBilinearMirror, vScreenUv, 0 );

        float2 scale = float2( g_vRenderTargetSize.x , g_vRenderTargetSize.y ) / 128;
        
        float2 vGrainUvs = TileAndOffsetUv( vScreenUv, scale, float2(frac(g_flTime * 4.042f), frac(g_flTime * 34.054f)) );
        float3 flGrain = g_tBlueNoise.SampleLevel( g_sBilinearWrap, vGrainUvs, 0 ).rgb;
        flGrain += g_tBlueNoise.SampleLevel( g_sBilinearWrap, -vGrainUvs * 5, 0 ).brg;
        flGrain *= 0.5;

        color.rgb = FilmGrain( color.rgb, flGrain, grainIntensity, grainResponse );
        return color;
    }
}

Source: Facepunch/sbox-docs (CC-BY-4.0) · updated 2025-10-02. Read it rendered on the official docs.

More in Rendering

AmbientLight
Animated Effects
Attributes and Variables
BeamEffect
Beams
Bindless API
C# Shader Nodes
Classes
← All documentation

Community wiki — not affiliated with Facepunch Studios. For the latest and most authoritative information, see the official developer wiki.