Vignettage et grain
Désaturation, vignettage et grain de pellicule sur une scène 3D, en onze lignes qui lisent la texture d'écran.
L'éditeur ci-dessus fait tourner ce projet. Changez une ligne et il se recharge.
Classes Godot utilisées
ColorRectCanvasLayerMeshInstance3D
Le code
scripts/main.gd
extends Node
var pivot: Node3D
func _ready() -> void:
_add_environment()
_add_light()
pivot = Node3D.new()
add_child(pivot)
var meshes: Array[Mesh] = [BoxMesh.new(), SphereMesh.new(), CylinderMesh.new(), TorusMesh.new(), PrismMesh.new()]
for i in meshes.size():
var angle: float = TAU * i / meshes.size()
var material: StandardMaterial3D = StandardMaterial3D.new()
material.albedo_color = Color.from_hsv(float(i) / meshes.size(), 0.45, 0.95)
material.roughness = 0.35
var item: MeshInstance3D = MeshInstance3D.new()
item.mesh = meshes[i]
item.material_override = material
item.position = Vector3(cos(angle), 0, sin(angle)) * 2.6
pivot.add_child(item)
var camera: Camera3D = Camera3D.new()
camera.position = Vector3(0, 3, 6.5)
add_child(camera)
camera.look_at(Vector3.ZERO, Vector3.UP)
camera.current = true
var layer: CanvasLayer = CanvasLayer.new()
layer.layer = 10
add_child(layer)
var material: ShaderMaterial = ShaderMaterial.new()
material.shader = Get.shader('vignette')
var screen: ColorRect = ColorRect.new()
screen.material = material
layer.add_child(screen)
screen.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
print('vignette, desaturation and film grain over a 3D scene, in eleven lines of shader')
func _process(delta: float) -> void:
pivot.rotate_y(delta * 0.3)
func _add_environment() -> void:
var sky_material: ProceduralSkyMaterial = ProceduralSkyMaterial.new()
sky_material.sky_top_color = Color(0.06, 0.07, 0.14)
sky_material.sky_horizon_color = Color(0.16, 0.18, 0.26)
sky_material.ground_bottom_color = Color(0.02, 0.02, 0.04)
sky_material.ground_horizon_color = Color(0.1, 0.11, 0.16)
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(-50, -35, 0)
light.light_energy = 1.2
add_child(light)shaders/vignette.gdshader
shader_type canvas_item;
uniform sampler2D screen_texture: hint_screen_texture, filter_linear;
uniform float vignette_size = 0.75;
uniform float vignette_softness = 0.55;
uniform float grain_amount = 0.06;
uniform float saturation = 0.85;
float hash(vec2 p) {
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}
void fragment() {
vec3 color = texture(screen_texture, SCREEN_UV).rgb;
float gray = dot(color, vec3(0.299, 0.587, 0.114));
color = mix(vec3(gray), color, saturation);
float distance_to_center = length(SCREEN_UV - vec2(0.5)) * 1.4142;
float vignette = 1.0 - smoothstep(vignette_size - vignette_softness, vignette_size, distance_to_center);
color *= vignette;
float grain = hash(SCREEN_UV + fract(TIME)) - 0.5;
color += grain * grain_amount;
COLOR = vec4(color, 1.0);
}