【Shadow Mapping】ShadowTactics/Desperado/Commando Enemy FOV

Shader code
#DepthBuffer Compositor Effect#
@tool
extends CompositorEffect
class_name DepthBuffer

var output_texture:RID
@export var texture_size:Vector2 = Vector2(1024,1024)

func _init() -> void:
	# print("DepthBuffer: Initializing...")  # Debug info
	# 使用 PRE_OPAQUE 回调
	effect_callback_type = CompositorEffect.EFFECT_CALLBACK_TYPE_PRE_OPAQUE
	RenderingServer.call_on_render_thread(_initialize_compute)
	# print("DepthBuffer: Effect callback type set to: ", effect_callback_type)  # Debug info

func _notification(what:int) -> void:
	if what == NOTIFICATION_PREDELETE:
		# print("DepthBuffer: Cleaning up...")  # Debug info
		if output_texture.is_valid():
			rd.free_rid(output_texture)
		if shader.is_valid():
			rd.free_rid(shader)
		if nearest_sampler.is_valid():
			rd.free_rid(nearest_sampler)

var rd : RenderingDevice
var nearest_sampler: RID
var shader : RID
var pipeline : RID

func _initialize_compute()->void:
	# print("DepthBuffer: Initializing compute...")  # Debug info
	rd = RenderingServer.get_rendering_device()
	
	if !rd:
		# print("DepthBuffer: No rendering device!")  # Debug info
		return

	# Create output texture
	var format := RDTextureFormat.new()
	format.width = texture_size.x
	format.height = texture_size.y
	format.format = RenderingDevice.DATA_FORMAT_R32_SFLOAT
	format.usage_bits = RenderingDevice.TEXTURE_USAGE_STORAGE_BIT | RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT
	output_texture = rd.texture_create(format, RDTextureView.new(), [])
	# print("DepthBuffer: Created output texture")  # Debug info

	var sampler_state: RDSamplerState = RDSamplerState.new()
	sampler_state.min_filter = RenderingDevice.SAMPLER_FILTER_LINEAR
	sampler_state.mag_filter = RenderingDevice.SAMPLER_FILTER_LINEAR
	sampler_state.repeat_u = RenderingDevice.SAMPLER_REPEAT_MODE_REPEAT
	sampler_state.repeat_v = RenderingDevice.SAMPLER_REPEAT_MODE_REPEAT
	nearest_sampler = rd.sampler_create(sampler_state)
	# print("DepthBuffer: Created sampler")  # Debug info

	var shader_file: RDShaderFile = load("res://RenderingEffects/DepthBuffer/depth_buffer.glsl")
	var shader_spirv: RDShaderSPIRV = shader_file.get_spirv()
	shader = rd.shader_create_from_spirv(shader_spirv)
	pipeline = rd.compute_pipeline_create(shader)
	# print("DepthBuffer: Created shader and pipeline")  # Debug info

