
Godot 3d Materials
- 238 installs
- 454 repo stars
- Updated July 28, 2026
- thedivergentai/gd-agentic-skills
Use godot-3d-materials for development tasks
About
godot-3d-materials: A skill for development. This provides functionality for development workflows.
- godot-3d-materials
Godot 3d Materials by the numbers
- 238 all-time installs (skills.sh)
- +22 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,651 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-3d-materialsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 238 |
|---|---|
| repo stars | ★ 454 |
| Last updated | July 28, 2026 |
| Repository | thedivergentai/gd-agentic-skills ↗ |
What it does
Use godot-3d-materials for development tasks
Files
3D Materials
Expert guidance for PBR materials and StandardMaterial3D in Godot.
NEVER Do
- NEVER use separate metallic/roughness/AO textures — Use ORM packing (1 RGB texture with Occlusion/Roughness/Metallic channels) to save texture slots and memory.
- NEVER forget to enable normal_enabled — Normal maps don't work unless you set
normal_enabled = true. Silent failure is common. - NEVER use TRANSPARENCY_ALPHA for cutout materials — Use TRANSPARENCY_ALPHA_SCISSOR or TRANSPARENCY_ALPHA_HASH instead. Full alpha blending is expensive and causes sorting issues.
- NEVER set metallic = 0.5 — Materials are either metallic (1.0) or dielectric (0.0). Values between are physically incorrect except for rust/dirt transitions.
- NEVER use emission without HDR — Emission values > 1.0 only work with HDR rendering enabled in Project Settings.
- NEVER use transparent materials for large environmental surfaces — Transparent objects cannot rely on the Z-buffer for early fragment rejection, resulting in massive overdraw. If only a tiny part of a mesh is transparent, split the mesh into two surfaces: one opaque, one transparent.
- NEVER create hundreds of slightly varied StandardMaterial3D resources if performance is dropping — Godot minimizes GPU state changes by automatically reusing the underlying shader for materials that share the exact same configuration flags (checkboxes). Try to group your material configurations.
- NEVER attempt to fix Z-fighting strictly by moving objects further apart — Floating-point precision degrades over distance. To fix flickering textures, increase your Camera3D's
Nearplane property and decrease theFarproperty to compress the precision range. - NEVER use unique Material resources per MeshInstance3D — This breaks draw call batching. Use 'Instance Uniforms' to vary parameters while keeping a single shared material.
- NEVER use Decals on dynamic moving actors without a Cull Mask — Bullet holes should not stick to the player's face as they walk over them. Mask out character layers.
---
Available Scripts
MANDATORY: Read the appropriate script before implementing the corresponding pattern.
material_fx.gd
Runtime material property animation for damage effects, dissolve, and texture swapping. Use for dynamic material state changes.
pbr_material_builder.gd
Runtime PBR material creation with ORM textures and triplanar mapping.
organic_material.gd
Subsurface scattering and rim lighting setup for organic surfaces (skin, leaves). Use for realistic character or vegetation materials.
triplanar_world.gdshader
Triplanar projection shader for terrain without UV mapping. Blends textures based on surface normals. Use for cliffs, caves, or procedural terrain.
pbr_orm_packer.gd
Expert PBR resource utility. Packs Ambient Occlusion, Roughness, and Metallic into a single ORM texture to optimize VRAM and draw calls.
vertex_wind_sway.gdshader
High-performance GPU-driven foliage animation. Uses vertex world coordinates and vertex color weight painting to simulate wind without skeletons.
triplanar_world_projection.gdshader
UV-less environment mapping. Projects textures along X/Y/Z axes for organic blending over complex rocks and terrain.
subsurface_scattering_setup.gd
Configuring realistic organic materials. Covers Skin Mode, Transmittance, and depth scattering settings for Forward+ rendering.
instance_uniform_batching.gdshader
Architecture pattern for high-speed batching. Allows 10,000 meshes to share one material while maintaining unique colors or health states via instance uniforms.
decal_placer_expert.gd
Dynamic 3D decal system with cull masking and life-cycle management for impact effects.
transparency_sorting_fix.gd
Solving visual artifacts using Alpha Hash and Depth Prepass strategies.
shader_state_manager.gd
Clean pattern for toggling shader-based visual states (Frozen, Burned) on multiple entities.
depth_precision_fix.gd
Camera-side fix for Z-fighting and texture flickering in large-scale worlds.
material_batcher.gd
Global override system to ensure environmental meshes draw in optimized, state-locked batches.
---
StandardMaterial3D Basics
PBR Texture Setup
# Create physically-based material
var mat := StandardMaterial3D.new()
# Albedo (base color)
mat.albedo_texture = load("res://textures/wood_albedo.png")
mat.albedo_color = Color.WHITE # Tint multiplier
# Normal map (surface detail)
mat.normal_enabled = true # CRITICAL: Must enable first
mat.normal_texture = load("res://textures/wood_normal.png")
mat.normal_scale = 1.0 # Bump strength
# ORM Texture (R=Occlusion, G=Roughness, B=Metallic)
mat.orm_texture = load("res://textures/wood_orm.png")
# Alternative: Separate textures (less efficient)
# mat.roughness_texture = load("res://textures/wood_roughness.png")
# mat.metallic_texture = load("res://textures/wood_metallic.png")
# mat.ao_texture = load("res://textures/wood_ao.png")
# Apply to mesh
$MeshInstance3D.material_override = mat---
Metallic vs Roughness
Metal Workflow
# Pure metal (steel, gold, copper)
mat.metallic = 1.0
mat.roughness = 0.2 # Polished metal
mat.albedo_color = Color(0.8, 0.8, 0.8) # Metal tint
# Rough metal (iron, aluminum)
mat.metallic = 1.0
mat.roughness = 0.7Dielectric Workflow
# Non-metal (wood, plastic, stone)
mat.metallic = 0.0
mat.roughness = 0.6 # Typical for wood
mat.albedo_color = Color(0.6, 0.4, 0.2) # Brown wood
# Glossy plastic
mat.metallic = 0.0
mat.roughness = 0.1 # Very smoothTransition Materials (Rust/Dirt)
# Use texture to blend metal/non-metal
mat.metallic_texture = load("res://rust_mask.png")
# White areas (1.0) = metal
# Black areas (0.0) = rust (dielectric)---
Transparency Modes
Decision Matrix
| Mode | Use Case | Performance | Sorting Issues |
|---|---|---|---|
| ALPHA_SCISSOR | Foliage, chain-link fence | Fast | No |
| ALPHA_HASH | Dithered fade, LOD transitions | Fast | Noisy |
| ALPHA | Glass, water, godot-particles | Slow | Yes (render order) |
Alpha Scissor (Cutout)
# For leaves, grass, fences
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA_SCISSOR
mat.alpha_scissor_threshold = 0.5 # Pixels < 0.5 alpha = discarded
mat.albedo_texture = load("res://leaf.png") # Must have alpha channel
# Enable backface culling for performance
mat.cull_mode = BaseMaterial3D.CULL_BACKAlpha Hash (Dithered)
# For smooth fade-outs without sorting issues
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA_HASH
mat.alpha_hash_scale = 1.0 # Dither pattern scale
# Animate fade
var tween := create_tween()
tween.tween_property(mat, "albedo_color:a", 0.0, 1.0)Alpha Blend (Full Transparency)
# For glass, water (expensive)
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
mat.blend_mode = BaseMaterial3D.BLEND_MODE_MIX
# Disable depth writing for correct blending
mat.depth_draw_mode = BaseMaterial3D.DEPTH_DRAW_DISABLED
mat.cull_mode = BaseMaterial3D.CULL_DISABLED # Show both sides---
Advanced Features
Emission (Glowing Materials)
mat.emission_enabled = true
mat.emission = Color(1.0, 0.5, 0.0) # Orange glow
mat.emission_energy_multiplier = 2.0 # Brightness (HDR)
mat.emission_texture = load("res://lava_emission.png")
# Animated emission
func _process(delta: float) -> void:
mat.emission_energy_multiplier = 1.0 + sin(Time.get_ticks_msec() * 0.005) * 0.5Rim Lighting (Fresnel)
mat.rim_enabled = true
mat.rim = 1.0 # Intensity
mat.rim_tint = 0.5 # How much albedo affects rim colorClearcoat (Car Paint)
mat.clearcoat_enabled = true
mat.clearcoat = 1.0 # Layer strength
mat.clearcoat_roughness = 0.1 # Glossy top layerAnisotropy (Brushed Metal)
mat.anisotropy_enabled = true
mat.anisotropy = 1.0 # Directional highlights
mat.anisotropy_flowmap = load("res://brushed_flow.png")---
Texture Channel Packing
ORM Texture (Recommended)
# External tool (GIMP, Substance, Python script):
# Combine 3 grayscale textures into 1 RGB:
# R channel = Ambient Occlusion (bright = no occlusion)
# G channel = Roughness (bright = rough)
# B channel = Metallic (bright = metal)# In Godot:
mat.orm_texture = load("res://textures/material_orm.png")
# This replaces ao_texture, roughness_texture, and metallic_texture!Custom Packing
# If using custom channel assignments:
mat.roughness_texture_channel = BaseMaterial3D.TEXTURE_CHANNEL_GREEN
mat.metallic_texture_channel = BaseMaterial3D.TEXTURE_CHANNEL_BLUE---
Shader Conversion
When to Convert to ShaderMaterial
- Need custom effects (dissolve, vertex displacement)
- StandardMaterial3D limitations hit
- Shader optimizations (remove unused features)
Conversion Workflow
# 1. Create StandardMaterial3D with all settings
var std_mat := StandardMaterial3D.new()
std_mat.albedo_color = Color.RED
std_mat.metallic = 1.0
std_mat.roughness = 0.2
# 2. Convert to ShaderMaterial
var shader_mat := ShaderMaterial.new()
shader_mat.shader = load("res://custom_shader.gdshader")
# 3. Transfer parameters manually
shader_mat.set_shader_parameter("albedo", std_mat.albedo_color)
shader_mat.set_shader_parameter("metallic", std_mat.metallic)
shader_mat.set_shader_parameter("roughness", std_mat.roughness)---
Material Variants (Godot 4.0+)
Efficient Material Reuse
# Base material (shared)
var base_red_metal := StandardMaterial3D.new()
base_red_metal.albedo_color = Color.RED
base_red_metal.metallic = 1.0
# Variant 1: Rough
var rough_variant := base_red_metal.duplicate()
rough_variant.roughness = 0.8
# Variant 2: Smooth
var smooth_variant := base_red_metal.duplicate()
smooth_variant.roughness = 0.1
# Note: Use resource_local_to_scene for per-instance tweaks---
Performance Optimization
Material Batching
# ✅ GOOD: Reuse materials across meshes
const SHARED_STONE := preload("res://materials/stone.tres")
func _ready() -> void:
for wall in get_tree().get_nodes_in_group("stone_walls"):
wall.material_override = SHARED_STONE
# All walls batched in single draw call
# ❌ BAD: Unique material per mesh
func _ready() -> void:
for wall in get_tree().get_nodes_in_group("stone_walls"):
var mat := StandardMaterial3D.new() # New material!
mat.albedo_color = Color(0.5, 0.5, 0.5)
wall.material_override = mat
# Each wall is separate draw callTexture Atlasing
# Combine multiple materials into one texture atlas
# Then use UV offsets to select regions
# material_atlas.gd
extends StandardMaterial3D
func set_atlas_region(tile_x: int, tile_y: int, tiles_per_row: int) -> void:
var tile_size := 1.0 / tiles_per_row
uv1_offset = Vector3(tile_x * tile_size, tile_y * tile_size, 0)
uv1_scale = Vector3(tile_size, tile_size, 1)---
Edge Cases
Normal Maps Not Working
# Problem: Forgot to enable
mat.normal_enabled = true # REQUIRED
# Problem: Wrong texture import settings
# In Import tab: Texture → Normal Map = trueTexture Seams on Models
# Problem: Mipmaps causing seams
# Solution: Disable mipmaps for tightly-packed UVs
# Import → Mipmaps → Generate = falseMaterial Looks Flat
# Problem: Missing normal map or roughness variation
# Solution: Add normal map + roughness texture
mat.normal_enabled = true
mat.normal_texture = load("res://normal.png")
mat.roughness_texture = load("res://roughness.png")---
Common Material Presets
# Glass
func create_glass() -> StandardMaterial3D:
var mat := StandardMaterial3D.new()
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
mat.albedo_color = Color(1, 1, 1, 0.2)
mat.metallic = 0.0
mat.roughness = 0.0
mat.refraction_enabled = true
mat.refraction_scale = 0.05
return mat
# Gold
func create_gold() -> StandardMaterial3D:
var mat := StandardMaterial3D.new()
mat.albedo_color = Color(1.0, 0.85, 0.3)
mat.metallic = 1.0
mat.roughness = 0.3
return mat---
Expert Techniques & Optimizations
1. LOD Transitions using Pixel Dither
When utilizing Hierarchical Level of Detail (HLOD) or Visibility Ranges to fade objects out at a distance, standard alpha blending causes severe performance hits due to overlapping transparent bounds. Instead, configure the Distance Fade mode on your material to Pixel Dither. This provides a perceptually smooth fade while remaining entirely within the high-performance opaque pipeline.
2. Stencil Buffers (Godot 4.5+)
Use the Stencil Buffer directly in StandardMaterial3D. This allows you to easily render outlines or X-ray effects for objects hidden behind walls without needing to write custom shaders for basic effects.
3. AR Shadow Overlay Shader
If you are developing an AR game, you might want virtual shadows to appear on real-world camera feeds. Instead of standard blending, use Godot's built-in shadow_to_opacity render mode in a spatial shader.
shader_type spatial;
// shadow_to_opacity makes the material invisible when lit,
// but opaque (dark) when it receives a shadow from another 3D object.
render_mode blend_mix, depth_draw_opaque, cull_back, shadow_to_opacity;
void fragment() {
// The surface color is black; opacity will be driven by incoming shadows
ALBEDO = vec3(0.0, 0.0, 0.0);
}---
Expert Pattern: Material-Texture-Array (Instanced Variation)
To render hundreds of varied objects (e.g., forest trees, crowd variants) in a single draw call, use Instance Uniforms with a texture array in a custom Spatial shader. This bypasses the need for unique material resources per variation.
The Spatial Shader (texture_array.gdshader)
shader_type spatial;
uniform sampler2D texture_array[4];
// This uniform is unique per GeometryInstance3D node, NOT per material
instance uniform int texture_index;
void fragment() {
vec4 tex_color;
switch (texture_index) {
case 0: tex_color = texture(texture_array[0], UV); break;
case 1: tex_color = texture(texture_array[1], UV); break;
case 2: tex_color = texture(texture_array[2], UV); break;
case 3: tex_color = texture(texture_array[3], UV); break;
}
ALBEDO = tex_color.rgb;
}The GDScript Controller
func apply_variant(mesh_instance: GeometryInstance3D, index: int) -> void:
# Set the per-instance uniform. The underlying material remains shared.
mesh_instance.set_instance_shader_parameter(&"texture_index", index)---
Expert Pattern: Dissolve-Shader-Integration (Alpha Scissor)
For high-performance impact or dissolve effects, use Alpha Scissor transparency. Unlike standard Alpha blending, Scissor allows the mesh to cast shadows and avoids the expensive transparency sorting pipeline.
func trigger_dissolve(mesh: MeshInstance3D, duration: float = 1.0) -> void:
var mat := mesh.get_surface_override_material(0) as StandardMaterial3D
if not mat: return
# 1. Force Alpha Scissor for performance
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA_SCISSOR
# 2. Tween threshold to discard pixels based on noise/albedo alpha
var tween := create_tween()
tween.tween_property(mat, "alpha_scissor_threshold", 1.0, duration).from(0.0)---
Expert Pattern: Material-LOD-System (HLOD)
While Godot handles Mesh LOD (geometry) automatically, it does not simplify material shading at a distance. Use Visibility Ranges to swap meshes and apply simplified materials to reduce fragment shading costs.
func setup_lod_materials(detailed_node: GeometryInstance3D, distant_node: GeometryInstance3D) -> void:
# 1. Detailed version: Hide at 50m
detailed_node.visibility_range_end = 50.0
detailed_node.visibility_range_fade_mode = GeometryInstance3D.VISIBILITY_RANGE_FADE_SELF
# 2. Distant version: Appear at 50m
distant_node.visibility_range_begin = 50.0
distant_node.visibility_range_fade_mode = GeometryInstance3D.VISIBILITY_RANGE_FADE_SELF
# 3. Simplify Distant Material
var dist_mat := distant_node.get_surface_override_material(0) as StandardMaterial3D
if dist_mat:
# Disable expensive shading features for distant LOD
dist_mat.normal_enabled = false
dist_mat.rim_enabled = false
dist_mat.clearcoat_enabled = false
dist_mat.subsurf_scatter_enabled = false
# Use Pixel Dither for seamless, non-transparent fading
dist_mat.distance_fade_mode = BaseMaterial3D.DISTANCE_FADE_PIXEL_DITHERReference
- Master Skill: godot-master
# Dynamic 3D Decal Placer
extends Node3D
## Expert pattern for placing high-performance decals
## (impact holes, footsteps) without mesh generation.
@export var max_decals := 50
var _decal_count := 0
func place_impact_decal(pos: Vector3, normal: Vector3, texture: Texture2D) -> void:
if _decal_count >= max_decals: return
var decal = Decal.new()
add_child(decal)
decal.global_position = pos
decal.look_at(pos + normal, Vector3.UP)
decal.texture_albedo = texture
decal.size = Vector3(0.5, 0.5, 0.5)
# Important: Limit decal influence to environment layers only
decal.cull_mask = 1 # Only affects Layer 1
_decal_count += 1
# Self-destruction timer
get_tree().create_timer(10.0).timeout.connect(func():
decal.queue_free()
_decal_count -= 1
)
# Floating Point Precision Fix (Z-fighting)
extends Camera3D
## Flickering textures (Z-fighting) occur when surfaces are too close.
## Compressing the Viewport precision range fixes this for distant terrain.
func optimize_depth_precision() -> void:
# Architecture Tip: Increase Near as much as usable,
# and decrease Far as much as possible.
near = 0.5 # Default 0.05 is too small for large scenes
far = 500.0 # Default 4000 is way too high for standard indoor/limited outdoor
# This compresses the depth buffer range and grants
# significantly more precision per unit of distance.
# Instance Uniform Shader Batching
shader_type spatial;
## Expert level batching trick: One material shared by 10,000 meshes,
## yet each mesh has a unique color or damage state.
instance uniform vec4 instance_color : source_color = vec4(1.0);
instance uniform float health_ratio = 1.0;
void fragment() {
// Combine shared base albedo with per-instance team color
ALBEDO = instance_color.rgb * health_ratio;
# Architecture Tip: Use set_instance_shader_parameter() in script
# to modify these without triggering a material duplication!
}
# Material Batching and Override logic
extends Node
## Efficiently sharing materials across multiple meshes
## to ensure GPU draw call batching.
func apply_global_material(group_name: String, mat: Material) -> void:
for node in get_tree().get_nodes_in_group(group_name):
if node is MeshInstance3D:
# Override ensures we don't modify the base .mesh file
node.material_override = mat
# Result: All meshes in group now draw in a single state-locked batch.
# skills/3d-materials/scripts/material_fx.gd
extends Node
## Material FX (Expert Pattern)
## Helper to apply damage flash or dissolve effects on StandardMaterial3D.
## Modifies material parameters temporarily.
class_name MaterialFX
static func flash_white(mesh: MeshInstance3D, duration: float = 0.1) -> void:
var mat = mesh.material_override as StandardMaterial3D
if not mat:
mat = mesh.get_active_material(0) as StandardMaterial3D
if not mat: return
# We need a unique material to not flash all enemies
# but duplicating is expensive every hit.
# PRE-REQUISITE: Enemies should have unique materials or MaterialOverlay.
# Using 'material_overlay' is best for flash
var flash_mat = StandardMaterial3D.new()
flash_mat.albedo_color = Color.WHITE
flash_mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
flash_mat.transparency = BaseMaterial3D.TRANSPARENCY_ADD
mesh.material_overlay = flash_mat
var tree = Engine.get_main_loop() as SceneTree
await tree.create_timer(duration).timeout
if is_instance_valid(mesh):
mesh.material_overlay = null
## EXPERT USAGE:
## Call MaterialFX.flash_white(self) on take_damage().
# skills/3d-materials/code/organic_material.gd
extends MeshInstance3D
## Runtime Texture & SSS Manipulation Expert Pattern
## Demonstrates dynamic wetness and organic material tuning.
func set_wetness(value: float) -> void:
# Efficiently update multiple instances via a shared material parameter
# OR per-instance via shader parameters.
var mat := get_active_material(0)
if mat is StandardMaterial3D:
# Standard PBR properties
mat.roughness = lerp(1.0, 0.1, value)
mat.specular = lerp(0.5, 1.0, value)
elif mat is ShaderMaterial:
mat.set_shader_parameter("wetness", value)
func configure_sss(depth: float) -> void:
# Calibrating Subsurface Scattering for skin/flesh
var mat := get_active_material(0) as StandardMaterial3D
if mat:
mat.subsurf_scatter_enabled = true
mat.subsurf_scatter_strength = depth
mat.subsurf_scatter_skin_mode = true
## EXPERT NOTE:
## When updating parameters every frame (like rain), ensure you are caching
## the material reference in _ready() to avoid repeated get_active_material() calls.
# skills/3d-materials/scripts/pbr_material_builder.gd
extends Node
## PBR Material Builder (Expert Pattern)
## Runtime helper to build StandardMaterial3D from texture sets.
## Automatically handles ORM packing if provided.
class_name PBRMaterialBuilder
static func build(albedo: Texture2D, normal: Texture2D = null, orm: Texture2D = null) -> StandardMaterial3D:
var mat = StandardMaterial3D.new()
# 1. Albedo
if albedo:
mat.albedo_texture = albedo
# 2. Normal
if normal:
mat.normal_enabled = true
mat.normal_texture = normal
# 3. ORM (Occlusion, Roughness, Metallic)
if orm:
mat.orm_texture = orm
mat.ao_enabled = true
# Godot Standard: ORM texture (R=AO, G=Rough, B=Metal)
# Verify channel mapping
mat.ao_texture_channel = BaseMaterial3D.TEXTURE_CHANNEL_RED
mat.roughness_texture_channel = BaseMaterial3D.TEXTURE_CHANNEL_GREEN
mat.metallic_texture_channel = BaseMaterial3D.TEXTURE_CHANNEL_BLUE
else:
# Default defaults
mat.roughness = 0.5
mat.metallic = 0.0
return mat
static func build_triplanar(albedo: Texture2D, normal: Texture2D = null) -> StandardMaterial3D:
var mat = build(albedo, normal)
mat.uv1_triplanar = true
return mat
## EXPERT USAGE:
## var mat = PBRMaterialBuilder.build(load("grass_c.png"), load("grass_n.png"))
## $MeshInstance.material_override = mat
# PBR ORM Texture Packer Utility
extends Resource
## Expert pattern: Combine Ambient Occlusion, Roughness, and Metallic
## into one RGB texture (ORM) to save 2 texture slots and GPU memory.
func get_orm_material(albedo: Texture, orm: Texture, normal: Texture) -> StandardMaterial3D:
var mat = StandardMaterial3D.new()
mat.albedo_texture = albedo
# Mandatory channel mapping for ORM
mat.orm_texture = orm # R=AO, G=Rough, B=Metal
mat.normal_enabled = true
mat.normal_texture = normal
# Optimization: Use triplanar in world space for large terrain meshes
mat.uv1_triplanar = true
mat.uv1_world_triplanar = true
return mat
# Shader Variant State Manager
extends MeshInstance3D
## Architectural pattern for swapping material states
## (e.g., Frozen, Burnt, Dissolved) without resource duplication.
func set_dissolve_strength(v: float) -> void:
var mat = get_active_material(0)
if mat is ShaderMaterial:
mat.set_shader_parameter("dissolve_amount", v)
func set_frozen_state(enabled: bool) -> void:
var mat = get_active_material(0)
if mat is ShaderMaterial:
# Using a float uniform as a boolean for efficiency
mat.set_shader_parameter("is_frozen", 1.0 if enabled else 0.0)
# Subsurface Scattering Configuration
extends MeshInstance3D
## PBR expert setup for wax, skin, and translucent organic materials.
func configure_organic_sss() -> void:
var mat = StandardMaterial3D.new()
# 1. Base SSS (Forward+ Renderer Required)
mat.subsurf_scatter_enabled = true
mat.subsurf_scatter_strength = 0.8
# 2. Skin Mode optimization (Tints red for dermal scattering)
mat.subsurf_scatter_skin_mode = true
# 3. Transmittance (Light passing through thin mesh parts like ears)
mat.subsurf_scatter_transmittance_enabled = true
mat.subsurf_scatter_transmittance_color = Color(1.0, 0.4, 0.3)
mat.subsurf_scatter_transmittance_depth = 0.1
material_override = mat
# Transparency Sorting Fix Logic
extends MeshInstance3D
## Resolves artifacts where back surfaces appear in front of closer ones.
## Covers Alpha Scissor vs Alpha Hash vs Depth Draw strategies.
func use_cutout_transparency() -> void:
var mat = material_override as StandardMaterial3D
# Best performance, writes to depth buffer, casts shadows
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA_SCISSOR
mat.alpha_scissor_threshold = 0.5
func use_dithered_transparency() -> void:
var mat = material_override as StandardMaterial3D
# Perceptually smooth fade, no sorting artifacts, slower than scissor
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA_HASH
func enforce_depth_prepass() -> void:
var mat = material_override as StandardMaterial3D
# Resolves overlapping alpha-blended sorting issues
mat.depth_draw_mode = BaseMaterial3D.DEPTH_DRAW_ALWAYS
# Triplanar World Texture Projection
shader_type spatial;
## UV-less mapping for cliffs, caves, or procedural meshes.
## Blends textures based on surface normals.
uniform sampler2D wall_texture : source_color;
uniform float triplanar_sharpness = 2.0;
void fragment() {
vec3 blending = abs(NORMAL);
blending /= (blending.x + blending.y + blending.z);
blending = pow(blending, vec3(triplanar_sharpness));
blending /= (blending.x + blending.y + blending.z);
vec3 x_tex = texture(wall_texture, VERTEX.zy).rgb;
vec3 y_tex = texture(wall_texture, VERTEX.xz).rgb;
vec3 z_tex = texture(wall_texture, VERTEX.xy).rgb;
ALBEDO = x_tex * blending.x + y_tex * blending.y + z_tex * blending.z;
}
// skills/3d-materials/code/triplanar_world.gdshader
shader_type ruby; // Using Ruby syntax highlighting as a placeholder for GDShader if needed, but the extension will be .gdshader
shader_type spatial;
## World-Aligned Triplanar Expert shader
## Perfect for terrain, caves, or architectural meshes.
uniform sampler2D wall_texture : source_color;
uniform float uv_scale = 1.0;
varying vec3 world_pos;
varying vec3 world_normal;
void vertex() {
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
world_normal = abs(NORMAL);
}
void fragment() {
// 1. Calculate triplanar blending weights
vec3 blending = world_normal;
blending /= (blending.x + blending.y + blending.z);
// 2. Project textures on 3 axes
vec3 x_tex = texture(wall_texture, world_pos.zy * uv_scale).rgb;
vec3 y_tex = texture(wall_texture, world_pos.xz * uv_scale).rgb;
vec3 z_tex = texture(wall_texture, world_pos.xy * uv_scale).rgb;
// 3. Blend based on normal direction
ALBEDO = x_tex * blending.x + y_tex * blending.y + z_tex * blending.z;
}
# High-Performance GPU Wind Sway
shader_type spatial;
## Expert level vertex sway. No bone overhead.
## Uses vertex colors (REd channel) as depth weight for foliage.
render_mode depth_prepass_alpha, cull_disabled, world_vertex_coords;
uniform sampler2D texture_albedo : source_color;
uniform float sway_speed = 1.0;
uniform float sway_strength = 0.05;
void vertex() {
// Weight derived from vertex painting
float weight = COLOR.r;
float time_offset = TIME * sway_speed + VERTEX.x + VERTEX.z;
VERTEX.x += sin(time_offset) * sway_strength * weight;
VERTEX.z += cos(time_offset) * sway_strength * weight;
}
void fragment() {
vec4 albedo_tex = texture(texture_albedo, UV);
ALBEDO = albedo_tex.rgb;
ALPHA = albedo_tex.a;
ALPHA_SCISSOR_THRESHOLD = 0.5;
}
shader_type spatial;
/**
* Expert Triplanar Smoothing Shader - Godot 4.6
* Prevents texture stretching on procedural meshes or steep slopes by
* projecting from three axes in World Space.
*/
render_mode world_vertex_coords;
uniform sampler2D albedo_tex : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
uniform float texture_scale = 1.0;
uniform float blend_sharpness = 4.0;
varying vec3 v_world_pos;
varying vec3 v_world_normal;
void vertex() {
v_world_pos = VERTEX;
v_world_normal = NORMAL;
}
void fragment() {
// 1. Calculate Weights
vec3 weights = abs(v_world_normal);
weights = pow(weights, vec3(blend_sharpness));
weights /= (weights.x + weights.y + weights.z);
// 2. Project UVs
vec2 uv_x = v_world_pos.zy * texture_scale;
vec2 uv_y = v_world_pos.xz * texture_scale;
vec2 uv_z = v_world_pos.xy * texture_scale;
// 3. Sample and Blend
vec3 samp_x = texture(albedo_tex, uv_x).rgb;
vec3 samp_y = texture(albedo_tex, uv_y).rgb;
vec3 samp_z = texture(albedo_tex, uv_z).rgb;
ALBEDO = (samp_x * weights.x) + (samp_y * weights.y) + (samp_z * weights.z);
}