Motion Based Pixelation Censor

A Godot 4 spatial shader that applies motion- eactive pixelation effects to 3D models. The pixelation intensity increases with movement and decreases when static, creating a dynamic censoring effect.

You can get a demo project on github.

Installation

  1. Copy cencored.gdshader to your Godot project
  2. Create a new ShaderMaterial
  3. Assign the shader to the material
  4. Apply the material to your 3D model

Usage

Method 1: UV Rectangle Masking

Define a rectangular area using UV coordinates:

# In the shader material parameters
censor_uv_min = Vector2(0.3, 0.2)  # Top-left corner
censor_uv_max = Vector2(0.7, 0.6)  # Bottom-right corner
use_vertex_color_mask = false
 

Method 2: Vertex Color Masking

For precise control over complex shapes:

  1. In your 3D modeling software, add a vertex color layer
  2. Paint areas to censor using RED channel (R=1.0)
  3. Export and import to Godot
  4. Enable vertex color masking:
use_vertex_color_mask = true
censor_threshold = 0.5  # Adjust sensitivity
 

Key Parameters

Pixelation Settings

  • pixel_size (1.0 – 100.0): Size of censored pixels. Higher values create larger blocks.
  • blur_strength (0.0 – 1.0): Maximum pixelation intensity when moving.
  • static_pixelation (0.0 – 1.0): Pixelation amount when stationary.

Motion Control

  • motion_scale (0.0 – 50.0): Sensitivity to movement. Higher values react more to motion.
  • motion_min (0.0 – 0.1): Minimum motion required to trigger effect.

Visual Adjustments

  • censor_tint: RGB color tint applied to censored areas.
  • censor_brightness (0.5 – 2.0): Brightness multiplier.
  • show_motion_debug: Visualize motion intensity (red = moving, blue = static).

Recommended Settings

For general use:

pixel_size = 30.0
blur_strength = 0.85
static_pixelation = 0.3
motion_scale = 10.0
 

For high sensitivity:

pixel_size = 40.0
motion_scale = 15.0
blur_strength = 1.0

 

Shader code
shader_type spatial;
render_mode unshaded;

// Screen texture for pixelation effect
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap;

// Main texture
uniform sampler2D albedo_texture : source_color;

// Censoring control
uniform vec2 censor_uv_min = vec2(0.0, 0.0);
uniform vec2 censor_uv_max = vec2(1.0, 1.0);
uniform bool use_vertex_color_mask = false;
uniform float censor_threshold : hint_range(0.0, 1.0) = 0.5; // Threshold for vertex color mask

// Pixelation settings
uniform float pixel_size : hint_range(1.0, 100.0) = 30.0; // Size of pixels (higher = bigger pixels)
uniform float blur_strength : hint_range(0.0, 1.0) = 0.9; // How much pixelation

// Motion-based effect (Enhanced version)
uniform float motion_scale : hint_range(0.0, 50.0) = 10.0; // Overall motion sensitivity
uniform float motion_min : hint_range(0.0, 0.1) = 0.01; // Minimum motion to show effect
uniform float static_pixelation : hint_range(0.0, 1.0) = 0.3; // Pixelation when not moving

// Visual settings
uniform vec3 censor_tint : source_color = vec3(1.0, 1.0, 1.0);
uniform float censor_brightness : hint_range(0.5, 2.0) = 1.0;
uniform bool show_motion_debug = false; // Visualize motion intensity

// Velocity calculation
varying vec3 vertex_world_pos;
varying vec3 vertex_world_normal;
varying float vertex_speed;

void vertex() {
    // Get world position
    vertex_world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
    vertex_world_normal = normalize((MODEL_MATRIX * vec4(NORMAL, 0.0)).xyz);

    // Calculate approximate velocity based on view direction change
    // This creates a motion effect when the camera or object moves
    vec3 view_dir = normalize(vertex_world_pos - (INV_VIEW_MATRIX[3]).xyz);

    // Use the dot product with normal to create parallax-based motion
    // Surfaces facing the view direction will show more motion
    float surface_motion = abs(dot(vertex_world_normal, view_dir));

    // Store speed (you can enhance this with actual object velocity if needed)
    vertex_speed = surface_motion;
}

void fragment() {
    // Get base texture color
    vec4 base_color = texture(albedo_texture, UV);

    // ===== DETERMINE CENSOR MASK =====
    float censor_mask = 0.0;

    if (use_vertex_color_mask) {
        // Use RED channel of vertex color as mask
        censor_mask = step(censor_threshold, COLOR.r);
    } else {
        // Use UV rectangle bounds
        vec2 uv_check = UV;
        bool in_bounds = uv_check.x >= censor_uv_min.x && uv_check.x <= censor_uv_max.x &&
                        uv_check.y >= censor_uv_min.y && uv_check.y <= censor_uv_max.y;
        censor_mask = float(in_bounds);
    }

    vec3 final_color = base_color.rgb;

    // Only do the censor effect if we're in the censor area
    if (censor_mask > 0.01) {
        // ===== CALCULATE MOTION INTENSITY =====
        // Get screen-space velocity approximation
        vec2 screen_pos = SCREEN_UV;
        vec2 viewport_size = VIEWPORT_SIZE;

        // Sample nearby pixels to create motion estimation
        vec2 velocity = vec2(0.0);
        vec3 center_color = textureLod(SCREEN_TEXTURE, screen_pos, 0.0).rgb;

        // Simple motion detection by comparing with offset samples
        for(float x = -1.0; x <= 1.0; x += 2.0) {
            for(float y = -1.0; y <= 1.0; y += 2.0) {
                vec2 offset = vec2(x, y) * (2.0 / viewport_size);
                vec3 sample_color = textureLod(SCREEN_TEXTURE, screen_pos + offset, 0.0).rgb;
                velocity += (sample_color - center_color).rg;
            }
        }

        float motion_magnitude = length(velocity) * motion_scale * vertex_speed;
        float motion_intensity = smoothstep(motion_min, motion_min + 0.05, motion_magnitude);
        motion_intensity = clamp(motion_intensity, 0.0, 1.0);

        // ===== CREATE PIXELATION EFFECT =====
        // Calculate grid position
        vec2 grid_uv = floor(screen_pos * viewport_size / pixel_size) * pixel_size;
        grid_uv = grid_uv / viewport_size;

        // Sample the pixelated/censored version
        vec4 pixelated_color = textureLod(SCREEN_TEXTURE, grid_uv, 0.0);

        // ===== MIX BASED ON MOTION =====
        // More motion = more pixelation (like your original effect!)
        float final_blur = mix(static_pixelation, blur_strength, motion_intensity);
        final_color = mix(base_color.rgb, pixelated_color.rgb, final_blur * censor_mask);

        // Apply visual tweaks
        final_color *= censor_tint * censor_brightness;

        // Optional: Debug visualization
        if (show_motion_debug) {
            final_color = mix(final_color, vec3(motion_intensity, 0.0, 1.0 - motion_intensity), 0.5);
        }
    }

    // ===== OUTPUT =====
    ALBEDO = final_color;
    ALPHA = base_color.a;
}
Tags
cencor, motion, pixelation
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

Related shaders

PSX Style Water Surface – Pixelation, Waves, Scrolling Textures

3D Pixelation

Angled pixelation with color palette quantization and fog

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments