mirror of
https://github.com/godotengine/godot-demo-projects.git
synced 2025-12-16 13:30:07 +01:00
Add an occlusion culling and mesh LOD demo (#801)
This commit is contained in:
43
3d/occlusion_culling_mesh_lod/README.md
Normal file
43
3d/occlusion_culling_mesh_lod/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Occlusion Culling and Mesh LOD
|
||||
|
||||
This demo showcases the use of occlusion culling and mesh level of detail in a 3D scene.
|
||||
|
||||
The demo contains 1,024 identical rooms in a 64×64 grid. The room geometry is
|
||||
used as a basis for a baked OccluderInstance3D, which allows each room to be
|
||||
culled if it is hidden by another room's walls.
|
||||
|
||||
Occlusion culling can provide a performance benefit in draw call-bound
|
||||
scenarios. However, since the Forward Plus backend uses a depth prepass, the
|
||||
shading cost is already reduced by the depth prepass. As a result, occlusion
|
||||
culling will not always result in a tangible performance increase, especially in
|
||||
outdoor scenes that have fewer occlusion culling opportunities. Indoor scenes
|
||||
with no DirectionalLight3D casting shadows are a best-case scenario for
|
||||
occlusion culling.
|
||||
|
||||
The blue spheres make use of automatically generated LODs, which reduce the
|
||||
number of triangles that need to be rendered each frame by the GPU. This
|
||||
provides a significant performance increase in scenes with complex geometry. In
|
||||
most scenes, this provides a greater performance benefit compared to occlusion
|
||||
culling.
|
||||
|
||||
While running the demo, you can toggle the use of mesh LOD and occlusion culling
|
||||
to view the performance difference these features make in this project. Results
|
||||
will vary depending on your CPU and GPU model.
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> If you are using a engine build that is not fully optimized, you may notice
|
||||
> that enabling occlusion culling decreases performance. This is because
|
||||
> occlusion culling is a demanding process on the CPU, which needs all the
|
||||
> build-time optimization it can get.
|
||||
>
|
||||
> Official builds are fully optimized, but self-compiled builds are not fully
|
||||
> optimized by default (use the `optimize=speed use_lto=yes` SCons options).
|
||||
|
||||
Language: GDScript
|
||||
|
||||
Renderer: Forward Plus
|
||||
|
||||
## Screenshot
|
||||
|
||||

|
||||
43
3d/occlusion_culling_mesh_lod/camera.gd
Normal file
43
3d/occlusion_culling_mesh_lod/camera.gd
Normal file
@@ -0,0 +1,43 @@
|
||||
extends Camera3D
|
||||
|
||||
const MOUSE_SENSITIVITY = 0.002
|
||||
const MOVE_SPEED = 1.5
|
||||
|
||||
var rot = Vector3()
|
||||
var velocity = Vector3()
|
||||
|
||||
|
||||
func _ready():
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||
|
||||
|
||||
func _input(event):
|
||||
# Mouse look (only if the mouse is captured).
|
||||
if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
|
||||
# Horizontal mouse look.
|
||||
rot.y -= event.relative.x * MOUSE_SENSITIVITY
|
||||
# Vertical mouse look.
|
||||
rot.x = clamp(rot.x - event.relative.y * MOUSE_SENSITIVITY, -1.57, 1.57)
|
||||
transform.basis = Basis.from_euler(rot)
|
||||
|
||||
if event.is_action_pressed("toggle_mouse_capture"):
|
||||
if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||
else:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||
|
||||
|
||||
func _process(delta):
|
||||
var motion = Vector3(
|
||||
Input.get_axis(&"move_left", &"move_right"),
|
||||
0,
|
||||
Input.get_axis(&"move_forward", &"move_back")
|
||||
)
|
||||
|
||||
# Normalize motion to prevent diagonal movement from being
|
||||
# `sqrt(2)` times faster than straight movement.
|
||||
motion = motion.normalized()
|
||||
|
||||
velocity += MOVE_SPEED * delta * (transform.basis * motion)
|
||||
velocity *= 0.85
|
||||
position += velocity
|
||||
BIN
3d/occlusion_culling_mesh_lod/icon.png
Normal file
BIN
3d/occlusion_culling_mesh_lod/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
34
3d/occlusion_culling_mesh_lod/icon.png.import
Normal file
34
3d/occlusion_culling_mesh_lod/icon.png.import
Normal file
@@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d1a87sh6t6ncl"
|
||||
path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
49
3d/occlusion_culling_mesh_lod/node_3d.gd
Normal file
49
3d/occlusion_culling_mesh_lod/node_3d.gd
Normal file
@@ -0,0 +1,49 @@
|
||||
extends Node3D
|
||||
|
||||
|
||||
func _input(event):
|
||||
if event.is_action_pressed("toggle_occlusion_culling"):
|
||||
get_viewport().use_occlusion_culling = not get_viewport().use_occlusion_culling
|
||||
update_labels()
|
||||
if event.is_action_pressed("toggle_mesh_lod"):
|
||||
get_viewport().mesh_lod_threshold = 1.0 if is_zero_approx(get_viewport().mesh_lod_threshold) else 0.0
|
||||
update_labels()
|
||||
if event.is_action_pressed("cycle_draw_mode"):
|
||||
get_viewport().debug_draw = wrapi(get_viewport().debug_draw + 1, 0, 5)
|
||||
update_labels()
|
||||
|
||||
|
||||
func _process(delta):
|
||||
$Performance.text = """%d FPS (%.2f mspf)
|
||||
|
||||
Currently rendering:
|
||||
%d objects
|
||||
%dK primitive indices
|
||||
%d draw calls
|
||||
""" % [
|
||||
Engine.get_frames_per_second(),
|
||||
1000.0 / Engine.get_frames_per_second(),
|
||||
RenderingServer.get_rendering_info(RenderingServer.RENDERING_INFO_TOTAL_OBJECTS_IN_FRAME),
|
||||
RenderingServer.get_rendering_info(RenderingServer.RENDERING_INFO_TOTAL_PRIMITIVES_IN_FRAME) * 0.001,
|
||||
RenderingServer.get_rendering_info(RenderingServer.RENDERING_INFO_TOTAL_DRAW_CALLS_IN_FRAME),
|
||||
]
|
||||
|
||||
|
||||
func update_labels():
|
||||
$OcclusionCulling.text = "Occlusion culling: %s" % ("Enabled" if get_viewport().use_occlusion_culling else "Disabled")
|
||||
$MeshLOD.text = "Mesh LOD: %s" % ("Enabled" if not is_zero_approx(get_viewport().mesh_lod_threshold) else "Disabled")
|
||||
$DrawMode.text = "Draw mode: %s" % get_draw_mode_string(get_viewport().debug_draw)
|
||||
|
||||
|
||||
func get_draw_mode_string(draw_mode):
|
||||
match draw_mode:
|
||||
0:
|
||||
return "Normal"
|
||||
1:
|
||||
return "Unshaded"
|
||||
2:
|
||||
return "Lighting"
|
||||
3:
|
||||
return "Overdraw"
|
||||
4:
|
||||
return "Wireframe"
|
||||
BIN
3d/occlusion_culling_mesh_lod/node_3d.occ
Normal file
BIN
3d/occlusion_culling_mesh_lod/node_3d.occ
Normal file
Binary file not shown.
3208
3d/occlusion_culling_mesh_lod/node_3d.tscn
Normal file
3208
3d/occlusion_culling_mesh_lod/node_3d.tscn
Normal file
File diff suppressed because it is too large
Load Diff
81
3d/occlusion_culling_mesh_lod/project.godot
Normal file
81
3d/occlusion_culling_mesh_lod/project.godot
Normal file
@@ -0,0 +1,81 @@
|
||||
; Engine configuration file.
|
||||
; It's best edited using the editor UI and not directly,
|
||||
; since the parameters that go here are not all obvious.
|
||||
;
|
||||
; Format:
|
||||
; [section] ; section goes between []
|
||||
; param=value ; assign values to parameters
|
||||
|
||||
config_version=5
|
||||
|
||||
[application]
|
||||
|
||||
config/name="Occlusion Culling and Mesh LOD"
|
||||
config/description="This demo showcases the use of occlusion culling and mesh level of detail in a 3D scene."
|
||||
run/main_scene="res://node_3d.tscn"
|
||||
config/features=PackedStringArray("4.0")
|
||||
config/icon="res://icon.png"
|
||||
|
||||
[display]
|
||||
|
||||
window/vsync/vsync_mode=0
|
||||
window/stretch/mode="canvas_items"
|
||||
window/stretch/aspect="expand"
|
||||
|
||||
[filesystem]
|
||||
|
||||
import/blender/enabled=false
|
||||
|
||||
[input]
|
||||
|
||||
move_forward={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_back={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_right={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_left={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
toggle_occlusion_culling={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":79,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
toggle_mesh_lod={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":76,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
cycle_draw_mode={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
toggle_mouse_capture={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194341,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[rendering]
|
||||
|
||||
anti_aliasing/quality/screen_space_aa=1
|
||||
anti_aliasing/quality/use_debanding=true
|
||||
occlusion_culling/use_occlusion_culling=true
|
||||
BIN
3d/occlusion_culling_mesh_lod/room.blend
Normal file
BIN
3d/occlusion_culling_mesh_lod/room.blend
Normal file
Binary file not shown.
45
3d/occlusion_culling_mesh_lod/room.blend.import
Normal file
45
3d/occlusion_culling_mesh_lod/room.blend.import
Normal file
@@ -0,0 +1,45 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cynp2w1tdntud"
|
||||
path="res://.godot/imported/room.blend-36213b70cb5ffa4e1f840d45a32b9acb.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://room.blend"
|
||||
dest_files=["res://.godot/imported/room.blend-36213b70cb5ffa4e1f840d45a32b9acb.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type="Node3D"
|
||||
nodes/root_name="Scene Root"
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
blender/nodes/visible=2
|
||||
blender/nodes/punctual_lights=true
|
||||
blender/nodes/cameras=true
|
||||
blender/nodes/custom_properties=true
|
||||
blender/nodes/modifiers=1
|
||||
blender/meshes/colors=false
|
||||
blender/meshes/uvs=true
|
||||
blender/meshes/normals=true
|
||||
blender/meshes/tangents=true
|
||||
blender/meshes/skins=2
|
||||
blender/meshes/export_bones_deforming_mesh_only=false
|
||||
blender/materials/unpack_enabled=true
|
||||
blender/materials/export_materials=1
|
||||
blender/animation/limit_playback=true
|
||||
blender/animation/always_sample=true
|
||||
blender/animation/group_tracks=true
|
||||
BIN
3d/occlusion_culling_mesh_lod/room.glb
Normal file
BIN
3d/occlusion_culling_mesh_lod/room.glb
Normal file
Binary file not shown.
29
3d/occlusion_culling_mesh_lod/room.glb.import
Normal file
29
3d/occlusion_culling_mesh_lod/room.glb.import
Normal file
@@ -0,0 +1,29 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://ck4wa4vxhhk1v"
|
||||
path="res://.godot/imported/room.glb-80432416ed16d717e78c80d413b8774e.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://room.glb"
|
||||
dest_files=["res://.godot/imported/room.glb-80432416ed16d717e78c80d413b8774e.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type="Node3D"
|
||||
nodes/root_name="Scene Root"
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
0
3d/occlusion_culling_mesh_lod/screenshots/.gdignore
Normal file
0
3d/occlusion_culling_mesh_lod/screenshots/.gdignore
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
Reference in New Issue
Block a user