卡通着色
在着色器的 light 函数里把光照量化成色阶,再加一个硬边高光点。
上方的编辑器正在运行这个项目。改一行代码,它就会重新加载。
用到的 Godot 类
MeshInstance3DDirectionalLight3DShaderMaterial
代码
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;
}