func _render_callback(p_effect_callback_type:int, p_render_data: RenderData)->void:	
	# print("DepthBuffer: Render callback called with type: ", p_effect_callback_type)  # Debug info
	
	if not output_texture.is_valid():		
		# print("DepthBuffer: Output texture is invalid!")  # Debug info
		return
		
	if rd and p_effect_callback_type == CompositorEffect.EFFECT_CALLBACK_TYPE_PRE_OPAQUE:
		var render_scene_buffers : RenderSceneBuffersRD = p_render_data.get_render_scene_buffers()
		if render_scene_buffers:
			var size:Vector2i = Vector2i(texture_size)
			# print("DepthBuffer: Using texture size: ", size)  # Debug info

			var x_groups:int = (size.x - 1) / 16 + 1
			var y_groups:int = (size.y - 1) / 16 + 1

			var view_count:int = render_scene_buffers.get_view_count()
			# print("DepthBuffer: View count: ", view_count)  # Debug info
			
			for view in range(view_count):
				var depth_tex: RID = render_scene_buffers.get_depth_layer(view)
				if not depth_tex.is_valid():
					# print("DepthBuffer: Invalid depth texture for view ", view)  # Debug info
					continue
					
				# print("DepthBuffer: Processing depth texture for view ", view)  # Debug info
			
				var depth_uniform : RDUniform = RDUniform.new()
				depth_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE
				depth_uniform.binding = 0
				depth_uniform.add_id(nearest_sampler)
				depth_uniform.add_id(depth_tex)

				var depth_copy_uniform : RDUniform = RDUniform.new()
				depth_copy_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE
				depth_copy_uniform.binding = 1
				depth_copy_uniform.add_id(output_texture)

				var params: PackedFloat32Array = PackedFloat32Array()
				params.push_back(size.x)
				params.push_back(size.y)
				var params_buffer: RID = rd.storage_buffer_create(params.size()* 4, params.to_byte_array())

				var params_uniform : RDUniform = RDUniform.new()
				params_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
				params_uniform.binding = 2
				params_uniform.add_id(params_buffer)

				var uniform_set: RID = rd.uniform_set_create([depth_uniform, depth_copy_uniform, params_uniform],shader,0)
		
				var compute_list := rd.compute_list_begin()
				rd.compute_list_bind_compute_pipeline(compute_list, pipeline)
				rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0)
				rd.compute_list_dispatch(compute_list, x_groups, y_groups, 1)
				rd.compute_list_end()

				rd.free_rid(uniform_set)
				rd.free_rid(params_buffer)
		else:
			# print("DepthBuffer: No render scene buffers!")  # Debug info
			pass
	else:
		# print("DepthBuffer: Invalid render device or wrong callback type!")  # Debug info
		pass


#Depth Buffer GLSL File#
#[compute]
#version 450

layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;

layout(set = 0, binding = 0) uniform sampler2D depth_buffer;
layout(r32f, set = 0, binding = 1) uniform restrict writeonly image2D depth_copy;

layout(set = 0, binding = 2, std430) restrict buffer Params {
    vec2 resolution;
} params;

void main() {
    ivec2 xy = ivec2(gl_GlobalInvocationID.xy);
    vec2 uv = vec2(xy) / params.resolution;
    
    float depth = texture(depth_buffer, uv).r;
    
    // 直接输出原始深度值,不做任何处理
    imageStore(depth_copy, xy, vec4(depth, 0.0, 0.0, 0.0));
}

#Custom Decal GDscript#
extends MeshInstance3D
class_name DecalBox

@onready var depth_camera: Camera3D = %DepthCamera
@onready var half_depth_camera: Camera3D = %HalfDepthCamera
@onready var fov: Node3D = %FOV




func _ready() -> void:
	await RenderingServer.frame_post_draw
	var effect : DepthBuffer = depth_camera.compositor.compositor_effects[0]
	var texture_rd = Texture2DRD.new()
	texture_rd.texture_rd_rid = effect.output_texture
	get_active_material(0).set_shader_parameter("depth_camera_texture", texture_rd)
	var effect1 : DepthBuffer = half_depth_camera.compositor.compositor_effects[0]
	var texture_rd1 = Texture2DRD.new()
	texture_rd1.texture_rd_rid = effect1.output_texture
	get_active_material(0).set_shader_parameter("half_depth_camera_texture", texture_rd1)
	
	


func _process(delta: float) -> void:
	global_rotation.y = 0.0
	depth_camera.fov = rad_to_deg(get_active_material(0).get_shader_parameter("fov_angle") * TAU)
	half_depth_camera.fov = depth_camera.fov
	
	get_active_material(0).set_shader_parameter("decal_inv_transform", global_transform.affine_inverse())
	var view_matrix: Transform3D = depth_camera.global_transform.affine_inverse()
	var projection_matrix: Projection = depth_camera.get_camera_projection()

	get_active_material(0).set_shader_parameter("depth_camera_view_matrix",view_matrix)
	get_active_material(0).set_shader_parameter("depth_camera_projection_matrix",projection_matrix)
	get_active_material(0).set_shader_parameter("rotation",fov.global_rotation.y)




