Sombreamento toon

Iluminação dividida em faixas dentro da função light do shader, com um ponto especular duro.

O editor acima está rodando este projeto. Mude uma linha e ele recarrega.

Classes do Godot usadas

MeshInstance3DDirectionalLight3DShaderMaterial

O código

scripts/main.gd
extends Node

var shapes: Node3D


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

	shapes = Node3D.new()
	add_child(shapes)

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

	for i in meshes.size():
		var material: ShaderMaterial = ShaderMaterial.new()
		material.shader = Get.shader('toon')
		material.set_shader_parameter('bands', 2 + i)

		var item: MeshInstance3D = MeshInstance3D.new()
		item.mesh = meshes[i]
		item.material_override = material
		item.position = Vector3((i - 1) * 2.8, 0, 0)
		shapes.add_child(item)

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

	print('2, 3 and 4 light bands, written in the shader light function')


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


func _add_environment() -> void:
	var env: Environment = Environment.new()
	env.background_mode = Environment.BG_COLOR
	env.background_color = Color(0.05, 0.06, 0.09)
	env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
	env.ambient_light_color = Color(0.1, 0.11, 0.16)
	# a strong ambient washes the bands out and the toon becomes a gradient
	env.ambient_light_energy = 0.15

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


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

uniform vec3 base_color: source_color = vec3(0.35, 0.6, 0.95);
uniform vec3 shadow_color: source_color = vec3(0.08, 0.1, 0.2);
uniform int bands = 3;
uniform float specular_size = 0.02;

void fragment() {
	ALBEDO = base_color;
}

void light() {
	float ndotl = max(dot(normalize(NORMAL), normalize(LIGHT)), 0.0);
	// with floor the top band stops at (bands-1)/bands and the light never reaches full
	float banded = ceil(ndotl * float(bands)) / float(bands);

	DIFFUSE_LIGHT += mix(shadow_color, ALBEDO * LIGHT_COLOR, banded) * ATTENUATION;

	vec3 half_vector = normalize(normalize(LIGHT) + normalize(VIEW));
	float glare = pow(max(dot(normalize(NORMAL), half_vector), 0.0), 64.0);

	SPECULAR_LIGHT += LIGHT_COLOR * step(1.0 - specular_size, glare) * ATTENUATION;
}

Mais exemplos de Shaders