Retro Parchment Paper

This shader simulates an old parchment or retro manuscript style for 2D Canvas Items. It automatically applies a sepia tone, vignette, and procedural noise to create a realistic paper texture.

Key Features:

  • Ink Bleed Effect: Detects dark lines and creates a soft, noisy glow around them, simulating ink soaking into paper.
  • Paper Texture: Uses layered noise to generate grain and subtle paper variations.
  • Dirt & Grunge: Procedural dirt spots for an aged look.
  • Customizable: Adjust glow range, dirt intensity, and sepia strength via uniforms.

Instructions:

Apply this shader to a Sprite2D, TextureRect, or any CanvasItem. It works best with text or line art sprites (with transparency). Adjust the glow_range and glow_strength to fit the size of your artwork.

Shader code
shader_type canvas_item;

uniform float noise_scale : hint_range(1.0, 10.0) = 4.0;
uniform float vignette_strength : hint_range(0.1, 2.0) = 0.7;
uniform float sepia_strength : hint_range(0.0, 1.0) = 0.85;
uniform float contrast : hint_range(0.5, 2.0) = 1.2;

uniform float glow_range : hint_range(1.0, 30.0) = 12.0;    // 内发光范围(像素)
uniform float glow_strength : hint_range(0.0, 1.0) = 0.75;  // 内发光强度
uniform float glow_falloff : hint_range(0.5, 4.0) = 2.0;    // 衰减曲线,越大越集中在线条边
uniform float dirt_strength : hint_range(0.0, 1.0) = 0.4;   // 脏污强度
uniform float dirt_scale : hint_range(1.0, 100.0) = 8.0;     // 脏污大小

float rand(vec2 co) { return fract(sin(dot(co,vec2(12.9898,78.233)))*43758.5453); }
float noise(vec2 p) {
    vec2 i=floor(p),f=fract(p); f=f*f*(3.0-2.0*f);
    return mix(mix(rand(i),rand(i+vec2(1,0)),f.x),mix(rand(i+vec2(0,1)),rand(i+vec2(1,1)),f.x),f.y);
}
float fbm(vec2 p) {
    return noise(p)*0.5+noise(p*2.1)*0.25+noise(p*4.3)*0.125+noise(p*8.7)*0.063;
}

void fragment() {
    vec4 tex = texture(TEXTURE, UV);

    if (tex.a < 0.1) {
        COLOR = vec4(0.0);
    } else {
        vec2 px = TEXTURE_PIXEL_SIZE;

        // 找周围最近线条像素,计算内发光强度
        float closest = 1.0;
        vec3 nearestLineColor = vec3(0.0);
        float ga = 2.399963;
        for (int i = 0; i < 32; i++) {
            float r = sqrt(float(i + 1) / 32.0);
            float angle = float(i) * ga;
            // 噪声扰动采样角度,让发光边缘不规则
            float nOffset = fbm(UV * 10.0 + vec2(float(i) * 0.3)) * 1.5;
            vec2 offset = vec2(cos(angle + nOffset), sin(angle + nOffset)) * r * px * glow_range;
            vec4 s = texture(TEXTURE, UV + offset);
            if (s.a > 0.1) {
                float b = dot(s.rgb, vec3(0.333));
                if (b < 0.45) { // 线条像素
                    if (r < closest) {
                        closest = r;
                        nearestLineColor = s.rgb;
                    }
                }
            }
        }

        // 距离转发光强度,falloff控制衰减曲线
        float glowAmount = pow(1.0 - closest, glow_falloff) * glow_strength;
        // 噪声让发光强度不均匀,模拟墨水洇纸
        float glowNoise = fbm(UV * 15.0 + vec2(2.3, 5.1));
        glowAmount *= (0.6 + glowNoise * 0.8);
        glowAmount = clamp(glowAmount, 0.0, 1.0);

        vec3 col = tex.rgb;

        // 叠内发光:当前颜色向线条色混合
        col = mix(col, nearestLineColor, glowAmount);

        // 脏污效果:多层噪声叠加暗斑
        float dirt1 = fbm(UV * dirt_scale);
        float dirt2 = fbm(UV * dirt_scale * 2.3 + vec2(4.1, 2.7));
        float dirt3 = fbm(UV * dirt_scale * 0.5 + vec2(1.2, 8.3));
        float dirt = dirt1 * dirt2 * dirt3; // 三层相乘,只有都暗的地方才出现斑点
        col -= pow(dirt, 1.5) * dirt_strength;

        // 对比度
        col = (col - 0.5) * contrast + 0.5;

        // sepia
        float gray = dot(col, vec3(0.299, 0.587, 0.114));
        vec3 sepia = vec3(gray) * vec3(1.12, 0.95, 0.70);
        col = mix(col, sepia, sepia_strength);

        // 羊皮纸噪声
        float n  = fbm(UV * noise_scale);
        float n2 = fbm(UV * noise_scale * 2.5 + vec2(3.7, 1.9));
        col += (n  - 0.5) * 0.05;
        col += (n2 - 0.5) * 0.025;

        // 纸纹
        float lines = sin(UV.y * 500.0) * 0.015 + sin(UV.y * 180.0) * 0.007;
        col += lines;

        // 边缘压暗
        vec2 e = UV * (1.0 - UV);
        float vignette = pow(clamp(e.x * e.y * 16.0, 0.0, 1.0), vignette_strength);
        col *= vignette;

        COLOR = vec4(col, tex.a);
    }
}
Live Preview
Tags
2d, ink, paper, parchment, retro, sepia, stylized, texture, vintage
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

guest

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
neytsomh
neytsomh
21 days ago

牛逼