#CustomDecal Shader :MeshInstance-Box instance#
shader_type spatial;
render_mode cull_back,unshaded;

uniform sampler2D depth_texture : hint_depth_texture;
uniform sampler2D depth_camera_texture;
uniform sampler2D half_depth_camera_texture;
uniform sampler2D normal_texture : hint_normal_roughness_texture;
uniform mat4 decal_inv_transform;
uniform mat4 depth_camera_view_matrix;
uniform mat4 depth_camera_projection_matrix;


uniform vec4 fov_color : source_color;
uniform float fov_angle : hint_range(0.0, 0.5) = 0.25;
uniform int stripe_count : hint_range(1, 500) = 100; // 斑马圈数量
uniform float rotation : hint_range(-3.14, 3.14) = 0.0; // 旋转角度(弧度)
uniform float normal_fade : hint_range(0.0, 1.0) = 0.4;
uniform float full_radius :hint_range (0.0,0.5,0.01) = 0.25;

void fragment() {
	float depth = texture(depth_texture, SCREEN_UV).x;
	vec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, depth);
	vec4 world = INV_VIEW_MATRIX * INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
	vec3 world_position = world.xyz / world.w;

	mat4 depth_camera_view_project_matrix = depth_camera_projection_matrix * depth_camera_view_matrix;
	vec4 depth_camera_space_pos = depth_camera_view_project_matrix * vec4(world_position, 1.0);
	vec3 shadow_uv = depth_camera_space_pos.xyz / depth_camera_space_pos.w;
	shadow_uv = shadow_uv * 0.5 + 0.5; // NDC to UV
	shadow_uv.y = 1.0 - shadow_uv.y;   // 反转Y轴
	float shadow_depth = texture(depth_camera_texture, clamp(shadow_uv.xy, 0.0, 1.0)).r;
	float half_shadow_depth = texture(half_depth_camera_texture, clamp(shadow_uv.xy, 0.0, 1.0)).r;
	// 世界坐标转到 Cube 局部空间
	vec4 local_pos = decal_inv_transform * vec4(world_position, 1.0);

	// 视角控制
	vec2 uv = local_pos.xz;
	vec2 dir = uv;
	float r = length(dir);
	float angle = atan(dir.y, dir.x);

	// 应用旋转和视角限制
	angle = mod(angle + rotation + PI * 0.5, 2.0 * PI);
	float normalized_angle = angle / (2.0 * PI);
	float half_fov = fov_angle * 0.5;

	// 斑马线效果
	float stripe = mod(floor(r * float(stripe_count)), 2.0);


	// 检查半径范围
	if (r > 0.5 || r < 0.02) {
		discard;
	}
	// 检查是否在视角范围内
	if (normalized_angle > half_fov && normalized_angle < (1.0 - half_fov)) {
		discard;
	}


	// 法线检查
	vec3 normal = normalize(texture(normal_texture, SCREEN_UV).xyz * 2.0 - 1.0);
	float fade = clamp(normal.y, 0.0, 1.0);
	if (fade < normal_fade) {
		discard;
	}

	bool visual_area = shadow_uv.z <=  1.0 - shadow_depth + 0.00001;
	bool half_visual_area = shadow_uv.z <=  1.0 - half_shadow_depth + 0.00001;
	if (visual_area) {
		if (r > full_radius || !half_visual_area) {
			if (stripe >= 1.0) discard;
		}
		ALBEDO = fov_color.rgb;
		ALPHA = fov_color.a;
	} else {
		ALBEDO = vec3(.0,.0,.0);
		ALPHA = 0.5;
	}
}
Live Preview
Tags
CompositorEffect, FOV, Shadow Mapping
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from JasonKim

Related shaders

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments