mirror of
https://github.com/godotengine/godot-demo-projects.git
synced 2025-12-16 05:20:06 +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