Mapeado triplanar

Cuatro mallas sin mapa UV, texturizadas proyectando desde los tres ejes del mundo y mezclando por la normal.

El editor de arriba está ejecutando este proyecto. Cambia una línea y se recarga.

Clases de Godot usadas

MeshInstance3DNoiseTexture2DShaderMaterial

El código

scripts/main.gd
extends Node

var shapes: Node3D


func _ready() -> void:
	_add_environment()
	_add_light()

	var material: ShaderMaterial = ShaderMaterial.new()
	material.shader = Get.shader('triplanar')
	material.set_shader_parameter('noise_texture', _make_noise())

	shapes = Node3D.new()
	add_child(shapes)

	var meshes: Array[Mesh] = [BoxMesh.new(), SphereMesh.new(), TorusMesh.new(), CylinderMesh.new()]

	for i in meshes.size():
		var item: MeshInstance3D = MeshInstance3D.new()
		item.mesh = meshes[i]
		item.material_override = material
		item.position = Vector3((i - 1.5) * 1.9, 0, 0)
		item.rotation = Vector3(0.4, 0.8, 0.2)
		shapes.add_child(item)

	var camera: Camera3D = Camera3D.new()
	camera.position = Vector3(0, 1.2, 5.4)
	add_child(camera)
	camera.look_at(Vector3.ZERO, Vector3.UP)
	camera.current = true

	print('four meshes, no UV map: the texture is projected from the three world axes')


func _process(delta: float) -> void:
	for shape in shapes.get_children():
		shape.rotate_y(delta * 0.35)


func _make_noise() -> NoiseTexture2D:
	var noise: FastNoiseLite = FastNoiseLite.new()
	noise.noise_type = FastNoiseLite.TYPE_CELLULAR
	noise.frequency = 0.02
	noise.fractal_octaves = 3

	var texture: NoiseTexture2D = NoiseTexture2D.new()
	texture.noise = noise
	texture.seamless = true
	texture.width = 256
	texture.height = 256

	return texture


func _add_environment() -> void:
	var sky_material: ProceduralSkyMaterial = ProceduralSkyMaterial.new()
	sky_material.sky_top_color = Color(0.08, 0.1, 0.18)
	sky_material.sky_horizon_color = Color(0.2, 0.22, 0.3)
	sky_material.ground_bottom_color = Color(0.03, 0.03, 0.05)
	sky_material.ground_horizon_color = Color(0.12, 0.13, 0.18)

	var sky: Sky = Sky.new()
	sky.sky_material = sky_material

	var env: Environment = Environment.new()
	env.background_mode = Environment.BG_SKY
	env.sky = sky
	env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
	env.ambient_light_energy = 0.9

	var world: WorldEnvironment = WorldEnvironment.new()
	world.environment = env
	add_child(world)


func _add_light() -> void:
	var light: DirectionalLight3D = DirectionalLight3D.new()
	light.rotation_degrees = Vector3(-45, -35, 0)
	light.light_energy = 1.3
	add_child(light)
shaders/triplanar.gdshader
shader_type spatial;

uniform sampler2D noise_texture: filter_linear_mipmap, repeat_enable;
uniform vec3 tint_a: source_color = vec3(0.09, 0.22, 0.16);
uniform vec3 tint_b: source_color = vec3(0.85, 0.75, 0.5);
uniform float texture_scale = 0.9;
uniform float blend_sharpness = 4.0;

varying vec3 world_position;
varying vec3 world_normal;

void vertex() {
	world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
	world_normal = normalize((MODEL_MATRIX * vec4(NORMAL, 0.0)).xyz);
}

void fragment() {
	vec3 blend = pow(abs(world_normal), vec3(blend_sharpness));
	blend /= blend.x + blend.y + blend.z;

	float x = texture(noise_texture, world_position.zy * texture_scale).r;
	float y = texture(noise_texture, world_position.xz * texture_scale).r;
	float z = texture(noise_texture, world_position.xy * texture_scale).r;

	float value = x * blend.x + y * blend.y + z * blend.z;

	ALBEDO = mix(tint_a, tint_b, value);
	ROUGHNESS = 0.8;
}

Más ejemplos de Shaders