function createOcclusionMask(tokenLayer, tile) {
    const vertexShader = `
        precision highp float;
        attribute vec2 position;
        attribute vec2 uv;
        varying vec2 vUv;
        
        void main() {
            vUv = uv;
            gl_Position = vec4(position, 0.0, 1.0);
        }
    `;

    const fragmentShader = `
        precision highp float;
        uniform sampler2D tileTexture;
        uniform vec4 tileRect;
        uniform float chunkSize;
        varying vec2 vUv;
        
        void main() {
            vec2 sourcePos = mix(vec2(tileRect.x, tileRect.y), 
                               vec2(tileRect.z, tileRect.w), 
                               vUv);
            
            vec4 color = texture2D(tileTexture, sourcePos);
            float alpha = color.a;
            
            // Check chunk bounds
            vec2 chunkUV = floor(vUv * vec2(chunkSize)) / chunkSize;
            vec4 mask = step(0.1, alpha);
            
            gl_FragColor = mask;
        }  
    `;

    // Create shader program
    const renderer = PIXI.Renderer.shared;
    const program = new PIXI.Program(vertexShader, fragmentShader);
    
    // Setup geometry
    const geometry = new PIXI.Geometry()
        .addAttribute('position', [-1,-1, 1,-1, 1,1, -1,1], 2)
        .addAttribute('uv', [0,0, 1,0, 1,1, 0,1], 2)
        .addIndex([0,1,2, 0,2,3]);

    // Calculate tile bounds 
    const tileBounds = tile.getBounds();
    const tokenBounds = tokenLayer.getBounds();
    const intersect = tileBounds.clone().intersection(tokenBounds);
    
    // Setup uniforms
    const uniforms = {
        tileTexture: tile.texture,
        tileRect: new Float32Array([
            intersect.x / tileBounds.width,
            intersect.y / tileBounds.height,
            (intersect.x + intersect.width) / tileBounds.width,
            (intersect.y + intersect.height) / tileBounds.height
        ]),
        chunkSize: 32.0
    };

    // Create and setup render target
    const rt = PIXI.RenderTexture.create({
        width: Math.ceil(intersect.width),
        height: Math.ceil(intersect.height)
    });

    // Draw mask using shader
    const mesh = new PIXI.Mesh(geometry, new PIXI.Shader(program, uniforms));
    renderer.render(mesh, {renderTexture: rt});
    
    return rt;
}