How Block Culling Works in Bedrock
When you use the standard full-block unit cube, Minecraft Bedrock automatically hides any face touching an opaque neighbor. The moment you switch to a custom model using the "minecraft:geometry" component, the engine can no longer assume your block fills the entire 16x16x16 space. By default, it draws every single polygon in your model, even faces pressed flat against stone or dirt.
To fix this without forcing the engine to guess your model's shape, Bedrock uses a dedicated culling system split across both packs. In the Resource Pack, a JSON file inside the "block_culling/" directory maps specific bones, cubes, and faces of your Blockbench model to directional neighbor checks. In the Behavior Pack, the "minecraft:geometry" component binds that culling rule to the block by its identifier.
This approach is essential for two reasons: rendering performance and visual cleanliness. If a player builds a wall out of 500 custom framed-glass blocks, unculled inner faces will tank the frame rate and create ugly overlapping translucent surfaces. Proper culling strips those interior faces out of the mesh before it goes to the GPU.
Linking Culling in the Behavior Pack
Before writing the culling rules themselves, your block needs to know which culling definition to use. This happens inside the expanded object form of the "minecraft:geometry" component in your Behavior Pack block file.
If you normally write "minecraft:geometry": "geometry.my_block", you must convert that shorthand string into an object containing the "identifier" and "culling" properties. The "culling" string must have a namespace—and unlike geometry identifiers, it cannot use the "minecraft:" namespace. It must use your own pack's custom namespace.
{
"format_version": "1.20.60",
"minecraft:block": {
"description": {
"identifier": "wiki:framed_glass"
},
"components": {
"minecraft:geometry": {
"identifier": "geometry.wiki_framed_glass",
"culling": "wiki:cull.framed_glass"
},
"minecraft:material_instances": {
"*": {
"texture": "wiki_framed_glass",
"render_method": "blend"
}
}
}
}
}Writing the Block Culling File
Block culling files live in the "RP/block_culling/" folder of your Resource Pack. The file name does not technically matter to the engine, as the game matches the rule using the "identifier" inside the JSON, though naming the file after your block keeps the pack organized.
Inside "minecraft:block_culling_rules", you define a "rules" array. Each entry in this array targets one specific part of your geometry and tells the game which neighboring direction to check. If a neighbor block in that direction meets the culling condition, the targeted geometry part disappears.
You can cull an entire bone, a whole cube inside a bone, or a single face on a specific cube. When targeting a cube, you index it using a zero-based integer corresponding to its order in the geometry file's "cubes" array for that bone.
| Field | What it does | Notes |
|---|---|---|
| direction | Specifies which adjacent block space triggers this rule. | Accepts "north", "south", "east", "west", "up", or "down". |
| geometry_part.bone | Names the bone in the geometry file to target. | Required for every rule. Case-sensitive. |
| geometry_part.cube | Zero-based index of the cube inside the chosen bone. | Optional. Omit this and "face" to cull the entire bone. |
| geometry_part.face | The specific UV face of the targeted cube to hide. | Optional. Requires "cube" to be set. Accepts the six cardinal directions. |
| condition | Determines how the neighbor is evaluated for culling. | Optional. Used for matching specific neighbor criteria like identical block types. |
{
"format_version": "1.20.60",
"minecraft:block_culling_rules": {
"description": {
"identifier": "wiki:cull.framed_glass"
},
"rules": [
{
"direction": "north",
"geometry_part": {
"bone": "frame",
"cube": 0,
"face": "north"
}
},
{
"direction": "south",
"geometry_part": {
"bone": "frame",
"cube": 0,
"face": "south"
}
},
{
"direction": "up",
"geometry_part": {
"bone": "top_cap"
}
}
]
}
}Matching the Geometry Structure
Your culling rules are tightly coupled to how your model is structured in Blockbench. If you move a cube from one bone to another, or reorder cubes inside a bone, the integer index in "geometry_part.cube" will point to the wrong piece of geometry.
Because cube indices are fragile when editing models, it is often cleaner to separate parts that get culled together into their own dedicated bones. For example, if you are making a pipe or fence post where an entire connector arm disappears when nothing is next to it, put that arm in a bone named "north_arm" and omit the "cube" and "face" keys in your culling rule. If you are only stripping the outer faces of a single cube, keep that cube at index 0 of a named bone.
{
"format_version": "1.12.0",
"minecraft:geometry": [
{
"description": {
"identifier": "geometry.wiki_framed_glass",
"texture_width": 16,
"texture_height": 16,
"visible_bounds_width": 2,
"visible_bounds_height": 2,
"visible_bounds_offset": [0, 0.5, 0]
},
"bones": [
{
"name": "frame",
"pivot": [0, 0, 0],
"cubes": [
{
"origin": [-8, 0, -8],
"size": [16, 16, 16],
"uv": {
"north": {"uv": [0, 0], "uv_size": [16, 16]},
"east": {"uv": [0, 0], "uv_size": [16, 16]},
"south": {"uv": [0, 0], "uv_size": [16, 16]},
"west": {"uv": [0, 0], "uv_size": [16, 16]},
"up": {"uv": [0, 0], "uv_size": [16, 16]},
"down": {"uv": [0, 0], "uv_size": [16, 16]}
}
}
]
},
{
"name": "top_cap",
"pivot": [0, 0, 0],
"cubes": [
{
"origin": [-4, 15, -4],
"size": [8, 1, 8],
"uv": [0, 0]
}
]
}
]
}
]
}Rotation and Transformation Caveats
When you rotate a custom block using the "minecraft:transformation" component inside permutations (for example, rotating a directional pillar or stair block based on cardinal direction), Bedrock rotates the culling directions along with the geometry.
This means you only need to write your culling rules for the default, unrotated state of the model in Blockbench. If your rule says to cull the "north" face of cube 0 when a block is to the "north", and a permutation rotates the block 90 degrees clockwise around the Y axis, the game automatically checks the "east" neighbor and hides the newly east-facing polygon.
Which versions this applies to
Stable in format_version 1.20.60 and later. No experimental toggles are required to use block_culling_rules or the culling property on minecraft:geometry.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| Content Log Error: [Blocks][error]-wiki:framed_glass | minecraft:geometry | culling: Invalid culling identifier | You either forgot to include a namespace in the culling identifier (e.g., writing "cull.framed_glass" instead of "wiki:cull.framed_glass"), or you used the reserved "minecraft:" namespace. | Ensure the identifier in both the Behavior Pack geometry component and the Resource Pack culling description uses your custom namespace separated by a colon. |
| The block loads without errors, but faces never cull when placed next to solid blocks. | The bone name in "geometry_part" does not exactly match the bone name in your .geo.json file, or your Resource Pack format_version in the culling file is older than 1.20.60. | Check that "format_version" is "1.20.60" or higher in both the block and culling JSON files, and verify the exact spelling and capitalization of your bone names. |
| Placing a custom translucent block next to another identical custom block still shows the inner faces between them. | By default, standard culling rules only trigger when the neighboring block is a full, opaque cube. A neighbor that also uses custom geometry or non-opaque materials will not trigger the default neighbor check. | Make sure your culling rule includes the appropriate condition check for matching blocks or non-full neighbors, or verify that the neighbor block is actually considered solid by the engine. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
