Compare commits
428 Commits
universal-
...
temp-viewp
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d78f0e12f | |||
| 74e9cad082 | |||
| 9d3fe9c60f | |||
| c654c7425f | |||
| 2b940716a1 | |||
| a5799f126d | |||
| a22dbfe116 | |||
| b0ae0e9559 | |||
| b96035aabd | |||
| 5f5ee2718e | |||
| d690b77840 | |||
| e7af4e486b | |||
| e7559019d4 | |||
| d928a42791 | |||
| 1bc006f0f5 | |||
| 7f8e7c7cda | |||
| d4efcef5e3 | |||
| 09d6ce320f | |||
| 6a22de3927 | |||
| e7d06c0bf2 | |||
| a87428f533 | |||
| d2a102214d | |||
| dc44f844c9 | |||
| 34c86aa389 | |||
| 3961f4fd27 | |||
| 2cbfeca07e | |||
| 7865678225 | |||
| a4f173b0f9 | |||
| 376f84e244 | |||
| a71a3a6267 | |||
| fd387c3100 | |||
| f4b15ae0b2 | |||
| d4b7f2e119 | |||
| b11d0d55cd | |||
| de214ddecb | |||
| 09bebf1fed | |||
| d7d22a4a8d | |||
| d507c55142 | |||
| fa85fded07 | |||
| 2f6991cbeb | |||
| 6f9f534149 | |||
| de7cfdb7b6 | |||
| ab4c98d943 | |||
| fb7132386c | |||
| a05431df88 | |||
| bd8e67779c | |||
| b705c4396d | |||
| d2a2c45f6b | |||
| c0e3d2c103 | |||
| 85252f3cba | |||
| 7881629e1e | |||
| 9ff695606d | |||
| 7931716b69 | |||
| 16fa14be2c | |||
| bedc219f85 | |||
| 7cf1902002 | |||
| 6e65439632 | |||
| 90d6ee8b6c | |||
| a7195ad450 | |||
| 2e9ea4e9d4 | |||
| babd96b677 | |||
| fa93f9f5b3 | |||
| b7d224b543 | |||
| 9838f9a44b | |||
| 4dee938b51 | |||
| b3a11b6584 | |||
| bd01a87b75 | |||
| 9de3f7834e | |||
| 7497a2bb36 | |||
| 6f92c8ceac | |||
| aa2bce7b3b | |||
| f65ea4cfbe | |||
| 4b40e9dbf9 | |||
| 7e22582377 | |||
| 311f8ba794 | |||
| af82d0fec7 | |||
| dc9e8a4075 | |||
| da0a8e384e | |||
| d73152b985 | |||
| a9b0728bd4 | |||
| 4bcc62a257 | |||
| 89bef5adb2 | |||
| a02eee7e98 | |||
| 4205629ea2 | |||
| dd6ee49d05 | |||
| 9c36da1c34 | |||
| 4477ac7c8a | |||
| 8a9f18d72c | |||
| 9d53e0cb07 | |||
| e654975b1e | |||
| 74a0a85181 | |||
| d02a3b4aeb | |||
| de6d0db833 | |||
| 6ebe1652d6 | |||
| 9d44753ab8 | |||
| 03be5c4140 | |||
| f7cbd80bd9 | |||
| 3b812973fa | |||
| 112c4345a3 | |||
| a3f79499b2 | |||
| 26f413d1f8 | |||
| ae024ca446 | |||
| 6f531aba94 | |||
| 5c9ce9d066 | |||
| 29824c6ed3 | |||
| e3027906d7 | |||
| ee6fafc615 | |||
| 94b35f9e9d | |||
| ac9dd50384 | |||
| a9fa50b6fb | |||
| b253532fa7 | |||
| e58b397a2a | |||
| 7dff6a074b | |||
| af49f0022a | |||
| 9a31f13298 | |||
| b7e2e075c4 | |||
| 2f9a7d536d | |||
| 4717151495 | |||
| 031181a6b5 | |||
| df349a2214 | |||
| 7bf65e8e11 | |||
| 9717f4fabd | |||
| 42213dc3c3 | |||
| 792e3fc6ed | |||
| 86ef6da834 | |||
| 314f5b20d5 | |||
| b2b1c0102c | |||
| 6f16ca39a2 | |||
| 31133d22b6 | |||
| b2d3960dc1 | |||
| bcd635e549 | |||
| 8af5e2d246 | |||
| c785a8e166 | |||
| f6d1ba8235 | |||
| 78e9d829de | |||
| 45885eaea3 | |||
| 249717f394 | |||
| 5b02f1c031 | |||
| e3f475c17c | |||
| bc2e5845de | |||
| e5dcd91755 | |||
| 0ef24bb0e2 | |||
| 1b2eeb9c64 | |||
| 0f964142b9 | |||
| a0c8c932fb | |||
| e167c1cb89 | |||
| e9fe318e8e | |||
| 8a20e3f229 | |||
| 664d31d40a | |||
| 088fc410d9 | |||
| 6f3a119464 | |||
| 7073959d48 | |||
| 76a4fb223e | |||
| a9a045fd2d | |||
| f39acfc874 | |||
| 484c97707f | |||
| 81502a99b5 | |||
| 9c6ceb4503 | |||
| 08439aebf1 | |||
| 4fd26cdd37 | |||
| 97c482ca42 | |||
| f7b03a7906 | |||
| 39c7e19d43 | |||
| 1f503d2ef5 | |||
| 9cc6d1dc32 | |||
| 8e7b535ce6 | |||
| 9b35b77716 | |||
| ea577c499f | |||
| 0ea5f3fb5d | |||
| 37cfceb7eb | |||
| c75f7143b6 | |||
| ea214f128e | |||
| 848d35c57e | |||
| a3c416f864 | |||
| d0e86c1b78 | |||
| 338c0e5f90 | |||
| 1f2b3cdd73 | |||
| 032baf06f3 | |||
| 0bdf574ea2 | |||
| 4226c484bd | |||
| af87b6d8cb | |||
| 610b67e13b | |||
| 65272b80e4 | |||
| 25b95e277f | |||
| 422a556eac | |||
| 9c3b42a188 | |||
| 06fd4c3617 | |||
| f18c18ae52 | |||
| 4516e86668 | |||
| 782cc8a795 | |||
| c38aebbafb | |||
| df192657da | |||
| a3c16b6279 | |||
| fea726180d | |||
| 1d4230e92a | |||
| 72ea3f954c | |||
| 21b83a7e8c | |||
| 4d688e7393 | |||
| 289852dfb5 | |||
| 6f119700ff | |||
| 03775f2b92 | |||
| 79fe3e14ac | |||
| ae7833d9da | |||
| 5ca6124207 | |||
| 5ed9bb563e | |||
| 4d3e44108e | |||
| a3be79303f | |||
| 61a3fad7aa | |||
| 37593cdf6d | |||
| 88494adecb | |||
| 9d0700196b | |||
| 88907357c2 | |||
| 8af0e52921 | |||
| 8a4f1f99ad | |||
| 506ad72c66 | |||
| 5ddddee07b | |||
| c85eb8fc7c | |||
| 55a6a8900a | |||
| 36bec765e2 | |||
| 0a7c4afd21 | |||
| 021bf5b171 | |||
| 181bc60214 | |||
| b23d9519d0 | |||
| 7303a453aa | |||
| 26335dfc57 | |||
| 1b00ca3575 | |||
| 68b0195bf3 | |||
| 68602f3d87 | |||
| a8a4f5f805 | |||
| 9bd070fbc2 | |||
| 7664e1dd79 | |||
| 55af3361bf | |||
| fbfbc9f15d | |||
| bf42246984 | |||
| 761ba97601 | |||
| ebb2b1120c | |||
| 2b61ca8f34 | |||
| 9db0734a1e | |||
| 9c4cf35414 | |||
| 090432e416 | |||
| c92b2d8bc4 | |||
| 2a6a06fab7 | |||
| fc6a430d8e | |||
| c746eeae93 | |||
| e5fadd8c84 | |||
| 682e1bea7e | |||
| 75db7522c6 | |||
| 96d354a84d | |||
| 5905b11c07 | |||
| a97234574d | |||
| 282a0c90d1 | |||
| b8ab3f2f52 | |||
| 92a92fc60f | |||
| 52a81b8175 | |||
| 55a85af05d | |||
| 3db3006d4c | |||
| 008fb3eed9 | |||
| b304514bd5 | |||
| b0da401292 | |||
| ee7deb09cf | |||
| 9f85107fef | |||
| bcd5bd6cd5 | |||
| 42d2c96d4c | |||
| 941fdefdb3 | |||
| a0df3c4d51 | |||
| 85dc7288e4 | |||
| 4affe3215b | |||
| 2ac637eef0 | |||
| 84bba36429 | |||
| 56b601e3a0 | |||
| 6f773e289b | |||
| d5f91a68c0 | |||
| 4984cba10d | |||
| e7c2e19096 | |||
| e28ae32461 | |||
| 59a0099b9f | |||
| 225c1b0708 | |||
| f8cfd7e288 | |||
| dc0c074ac4 | |||
| 2994b6dd98 | |||
| ab6a6ffed4 | |||
| f466a0f1df | |||
| 1025ee77ed | |||
| ad19f7d558 | |||
| 9412113834 | |||
| 405f8edf02 | |||
| a0826667f1 | |||
| 58e9a40d5f | |||
| dfb14c1e9f | |||
| dd51d5acd4 | |||
| d3a825678a | |||
| ae22642690 | |||
| 41c84bb08b | |||
| 9711cddbe0 | |||
| edfeca758c | |||
| 85b6e6da4a | |||
| c51604e308 | |||
| 9207920c1e | |||
| f79788f767 | |||
| c4287db151 | |||
| 62aa72dac8 | |||
| 0053d2fc81 | |||
| 6206a30519 | |||
| e3ff83a4a8 | |||
| 80b92467f0 | |||
| 169a2a54d7 | |||
| d8ec228a76 | |||
| 27adad0b0d | |||
| c4a3ba6f83 | |||
| e962002db2 | |||
| 3ad7832a8d | |||
| 209ab8c424 | |||
| 94f813db70 | |||
| c844497aee | |||
| a6ae942540 | |||
| 1f262a461c | |||
| 4c816924e7 | |||
| a1459e1fcf | |||
| 4260823e1e | |||
| 89a002c4e3 | |||
| 6c1e7868c7 | |||
| 9b153666e7 | |||
| 93881a2a8e | |||
| 04f053c6a4 | |||
| d66b98e9c8 | |||
| ae529ed93e | |||
| 581cb48c9e | |||
| dc64186d75 | |||
| 308d42d072 | |||
| e6d94b83ba | |||
| 3caf7ba32d | |||
| b3084d23bf | |||
| 7f5d787952 | |||
| 79a5322fa4 | |||
| 92aedc5eda | |||
| 33ff463ea1 | |||
| 7d3f65a044 | |||
| 6d3c7a8281 | |||
| 8e6deba985 | |||
| d31e74d3f8 | |||
| bdcf0ccead | |||
| c8b40c5fd6 | |||
| 2dc9db65d7 | |||
| ff00c1d6eb | |||
| 1f5c3c9d74 | |||
| d87161e574 | |||
| 14df74ea8b | |||
| 060c462f3a | |||
| 25dd16a8cd | |||
| 0fb1621594 | |||
| 5fb1b27d17 | |||
| 6376b575d9 | |||
| 31963c8d86 | |||
| 4495060185 | |||
| f4dbdd7b52 | |||
| e19c028cc4 | |||
| f04011dd87 | |||
| 1bc0a70d94 | |||
| 2b6c70a780 | |||
| 113c16d7a9 | |||
| 06ca1818d0 | |||
| 6c0d8c4b75 | |||
| 44bb4be66c | |||
| f7f1ee9e99 | |||
| e91df656f5 | |||
| 10cf16270a | |||
| 7d36a00d14 | |||
| 9a857d83a6 | |||
| 25806227e8 | |||
| 6dc49ec992 | |||
| 0c71240f14 | |||
| f46661428b | |||
| 4500a90cdc | |||
| 99a5d49a38 | |||
| c59156dac7 | |||
| c7fb0d5c7b | |||
| f1a5c5f6cb | |||
| 556478c20e | |||
| 5df8d5af8a | |||
| 1d3de154e2 | |||
| 4090bac8c8 | |||
| 0932d508c8 | |||
| 0fdd8a64b4 | |||
| 9dddfe2ef6 | |||
| e808500ba1 | |||
| 2fd359684d | |||
| da91f87764 | |||
| 610294205f | |||
| 5697f96608 | |||
| 93b774a661 | |||
| bf0ca28494 | |||
| 89af2b0485 | |||
| ddc1be5556 | |||
| 23584ee52f | |||
| 520962b3d6 | |||
| 017e9d852e | |||
| ab55a1b67b | |||
| 9bf6fa974d | |||
| 431a662f4f | |||
| 9c74f9322d | |||
| a4ae2b91c9 | |||
| 0cd4896037 | |||
| e540c9c47a | |||
| f9b15edbde | |||
| 1857fa8bc9 | |||
| ac6e2b0083 | |||
| d18e74d822 | |||
| 8c753a2c80 | |||
| 2a7d9d4515 | |||
| 89f2d3427e | |||
| 309b90c955 | |||
| a766ee6d5d | |||
| ebf455daba | |||
| fe9ea5d5cb | |||
| 8a2f400cf3 | |||
| 448d10a31a | |||
| e0e1dd73bb | |||
| fa88f5af4c | |||
| fcb85633ea | |||
| 935da83e2c | |||
| ef174ffcb3 | |||
| ad23570fa2 | |||
| 8777497c4b | |||
| 25af8b801d | |||
| 829e2e4a24 | |||
| 09e1f276ff | |||
| 81632de706 | |||
| f7cb19956f |
@@ -264,6 +264,9 @@ ForEachMacros:
|
||||
- SET_SLOT_PROBING_BEGIN
|
||||
- MAP_SLOT_PROBING_BEGIN
|
||||
- VECTOR_SET_SLOT_PROBING_BEGIN
|
||||
- LIGHT_FOREACH_BEGIN_DIRECTIONAL
|
||||
- LIGHT_FOREACH_BEGIN_LOCAL
|
||||
- LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL
|
||||
|
||||
StatementMacros:
|
||||
- PyObject_HEAD
|
||||
|
||||
@@ -117,8 +117,21 @@ class DATA_PT_lens(CameraButtonsPanel, Panel):
|
||||
col.prop(ccam, "fisheye_polynomial_k2", text="K2")
|
||||
col.prop(ccam, "fisheye_polynomial_k3", text="K3")
|
||||
col.prop(ccam, "fisheye_polynomial_k4", text="K4")
|
||||
|
||||
elif engine in {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}:
|
||||
elif engine == 'BLENDER_EEVEE':
|
||||
col.prop(cam, "panorama_type")
|
||||
if cam.panorama_type == 'FISHEYE_EQUIDISTANT':
|
||||
col.prop(cam, "fisheye_fov")
|
||||
elif cam.panorama_type == 'FISHEYE_EQUISOLID':
|
||||
col.prop(cam, "fisheye_lens", text="Lens")
|
||||
col.prop(cam, "fisheye_fov")
|
||||
elif cam.panorama_type == 'EQUIRECTANGULAR':
|
||||
sub = col.column(align=True)
|
||||
sub.prop(cam, "latitude_min", text="Latitude Min")
|
||||
sub.prop(cam, "latitude_max", text="Max")
|
||||
sub = col.column(align=True)
|
||||
sub.prop(cam, "longitude_min", text="Longitude Min")
|
||||
sub.prop(cam, "longitude_max", text="Max")
|
||||
elif engine in {'BLENDER_RENDER', 'BLENDER_WORKBENCH'}:
|
||||
if cam.lens_unit == 'MILLIMETERS':
|
||||
col.prop(cam, "lens")
|
||||
elif cam.lens_unit == 'FOV':
|
||||
|
||||
@@ -227,8 +227,6 @@ def draw_material_settings(self, context):
|
||||
layout.prop(mat, "show_transparent_back")
|
||||
|
||||
layout.prop(mat, "use_screen_refraction")
|
||||
layout.prop(mat, "refraction_depth")
|
||||
layout.prop(mat, "use_sss_translucency")
|
||||
layout.prop(mat, "pass_index")
|
||||
|
||||
|
||||
|
||||
@@ -180,6 +180,37 @@ class RENDER_PT_eevee_motion_blur(RenderButtonsPanel, Panel):
|
||||
col.prop(props, "motion_blur_max")
|
||||
col.prop(props, "motion_blur_steps", text="Steps")
|
||||
|
||||
class RENDER_PT_eevee_motion_blur_curve(RenderButtonsPanel, Panel):
|
||||
bl_label = "Shutter Curve"
|
||||
bl_parent_id = "RENDER_PT_eevee_motion_blur"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.engine in cls.COMPAT_ENGINES)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
scene = context.scene
|
||||
rd = scene.render
|
||||
props = scene.eevee
|
||||
|
||||
layout.active = props.use_motion_blur
|
||||
col = layout.column()
|
||||
|
||||
col.template_curve_mapping(rd, "motion_blur_shutter_curve")
|
||||
|
||||
col = layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
row.operator("render.shutter_curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
|
||||
row.operator("render.shutter_curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
|
||||
row.operator("render.shutter_curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
|
||||
row.operator("render.shutter_curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
|
||||
row.operator("render.shutter_curve_preset", icon='LINCURVE', text="").shape = 'LINE'
|
||||
row.operator("render.shutter_curve_preset", icon='NOCURVE', text="").shape = 'MAX'
|
||||
|
||||
|
||||
class RENDER_PT_eevee_depth_of_field(RenderButtonsPanel, Panel):
|
||||
bl_label = "Depth of Field"
|
||||
@@ -329,8 +360,8 @@ class RENDER_PT_eevee_subsurface_scattering(RenderButtonsPanel, Panel):
|
||||
col.prop(props, "sss_jitter_threshold")
|
||||
|
||||
|
||||
class RENDER_PT_eevee_screen_space_reflections(RenderButtonsPanel, Panel):
|
||||
bl_label = "Screen Space Reflections"
|
||||
class RENDER_PT_eevee_raytracing(RenderButtonsPanel, Panel):
|
||||
bl_label = "Raytracing"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
||||
|
||||
@@ -352,12 +383,9 @@ class RENDER_PT_eevee_screen_space_reflections(RenderButtonsPanel, Panel):
|
||||
|
||||
col = layout.column()
|
||||
col.active = props.use_ssr
|
||||
col.prop(props, "use_ssr_refraction", text="Refraction")
|
||||
col.prop(props, "use_ssr_halfres")
|
||||
col.prop(props, "ssr_quality")
|
||||
col.prop(props, "ssr_max_roughness")
|
||||
col.prop(props, "ssr_thickness")
|
||||
col.prop(props, "ssr_border_fade")
|
||||
col.prop(props, "ssr_firefly_fac")
|
||||
|
||||
|
||||
@@ -491,6 +519,7 @@ class RENDER_PT_eevee_film(RenderButtonsPanel, Panel):
|
||||
col = layout.column()
|
||||
col.prop(rd, "filter_size")
|
||||
col.prop(rd, "film_transparent", text="Transparent")
|
||||
col.prop(props, "use_log_space")
|
||||
|
||||
col = layout.column(align=False, heading="Overscan")
|
||||
row = col.row(align=True)
|
||||
@@ -703,8 +732,9 @@ classes = (
|
||||
RENDER_PT_eevee_bloom,
|
||||
RENDER_PT_eevee_depth_of_field,
|
||||
RENDER_PT_eevee_subsurface_scattering,
|
||||
RENDER_PT_eevee_screen_space_reflections,
|
||||
RENDER_PT_eevee_raytracing,
|
||||
RENDER_PT_eevee_motion_blur,
|
||||
RENDER_PT_eevee_motion_blur_curve,
|
||||
RENDER_PT_eevee_volumetric,
|
||||
RENDER_PT_eevee_volumetric_lighting,
|
||||
RENDER_PT_eevee_volumetric_shadows,
|
||||
|
||||
@@ -76,6 +76,8 @@ class VIEWLAYER_PT_eevee_layer_passes_data(ViewLayerButtonsPanel, Panel):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
scene = context.scene
|
||||
scene_eevee = scene.eevee
|
||||
|
||||
view_layer = context.view_layer
|
||||
|
||||
@@ -84,6 +86,9 @@ class VIEWLAYER_PT_eevee_layer_passes_data(ViewLayerButtonsPanel, Panel):
|
||||
col.prop(view_layer, "use_pass_z")
|
||||
col.prop(view_layer, "use_pass_mist")
|
||||
col.prop(view_layer, "use_pass_normal")
|
||||
sub = col.column()
|
||||
sub.active = not scene_eevee.use_motion_blur
|
||||
sub.prop(view_layer, "use_pass_vector")
|
||||
|
||||
|
||||
class VIEWLAYER_PT_eevee_layer_passes_light(ViewLayerButtonsPanel, Panel):
|
||||
|
||||
@@ -5989,6 +5989,26 @@ class VIEW3D_PT_shading_render_pass(Panel):
|
||||
layout.prop(shading, "render_pass", text="")
|
||||
|
||||
|
||||
class VIEW3D_PT_shading_compositor(Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'HEADER'
|
||||
bl_label = "Compositor"
|
||||
bl_parent_id = 'VIEW3D_PT_shading'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
(context.space_data.shading.type == 'MATERIAL') or
|
||||
(context.space_data.shading.type == 'RENDERED')
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
shading = context.space_data.shading
|
||||
|
||||
layout = self.layout
|
||||
layout.prop(shading, "use_compositor")
|
||||
|
||||
|
||||
class VIEW3D_PT_gizmo_display(Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'HEADER'
|
||||
@@ -7715,6 +7735,7 @@ classes = (
|
||||
VIEW3D_PT_shading_options_shadow,
|
||||
VIEW3D_PT_shading_options_ssao,
|
||||
VIEW3D_PT_shading_render_pass,
|
||||
VIEW3D_PT_shading_compositor,
|
||||
VIEW3D_PT_gizmo_display,
|
||||
VIEW3D_PT_overlay,
|
||||
VIEW3D_PT_overlay_guides,
|
||||
|
||||
@@ -129,6 +129,7 @@ add_subdirectory(sequencer)
|
||||
add_subdirectory(shader_fx)
|
||||
add_subdirectory(io)
|
||||
add_subdirectory(functions)
|
||||
add_subdirectory(viewport_compositor)
|
||||
add_subdirectory(makesdna)
|
||||
add_subdirectory(makesrna)
|
||||
|
||||
|
||||
@@ -144,6 +144,14 @@ void BKE_curvemapping_evaluate_premulRGBF(const struct CurveMapping *cumap,
|
||||
bool BKE_curvemapping_RGBA_does_something(const struct CurveMapping *cumap);
|
||||
void BKE_curvemapping_table_F(const struct CurveMapping *cumap, float **array, int *size);
|
||||
void BKE_curvemapping_table_RGBA(const struct CurveMapping *cumap, float **array, int *size);
|
||||
void BKE_curvemapping_get_range_minimums(const struct CurveMapping *curve_mapping,
|
||||
float minimums[4]);
|
||||
void BKE_curvemapping_compute_range_dividers(const struct CurveMapping *curve_mapping,
|
||||
float dividers[4]);
|
||||
void BKE_curvemapping_compute_slopes(const struct CurveMapping *curve_mapping,
|
||||
float start_slopes[4],
|
||||
float end_slopes[4]);
|
||||
bool BKE_curvemapping_is_map_identity(const struct CurveMapping *curve_mapping, int index);
|
||||
|
||||
/**
|
||||
* Call when you do images etc, needs restore too. also verifies tables.
|
||||
|
||||
@@ -116,6 +116,7 @@ typedef struct bNodeSocketTemplate {
|
||||
#ifdef __cplusplus
|
||||
namespace blender {
|
||||
namespace nodes {
|
||||
class DNode;
|
||||
class NodeMultiFunctionBuilder;
|
||||
class GeoNodeExecParams;
|
||||
class NodeDeclarationBuilder;
|
||||
@@ -125,6 +126,11 @@ namespace fn {
|
||||
class CPPType;
|
||||
class MFDataType;
|
||||
} // namespace fn
|
||||
namespace viewport_compositor {
|
||||
class Context;
|
||||
class NodeOperation;
|
||||
class GPUMaterialNode;
|
||||
} // namespace viewport_compositor
|
||||
} // namespace blender
|
||||
|
||||
using CPPTypeHandle = blender::fn::CPPType;
|
||||
@@ -139,7 +145,14 @@ using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket
|
||||
using NodeGatherSocketLinkOperationsFunction =
|
||||
void (*)(blender::nodes::GatherLinkSearchOpParams ¶ms);
|
||||
|
||||
using NodeGetCompositorOperationFunction = blender::viewport_compositor::NodeOperation
|
||||
*(*)(blender::viewport_compositor::Context &context, blender::nodes::DNode node);
|
||||
using NodeGetCompositorGPUMaterialNodeFunction =
|
||||
blender::viewport_compositor::GPUMaterialNode *(*)(blender::nodes::DNode node);
|
||||
|
||||
#else
|
||||
typedef void *NodeGetCompositorOperationFunction;
|
||||
typedef void *NodeGetCompositorGPUMaterialNodeFunction;
|
||||
typedef void *NodeMultiFunctionBuildFunction;
|
||||
typedef void *NodeGeometryExecFunction;
|
||||
typedef void *NodeDeclareFunction;
|
||||
@@ -322,6 +335,14 @@ typedef struct bNodeType {
|
||||
/* gpu */
|
||||
NodeGPUExecFunction gpu_fn;
|
||||
|
||||
/* Get an instance of this node's compositor operation. Freeing the instance is the
|
||||
* responsibility of the caller. */
|
||||
NodeGetCompositorOperationFunction get_compositor_operation;
|
||||
|
||||
/* Get an instance of this node's compositor GPU material node. Freeing the instance is the
|
||||
* responsibility of the caller. */
|
||||
NodeGetCompositorGPUMaterialNodeFunction get_compositor_gpu_material_node;
|
||||
|
||||
/* Build a multi-function for this node. */
|
||||
NodeMultiFunctionBuildFunction build_multi_function;
|
||||
|
||||
|
||||
@@ -368,6 +368,10 @@ void BKE_scene_cursor_from_mat4(struct View3DCursor *cursor,
|
||||
const float mat[4][4],
|
||||
bool use_compat);
|
||||
|
||||
/* Dependency graph evaluation. */
|
||||
|
||||
void BKE_scene_eval_compositor_nodetree(struct Depsgraph *depsgraph, struct Scene *scene);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -33,6 +33,15 @@ struct World;
|
||||
struct World *BKE_world_add(struct Main *bmain, const char *name);
|
||||
void BKE_world_eval(struct Depsgraph *depsgraph, struct World *world);
|
||||
|
||||
struct World *BKE_world_default(void);
|
||||
|
||||
void BKE_world_defaults_free_gpu(void);
|
||||
|
||||
/* Module */
|
||||
|
||||
void BKE_worlds_init(void);
|
||||
void BKE_worlds_exit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1174,6 +1174,98 @@ bool BKE_curvemapping_RGBA_does_something(const CurveMapping *cumap)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get the minimum x value of each curve map table. */
|
||||
void BKE_curvemapping_get_range_minimums(const CurveMapping *curve_mapping, float minimums[CM_TOT])
|
||||
{
|
||||
for (int i = 0; i < CM_TOT; i++) {
|
||||
minimums[i] = curve_mapping->cm[i].mintable;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the reciprocal of the difference between the maximum and the minimum x value of each curve
|
||||
* map table. Evaluation parameters can be multiplied by this value to be normalized. If the
|
||||
* difference is zero, 1^8 is returned. */
|
||||
void BKE_curvemapping_compute_range_dividers(const CurveMapping *curve_mapping,
|
||||
float dividers[CM_TOT])
|
||||
{
|
||||
for (int i = 0; i < CM_TOT; i++) {
|
||||
const CurveMap *curve_map = &curve_mapping->cm[i];
|
||||
dividers[i] = 1.0f / max_ff(1e-8f, curve_map->maxtable - curve_map->mintable);
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute the slopes at the start and end points of each curve map. The slopes are multiplied by
|
||||
* the range of the curve map to compensate for parameter normalization. If the slope is vertical,
|
||||
* 1^8 is returned. */
|
||||
void BKE_curvemapping_compute_slopes(const CurveMapping *curve_mapping,
|
||||
float start_slopes[CM_TOT],
|
||||
float end_slopes[CM_TOT])
|
||||
{
|
||||
float range_dividers[CM_TOT];
|
||||
BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
|
||||
for (int i = 0; i < CM_TOT; i++) {
|
||||
const CurveMap *curve_map = &curve_mapping->cm[i];
|
||||
/* If extrapolation is not enabled, the slopes are horizontal. */
|
||||
if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) {
|
||||
start_slopes[i] = 0.0f;
|
||||
end_slopes[i] = 0.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (curve_map->ext_in[0] != 0.0f) {
|
||||
start_slopes[i] = curve_map->ext_in[1] / (curve_map->ext_in[0] * range_dividers[i]);
|
||||
}
|
||||
else {
|
||||
start_slopes[i] = 1e8f;
|
||||
}
|
||||
|
||||
if (curve_map->ext_out[0] != 0.0f) {
|
||||
end_slopes[i] = curve_map->ext_out[1] / (curve_map->ext_out[0] * range_dividers[i]);
|
||||
}
|
||||
else {
|
||||
end_slopes[i] = 1e8f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the curve map at the index is identity, that is, does nothing. A curve map is said to
|
||||
* be identity if:
|
||||
* - The curve mapping uses extrapolation.
|
||||
* - Its range is 1.
|
||||
* - The slope at its start point is 1.
|
||||
* - The slope at its end point is 1.
|
||||
* - The number of points is 2.
|
||||
* - The start point is at (0, 0).
|
||||
* - The end point is at (1, 1).
|
||||
* Note that this could return false even if the curve map is identity, this happens in the case
|
||||
* when more than 2 points exist in the curve map but all points are collinear. */
|
||||
bool BKE_curvemapping_is_map_identity(const CurveMapping *curve_mapping, int index)
|
||||
{
|
||||
if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) {
|
||||
return false;
|
||||
}
|
||||
const CurveMap *curve_map = &curve_mapping->cm[index];
|
||||
if (curve_map->maxtable - curve_map->mintable != 1.0f) {
|
||||
return false;
|
||||
}
|
||||
if (curve_map->ext_in[0] != curve_map->ext_in[1]) {
|
||||
return false;
|
||||
}
|
||||
if (curve_map->ext_out[0] != curve_map->ext_out[1]) {
|
||||
return false;
|
||||
}
|
||||
if (curve_map->totpoint != 2) {
|
||||
return false;
|
||||
}
|
||||
if (curve_map->curve[0].x != 0 || curve_map->curve[0].y != 0) {
|
||||
return false;
|
||||
}
|
||||
if (curve_map->curve[1].x != 0 || curve_map->curve[1].y != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BKE_curvemapping_init(CurveMapping *cumap)
|
||||
{
|
||||
int a;
|
||||
|
||||
@@ -127,6 +127,8 @@
|
||||
#include "IMB_colormanagement.h"
|
||||
#include "IMB_imbuf.h"
|
||||
|
||||
#include "GPU_material.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
static void scene_init_data(ID *id)
|
||||
@@ -364,6 +366,9 @@ static void scene_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
|
||||
scene_dst->preview = NULL;
|
||||
}
|
||||
|
||||
BLI_listbase_clear(&scene_dst->gpumaterial);
|
||||
BLI_listbase_clear(&scene_dst->compositor_depsgraphs);
|
||||
|
||||
BKE_scene_copy_data_eevee(scene_dst, scene_src);
|
||||
}
|
||||
|
||||
@@ -388,6 +393,9 @@ static void scene_free_data(ID *id)
|
||||
|
||||
BKE_keyingsets_free(&scene->keyingsets);
|
||||
|
||||
/* Free gpu material before the ntree */
|
||||
GPU_material_free(&scene->gpumaterial);
|
||||
|
||||
/* is no lib link block, but scene extension */
|
||||
if (scene->nodetree) {
|
||||
ntreeFreeEmbeddedTree(scene->nodetree);
|
||||
@@ -462,6 +470,11 @@ static void scene_free_data(ID *id)
|
||||
scene->display.shading.prop = NULL;
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH_MUTABLE (LinkData *, link, &scene->compositor_depsgraphs) {
|
||||
DEG_graph_free((Depsgraph *)link->data);
|
||||
}
|
||||
BLI_freelistN(&scene->compositor_depsgraphs);
|
||||
|
||||
/* These are freed on doversion. */
|
||||
BLI_assert(scene->layer_properties == NULL);
|
||||
}
|
||||
@@ -937,6 +950,8 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres
|
||||
|
||||
if (BLO_write_is_undo(writer)) {
|
||||
/* Clean up, important in undo case to reduce false detection of changed data-blocks. */
|
||||
BLI_listbase_clear(&sce->gpumaterial);
|
||||
BLI_listbase_clear(&sce->compositor_depsgraphs);
|
||||
/* XXX This UI data should not be stored in Scene at all... */
|
||||
memset(&sce->cursor, 0, sizeof(sce->cursor));
|
||||
}
|
||||
@@ -1367,6 +1382,9 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
|
||||
|
||||
BKE_screen_view3d_shading_blend_read_data(reader, &sce->display.shading);
|
||||
|
||||
BLI_listbase_clear(&sce->gpumaterial);
|
||||
BLI_listbase_clear(&sce->compositor_depsgraphs);
|
||||
|
||||
BLO_read_data_address(reader, &sce->layer_properties);
|
||||
IDP_BlendDataRead(reader, &sce->layer_properties);
|
||||
}
|
||||
@@ -3666,3 +3684,14 @@ void BKE_scene_cursor_from_mat4(View3DCursor *cursor, const float mat[4][4], boo
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void BKE_scene_eval_compositor_nodetree(struct Depsgraph *depsgraph, Scene *scene)
|
||||
{
|
||||
DEG_debug_print_eval(depsgraph, __func__, scene->id.name, scene);
|
||||
GPU_material_free(&scene->gpumaterial);
|
||||
|
||||
LISTBASE_FOREACH_MUTABLE (LinkData *, link, &scene->compositor_depsgraphs) {
|
||||
DEG_graph_free((Depsgraph *)link->data);
|
||||
}
|
||||
BLI_freelistN(&scene->compositor_depsgraphs);
|
||||
}
|
||||
|
||||
@@ -1417,7 +1417,6 @@ static void direct_link_region(BlendDataReader *reader, ARegion *region, int spa
|
||||
BLO_read_data_address(reader, &rv3d->localvd);
|
||||
BLO_read_data_address(reader, &rv3d->clipbb);
|
||||
|
||||
rv3d->render_engine = NULL;
|
||||
rv3d->sms = NULL;
|
||||
rv3d->smooth_timer = NULL;
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "NOD_shader.h"
|
||||
|
||||
#include "DRW_engine.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
@@ -227,3 +229,57 @@ void BKE_world_eval(struct Depsgraph *depsgraph, World *world)
|
||||
DEG_debug_print_eval(depsgraph, __func__, world->id.name, world);
|
||||
GPU_material_free(&world->gpumaterial);
|
||||
}
|
||||
|
||||
/* Default World
|
||||
*
|
||||
* Used for rendering when a scene have no world assigned. */
|
||||
|
||||
static World default_world;
|
||||
|
||||
static void world_default_init(World *ma)
|
||||
{
|
||||
bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
|
||||
ma->nodetree = ntree;
|
||||
ma->use_nodes = true;
|
||||
|
||||
bNode *background = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND);
|
||||
bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD);
|
||||
bNodeSocket *background_out = nodeFindSocket(background, SOCK_OUT, "Background");
|
||||
bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface");
|
||||
nodeAddLink(ntree, background, background_out, output, output_in);
|
||||
nodeSetActive(ntree, output);
|
||||
|
||||
bNodeSocketValueRGBA *color_socket_ =
|
||||
(bNodeSocketValueRGBA *)nodeFindSocket(background, SOCK_IN, "Color")->default_value;
|
||||
|
||||
color_socket_->value[0] = 0.0f;
|
||||
color_socket_->value[1] = 0.0f;
|
||||
color_socket_->value[2] = 0.0f;
|
||||
color_socket_->value[3] = 1.0f;
|
||||
}
|
||||
|
||||
World *BKE_world_default(void)
|
||||
{
|
||||
return &default_world;
|
||||
}
|
||||
|
||||
void BKE_world_defaults_free_gpu(void)
|
||||
{
|
||||
if (default_world.gpumaterial.first) {
|
||||
GPU_material_free(&default_world.gpumaterial);
|
||||
}
|
||||
}
|
||||
|
||||
/* Module functions called on startup and exit. */
|
||||
|
||||
void BKE_worlds_init(void)
|
||||
{
|
||||
world_init_data(&default_world.id);
|
||||
|
||||
world_default_init(&default_world);
|
||||
}
|
||||
|
||||
void BKE_worlds_exit(void)
|
||||
{
|
||||
world_free_data(&default_world.id);
|
||||
}
|
||||
|
||||
@@ -100,6 +100,9 @@ void _BLI_assert_unreachable_print(const char *file, int line, const char *funct
|
||||
#define BLI_STATIC_ASSERT_ALIGN(st, align) \
|
||||
BLI_STATIC_ASSERT((sizeof(st) % (align) == 0), "Structure must be strictly aligned")
|
||||
|
||||
#define BLI_STATIC_ASSERT_SIZE(st, max_size) \
|
||||
BLI_STATIC_ASSERT(sizeof(CullingData) <= max_size, "Structure is too big")
|
||||
|
||||
/**
|
||||
* Indicates that this line of code should never be executed. If it is reached, it will abort in
|
||||
* debug builds and print an error in release builds.
|
||||
|
||||
@@ -90,7 +90,7 @@ struct float4x4 {
|
||||
return matrix;
|
||||
}
|
||||
|
||||
static float4x4 identity()
|
||||
static float4x4 identity() ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
float4x4 mat;
|
||||
unit_m4(mat.values);
|
||||
@@ -259,6 +259,16 @@ struct float4x4 {
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const float4x4 &m)
|
||||
{
|
||||
stream << "(\n";
|
||||
for (int i = 0; i < 4; i++) {
|
||||
stream << " " << *reinterpret_cast<const float4 *>(m.values[i]) << std::endl;
|
||||
}
|
||||
stream << ")";
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
|
||||
170
source/blender/blenlib/BLI_int2.hh
Normal file
170
source/blender/blenlib/BLI_int2.hh
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_float2.hh"
|
||||
#include "BLI_int3.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct int2 {
|
||||
int32_t x, y;
|
||||
|
||||
int2() = default;
|
||||
|
||||
int2(const int32_t *ptr) : x{ptr[0]}, y{ptr[1]}
|
||||
{
|
||||
}
|
||||
|
||||
explicit int2(int32_t value) : x(value), y(value)
|
||||
{
|
||||
}
|
||||
|
||||
int2(int32_t x, int32_t y) : x(x), y(y)
|
||||
{
|
||||
}
|
||||
|
||||
explicit int2(const float2 &other) : x(other.x), y(other.y)
|
||||
{
|
||||
}
|
||||
|
||||
int2(const int3 &other) : x(other.x), y(other.y)
|
||||
{
|
||||
}
|
||||
|
||||
operator int32_t *()
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
operator float2() const
|
||||
{
|
||||
return float2(x, y);
|
||||
}
|
||||
|
||||
operator const int32_t *() const
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
bool is_zero() const
|
||||
{
|
||||
return this->x == 0 && this->y == 0;
|
||||
}
|
||||
|
||||
int2 &operator+=(const int2 &other)
|
||||
{
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int2 &operator-=(const int2 &other)
|
||||
{
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int2 &operator*=(int32_t factor)
|
||||
{
|
||||
x *= factor;
|
||||
y *= factor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int2 &operator/=(int32_t divisor)
|
||||
{
|
||||
x /= divisor;
|
||||
y /= divisor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend int2 operator+(const int2 &a, const int2 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y};
|
||||
}
|
||||
|
||||
friend int2 operator-(const int2 &a, const int2 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y};
|
||||
}
|
||||
|
||||
friend int2 operator*(const int2 &a, int32_t b)
|
||||
{
|
||||
return {a.x * b, a.y * b};
|
||||
}
|
||||
|
||||
friend int2 operator/(const int2 &a, int32_t b)
|
||||
{
|
||||
BLI_assert(b != 0);
|
||||
return {a.x / b, a.y / b};
|
||||
}
|
||||
|
||||
friend int2 operator*(int32_t a, const int2 &b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
friend float2 operator*(const int2 &a, float b)
|
||||
{
|
||||
return b * float2(a.x, a.y);
|
||||
}
|
||||
|
||||
friend float2 operator/(const int2 &a, float b)
|
||||
{
|
||||
return float2(a.x, a.y) / b;
|
||||
}
|
||||
|
||||
friend float2 operator*(float a, const int2 &b)
|
||||
{
|
||||
return a * float2(b.x, b.y);
|
||||
}
|
||||
|
||||
friend float2 operator/(float a, const int2 &b)
|
||||
{
|
||||
return a / float2(b.x, b.y);
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const int2 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
static int2 clamp(const int2 &a, const int2 &min, const int2 &max)
|
||||
{
|
||||
return int2(clamp_i(a.x, min.x, max.x), clamp_i(a.y, min.y, max.y));
|
||||
}
|
||||
|
||||
static int2 clamp(const int2 &a, const int32_t &min, const int32_t &max)
|
||||
{
|
||||
return int2(clamp_i(a.x, min, max), clamp_i(a.y, min, max));
|
||||
}
|
||||
|
||||
friend bool operator==(const int2 &a, const int2 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
||||
friend bool operator!=(const int2 &a, const int2 &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
156
source/blender/blenlib/BLI_int3.hh
Normal file
156
source/blender/blenlib/BLI_int3.hh
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "BLI_float3.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct int3 {
|
||||
int32_t x, y, z;
|
||||
|
||||
int3() = default;
|
||||
|
||||
int3(const int *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]}
|
||||
{
|
||||
}
|
||||
|
||||
explicit int3(int value) : x(value), y(value), z(value)
|
||||
{
|
||||
}
|
||||
|
||||
int3(int x, int y, int z) : x(x), y(y), z(z)
|
||||
{
|
||||
}
|
||||
|
||||
int3(const int3 &other) : x(other.x), y(other.y), z(other.z)
|
||||
{
|
||||
}
|
||||
|
||||
explicit int3(const float3 &other) : x(other.x), y(other.y), z(other.z)
|
||||
{
|
||||
}
|
||||
|
||||
operator int *()
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
operator float3() const
|
||||
{
|
||||
return float3(x, y, z);
|
||||
}
|
||||
|
||||
operator const int *() const
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
bool is_zero() const
|
||||
{
|
||||
return this->x == 0 && this->y == 0 && this->z == 0;
|
||||
}
|
||||
|
||||
int3 &operator+=(const int3 &other)
|
||||
{
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int3 &operator-=(const int3 &other)
|
||||
{
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
z -= other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int3 &operator*=(int factor)
|
||||
{
|
||||
x *= factor;
|
||||
y *= factor;
|
||||
z *= factor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int3 &operator/=(int divisor)
|
||||
{
|
||||
x /= divisor;
|
||||
y /= divisor;
|
||||
z /= divisor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend int3 operator+(const int3 &a, const int3 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y, a.z + b.z};
|
||||
}
|
||||
|
||||
friend int3 operator-(const int3 &a, const int3 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y, a.z - b.z};
|
||||
}
|
||||
|
||||
friend int3 operator*(const int3 &a, int b)
|
||||
{
|
||||
return {a.x * b, a.y * b, a.z * b};
|
||||
}
|
||||
|
||||
friend int3 operator/(const int3 &a, int b)
|
||||
{
|
||||
BLI_assert(b != 0);
|
||||
return {a.x / b, a.y / b, a.z / b};
|
||||
}
|
||||
|
||||
friend int3 operator*(int a, const int3 &b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const int3 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
static int3 clamp(const int3 &a, const int3 &min, const int3 &max)
|
||||
{
|
||||
return int3(
|
||||
clamp_i(a.x, min.x, max.x), clamp_i(a.y, min.y, max.y), clamp_i(a.z, min.z, max.z));
|
||||
}
|
||||
|
||||
static int3 clamp(const int3 &a, const int32_t &min, const int32_t &max)
|
||||
{
|
||||
return int3(clamp_i(a.x, min, max), clamp_i(a.y, min, max), clamp_i(a.z, min, max));
|
||||
}
|
||||
|
||||
friend bool operator==(const int3 &a, const int3 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
||||
friend bool operator!=(const int3 &a, const int3 &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
175
source/blender/blenlib/BLI_transformation_2d.hh
Normal file
175
source/blender/blenlib/BLI_transformation_2d.hh
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_vec_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* A 2D affine transformation stored in a transformation matrix in homogeneous coordinates form.
|
||||
* The stores matrix is stored column major order. */
|
||||
struct Transformation2D {
|
||||
private:
|
||||
float matrix_[3][3];
|
||||
|
||||
public:
|
||||
static Transformation2D zero()
|
||||
{
|
||||
Transformation2D result;
|
||||
result.matrix_[0][0] = 0.0f;
|
||||
result.matrix_[0][1] = 0.0f;
|
||||
result.matrix_[0][2] = 0.0f;
|
||||
result.matrix_[1][0] = 0.0f;
|
||||
result.matrix_[1][1] = 0.0f;
|
||||
result.matrix_[1][2] = 0.0f;
|
||||
result.matrix_[2][0] = 0.0f;
|
||||
result.matrix_[2][1] = 0.0f;
|
||||
result.matrix_[2][2] = 0.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
static Transformation2D identity()
|
||||
{
|
||||
Transformation2D result = zero();
|
||||
result.matrix_[0][0] = 1.0f;
|
||||
result.matrix_[1][1] = 1.0f;
|
||||
result.matrix_[2][2] = 1.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
static Transformation2D from_translation(const float2 translation)
|
||||
{
|
||||
Transformation2D result = identity();
|
||||
result.matrix_[2][0] = translation.x;
|
||||
result.matrix_[2][1] = translation.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
static Transformation2D from_rotation(float rotation)
|
||||
{
|
||||
Transformation2D result = zero();
|
||||
const float cosine = std::cos(rotation);
|
||||
const float sine = std::sin(rotation);
|
||||
result.matrix_[0][0] = cosine;
|
||||
result.matrix_[0][1] = sine;
|
||||
result.matrix_[1][0] = -sine;
|
||||
result.matrix_[1][1] = cosine;
|
||||
result.matrix_[2][2] = 1.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
static Transformation2D from_translation_rotation_scale(const float2 translation,
|
||||
float rotation,
|
||||
const float2 scale)
|
||||
{
|
||||
Transformation2D result;
|
||||
const float cosine = std::cos(rotation);
|
||||
const float sine = std::sin(rotation);
|
||||
result.matrix_[0][0] = scale.x * cosine;
|
||||
result.matrix_[0][1] = scale.y * sine;
|
||||
result.matrix_[0][2] = 0.0f;
|
||||
result.matrix_[1][0] = scale.x * -sine;
|
||||
result.matrix_[1][1] = scale.y * cosine;
|
||||
result.matrix_[1][2] = 0.0f;
|
||||
result.matrix_[2][0] = translation.x;
|
||||
result.matrix_[2][1] = translation.y;
|
||||
result.matrix_[2][2] = 1.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
static Transformation2D from_normalized_axes(const float2 translation,
|
||||
const float2 horizontal,
|
||||
const float2 vertical)
|
||||
{
|
||||
BLI_ASSERT_UNIT_V2(horizontal);
|
||||
BLI_ASSERT_UNIT_V2(vertical);
|
||||
|
||||
Transformation2D result;
|
||||
result.matrix_[0][0] = horizontal.x;
|
||||
result.matrix_[0][1] = horizontal.y;
|
||||
result.matrix_[0][2] = 0.0f;
|
||||
result.matrix_[1][0] = vertical.x;
|
||||
result.matrix_[1][1] = vertical.y;
|
||||
result.matrix_[1][2] = 0.0f;
|
||||
result.matrix_[2][0] = translation.x;
|
||||
result.matrix_[2][1] = translation.y;
|
||||
result.matrix_[2][2] = 1.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
friend Transformation2D operator*(const Transformation2D &a, const Transformation2D &b)
|
||||
{
|
||||
Transformation2D result;
|
||||
mul_m3_m3m3(result.matrix_, a.matrix_, b.matrix_);
|
||||
return result;
|
||||
}
|
||||
|
||||
void operator*=(const Transformation2D &other)
|
||||
{
|
||||
mul_m3_m3_post(matrix_, other.matrix_);
|
||||
}
|
||||
|
||||
friend float2 operator*(const Transformation2D &transformation, const float2 &vector)
|
||||
{
|
||||
float2 result;
|
||||
mul_v2_m3v2(result, transformation.matrix_, vector);
|
||||
return result;
|
||||
}
|
||||
|
||||
friend float2 operator*(const Transformation2D &transformation, const float (*vector)[2])
|
||||
{
|
||||
return transformation * float2(vector);
|
||||
}
|
||||
|
||||
Transformation2D transposed() const
|
||||
{
|
||||
Transformation2D result;
|
||||
transpose_m3_m3(result.matrix_, matrix_);
|
||||
return result;
|
||||
}
|
||||
|
||||
Transformation2D inverted() const
|
||||
{
|
||||
Transformation2D result;
|
||||
invert_m3_m3(result.matrix_, matrix_);
|
||||
return result;
|
||||
}
|
||||
|
||||
Transformation2D set_pivot(float2 pivot) const
|
||||
{
|
||||
return from_translation(pivot) * *this * from_translation(-pivot);
|
||||
}
|
||||
|
||||
using matrix_array = float[3][3];
|
||||
const matrix_array &matrix() const
|
||||
{
|
||||
return matrix_;
|
||||
}
|
||||
|
||||
friend bool operator==(const Transformation2D &a, const Transformation2D &b)
|
||||
{
|
||||
return equals_m3m3(a.matrix_, b.matrix_);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
@@ -303,6 +303,7 @@ set(SRC
|
||||
BLI_threads.h
|
||||
BLI_timecode.h
|
||||
BLI_timeit.hh
|
||||
BLI_transformation_2d.hh
|
||||
BLI_timer.h
|
||||
BLI_user_counter.hh
|
||||
BLI_utildefines.h
|
||||
|
||||
@@ -388,6 +388,14 @@ MINLINE uint divide_ceil_u(uint a, uint b)
|
||||
return (a + b - 1) / b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next multiple of `b` after `a`.
|
||||
*/
|
||||
MINLINE uint ceil_multiple_u(uint a, uint b)
|
||||
{
|
||||
return ((a + b - 1) / b) * b;
|
||||
}
|
||||
|
||||
MINLINE int mod_i(int i, int n)
|
||||
{
|
||||
return (i % n + n) % n;
|
||||
|
||||
@@ -681,6 +681,12 @@ MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
|
||||
r[2] += a[2] * f;
|
||||
}
|
||||
|
||||
MINLINE void madd_v2_v2v2(float r[2], const float a[2], const float b[2])
|
||||
{
|
||||
r[0] += a[0] * b[0];
|
||||
r[1] += a[1] * b[1];
|
||||
}
|
||||
|
||||
MINLINE void madd_v3_v3v3(float r[3], const float a[3], const float b[3])
|
||||
{
|
||||
r[0] += a[0] * b[0];
|
||||
|
||||
@@ -2273,8 +2273,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
scene->eevee.shadow_cascade_size = 1024;
|
||||
|
||||
scene->eevee.flag = SCE_EEVEE_VOLUMETRIC_LIGHTS | SCE_EEVEE_GTAO_BENT_NORMALS |
|
||||
SCE_EEVEE_GTAO_BOUNCE | SCE_EEVEE_TAA_REPROJECTION |
|
||||
SCE_EEVEE_SSR_HALF_RESOLUTION;
|
||||
SCE_EEVEE_GTAO_BOUNCE | SCE_EEVEE_TAA_REPROJECTION;
|
||||
|
||||
/* If the file is pre-2.80 move on. */
|
||||
if (scene->layer_properties == NULL) {
|
||||
@@ -2342,9 +2341,9 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
EEVEE_GET_BOOL(props, taa_reprojection, SCE_EEVEE_TAA_REPROJECTION);
|
||||
// EEVEE_GET_BOOL(props, sss_enable, SCE_EEVEE_SSS_ENABLED);
|
||||
// EEVEE_GET_BOOL(props, sss_separate_albedo, SCE_EEVEE_SSS_SEPARATE_ALBEDO);
|
||||
EEVEE_GET_BOOL(props, ssr_enable, SCE_EEVEE_SSR_ENABLED);
|
||||
EEVEE_GET_BOOL(props, ssr_refraction, SCE_EEVEE_SSR_REFRACTION);
|
||||
EEVEE_GET_BOOL(props, ssr_halfres, SCE_EEVEE_SSR_HALF_RESOLUTION);
|
||||
EEVEE_GET_BOOL(props, ssr_enable, SCE_EEVEE_RAYTRACING_ENABLED);
|
||||
// EEVEE_GET_BOOL(props, ssr_refraction, SCE_EEVEE_SSR_REFRACTION);
|
||||
// EEVEE_GET_BOOL(props, ssr_halfres, SCE_EEVEE_SSR_HALF_RESOLUTION);
|
||||
|
||||
EEVEE_GET_INT(props, gi_diffuse_bounces);
|
||||
EEVEE_GET_INT(props, gi_diffuse_bounces);
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_cachefile_types.h"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_constraint_types.h"
|
||||
#include "DNA_fluid_types.h"
|
||||
@@ -2036,5 +2037,22 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
*/
|
||||
{
|
||||
/* Keep this block, even when empty. */
|
||||
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "Camera", "float", "fisheye_fov")) {
|
||||
/* EEVEE Panoramic Camera support. */
|
||||
LISTBASE_FOREACH (Camera *, camera, &bmain->cameras) {
|
||||
camera->panorama_type = CAM_PANO_FISHEYE_EQUISOLID;
|
||||
camera->fisheye_fov = DEG2RADF(180.0f);
|
||||
camera->fisheye_lens = 10.5f;
|
||||
camera->latitude_min = DEG2RADF(-90.0f);
|
||||
camera->latitude_max = DEG2RADF(90.0f);
|
||||
camera->longitude_min = DEG2RADF(-180.0f);
|
||||
camera->longitude_max = DEG2RADF(180.0f);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
scene->eevee.flag |= SCE_EEVEE_FILM_LOG_ENCODING;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_scene.h"
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
void DepsgraphNodeBuilder::build_scene_render(Scene *scene, ViewLayer *view_layer)
|
||||
@@ -84,6 +86,14 @@ void DepsgraphNodeBuilder::build_scene_compositor(Scene *scene)
|
||||
if (scene->nodetree == nullptr) {
|
||||
return;
|
||||
}
|
||||
Scene *scene_cow = get_cow_datablock(scene);
|
||||
add_operation_node(&scene->id,
|
||||
NodeType::SHADING,
|
||||
OperationCode::COMPOSITOR_EVAL,
|
||||
[scene_cow](::Depsgraph *depsgraph) {
|
||||
BKE_scene_eval_compositor_nodetree(depsgraph, scene_cow);
|
||||
});
|
||||
|
||||
build_nodetree(scene->nodetree);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "intern/builder/deg_builder_relations.h"
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
namespace blender::deg {
|
||||
@@ -72,7 +73,15 @@ void DepsgraphRelationBuilder::build_scene_compositor(Scene *scene)
|
||||
if (scene->nodetree == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
build_nodetree(scene->nodetree);
|
||||
|
||||
OperationKey scene_key(&scene->id, NodeType::SHADING, OperationCode::COMPOSITOR_EVAL);
|
||||
OperationKey ntree_key(
|
||||
&scene->nodetree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT);
|
||||
add_relation(ntree_key, scene_key, "Compositor's NTree");
|
||||
|
||||
build_nested_nodetree(&scene->id, scene->nodetree);
|
||||
}
|
||||
|
||||
} // namespace blender::deg
|
||||
|
||||
@@ -202,6 +202,9 @@ const char *operationCodeAsString(OperationCode opcode)
|
||||
/* Sequencer. */
|
||||
case OperationCode::SEQUENCES_EVAL:
|
||||
return "SEQUENCES_EVAL";
|
||||
/* Sequencer. */
|
||||
case OperationCode::COMPOSITOR_EVAL:
|
||||
return "COMPOSITOR_EVAL";
|
||||
/* instancing/duplication. */
|
||||
case OperationCode::DUPLI:
|
||||
return "DUPLI";
|
||||
|
||||
@@ -201,9 +201,11 @@ enum class OperationCode {
|
||||
GENERIC_DATABLOCK_UPDATE,
|
||||
|
||||
/* Sequencer. ----------------------------------------------------------- */
|
||||
|
||||
SEQUENCES_EVAL,
|
||||
|
||||
/* Sequencer. ----------------------------------------------------------- */
|
||||
COMPOSITOR_EVAL,
|
||||
|
||||
/* Duplication/instancing system. --------------------------------------- */
|
||||
DUPLI,
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ set(INC
|
||||
../nodes
|
||||
../render
|
||||
../render/intern
|
||||
../viewport_compositor
|
||||
../windowmanager
|
||||
|
||||
../../../intern/atomic
|
||||
@@ -52,6 +53,25 @@ set(INC
|
||||
${OPENSUBDIV_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(EEVEE_BUILD_DIR
|
||||
${CMAKE_CURRENT_BINARY_DIR}/engines/eevee
|
||||
)
|
||||
|
||||
set(EEVEE_BUILD_SRC
|
||||
engines/eevee/eevee_build.cc
|
||||
engines/eevee/eevee_lut.c
|
||||
)
|
||||
add_executable(eevee_build ${EEVEE_BUILD_SRC})
|
||||
target_include_directories(eevee_build PRIVATE ${INC})
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl
|
||||
COMMAND
|
||||
"$<TARGET_FILE:eevee_build>" --resolve_sample_table ${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl
|
||||
DEPENDS eevee_build
|
||||
)
|
||||
|
||||
set(SRC
|
||||
intern/draw_cache.c
|
||||
intern/draw_cache_extract_mesh.cc
|
||||
@@ -117,35 +137,35 @@ set(SRC
|
||||
intern/smaa_textures.c
|
||||
engines/basic/basic_engine.c
|
||||
engines/basic/basic_shader.c
|
||||
engines/compositor/compositor_engine.cc
|
||||
engines/image/image_engine.cc
|
||||
engines/image/image_shader.cc
|
||||
engines/eevee/eevee_bloom.c
|
||||
engines/eevee/eevee_cryptomatte.c
|
||||
engines/eevee/eevee_data.c
|
||||
engines/eevee/eevee_depth_of_field.c
|
||||
engines/eevee/eevee_effects.c
|
||||
engines/eevee/eevee_engine.c
|
||||
engines/eevee/eevee_lightcache.c
|
||||
engines/eevee/eevee_lightprobes.c
|
||||
engines/eevee/eevee_lights.c
|
||||
engines/eevee/eevee_lookdev.c
|
||||
engines/eevee/eevee_camera.cc
|
||||
engines/eevee/eevee_depth_of_field.cc
|
||||
engines/eevee/eevee_engine.cc
|
||||
engines/eevee/eevee_film.cc
|
||||
engines/eevee/eevee_gpencil.cc
|
||||
engines/eevee/eevee_hair.cc
|
||||
engines/eevee/eevee_hizbuffer.cc
|
||||
engines/eevee/eevee_id_map.cc
|
||||
engines/eevee/eevee_instance.cc
|
||||
engines/eevee/eevee_light.cc
|
||||
engines/eevee/eevee_lightcache.cc
|
||||
engines/eevee/eevee_lightprobe.cc
|
||||
engines/eevee/eevee_lookdev.cc
|
||||
engines/eevee/eevee_lut.c
|
||||
engines/eevee/eevee_lut_gen.c
|
||||
engines/eevee/eevee_materials.c
|
||||
engines/eevee/eevee_mist.c
|
||||
engines/eevee/eevee_motion_blur.c
|
||||
engines/eevee/eevee_occlusion.c
|
||||
engines/eevee/eevee_render.c
|
||||
engines/eevee/eevee_renderpasses.c
|
||||
engines/eevee/eevee_sampling.c
|
||||
engines/eevee/eevee_screen_raytrace.c
|
||||
engines/eevee/eevee_shaders.c
|
||||
engines/eevee/eevee_shadows.c
|
||||
engines/eevee/eevee_shadows_cascade.c
|
||||
engines/eevee/eevee_shadows_cube.c
|
||||
engines/eevee/eevee_subsurface.c
|
||||
engines/eevee/eevee_temporal_sampling.c
|
||||
engines/eevee/eevee_volumes.c
|
||||
engines/eevee/eevee_material.cc
|
||||
engines/eevee/eevee_mesh.cc
|
||||
engines/eevee/eevee_motion_blur.cc
|
||||
engines/eevee/eevee_raytracing.cc
|
||||
engines/eevee/eevee_renderpasses.cc
|
||||
engines/eevee/eevee_shader.cc
|
||||
engines/eevee/eevee_shading.cc
|
||||
engines/eevee/eevee_shadow.cc
|
||||
engines/eevee/eevee_subsurface.cc
|
||||
engines/eevee/eevee_velocity.cc
|
||||
engines/eevee/eevee_view.cc
|
||||
engines/eevee/eevee_world.cc
|
||||
engines/workbench/workbench_data.c
|
||||
engines/workbench/workbench_effect_antialiasing.c
|
||||
engines/workbench/workbench_effect_cavity.c
|
||||
@@ -223,10 +243,14 @@ set(SRC
|
||||
intern/smaa_textures.h
|
||||
engines/basic/basic_engine.h
|
||||
engines/basic/basic_private.h
|
||||
engines/compositor/compositor_engine.h
|
||||
engines/eevee/eevee_engine.h
|
||||
engines/eevee/eevee_lightcache.h
|
||||
engines/eevee/eevee_camera.hh
|
||||
engines/eevee/eevee_instance.hh
|
||||
engines/eevee/eevee_light.hh
|
||||
engines/eevee/eevee_depth_of_field.hh
|
||||
engines/eevee/eevee_lut.h
|
||||
engines/eevee/eevee_private.h
|
||||
engines/external/external_engine.h
|
||||
engines/image/image_batches.hh
|
||||
engines/image/image_drawing_mode.hh
|
||||
@@ -251,99 +275,120 @@ set(SRC
|
||||
set(LIB
|
||||
bf_blenkernel
|
||||
bf_blenlib
|
||||
bf_viewport_compositor
|
||||
bf_windowmanager
|
||||
)
|
||||
|
||||
set(GLSL_SRC
|
||||
engines/eevee/shaders/ambient_occlusion_lib.glsl
|
||||
engines/eevee/shaders/background_vert.glsl
|
||||
engines/eevee/shaders/common_uniforms_lib.glsl
|
||||
engines/eevee/shaders/common_utiltex_lib.glsl
|
||||
engines/eevee/shaders/lights_lib.glsl
|
||||
engines/eevee/shaders/lightprobe_lib.glsl
|
||||
engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_geom.glsl
|
||||
engines/eevee/shaders/lightprobe_vert.glsl
|
||||
engines/eevee/shaders/lightprobe_cube_display_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_cube_display_vert.glsl
|
||||
engines/eevee/shaders/lightprobe_grid_display_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_grid_display_vert.glsl
|
||||
engines/eevee/shaders/lightprobe_grid_fill_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_planar_display_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_planar_display_vert.glsl
|
||||
engines/eevee/shaders/lookdev_world_frag.glsl
|
||||
engines/eevee/shaders/closure_eval_lib.glsl
|
||||
engines/eevee/shaders/closure_eval_diffuse_lib.glsl
|
||||
engines/eevee/shaders/closure_eval_glossy_lib.glsl
|
||||
engines/eevee/shaders/closure_eval_refraction_lib.glsl
|
||||
engines/eevee/shaders/closure_eval_translucent_lib.glsl
|
||||
engines/eevee/shaders/closure_type_lib.glsl
|
||||
engines/eevee/shaders/effect_bloom_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_bokeh_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_downsample_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_filter_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_flatten_tiles_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_gather_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_lib.glsl
|
||||
engines/eevee/shaders/effect_dof_reduce_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_resolve_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_scatter_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_scatter_vert.glsl
|
||||
engines/eevee/shaders/effect_dof_setup_frag.glsl
|
||||
engines/eevee/shaders/effect_reflection_lib.glsl
|
||||
engines/eevee/shaders/effect_reflection_resolve_frag.glsl
|
||||
engines/eevee/shaders/effect_reflection_trace_frag.glsl
|
||||
engines/eevee/shaders/effect_downsample_frag.glsl
|
||||
engines/eevee/shaders/effect_downsample_cube_frag.glsl
|
||||
engines/eevee/shaders/effect_gtao_frag.glsl
|
||||
engines/eevee/shaders/effect_velocity_resolve_frag.glsl
|
||||
engines/eevee/shaders/effect_velocity_tile_frag.glsl
|
||||
engines/eevee/shaders/effect_minmaxz_frag.glsl
|
||||
engines/eevee/shaders/effect_mist_frag.glsl
|
||||
engines/eevee/shaders/effect_motion_blur_frag.glsl
|
||||
engines/eevee/shaders/effect_subsurface_frag.glsl
|
||||
engines/eevee/shaders/effect_translucency_frag.glsl
|
||||
engines/eevee/shaders/effect_temporal_aa.glsl
|
||||
engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl
|
||||
engines/eevee/shaders/lightprobe_planar_downsample_vert.glsl
|
||||
engines/eevee/shaders/object_motion_frag.glsl
|
||||
engines/eevee/shaders/object_motion_vert.glsl
|
||||
engines/eevee/shaders/prepass_frag.glsl
|
||||
engines/eevee/shaders/prepass_vert.glsl
|
||||
engines/eevee/shaders/shadow_accum_frag.glsl
|
||||
engines/eevee/shaders/shadow_frag.glsl
|
||||
engines/eevee/shaders/shadow_vert.glsl
|
||||
engines/eevee/shaders/bsdf_lut_frag.glsl
|
||||
engines/eevee/shaders/btdf_lut_frag.glsl
|
||||
engines/eevee/shaders/bsdf_common_lib.glsl
|
||||
engines/eevee/shaders/irradiance_lib.glsl
|
||||
engines/eevee/shaders/octahedron_lib.glsl
|
||||
engines/eevee/shaders/cubemap_lib.glsl
|
||||
engines/eevee/shaders/bsdf_sampling_lib.glsl
|
||||
engines/eevee/shaders/random_lib.glsl
|
||||
engines/eevee/shaders/raytrace_lib.glsl
|
||||
engines/eevee/shaders/renderpass_lib.glsl
|
||||
engines/eevee/shaders/renderpass_postprocess_frag.glsl
|
||||
engines/eevee/shaders/cryptomatte_frag.glsl
|
||||
engines/eevee/shaders/ltc_lib.glsl
|
||||
engines/eevee/shaders/ssr_lib.glsl
|
||||
engines/eevee/shaders/surface_frag.glsl
|
||||
engines/eevee/shaders/surface_geom.glsl
|
||||
engines/eevee/shaders/surface_lib.glsl
|
||||
engines/eevee/shaders/surface_vert.glsl
|
||||
engines/eevee/shaders/update_noise_frag.glsl
|
||||
engines/eevee/shaders/volumetric_accum_frag.glsl
|
||||
engines/eevee/shaders/volumetric_lib.glsl
|
||||
engines/eevee/shaders/volumetric_frag.glsl
|
||||
engines/eevee/shaders/volumetric_geom.glsl
|
||||
engines/eevee/shaders/volumetric_vert.glsl
|
||||
engines/eevee/shaders/volumetric_resolve_frag.glsl
|
||||
engines/eevee/shaders/volumetric_scatter_frag.glsl
|
||||
engines/eevee/shaders/volumetric_integration_frag.glsl
|
||||
engines/compositor/shaders/compositor_frag.glsl
|
||||
engines/compositor/shaders/compositor_lib.glsl
|
||||
engines/compositor/shaders/compositor_nodetree_eval_lib.glsl
|
||||
|
||||
engines/eevee/shaders/eevee_bsdf_lib.glsl
|
||||
engines/eevee/shaders/eevee_bsdf_microfacet_lib.glsl
|
||||
engines/eevee/shaders/eevee_bsdf_sampling_lib.glsl
|
||||
engines/eevee/shaders/eevee_bsdf_stubs_lib.glsl
|
||||
engines/eevee/shaders/eevee_camera_lib.glsl
|
||||
engines/eevee/shaders/eevee_closure_lib.glsl
|
||||
engines/eevee/shaders/eevee_culling_debug_frag.glsl
|
||||
engines/eevee/shaders/eevee_culling_iter_lib.glsl
|
||||
engines/eevee/shaders/eevee_culling_lib.glsl
|
||||
engines/eevee/shaders/eevee_culling_select_comp.glsl
|
||||
engines/eevee/shaders/eevee_culling_sort_comp.glsl
|
||||
engines/eevee/shaders/eevee_culling_tile_comp.glsl
|
||||
engines/eevee/shaders/eevee_deferred_direct_frag.glsl
|
||||
engines/eevee/shaders/eevee_deferred_holdout_frag.glsl
|
||||
engines/eevee/shaders/eevee_deferred_transparent_frag.glsl
|
||||
engines/eevee/shaders/eevee_deferred_volume_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_clear_frag.glsl
|
||||
engines/eevee/shaders/eevee_hiz_copy_frag.glsl
|
||||
engines/eevee/shaders/eevee_hiz_downsample_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_accumulator_lib.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_bokeh_lut_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_filter_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_gather_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_gather_holefill_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_lib.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_reduce_copy_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_reduce_downsample_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_reduce_recursive_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_resolve_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_scatter_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_scatter_vert.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_setup_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_tiles_dilate_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_tiles_flatten_frag.glsl
|
||||
engines/eevee/shaders/eevee_film_filter_frag.glsl
|
||||
engines/eevee/shaders/eevee_film_lib.glsl
|
||||
engines/eevee/shaders/eevee_film_resolve_frag.glsl
|
||||
engines/eevee/shaders/eevee_film_resolve_depth_frag.glsl
|
||||
engines/eevee/shaders/eevee_gbuffer_lib.glsl
|
||||
engines/eevee/shaders/eevee_irradiance_lib.glsl
|
||||
engines/eevee/shaders/eevee_light_lib.glsl
|
||||
engines/eevee/shaders/eevee_light_eval_lib.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_display_cubemap_frag.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_display_cubemap_vert.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_display_grid_frag.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_display_grid_vert.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_eval_cubemap_lib.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_eval_grid_lib.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_filter_diffuse_frag.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_filter_downsample_frag.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_filter_glossy_frag.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_filter_vert.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_filter_visibility_frag.glsl
|
||||
engines/eevee/shaders/eevee_lookdev_background_frag.glsl
|
||||
engines/eevee/shaders/eevee_ltc_lib.glsl
|
||||
engines/eevee/shaders/eevee_motion_blur_gather_frag.glsl
|
||||
engines/eevee/shaders/eevee_motion_blur_lib.glsl
|
||||
engines/eevee/shaders/eevee_motion_blur_tiles_dilate_frag.glsl
|
||||
engines/eevee/shaders/eevee_motion_blur_tiles_flatten_frag.glsl
|
||||
engines/eevee/shaders/eevee_nodetree_eval_lib.glsl
|
||||
engines/eevee/shaders/eevee_raytrace_denoise_comp.glsl
|
||||
engines/eevee/shaders/eevee_raytrace_raygen_frag.glsl
|
||||
engines/eevee/shaders/eevee_raytrace_raygen_lib.glsl
|
||||
engines/eevee/shaders/eevee_raytrace_resolve_frag.glsl
|
||||
engines/eevee/shaders/eevee_raytrace_trace_lib.glsl
|
||||
engines/eevee/shaders/eevee_sampling_lib.glsl
|
||||
engines/eevee/shaders/eevee_shadow_debug_frag.glsl
|
||||
engines/eevee/shaders/eevee_shadow_lib.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_alloc_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_copy_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_debug_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_defrag_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_free_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_init_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_lib.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_mark_vert.glsl
|
||||
engines/eevee/shaders/eevee_shadow_tilemap_depth_scan_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_tilemap_lod_mask_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl
|
||||
engines/eevee/shaders/eevee_shadow_tilemap_setup_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl
|
||||
engines/eevee/shaders/eevee_subsurface_eval_frag.glsl
|
||||
engines/eevee/shaders/eevee_surface_background_frag.glsl
|
||||
engines/eevee/shaders/eevee_surface_deferred_frag.glsl
|
||||
engines/eevee/shaders/eevee_surface_depth_frag.glsl
|
||||
engines/eevee/shaders/eevee_surface_depth_simple_frag.glsl
|
||||
engines/eevee/shaders/eevee_surface_forward_frag.glsl
|
||||
engines/eevee/shaders/eevee_surface_gpencil_vert.glsl
|
||||
engines/eevee/shaders/eevee_surface_hair_vert.glsl
|
||||
engines/eevee/shaders/eevee_surface_lib.glsl
|
||||
engines/eevee/shaders/eevee_surface_lookdev_vert.glsl
|
||||
engines/eevee/shaders/eevee_surface_mesh_geom.glsl
|
||||
engines/eevee/shaders/eevee_surface_mesh_vert.glsl
|
||||
engines/eevee/shaders/eevee_surface_world_vert.glsl
|
||||
engines/eevee/shaders/eevee_velocity_lib.glsl
|
||||
engines/eevee/shaders/eevee_velocity_camera_frag.glsl
|
||||
engines/eevee/shaders/eevee_velocity_surface_mesh_vert.glsl
|
||||
engines/eevee/shaders/eevee_velocity_surface_frag.glsl
|
||||
engines/eevee/shaders/eevee_volume_deferred_frag.glsl
|
||||
engines/eevee/shaders/eevee_volume_eval_lib.glsl
|
||||
engines/eevee/shaders/eevee_volume_vert.glsl
|
||||
|
||||
engines/eevee/eevee_defines.hh
|
||||
engines/eevee/eevee_shader_shared.hh
|
||||
|
||||
engines/workbench/shaders/workbench_cavity_lib.glsl
|
||||
engines/workbench/shaders/workbench_common_lib.glsl
|
||||
@@ -375,19 +420,25 @@ set(GLSL_SRC
|
||||
|
||||
engines/workbench/workbench_shader_shared.h
|
||||
|
||||
intern/shaders/common_attribute_lib.glsl
|
||||
intern/shaders/common_colormanagement_lib.glsl
|
||||
intern/shaders/common_globals_lib.glsl
|
||||
intern/shaders/common_pointcloud_lib.glsl
|
||||
intern/shaders/common_hair_lib.glsl
|
||||
intern/shaders/common_hair_refine_vert.glsl
|
||||
intern/shaders/common_hair_refine_comp.glsl
|
||||
intern/shaders/common_math_lib.glsl
|
||||
intern/shaders/common_math_geom_lib.glsl
|
||||
intern/shaders/common_view_clipping_lib.glsl
|
||||
intern/shaders/common_view_lib.glsl
|
||||
intern/shaders/common_fxaa_lib.glsl
|
||||
intern/shaders/common_smaa_lib.glsl
|
||||
intern/shaders/common_debug_lib.glsl
|
||||
intern/shaders/common_debug_print_lib.glsl
|
||||
intern/shaders/common_fullscreen_vert.glsl
|
||||
intern/shaders/common_fxaa_lib.glsl
|
||||
intern/shaders/common_globals_lib.glsl
|
||||
intern/shaders/common_gpencil_lib.glsl
|
||||
intern/shaders/common_hair_lib.glsl
|
||||
intern/shaders/common_hair_refine_comp.glsl
|
||||
intern/shaders/common_hair_refine_vert.glsl
|
||||
intern/shaders/common_intersection_lib.glsl
|
||||
intern/shaders/common_math_geom_lib.glsl
|
||||
intern/shaders/common_math_lib.glsl
|
||||
intern/shaders/common_obinfos_lib.glsl
|
||||
intern/shaders/common_pointcloud_lib.glsl
|
||||
intern/shaders/common_smaa_lib.glsl
|
||||
intern/shaders/common_view_lib.glsl
|
||||
intern/shaders/common_view_clipping_lib.glsl
|
||||
|
||||
intern/shaders/common_subdiv_custom_data_interp_comp.glsl
|
||||
intern/shaders/common_subdiv_ibo_lines_comp.glsl
|
||||
@@ -402,6 +453,9 @@ set(GLSL_SRC
|
||||
intern/shaders/common_subdiv_vbo_lnor_comp.glsl
|
||||
intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl
|
||||
|
||||
intern/shaders/draw_debug_print_display_vert.glsl
|
||||
intern/shaders/draw_debug_print_display_frag.glsl
|
||||
|
||||
intern/draw_shader_shared.h
|
||||
|
||||
engines/gpencil/shaders/gpencil_frag.glsl
|
||||
@@ -530,6 +584,14 @@ foreach(GLSL_FILE ${GLSL_SRC})
|
||||
data_to_c_simple(${GLSL_FILE} GLSL_C)
|
||||
endforeach()
|
||||
|
||||
# Generated shader files
|
||||
# TODO(@fclem): Clean that by wrapping it into a function.
|
||||
data_to_c(${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl ${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl.c GLSL_C)
|
||||
|
||||
list(APPEND GLSL_SRC
|
||||
${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl
|
||||
)
|
||||
|
||||
blender_add_lib(bf_draw_shaders "${GLSL_C}" "" "" "")
|
||||
|
||||
list(APPEND LIB
|
||||
|
||||
@@ -46,8 +46,10 @@ struct Render;
|
||||
struct RenderEngine;
|
||||
struct RenderEngineType;
|
||||
struct Scene;
|
||||
struct ScrArea;
|
||||
struct View3D;
|
||||
struct ViewLayer;
|
||||
struct wmWindow;
|
||||
struct bContext;
|
||||
struct rcti;
|
||||
|
||||
@@ -62,11 +64,13 @@ typedef struct DRWUpdateContext {
|
||||
struct Depsgraph *depsgraph;
|
||||
struct Scene *scene;
|
||||
struct ViewLayer *view_layer;
|
||||
struct ScrArea *area;
|
||||
struct ARegion *region;
|
||||
struct View3D *v3d;
|
||||
struct RenderEngineType *engine_type;
|
||||
struct wmWindow *window;
|
||||
} DRWUpdateContext;
|
||||
void DRW_notify_view_update(const DRWUpdateContext *update_ctx);
|
||||
void DRW_notify_view_update(const DRWUpdateContext *update_ctx, bool do_update);
|
||||
|
||||
typedef enum eDRWSelectStage {
|
||||
DRW_SELECT_PASS_PRE = 1,
|
||||
@@ -222,6 +226,10 @@ void DRW_drawdata_free(struct ID *id);
|
||||
|
||||
struct DRWData *DRW_viewport_data_create(void);
|
||||
void DRW_viewport_data_free(struct DRWData *drw_data);
|
||||
bool DRW_viewport_has_external_engine(struct GPUViewport *viewport);
|
||||
void DRW_viewport_free_external_engines(struct GPUViewport *viewport);
|
||||
bool DRW_viewport_engines_stereo_support(struct GPUViewport *viewport);
|
||||
void DRW_viewport_tag_redraw(struct GPUViewport *viewport, struct ARegion *region);
|
||||
|
||||
bool DRW_opengl_context_release(void);
|
||||
void DRW_opengl_context_activate(bool drw_state);
|
||||
|
||||
172
source/blender/draw/engines/compositor/compositor_engine.cc
Normal file
172
source/blender/draw/engines/compositor/compositor_engine.cc
Normal file
@@ -0,0 +1,172 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2022 Blender Foundation. All rights reserved. */
|
||||
|
||||
#include "BLI_math_vec_types.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "DNA_ID_enums.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "IMB_colormanagement.h"
|
||||
|
||||
#include "VPC_context.hh"
|
||||
#include "VPC_evaluator.hh"
|
||||
#include "VPC_texture_pool.hh"
|
||||
|
||||
#include "GPU_texture.h"
|
||||
|
||||
namespace blender::viewport_compositor {
|
||||
|
||||
class DRWTexturePool : public TexturePool {
|
||||
public:
|
||||
GPUTexture *allocate_texture(int2 size, eGPUTextureFormat format) override
|
||||
{
|
||||
DrawEngineType *owner = (DrawEngineType *)this;
|
||||
return DRW_texture_pool_query_2d(size.x, size.y, format, owner);
|
||||
}
|
||||
};
|
||||
|
||||
class DRWContext : public Context {
|
||||
public:
|
||||
using Context::Context;
|
||||
|
||||
const Scene *get_scene() override
|
||||
{
|
||||
return DRW_context_state_get()->scene;
|
||||
}
|
||||
|
||||
int2 get_viewport_size() override
|
||||
{
|
||||
return int2(float2(DRW_viewport_size_get()));
|
||||
}
|
||||
|
||||
GPUTexture *get_viewport_texture() override
|
||||
{
|
||||
return DRW_viewport_texture_list_get()->color;
|
||||
}
|
||||
|
||||
GPUTexture *get_pass_texture(int view_layer, eScenePassType pass_type) override
|
||||
{
|
||||
Scene *original_scene = (Scene *)DEG_get_original_id(&DRW_context_state_get()->scene->id);
|
||||
return DRW_render_pass_find(original_scene, view_layer, pass_type)->pass_tx;
|
||||
}
|
||||
};
|
||||
|
||||
class Engine {
|
||||
private:
|
||||
DRWTexturePool texture_pool_;
|
||||
DRWContext context_;
|
||||
Evaluator evaluator_;
|
||||
/* Stores the viewport size at the time the last compositor evaluation happened. See the
|
||||
* update_viewport_size method for more information. */
|
||||
int2 viewport_size_;
|
||||
|
||||
public:
|
||||
Engine() : context_(texture_pool_), evaluator_(context_, node_tree())
|
||||
{
|
||||
}
|
||||
|
||||
/* Update the viewport size and evaluate the compositor. */
|
||||
void draw()
|
||||
{
|
||||
update_viewport_size();
|
||||
evaluator_.evaluate();
|
||||
}
|
||||
|
||||
/* If the size of the viewport changed from the last time the compositor was evaluated, update
|
||||
* the viewport size and reset the evaluator. That's because the evaluator compiles the node tree
|
||||
* in a manner that is specifically optimized for the size of the viewport. This should be called
|
||||
* before evaluating the compositor. */
|
||||
void update_viewport_size()
|
||||
{
|
||||
if (viewport_size_ == context_.get_viewport_size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
viewport_size_ = context_.get_viewport_size();
|
||||
|
||||
evaluator_.reset();
|
||||
}
|
||||
|
||||
/* If the compositor node tree changed, reset the evaluator. */
|
||||
void update(const Depsgraph *depsgraph)
|
||||
{
|
||||
if (DEG_id_type_updated(depsgraph, ID_NT)) {
|
||||
evaluator_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/* Get a reference to the compositor node tree. */
|
||||
static bNodeTree &node_tree()
|
||||
{
|
||||
return *DRW_context_state_get()->scene->nodetree;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::viewport_compositor
|
||||
|
||||
using namespace blender::viewport_compositor;
|
||||
|
||||
typedef struct CompositorData {
|
||||
DrawEngineType *engine_type;
|
||||
DRWViewportEmptyList *fbl;
|
||||
DRWViewportEmptyList *txl;
|
||||
DRWViewportEmptyList *psl;
|
||||
DRWViewportEmptyList *stl;
|
||||
Engine *instance_data;
|
||||
} CompositorData;
|
||||
|
||||
static void compositor_engine_init(void *data)
|
||||
{
|
||||
CompositorData *compositor_data = static_cast<CompositorData *>(data);
|
||||
|
||||
if (!compositor_data->instance_data) {
|
||||
compositor_data->instance_data = new Engine();
|
||||
}
|
||||
}
|
||||
|
||||
static void compositor_engine_free(void *instance_data)
|
||||
{
|
||||
Engine *engine = static_cast<Engine *>(instance_data);
|
||||
delete engine;
|
||||
}
|
||||
|
||||
static void compositor_engine_draw(void *data)
|
||||
{
|
||||
const CompositorData *compositor_data = static_cast<CompositorData *>(data);
|
||||
compositor_data->instance_data->draw();
|
||||
}
|
||||
|
||||
static void compositor_engine_update(void *data)
|
||||
{
|
||||
const CompositorData *compositor_data = static_cast<CompositorData *>(data);
|
||||
compositor_data->instance_data->update(DRW_context_state_get()->depsgraph);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
static const DrawEngineDataSize compositor_data_size = DRW_VIEWPORT_DATA_SIZE(CompositorData);
|
||||
|
||||
DrawEngineType draw_engine_compositor_type = {
|
||||
nullptr, /* next */
|
||||
nullptr, /* prev */
|
||||
N_("Compositor"), /* idname */
|
||||
&compositor_data_size, /* vedata_size */
|
||||
&compositor_engine_init, /* engine_init */
|
||||
nullptr, /* engine_free */
|
||||
&compositor_engine_free, /* instance_free */
|
||||
nullptr, /* cache_init */
|
||||
nullptr, /* cache_populate */
|
||||
nullptr, /* cache_finish */
|
||||
&compositor_engine_draw, /* draw_scene */
|
||||
&compositor_engine_update, /* view_update */
|
||||
nullptr, /* id_update */
|
||||
nullptr, /* render_to_image */
|
||||
nullptr, /* store_metadata */
|
||||
};
|
||||
}
|
||||
14
source/blender/draw/engines/compositor/compositor_engine.h
Normal file
14
source/blender/draw/engines/compositor/compositor_engine.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2022 Blender Foundation. All rights reserved. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern DrawEngineType draw_engine_compositor_type;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
#pragma BLENDER_REQUIRE(compositor_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(compositor_nodetree_eval_lib.glsl)
|
||||
|
||||
in vec4 uvcoordsvar;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
g_data.uv_render_layer = uvcoordsvar.xy;
|
||||
g_data.uv_texco = uvcoordsvar.xy * CameraTexCoFactors.xy + CameraTexCoFactors.zw;
|
||||
|
||||
out_color = nodetree_composite();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/* Keep in sync with CompositorData in compositor_engine.cc. */
|
||||
struct CompositorData {
|
||||
vec3 luminance_coefficients;
|
||||
float frame_number;
|
||||
};
|
||||
|
||||
layout(std140) uniform compositor_block
|
||||
{
|
||||
CompositorData compositor_data;
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
|
||||
|
||||
struct GlobalData {
|
||||
/** UV to sample render layer. Cover the whole viewport. */
|
||||
vec2 uv_render_layer;
|
||||
/** UV of the compositing area. Also known as Camera Texture coordinates in material shaders. */
|
||||
vec2 uv_texco;
|
||||
};
|
||||
|
||||
GlobalData g_data;
|
||||
|
||||
void ntree_eval_init()
|
||||
{
|
||||
}
|
||||
|
||||
void ntree_eval_weights()
|
||||
{
|
||||
}
|
||||
219
source/blender/draw/engines/eevee/eevee_allocator.hh
Normal file
219
source/blender/draw/engines/eevee/eevee_allocator.hh
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Allocators that may be moved to BLI at some point.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_bits.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/**
|
||||
* Allow allocation and deletion of elements without reordering.
|
||||
* Useful to keed valid indices to items inside this allocator.
|
||||
* Type T need to implement the free_resources() method.
|
||||
*/
|
||||
template<typename T> class IndexedAllocator {
|
||||
private:
|
||||
Vector<T> items_;
|
||||
/** Bitmap of used items. Make search of unused slot faster. */
|
||||
Vector<uint32_t> unused_;
|
||||
/** First unused batch of items in the vector for fast reallocating. */
|
||||
int64_t unused_first_ = LONG_MAX;
|
||||
/** Unused item count in the vector for fast reallocating. */
|
||||
int64_t unused_count_ = 0;
|
||||
|
||||
public:
|
||||
int64_t alloc(T &&value)
|
||||
{
|
||||
if (unused_count_ > 0) {
|
||||
/* Reclaim unused slot. */
|
||||
int64_t index = unused_first_;
|
||||
items_[index] = std::move(value);
|
||||
set_slot_used(index);
|
||||
|
||||
unused_count_ -= 1;
|
||||
unused_first_ = first_unused_slot_get();
|
||||
return index;
|
||||
}
|
||||
/* Not enough place, grow the vector. */
|
||||
int64_t index = items_.append_and_get_index(value);
|
||||
int64_t size_needed = (index + 32) / 32;
|
||||
int64_t size_old = unused_.size();
|
||||
unused_.resize(size_needed);
|
||||
if (size_old < size_needed) {
|
||||
for (auto i : IndexRange(size_old, size_needed - size_old)) {
|
||||
/* Used by default. */
|
||||
unused_[i] = 0;
|
||||
}
|
||||
}
|
||||
set_slot_used(index);
|
||||
return index;
|
||||
}
|
||||
|
||||
void free(int64_t index)
|
||||
{
|
||||
unused_count_ += 1;
|
||||
if (index < unused_first_) {
|
||||
unused_first_ = index;
|
||||
}
|
||||
set_slot_unused(index);
|
||||
is_slot_unused(index);
|
||||
items_[index].free_resources();
|
||||
}
|
||||
|
||||
/* Pruned unused shadows at the end of the vector. */
|
||||
void resize()
|
||||
{
|
||||
while (items_.size() > 0 && is_slot_unused(items_.size() - 1)) {
|
||||
set_slot_used(items_.size() - 1);
|
||||
items_.remove_last();
|
||||
unused_count_--;
|
||||
}
|
||||
if (unused_first_ >= items_.size()) {
|
||||
/* First unused has been pruned. */
|
||||
unused_first_ = first_unused_slot_get();
|
||||
}
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return items_.size() - unused_count_;
|
||||
}
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = T;
|
||||
using pointer = const T *;
|
||||
using reference = const T &;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
private:
|
||||
IndexedAllocator &allocator_;
|
||||
int64_t current_;
|
||||
|
||||
public:
|
||||
constexpr explicit Iterator(IndexedAllocator &allocator, int64_t current)
|
||||
: allocator_(allocator), current_(current)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr Iterator &operator++()
|
||||
{
|
||||
current_++;
|
||||
while ((current_ < allocator_.items_.size()) && allocator_.is_slot_unused(current_)) {
|
||||
current_++;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Iterator operator++(int) const
|
||||
{
|
||||
Iterator iterator = *this;
|
||||
++*this;
|
||||
return iterator;
|
||||
}
|
||||
|
||||
constexpr friend bool operator!=(const Iterator &a, const Iterator &b)
|
||||
{
|
||||
return a.current_ != b.current_;
|
||||
}
|
||||
|
||||
T &operator*()
|
||||
{
|
||||
BLI_assert(allocator_.is_slot_unused(current_) == false);
|
||||
return allocator_[current_];
|
||||
}
|
||||
};
|
||||
|
||||
constexpr Iterator begin()
|
||||
{
|
||||
int64_t first_used = first_used_slot_get();
|
||||
if (first_used == LONG_MAX) {
|
||||
/* Will produce no iteration. */
|
||||
first_used = items_.size();
|
||||
}
|
||||
return Iterator(*this, first_used);
|
||||
}
|
||||
|
||||
constexpr Iterator end()
|
||||
{
|
||||
return Iterator(*this, items_.size());
|
||||
}
|
||||
|
||||
const T &operator[](int64_t index) const
|
||||
{
|
||||
BLI_assert(is_slot_unused(index) == false);
|
||||
return items_[index];
|
||||
}
|
||||
|
||||
T &operator[](int64_t index)
|
||||
{
|
||||
BLI_assert(is_slot_unused(index) == false);
|
||||
return items_[index];
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t first_unused_slot_get(void) const
|
||||
{
|
||||
if (unused_count_ > 0) {
|
||||
for (auto i : IndexRange(unused_.size())) {
|
||||
if (unused_[i] != 0) {
|
||||
return i * 32 + bitscan_forward_uint(unused_[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return LONG_MAX;
|
||||
}
|
||||
|
||||
int64_t first_used_slot_get(void) const
|
||||
{
|
||||
if (unused_count_ < items_.size()) {
|
||||
for (auto i : IndexRange(unused_.size())) {
|
||||
if (~unused_[i] != 0) {
|
||||
return i * 32 + bitscan_forward_uint(~unused_[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return LONG_MAX;
|
||||
}
|
||||
|
||||
bool is_slot_unused(int64_t index) const
|
||||
{
|
||||
return (unused_[index / 32] & (1u << uint32_t(index % 32))) != 0;
|
||||
}
|
||||
|
||||
void set_slot_unused(int64_t index)
|
||||
{
|
||||
SET_FLAG_FROM_TEST(unused_[index / 32], true, (1u << uint32_t(index % 32)));
|
||||
}
|
||||
|
||||
void set_slot_used(int64_t index)
|
||||
{
|
||||
SET_FLAG_FROM_TEST(unused_[index / 32], false, (1u << uint32_t(index % 32)));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::eevee
|
||||
@@ -1,344 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*
|
||||
* Eevee's bloom shader.
|
||||
*/
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "eevee_private.h"
|
||||
|
||||
static const bool use_highres = true;
|
||||
|
||||
int EEVEE_bloom_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
|
||||
|
||||
if (scene_eval->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) {
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
|
||||
/* Bloom */
|
||||
int blitsize[2], texsize[2];
|
||||
|
||||
/* Blit Buffer */
|
||||
effects->source_texel_size[0] = 1.0f / viewport_size[0];
|
||||
effects->source_texel_size[1] = 1.0f / viewport_size[1];
|
||||
|
||||
blitsize[0] = (int)viewport_size[0];
|
||||
blitsize[1] = (int)viewport_size[1];
|
||||
|
||||
effects->blit_texel_size[0] = 1.0f / (float)blitsize[0];
|
||||
effects->blit_texel_size[1] = 1.0f / (float)blitsize[1];
|
||||
|
||||
effects->bloom_blit = DRW_texture_pool_query_2d(
|
||||
blitsize[0], blitsize[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
|
||||
|
||||
GPU_framebuffer_ensure_config(
|
||||
&fbl->bloom_blit_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->bloom_blit)});
|
||||
|
||||
/* Parameters */
|
||||
const float threshold = scene_eval->eevee.bloom_threshold;
|
||||
const float knee = scene_eval->eevee.bloom_knee;
|
||||
const float intensity = scene_eval->eevee.bloom_intensity;
|
||||
const float *color = scene_eval->eevee.bloom_color;
|
||||
const float radius = scene_eval->eevee.bloom_radius;
|
||||
effects->bloom_clamp = scene_eval->eevee.bloom_clamp;
|
||||
|
||||
/* determine the iteration count */
|
||||
const float minDim = (float)MIN2(blitsize[0], blitsize[1]);
|
||||
const float maxIter = (radius - 8.0f) + log(minDim) / log(2);
|
||||
const int maxIterInt = effects->bloom_iteration_len = (int)maxIter;
|
||||
|
||||
CLAMP(effects->bloom_iteration_len, 1, MAX_BLOOM_STEP);
|
||||
|
||||
effects->bloom_sample_scale = 0.5f + maxIter - (float)maxIterInt;
|
||||
effects->bloom_curve_threshold[0] = threshold - knee;
|
||||
effects->bloom_curve_threshold[1] = knee * 2.0f;
|
||||
effects->bloom_curve_threshold[2] = 0.25f / max_ff(1e-5f, knee);
|
||||
effects->bloom_curve_threshold[3] = threshold;
|
||||
|
||||
mul_v3_v3fl(effects->bloom_color, color, intensity);
|
||||
|
||||
/* Downsample buffers */
|
||||
copy_v2_v2_int(texsize, blitsize);
|
||||
for (int i = 0; i < effects->bloom_iteration_len; i++) {
|
||||
texsize[0] /= 2;
|
||||
texsize[1] /= 2;
|
||||
|
||||
texsize[0] = MAX2(texsize[0], 2);
|
||||
texsize[1] = MAX2(texsize[1], 2);
|
||||
|
||||
effects->downsamp_texel_size[i][0] = 1.0f / (float)texsize[0];
|
||||
effects->downsamp_texel_size[i][1] = 1.0f / (float)texsize[1];
|
||||
|
||||
effects->bloom_downsample[i] = DRW_texture_pool_query_2d(
|
||||
texsize[0], texsize[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
|
||||
GPU_framebuffer_ensure_config(
|
||||
&fbl->bloom_down_fb[i],
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->bloom_downsample[i])});
|
||||
}
|
||||
|
||||
/* Upsample buffers */
|
||||
copy_v2_v2_int(texsize, blitsize);
|
||||
for (int i = 0; i < effects->bloom_iteration_len - 1; i++) {
|
||||
texsize[0] /= 2;
|
||||
texsize[1] /= 2;
|
||||
|
||||
texsize[0] = MAX2(texsize[0], 2);
|
||||
texsize[1] = MAX2(texsize[1], 2);
|
||||
|
||||
effects->bloom_upsample[i] = DRW_texture_pool_query_2d(
|
||||
texsize[0], texsize[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
|
||||
GPU_framebuffer_ensure_config(
|
||||
&fbl->bloom_accum_fb[i],
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->bloom_upsample[i])});
|
||||
}
|
||||
|
||||
return EFFECT_BLOOM | EFFECT_POST_BUFFER;
|
||||
}
|
||||
|
||||
/* Cleanup to release memory */
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_blit_fb);
|
||||
|
||||
for (int i = 0; i < MAX_BLOOM_STEP - 1; i++) {
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_down_fb[i]);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_accum_fb[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DRWShadingGroup *eevee_create_bloom_pass(const char *name,
|
||||
EEVEE_EffectsInfo *effects,
|
||||
struct GPUShader *sh,
|
||||
DRWPass **pass,
|
||||
bool upsample,
|
||||
bool resolve)
|
||||
{
|
||||
struct GPUBatch *quad = DRW_cache_fullscreen_quad_get();
|
||||
|
||||
*pass = DRW_pass_create(name, DRW_STATE_WRITE_COLOR);
|
||||
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, *pass);
|
||||
DRW_shgroup_call(grp, quad, NULL);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "sourceBuffer", &effects->unf_source_buffer);
|
||||
DRW_shgroup_uniform_vec2(grp, "sourceBufferTexelSize", effects->unf_source_texel_size, 1);
|
||||
if (upsample) {
|
||||
DRW_shgroup_uniform_texture_ref(grp, "baseBuffer", &effects->unf_base_buffer);
|
||||
DRW_shgroup_uniform_float(grp, "sampleScale", &effects->bloom_sample_scale, 1);
|
||||
}
|
||||
if (resolve) {
|
||||
DRW_shgroup_uniform_vec3(grp, "bloomColor", effects->bloom_color, 1);
|
||||
DRW_shgroup_uniform_bool_copy(grp, "bloomAddBase", true);
|
||||
}
|
||||
|
||||
return grp;
|
||||
}
|
||||
|
||||
void EEVEE_bloom_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
psl->bloom_accum_ps = NULL;
|
||||
|
||||
if ((effects->enabled_effects & EFFECT_BLOOM) != 0) {
|
||||
/**
|
||||
* Bloom Algorithm
|
||||
*
|
||||
* Overview:
|
||||
* - Down-sample the color buffer doing a small blur during each step.
|
||||
* - Accumulate bloom color using previously down-sampled color buffers
|
||||
* and do an up-sample blur for each new accumulated layer.
|
||||
* - Finally add accumulation buffer onto the source color buffer.
|
||||
*
|
||||
* [1/1] is original copy resolution (can be half or quarter res for performance)
|
||||
* <pre>
|
||||
* [DOWNSAMPLE CHAIN] [UPSAMPLE CHAIN]
|
||||
*
|
||||
* Source Color ─ [Blit] ─> Bright Color Extract [1/1] Final Color
|
||||
* | Λ
|
||||
* [Downsample First] Source Color ─> + [Resolve]
|
||||
* v |
|
||||
* Color Downsampled [1/2] ────────────> + Accumulation Buffer [1/2]
|
||||
* | Λ
|
||||
* ─── ───
|
||||
* Repeat Repeat
|
||||
* ─── ───
|
||||
* v |
|
||||
* Color Downsampled [1/N-1] ──────────> + Accumulation Buffer [1/N-1]
|
||||
* | Λ
|
||||
* [Downsample] [Upsample]
|
||||
* v |
|
||||
* Color Downsampled [1/N] ─────────────────────────┘
|
||||
* </pre>
|
||||
*/
|
||||
DRWShadingGroup *grp;
|
||||
const bool use_antiflicker = true;
|
||||
eevee_create_bloom_pass("Bloom Downsample First",
|
||||
effects,
|
||||
EEVEE_shaders_bloom_downsample_get(use_antiflicker),
|
||||
&psl->bloom_downsample_first,
|
||||
false,
|
||||
false);
|
||||
eevee_create_bloom_pass("Bloom Downsample",
|
||||
effects,
|
||||
EEVEE_shaders_bloom_downsample_get(false),
|
||||
&psl->bloom_downsample,
|
||||
false,
|
||||
false);
|
||||
eevee_create_bloom_pass("Bloom Upsample",
|
||||
effects,
|
||||
EEVEE_shaders_bloom_upsample_get(use_highres),
|
||||
&psl->bloom_upsample,
|
||||
true,
|
||||
false);
|
||||
|
||||
grp = eevee_create_bloom_pass("Bloom Blit",
|
||||
effects,
|
||||
EEVEE_shaders_bloom_blit_get(use_antiflicker),
|
||||
&psl->bloom_blit,
|
||||
false,
|
||||
false);
|
||||
DRW_shgroup_uniform_vec4(grp, "curveThreshold", effects->bloom_curve_threshold, 1);
|
||||
DRW_shgroup_uniform_float(grp, "clampIntensity", &effects->bloom_clamp, 1);
|
||||
|
||||
grp = eevee_create_bloom_pass("Bloom Resolve",
|
||||
effects,
|
||||
EEVEE_shaders_bloom_resolve_get(use_highres),
|
||||
&psl->bloom_resolve,
|
||||
true,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_bloom_draw(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
/* Bloom */
|
||||
if ((effects->enabled_effects & EFFECT_BLOOM) != 0) {
|
||||
struct GPUTexture *last;
|
||||
|
||||
/* Extract bright pixels */
|
||||
copy_v2_v2(effects->unf_source_texel_size, effects->source_texel_size);
|
||||
effects->unf_source_buffer = effects->source_buffer;
|
||||
|
||||
GPU_framebuffer_bind(fbl->bloom_blit_fb);
|
||||
DRW_draw_pass(psl->bloom_blit);
|
||||
|
||||
/* Downsample */
|
||||
copy_v2_v2(effects->unf_source_texel_size, effects->blit_texel_size);
|
||||
effects->unf_source_buffer = effects->bloom_blit;
|
||||
|
||||
GPU_framebuffer_bind(fbl->bloom_down_fb[0]);
|
||||
DRW_draw_pass(psl->bloom_downsample_first);
|
||||
|
||||
last = effects->bloom_downsample[0];
|
||||
|
||||
for (int i = 1; i < effects->bloom_iteration_len; i++) {
|
||||
copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[i - 1]);
|
||||
effects->unf_source_buffer = last;
|
||||
|
||||
GPU_framebuffer_bind(fbl->bloom_down_fb[i]);
|
||||
DRW_draw_pass(psl->bloom_downsample);
|
||||
|
||||
/* Used in next loop */
|
||||
last = effects->bloom_downsample[i];
|
||||
}
|
||||
|
||||
/* Upsample and accumulate */
|
||||
for (int i = effects->bloom_iteration_len - 2; i >= 0; i--) {
|
||||
copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[i]);
|
||||
effects->unf_source_buffer = last;
|
||||
effects->unf_base_buffer = effects->bloom_downsample[i];
|
||||
|
||||
GPU_framebuffer_bind(fbl->bloom_accum_fb[i]);
|
||||
DRW_draw_pass(psl->bloom_upsample);
|
||||
|
||||
last = effects->bloom_upsample[i];
|
||||
}
|
||||
|
||||
/* Resolve */
|
||||
copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[0]);
|
||||
effects->unf_source_buffer = last;
|
||||
effects->unf_base_buffer = effects->source_buffer;
|
||||
|
||||
GPU_framebuffer_bind(effects->target_buffer);
|
||||
DRW_draw_pass(psl->bloom_resolve);
|
||||
SWAP_BUFFERS();
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_bloom_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
|
||||
EEVEE_Data *vedata,
|
||||
uint UNUSED(tot_samples))
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
/* Create FrameBuffer. */
|
||||
DRW_texture_ensure_fullscreen_2d(&txl->bloom_accum, GPU_R11F_G11F_B10F, 0);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->bloom_pass_accum_fb,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->bloom_accum)});
|
||||
|
||||
/* Create Pass and shgroup. */
|
||||
DRWShadingGroup *grp = eevee_create_bloom_pass("Bloom Accumulate",
|
||||
effects,
|
||||
EEVEE_shaders_bloom_resolve_get(use_highres),
|
||||
&psl->bloom_accum_ps,
|
||||
true,
|
||||
true);
|
||||
DRW_shgroup_uniform_bool_copy(grp, "bloomAddBase", false);
|
||||
}
|
||||
|
||||
void EEVEE_bloom_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
|
||||
if (stl->g_data->render_passes & EEVEE_RENDER_PASS_BLOOM) {
|
||||
GPU_framebuffer_bind(fbl->bloom_pass_accum_fb);
|
||||
DRW_draw_pass(psl->bloom_accum_ps);
|
||||
|
||||
/* Restore */
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
}
|
||||
}
|
||||
155
source/blender/draw/engines/eevee/eevee_build.cc
Normal file
155
source/blender/draw/engines/eevee/eevee_build.cc
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Compile time computation and code generation.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/* For blue_noise. */
|
||||
#include "eevee_lut.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef unsigned char uchar;
|
||||
typedef float float3[3];
|
||||
|
||||
const int samples_per_pool = 32;
|
||||
|
||||
static void raytrace_sample_reuse_table(string &output_name, bool debug)
|
||||
{
|
||||
/** Following "Stochastic All The Things: Raytracing in Hybrid Real-Time Rendering"
|
||||
* by Tomasz Stachowiak
|
||||
* https://www.ea.com/seed/news/seed-dd18-presentation-slides-raytracing
|
||||
*/
|
||||
|
||||
struct sample {
|
||||
int x, y;
|
||||
sample(int _x, int _y) : x(_x), y(_y){};
|
||||
};
|
||||
|
||||
array<vector<sample>, 4> pools;
|
||||
array<float3, 4> pools_color = {1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0};
|
||||
vector<float3> debug_image(64 * 64);
|
||||
|
||||
ofstream ppm;
|
||||
auto ppm_file_out = [&](const char *name, vector<float3> &debug_image) {
|
||||
ppm.open(name);
|
||||
ppm << "P3\n64 64\n255\n";
|
||||
for (auto &vec : debug_image) {
|
||||
uchar ucol[3] = {(uchar)(vec[0] * 255), (uchar)(vec[1] * 255), (uchar)(vec[2] * 255)};
|
||||
ppm << (int)ucol[0] << " " << (int)ucol[1] << " " << (int)ucol[2] << "\n";
|
||||
/* Clear the image. */
|
||||
vec[0] = vec[1] = vec[2] = 0.0f;
|
||||
}
|
||||
ppm.close();
|
||||
};
|
||||
|
||||
/* Using sample_reuse_1.ppm, set a center position where there is 4 different pool (color). */
|
||||
int center[4][2] = {{49, 47}, {48, 46}, {49, 46}, {48, 47}};
|
||||
/* Remapping of pool order since the center samples may not match the pool order from shader.
|
||||
* Order is (0,0), (1,0), (0,1), (1,1).
|
||||
* IMPORTANT: the actual PPM picture has Y flipped compared to OpenGL. */
|
||||
int poolmap[4] = {3, 0, 1, 2};
|
||||
|
||||
for (int x = 0; x < 64; x++) {
|
||||
for (int y = 0; y < 64; y++) {
|
||||
int px = y * 64 + x;
|
||||
float noise_value = blue_noise[px][0];
|
||||
int pool_id = floorf(noise_value * 4.0f);
|
||||
sample ofs(x - center[pool_id][0], y - center[pool_id][1]);
|
||||
pools[pool_id].push_back(ofs);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
debug_image[px][i] = pools_color[pool_id][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
ppm_file_out("sample_reuse_1.ppm", debug_image);
|
||||
}
|
||||
|
||||
for (int pool_id = 0; pool_id < 4; pool_id++) {
|
||||
auto &pool = pools[pool_id];
|
||||
auto sort = [](const sample &a, const sample &b) {
|
||||
/* Manhattan distance. */
|
||||
return abs(a.x) + abs(a.y) < abs(b.x) + abs(b.y);
|
||||
};
|
||||
std::sort(pool.begin(), pool.end(), sort);
|
||||
|
||||
for (int j = 0; j < samples_per_pool; j++) {
|
||||
int pos[2] = {pool[j].x + center[pool_id][0], pool[j].y + center[pool_id][1]};
|
||||
int px = pos[1] * 64 + pos[0];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
debug_image[px][i] = pools_color[pool_id][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
ppm_file_out("sample_reuse_2.ppm", debug_image);
|
||||
}
|
||||
|
||||
/* TODO(fclem): Order the samples to have better spatial coherence. */
|
||||
|
||||
ofstream table_out;
|
||||
int total = samples_per_pool * 4;
|
||||
table_out.open(output_name);
|
||||
|
||||
table_out << "\n/* Sample table generated at build time. */\n";
|
||||
table_out << "const int resolve_sample_max = " << samples_per_pool << ";\n";
|
||||
table_out << "const float2 resolve_sample_offsets[" << total << "] = float2[" << total << "](\n";
|
||||
for (int pool_id = 0; pool_id < 4; pool_id++) {
|
||||
auto &pool = pools[poolmap[pool_id]];
|
||||
for (int i = 0; i < samples_per_pool; i++) {
|
||||
table_out << " float2(" << pool[i].x << ", " << pool[i].y << ")";
|
||||
if (i < samples_per_pool - 1) {
|
||||
table_out << ",\n";
|
||||
}
|
||||
}
|
||||
if (pool_id < 3) {
|
||||
table_out << ",\n";
|
||||
}
|
||||
}
|
||||
table_out << ");\n\n";
|
||||
|
||||
table_out.close();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "usage: eevee_build [--resolve_sample_table] output_file\n");
|
||||
return -1;
|
||||
}
|
||||
if (string(argv[1]) == "--resolve_sample_table") {
|
||||
string output_name(argv[2]);
|
||||
raytrace_sample_reuse_table(output_name, false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
167
source/blender/draw/engines/eevee/eevee_camera.cc
Normal file
167
source/blender/draw/engines/eevee/eevee_camera.cc
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BKE_camera.h"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "eevee_camera.hh"
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name eCameraType
|
||||
* \{ */
|
||||
|
||||
static eCameraType from_camera(const ::Camera *camera)
|
||||
{
|
||||
switch (camera->type) {
|
||||
default:
|
||||
case CAM_PERSP:
|
||||
return CAMERA_PERSP;
|
||||
case CAM_ORTHO:
|
||||
return CAMERA_ORTHO;
|
||||
case CAM_PANO:
|
||||
switch (camera->panorama_type) {
|
||||
default:
|
||||
case CAM_PANO_EQUIRECTANGULAR:
|
||||
return CAMERA_PANO_EQUIRECT;
|
||||
case CAM_PANO_FISHEYE_EQUIDISTANT:
|
||||
return CAMERA_PANO_EQUIDISTANT;
|
||||
case CAM_PANO_FISHEYE_EQUISOLID:
|
||||
return CAMERA_PANO_EQUISOLID;
|
||||
case CAM_PANO_MIRRORBALL:
|
||||
return CAMERA_PANO_MIRROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Camera
|
||||
* \{ */
|
||||
|
||||
void Camera::init(void)
|
||||
{
|
||||
const Object *camera_eval = inst_.camera_eval_object;
|
||||
synced_ = false;
|
||||
/* Swap! */
|
||||
data_id_ = !data_id_;
|
||||
|
||||
CameraData &data = data_[data_id_];
|
||||
|
||||
if (camera_eval) {
|
||||
const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data);
|
||||
data.type = from_camera(cam);
|
||||
}
|
||||
else if (inst_.drw_view) {
|
||||
data.type = DRW_view_is_persp_get(inst_.drw_view) ? CAMERA_PERSP : CAMERA_ORTHO;
|
||||
}
|
||||
else {
|
||||
/* Lightprobe baking. */
|
||||
data.type = CAMERA_PERSP;
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::sync(void)
|
||||
{
|
||||
const Object *camera_eval = inst_.camera_eval_object;
|
||||
CameraData &data = data_[data_id_];
|
||||
|
||||
data.filter_size = inst_.scene->r.gauss;
|
||||
|
||||
if (inst_.drw_view) {
|
||||
DRW_view_viewmat_get(inst_.drw_view, data.viewmat.ptr(), false);
|
||||
DRW_view_viewmat_get(inst_.drw_view, data.viewinv.ptr(), true);
|
||||
DRW_view_winmat_get(inst_.drw_view, data.winmat.ptr(), false);
|
||||
DRW_view_winmat_get(inst_.drw_view, data.wininv.ptr(), true);
|
||||
DRW_view_persmat_get(inst_.drw_view, data.persmat.ptr(), false);
|
||||
DRW_view_persmat_get(inst_.drw_view, data.persinv.ptr(), true);
|
||||
DRW_view_camtexco_get(inst_.drw_view, data.uv_scale);
|
||||
}
|
||||
else if (inst_.render) {
|
||||
/* TODO(fclem) Overscan */
|
||||
// RE_GetCameraWindowWithOverscan(inst_.render->re, g_data->overscan, data.winmat);
|
||||
RE_GetCameraWindow(inst_.render->re, camera_eval, data.winmat.ptr());
|
||||
RE_GetCameraModelMatrix(inst_.render->re, camera_eval, data.viewinv.ptr());
|
||||
invert_m4_m4(data.viewmat.ptr(), data.viewinv.ptr());
|
||||
invert_m4_m4(data.wininv.ptr(), data.winmat.ptr());
|
||||
mul_m4_m4m4(data.persmat.ptr(), data.winmat.ptr(), data.viewmat.ptr());
|
||||
invert_m4_m4(data.persinv.ptr(), data.persmat.ptr());
|
||||
data.uv_scale = float2(1.0f);
|
||||
data.uv_bias = float2(0.0f);
|
||||
}
|
||||
else {
|
||||
data.viewmat = float4x4::identity();
|
||||
data.viewinv = float4x4::identity();
|
||||
perspective_m4(data.winmat.ptr(), -0.1f, 0.1f, -0.1f, 0.1f, 0.1f, 1.0f);
|
||||
data.wininv = data.winmat.inverted();
|
||||
data.persmat = data.winmat * data.viewmat;
|
||||
data.persinv = data.persmat.inverted();
|
||||
}
|
||||
|
||||
if (camera_eval) {
|
||||
const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data);
|
||||
data.clip_near = cam->clip_start;
|
||||
data.clip_far = cam->clip_end;
|
||||
data.fisheye_fov = cam->fisheye_fov;
|
||||
data.fisheye_lens = cam->fisheye_lens;
|
||||
data.equirect_bias.x = -cam->longitude_min + M_PI_2;
|
||||
data.equirect_bias.y = -cam->latitude_min + M_PI_2;
|
||||
data.equirect_scale.x = cam->longitude_min - cam->longitude_max;
|
||||
data.equirect_scale.y = cam->latitude_min - cam->latitude_max;
|
||||
/* Combine with uv_scale/bias to avoid doing extra computation. */
|
||||
data.equirect_bias += data.uv_bias * data.equirect_scale;
|
||||
data.equirect_scale *= data.uv_scale;
|
||||
|
||||
data.equirect_scale_inv = 1.0f / data.equirect_scale;
|
||||
}
|
||||
else if (inst_.drw_view) {
|
||||
data.clip_near = DRW_view_near_distance_get(inst_.drw_view);
|
||||
data.clip_far = DRW_view_far_distance_get(inst_.drw_view);
|
||||
data.fisheye_fov = data.fisheye_lens = -1.0f;
|
||||
data.equirect_bias = float2(0.0f);
|
||||
data.equirect_scale = float2(0.0f);
|
||||
}
|
||||
|
||||
data_[data_id_].push_update();
|
||||
|
||||
synced_ = true;
|
||||
|
||||
/* Detect changes in parameters. */
|
||||
if (data_[data_id_] != data_[!data_id_]) {
|
||||
inst_.sampling.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
149
source/blender/draw/engines/eevee/eevee_camera.hh
Normal file
149
source/blender/draw/engines/eevee/eevee_camera.hh
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "eevee_sampling.hh"
|
||||
#include "eevee_shader_shared.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* TODO(fclem) Might want to move to eevee_shader_shared.hh. */
|
||||
static const float cubeface_mat[6][4][4] = {
|
||||
/* Pos X */
|
||||
{{0.0f, 0.0f, -1.0f, 0.0f},
|
||||
{0.0f, -1.0f, 0.0f, 0.0f},
|
||||
{-1.0f, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f}},
|
||||
/* Neg X */
|
||||
{{0.0f, 0.0f, 1.0f, 0.0f},
|
||||
{0.0f, -1.0f, 0.0f, 0.0f},
|
||||
{1.0f, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f}},
|
||||
/* Pos Y */
|
||||
{{1.0f, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, -1.0f, 0.0f},
|
||||
{0.0f, 1.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f}},
|
||||
/* Neg Y */
|
||||
{{1.0f, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, 1.0f, 0.0f},
|
||||
{0.0f, -1.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f}},
|
||||
/* Pos Z */
|
||||
{{1.0f, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, -1.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, -1.0f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f}},
|
||||
/* Neg Z */
|
||||
{{-1.0f, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, -1.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, 1.0f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f}},
|
||||
};
|
||||
|
||||
inline void cubeface_winmat_get(float4x4 &winmat, float near, float far)
|
||||
{
|
||||
/* Simple 90° FOV projection. */
|
||||
perspective_m4(winmat.ptr(), -near, near, -near, near, near, far);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name CameraData operators
|
||||
* \{ */
|
||||
|
||||
inline bool operator==(const CameraData &a, const CameraData &b)
|
||||
{
|
||||
return compare_m4m4(a.persmat.ptr(), b.persmat.ptr(), FLT_MIN) && (a.uv_scale == b.uv_scale) &&
|
||||
(a.uv_bias == b.uv_bias) && (a.equirect_scale == b.equirect_scale) &&
|
||||
(a.equirect_bias == b.equirect_bias) && (a.fisheye_fov == b.fisheye_fov) &&
|
||||
(a.fisheye_lens == b.fisheye_lens) && (a.filter_size == b.filter_size) &&
|
||||
(a.type == b.type);
|
||||
}
|
||||
|
||||
inline bool operator!=(const CameraData &a, const CameraData &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Camera
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Point of view in the scene. Can be init from viewport or camera object.
|
||||
*/
|
||||
class Camera {
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
/** Double buffered to detect changes and have history for re-projection. */
|
||||
CameraDataBuf data_[2];
|
||||
/** Active data index in data_. */
|
||||
int data_id_ = 0;
|
||||
/** Detects wrong usage. */
|
||||
bool synced_ = false;
|
||||
|
||||
public:
|
||||
Camera(Instance &inst) : inst_(inst){};
|
||||
~Camera(){};
|
||||
|
||||
void init(void);
|
||||
void sync(void);
|
||||
|
||||
/**
|
||||
* Getters
|
||||
**/
|
||||
const CameraData &data_get(void) const
|
||||
{
|
||||
BLI_assert(synced_);
|
||||
return data_[data_id_];
|
||||
}
|
||||
const GPUUniformBuf *ubo_get(void) const
|
||||
{
|
||||
return data_[data_id_];
|
||||
}
|
||||
bool is_panoramic(void) const
|
||||
{
|
||||
return eevee::is_panoramic(data_[data_id_].type);
|
||||
}
|
||||
bool is_orthographic(void) const
|
||||
{
|
||||
return data_[data_id_].type == CAMERA_ORTHO;
|
||||
}
|
||||
float3 position(void) const
|
||||
{
|
||||
return float3(data_[data_id_].viewinv[3]);
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
@@ -1,721 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2020, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup EEVEE
|
||||
*
|
||||
* This file implements Cryptomatte for EEVEE. Cryptomatte is used to extract mattes using
|
||||
* information already available at render time. See
|
||||
* https://raw.githubusercontent.com/Psyop/Cryptomatte/master/specification/IDmattes_poster.pdf
|
||||
* for reference to the cryptomatte specification.
|
||||
*
|
||||
* The challenge with cryptomatte in EEVEE is the merging and sorting of the samples.
|
||||
* User can enable up to 3 cryptomatte layers (Object, Material and Asset).
|
||||
*
|
||||
* Process
|
||||
*
|
||||
* - Cryptomatte sample: Rendering of a cryptomatte sample is stored in a GPUBuffer. The buffer
|
||||
* holds a single float per pixel per number of active cryptomatte layers. The float is the
|
||||
* cryptomatte hash of each layer. After drawing the cryptomatte sample the intermediate result is
|
||||
* downloaded to a CPU buffer (`cryptomatte_download_buffer`).
|
||||
*
|
||||
* Accurate mode
|
||||
*
|
||||
* There are two accuracy modes. The difference between the two is the number of render samples
|
||||
* they take into account to create the render passes. When accurate mode is off the number of
|
||||
* levels is used as the number of cryptomatte samples to take. When accuracy mode is on the number
|
||||
* of render samples is used.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "DRW_engine.h"
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BKE_cryptomatte.h"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "BLI_alloca.h"
|
||||
#include "BLI_math_bits.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_particle_types.h"
|
||||
|
||||
#include "eevee_private.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Data Management cryptomatte accum buffer
|
||||
* \{ */
|
||||
|
||||
BLI_INLINE eViewLayerCryptomatteFlags eevee_cryptomatte_active_layers(const ViewLayer *view_layer)
|
||||
{
|
||||
const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag &
|
||||
VIEW_LAYER_CRYPTOMATTE_ALL;
|
||||
return cryptomatte_layers;
|
||||
}
|
||||
|
||||
/* The number of cryptomatte layers that are enabled */
|
||||
BLI_INLINE int eevee_cryptomatte_layers_count(const ViewLayer *view_layer)
|
||||
{
|
||||
const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
|
||||
view_layer);
|
||||
return count_bits_i(cryptomatte_layers);
|
||||
}
|
||||
|
||||
/* The number of render result passes are needed to store a single cryptomatte layer. Per
|
||||
* renderpass 2 cryptomatte samples can be stored. */
|
||||
BLI_INLINE int eevee_cryptomatte_passes_per_layer(const ViewLayer *view_layer)
|
||||
{
|
||||
const int num_cryptomatte_levels = view_layer->cryptomatte_levels;
|
||||
const int num_cryptomatte_passes = (num_cryptomatte_levels + 1) / 2;
|
||||
return num_cryptomatte_passes;
|
||||
}
|
||||
|
||||
BLI_INLINE int eevee_cryptomatte_layer_stride(const ViewLayer *view_layer)
|
||||
{
|
||||
return view_layer->cryptomatte_levels;
|
||||
}
|
||||
|
||||
BLI_INLINE int eevee_cryptomatte_layer_offset(const ViewLayer *view_layer, const int layer)
|
||||
{
|
||||
return view_layer->cryptomatte_levels * layer;
|
||||
}
|
||||
|
||||
BLI_INLINE int eevee_cryptomatte_pixel_stride(const ViewLayer *view_layer)
|
||||
{
|
||||
return eevee_cryptomatte_layer_stride(view_layer) * eevee_cryptomatte_layers_count(view_layer);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Init Renderpasses
|
||||
* \{ */
|
||||
|
||||
void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
|
||||
/* Cryptomatte is only rendered for final image renders */
|
||||
if (!DRW_state_is_scene_render()) {
|
||||
return;
|
||||
}
|
||||
const eViewLayerCryptomatteFlags active_layers = eevee_cryptomatte_active_layers(view_layer);
|
||||
if (active_layers) {
|
||||
struct CryptomatteSession *session = BKE_cryptomatte_init();
|
||||
if ((active_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
|
||||
BKE_cryptomatte_add_layer(session, "CryptoObject");
|
||||
}
|
||||
if ((active_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
|
||||
BKE_cryptomatte_add_layer(session, "CryptoMaterial");
|
||||
}
|
||||
if ((active_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
|
||||
BKE_cryptomatte_add_layer(session, "CryptoAsset");
|
||||
}
|
||||
g_data->cryptomatte_session = session;
|
||||
|
||||
g_data->render_passes |= EEVEE_RENDER_PASS_CRYPTOMATTE | EEVEE_RENDER_PASS_VOLUME_LIGHT;
|
||||
g_data->cryptomatte_accurate_mode = (view_layer->cryptomatte_flag &
|
||||
VIEW_LAYER_CRYPTOMATTE_ACCURATE) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
|
||||
EEVEE_Data *vedata,
|
||||
int UNUSED(tot_samples))
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
|
||||
const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
|
||||
eGPUTextureFormat format = (num_cryptomatte_layers == 1) ? GPU_R32F :
|
||||
(num_cryptomatte_layers == 2) ? GPU_RG32F :
|
||||
GPU_RGBA32F;
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
const int buffer_size = viewport_size[0] * viewport_size[1];
|
||||
|
||||
if (g_data->cryptomatte_accum_buffer == NULL) {
|
||||
g_data->cryptomatte_accum_buffer = MEM_calloc_arrayN(
|
||||
buffer_size * eevee_cryptomatte_pixel_stride(view_layer),
|
||||
sizeof(EEVEE_CryptomatteSample),
|
||||
__func__);
|
||||
/* Download buffer should store a float per active cryptomatte layer. */
|
||||
g_data->cryptomatte_download_buffer = MEM_malloc_arrayN(
|
||||
buffer_size * num_cryptomatte_layers, sizeof(float), __func__);
|
||||
}
|
||||
else {
|
||||
/* During multiview rendering the `cryptomatte_accum_buffer` is deallocated after all views
|
||||
* have been rendered. Clear it here to be reused by the next view. */
|
||||
memset(g_data->cryptomatte_accum_buffer,
|
||||
0,
|
||||
buffer_size * eevee_cryptomatte_pixel_stride(view_layer) *
|
||||
sizeof(EEVEE_CryptomatteSample));
|
||||
}
|
||||
|
||||
DRW_texture_ensure_fullscreen_2d(&txl->cryptomatte, format, 0);
|
||||
GPU_framebuffer_ensure_config(&fbl->cryptomatte_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_TEXTURE(dtxl->depth),
|
||||
GPU_ATTACHMENT_TEXTURE(txl->cryptomatte),
|
||||
});
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Populate Cache
|
||||
* \{ */
|
||||
|
||||
void EEVEE_cryptomatte_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) {
|
||||
DRW_PASS_CREATE(psl->cryptomatte_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
|
||||
}
|
||||
}
|
||||
|
||||
static DRWShadingGroup *eevee_cryptomatte_shading_group_create(EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *UNUSED(sldata),
|
||||
Object *ob,
|
||||
Material *material,
|
||||
bool is_hair)
|
||||
{
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
|
||||
view_layer);
|
||||
EEVEE_PrivateData *g_data = vedata->stl->g_data;
|
||||
float cryptohash[4] = {0.0f};
|
||||
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
int layer_offset = 0;
|
||||
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
|
||||
uint32_t cryptomatte_hash = BKE_cryptomatte_object_hash(
|
||||
g_data->cryptomatte_session, "CryptoObject", ob);
|
||||
float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
|
||||
cryptohash[layer_offset] = cryptomatte_color_value;
|
||||
layer_offset++;
|
||||
}
|
||||
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
|
||||
uint32_t cryptomatte_hash = BKE_cryptomatte_material_hash(
|
||||
g_data->cryptomatte_session, "CryptoMaterial", material);
|
||||
float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
|
||||
cryptohash[layer_offset] = cryptomatte_color_value;
|
||||
layer_offset++;
|
||||
}
|
||||
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
|
||||
uint32_t cryptomatte_hash = BKE_cryptomatte_asset_hash(
|
||||
g_data->cryptomatte_session, "CryptoAsset", ob);
|
||||
float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
|
||||
cryptohash[layer_offset] = cryptomatte_color_value;
|
||||
layer_offset++;
|
||||
}
|
||||
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_cryptomatte_sh_get(is_hair),
|
||||
psl->cryptomatte_ps);
|
||||
DRW_shgroup_uniform_vec4_copy(grp, "cryptohash", cryptohash);
|
||||
|
||||
return grp;
|
||||
}
|
||||
|
||||
static void eevee_cryptomatte_hair_cache_populate(EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata,
|
||||
Object *ob,
|
||||
ParticleSystem *psys,
|
||||
ModifierData *md,
|
||||
Material *material)
|
||||
{
|
||||
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
|
||||
vedata, sldata, ob, material, true);
|
||||
DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL);
|
||||
}
|
||||
|
||||
void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata,
|
||||
Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_HAIR);
|
||||
Material *material = BKE_object_material_get_eval(ob, HAIR_MATERIAL_NR);
|
||||
eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, NULL, NULL, material);
|
||||
}
|
||||
|
||||
void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata,
|
||||
Object *ob)
|
||||
{
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
|
||||
if (ob->type == OB_MESH) {
|
||||
if (ob != draw_ctx->object_edit) {
|
||||
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
||||
if (md->type != eModifierType_ParticleSystem) {
|
||||
continue;
|
||||
}
|
||||
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
|
||||
if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) {
|
||||
continue;
|
||||
}
|
||||
ParticleSettings *part = psys->part;
|
||||
const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
|
||||
if (draw_as != PART_DRAW_PATH) {
|
||||
continue;
|
||||
}
|
||||
Material *material = BKE_object_material_get_eval(ob, part->omat);
|
||||
eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, psys, md, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob)
|
||||
{
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
|
||||
view_layer);
|
||||
|
||||
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
|
||||
const int materials_len = DRW_cache_object_material_count_get(ob);
|
||||
struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len);
|
||||
memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len);
|
||||
struct GPUBatch **geoms = DRW_cache_object_surface_material_get(
|
||||
ob, gpumat_array, materials_len);
|
||||
if (geoms) {
|
||||
for (int i = 0; i < materials_len; i++) {
|
||||
struct GPUBatch *geom = geoms[i];
|
||||
if (geom == NULL) {
|
||||
continue;
|
||||
}
|
||||
Material *material = BKE_object_material_get_eval(ob, i + 1);
|
||||
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
|
||||
vedata, sldata, ob, material, false);
|
||||
DRW_shgroup_call(grp, geom, ob);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
GPUBatch *geom = DRW_cache_object_surface_get(ob);
|
||||
if (geom) {
|
||||
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
|
||||
vedata, sldata, ob, NULL, false);
|
||||
DRW_shgroup_call(grp, geom, ob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Accumulate Samples
|
||||
* \{ */
|
||||
|
||||
/* Downloads cryptomatte sample buffer from the GPU and integrate the samples with the accumulated
|
||||
* cryptomatte samples. */
|
||||
static void eevee_cryptomatte_download_buffer(EEVEE_Data *vedata, GPUFrameBuffer *framebuffer)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
|
||||
const int num_levels = view_layer->cryptomatte_levels;
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
const int buffer_size = viewport_size[0] * viewport_size[1];
|
||||
|
||||
EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
|
||||
float *download_buffer = g_data->cryptomatte_download_buffer;
|
||||
|
||||
BLI_assert(accum_buffer);
|
||||
BLI_assert(download_buffer);
|
||||
|
||||
GPU_framebuffer_read_color(framebuffer,
|
||||
0,
|
||||
0,
|
||||
viewport_size[0],
|
||||
viewport_size[1],
|
||||
num_cryptomatte_layers,
|
||||
0,
|
||||
GPU_DATA_FLOAT,
|
||||
download_buffer);
|
||||
|
||||
/* Integrate download buffer into the accum buffer.
|
||||
* The download buffer contains up to 3 floats per pixel (one float per cryptomatte layer.
|
||||
*
|
||||
* NOTE: here we deviate from the cryptomatte standard. During integration the standard always
|
||||
* sort the samples by its weight to make sure that samples with the lowest weight
|
||||
* are discarded first. In our case the weight of each sample is always 1 as we don't have
|
||||
* subsamples and apply the coverage during the post processing. When there is no room for new
|
||||
* samples the new samples has a weight of 1 and will always be discarded. */
|
||||
int download_pixel_index = 0;
|
||||
int accum_pixel_index = 0;
|
||||
int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);
|
||||
for (int pixel_index = 0; pixel_index < buffer_size; pixel_index++) {
|
||||
for (int layer = 0; layer < num_cryptomatte_layers; layer++) {
|
||||
const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer);
|
||||
float download_hash = download_buffer[download_pixel_index++];
|
||||
for (int level = 0; level < num_levels; level++) {
|
||||
EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level];
|
||||
if (sample->hash == download_hash) {
|
||||
sample->weight += 1.0f;
|
||||
break;
|
||||
}
|
||||
/* We test against weight as hash 0.0f is used for samples hitting the world background. */
|
||||
if (sample->weight == 0.0f) {
|
||||
sample->hash = download_hash;
|
||||
sample->weight = 1.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
accum_pixel_index += accum_pixel_stride;
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
const int cryptomatte_levels = view_layer->cryptomatte_levels;
|
||||
const int current_sample = effects->taa_current_sample;
|
||||
|
||||
/* In accurate mode all render samples are evaluated. In inaccurate mode this is limited to the
|
||||
* number of cryptomatte levels. This will reduce the overhead of downloading the GPU buffer and
|
||||
* integrating it into the accum buffer. */
|
||||
if (g_data->cryptomatte_accurate_mode || current_sample < cryptomatte_levels) {
|
||||
static float clear_color[4] = {0.0};
|
||||
GPU_framebuffer_bind(fbl->cryptomatte_fb);
|
||||
GPU_framebuffer_clear_color(fbl->cryptomatte_fb, clear_color);
|
||||
DRW_draw_pass(psl->cryptomatte_ps);
|
||||
|
||||
eevee_cryptomatte_download_buffer(vedata, fbl->cryptomatte_fb);
|
||||
|
||||
/* Restore */
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Update Render Passes
|
||||
* \{ */
|
||||
|
||||
void EEVEE_cryptomatte_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
|
||||
{
|
||||
char cryptomatte_pass_name[MAX_NAME];
|
||||
const short num_passes = eevee_cryptomatte_passes_per_layer(view_layer);
|
||||
if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
|
||||
for (short pass = 0; pass < num_passes; pass++) {
|
||||
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoObject%02d", pass);
|
||||
RE_engine_register_pass(
|
||||
engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
|
||||
}
|
||||
}
|
||||
if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
|
||||
for (short pass = 0; pass < num_passes; pass++) {
|
||||
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoMaterial%02d", pass);
|
||||
RE_engine_register_pass(
|
||||
engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
|
||||
}
|
||||
}
|
||||
if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
|
||||
for (short pass = 0; pass < num_passes; pass++) {
|
||||
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoAsset%02d", pass);
|
||||
RE_engine_register_pass(
|
||||
engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Construct Render Result
|
||||
* \{ */
|
||||
|
||||
/* Compare function for cryptomatte samples. Samples with the highest weight will be at the
|
||||
* beginning of the list. */
|
||||
static int eevee_cryptomatte_sample_cmp_reverse(const void *a_, const void *b_)
|
||||
{
|
||||
const EEVEE_CryptomatteSample *a = a_;
|
||||
const EEVEE_CryptomatteSample *b = b_;
|
||||
if (a->weight < b->weight) {
|
||||
return 1;
|
||||
}
|
||||
if (a->weight > b->weight) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Post process the weights. The accumulated weights buffer adds one to each weight per sample.
|
||||
* During post processing ensure that the total of weights per sample is between 0 and 1. */
|
||||
static void eevee_cryptomatte_postprocess_weights(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
|
||||
const int num_levels = view_layer->cryptomatte_levels;
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
const int buffer_size = viewport_size[0] * viewport_size[1];
|
||||
|
||||
EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
|
||||
BLI_assert(accum_buffer);
|
||||
float *volumetric_transmittance_buffer = NULL;
|
||||
if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
|
||||
volumetric_transmittance_buffer = GPU_texture_read(
|
||||
txl->volume_transmittance_accum, GPU_DATA_FLOAT, 0);
|
||||
}
|
||||
const int num_samples = effects->taa_current_sample - 1;
|
||||
|
||||
int accum_pixel_index = 0;
|
||||
int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);
|
||||
|
||||
for (int pixel_index = 0; pixel_index < buffer_size;
|
||||
pixel_index++, accum_pixel_index += accum_pixel_stride) {
|
||||
float coverage = 1.0f;
|
||||
if (volumetric_transmittance_buffer != NULL) {
|
||||
coverage = (volumetric_transmittance_buffer[pixel_index * 4] +
|
||||
volumetric_transmittance_buffer[pixel_index * 4 + 1] +
|
||||
volumetric_transmittance_buffer[pixel_index * 4 + 2]) /
|
||||
(3.0f * num_samples);
|
||||
}
|
||||
for (int layer = 0; layer < num_cryptomatte_layers; layer++) {
|
||||
const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer);
|
||||
/* Calculate the total weight of the sample. */
|
||||
float total_weight = 0.0f;
|
||||
for (int level = 0; level < num_levels; level++) {
|
||||
EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level];
|
||||
total_weight += sample->weight;
|
||||
}
|
||||
BLI_assert(total_weight > 0.0f);
|
||||
|
||||
float total_weight_inv = coverage / total_weight;
|
||||
if (total_weight_inv > 0.0f) {
|
||||
for (int level = 0; level < num_levels; level++) {
|
||||
EEVEE_CryptomatteSample *sample =
|
||||
&accum_buffer[accum_pixel_index + layer_offset + level];
|
||||
/* Remove background samples. These samples were used to determine the correct weight
|
||||
* but won't be part of the final result. */
|
||||
if (sample->hash == 0.0f) {
|
||||
sample->weight = 0.0f;
|
||||
}
|
||||
sample->weight *= total_weight_inv;
|
||||
}
|
||||
|
||||
/* Sort accum buffer by coverage of each sample. */
|
||||
qsort(&accum_buffer[accum_pixel_index + layer_offset],
|
||||
num_levels,
|
||||
sizeof(EEVEE_CryptomatteSample),
|
||||
eevee_cryptomatte_sample_cmp_reverse);
|
||||
}
|
||||
else {
|
||||
/* This pixel doesn't have any weight, so clear it fully. */
|
||||
for (int level = 0; level < num_levels; level++) {
|
||||
EEVEE_CryptomatteSample *sample =
|
||||
&accum_buffer[accum_pixel_index + layer_offset + level];
|
||||
sample->weight = 0.0f;
|
||||
sample->hash = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (volumetric_transmittance_buffer) {
|
||||
MEM_freeN(volumetric_transmittance_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract cryptomatte layer from the cryptomatte_accum_buffer to render passes. */
|
||||
static void eevee_cryptomatte_extract_render_passes(
|
||||
RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const char *render_pass_name_format,
|
||||
EEVEE_CryptomatteSample *accum_buffer,
|
||||
/* number of render passes per cryptomatte layer. */
|
||||
const int num_cryptomatte_passes,
|
||||
const int num_cryptomatte_levels,
|
||||
const int accum_pixel_stride,
|
||||
const int layer_stride,
|
||||
const int layer_index,
|
||||
const int rect_width,
|
||||
const int rect_height,
|
||||
const int rect_offset_x,
|
||||
const int rect_offset_y,
|
||||
const int viewport_width)
|
||||
{
|
||||
char cryptomatte_pass_name[MAX_NAME];
|
||||
/* A pass can store 2 levels. Technically the last pass can have a single level if the number of
|
||||
* levels is an odd number. This parameter counts the number of levels it has processed. */
|
||||
int levels_done = 0;
|
||||
for (int pass = 0; pass < num_cryptomatte_passes; pass++) {
|
||||
/* Each pass holds 2 cryptomatte samples. */
|
||||
const int pass_offset = pass * 2;
|
||||
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, render_pass_name_format, pass);
|
||||
RenderPass *rp_object = RE_pass_find_by_name(rl, cryptomatte_pass_name, viewname);
|
||||
for (int y = 0; y < rect_height; y++) {
|
||||
for (int x = 0; x < rect_width; x++) {
|
||||
const int accum_buffer_offset = (rect_offset_x + x +
|
||||
(rect_offset_y + y) * viewport_width) *
|
||||
accum_pixel_stride +
|
||||
layer_index * layer_stride + pass_offset;
|
||||
const int render_pass_offset = (y * rect_width + x) * 4;
|
||||
rp_object->rect[render_pass_offset] = accum_buffer[accum_buffer_offset].hash;
|
||||
rp_object->rect[render_pass_offset + 1] = accum_buffer[accum_buffer_offset].weight;
|
||||
if (levels_done + 1 < num_cryptomatte_levels) {
|
||||
rp_object->rect[render_pass_offset + 2] = accum_buffer[accum_buffer_offset + 1].hash;
|
||||
rp_object->rect[render_pass_offset + 3] = accum_buffer[accum_buffer_offset + 1].weight;
|
||||
}
|
||||
else {
|
||||
rp_object->rect[render_pass_offset + 2] = 0.0f;
|
||||
rp_object->rect[render_pass_offset + 3] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
levels_done++;
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_cryptomatte_render_result(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *UNUSED(sldata))
|
||||
{
|
||||
EEVEE_PrivateData *g_data = vedata->stl->g_data;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag &
|
||||
VIEW_LAYER_CRYPTOMATTE_ALL;
|
||||
|
||||
eevee_cryptomatte_postprocess_weights(vedata);
|
||||
|
||||
const int rect_width = BLI_rcti_size_x(rect);
|
||||
const int rect_height = BLI_rcti_size_y(rect);
|
||||
const int rect_offset_x = vedata->stl->g_data->overscan_pixels + rect->xmin;
|
||||
const int rect_offset_y = vedata->stl->g_data->overscan_pixels + rect->ymin;
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
const int viewport_width = viewport_size[0];
|
||||
EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
|
||||
BLI_assert(accum_buffer);
|
||||
const int num_cryptomatte_levels = view_layer->cryptomatte_levels;
|
||||
const int num_cryptomatte_passes = eevee_cryptomatte_passes_per_layer(view_layer);
|
||||
const int layer_stride = eevee_cryptomatte_layer_stride(view_layer);
|
||||
const int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);
|
||||
|
||||
int layer_index = 0;
|
||||
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
|
||||
eevee_cryptomatte_extract_render_passes(rl,
|
||||
viewname,
|
||||
"CryptoObject%02d",
|
||||
accum_buffer,
|
||||
num_cryptomatte_passes,
|
||||
num_cryptomatte_levels,
|
||||
accum_pixel_stride,
|
||||
layer_stride,
|
||||
layer_index,
|
||||
rect_width,
|
||||
rect_height,
|
||||
rect_offset_x,
|
||||
rect_offset_y,
|
||||
viewport_width);
|
||||
layer_index++;
|
||||
}
|
||||
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
|
||||
eevee_cryptomatte_extract_render_passes(rl,
|
||||
viewname,
|
||||
"CryptoMaterial%02d",
|
||||
accum_buffer,
|
||||
num_cryptomatte_passes,
|
||||
num_cryptomatte_levels,
|
||||
accum_pixel_stride,
|
||||
layer_stride,
|
||||
layer_index,
|
||||
rect_width,
|
||||
rect_height,
|
||||
rect_offset_x,
|
||||
rect_offset_y,
|
||||
viewport_width);
|
||||
layer_index++;
|
||||
}
|
||||
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
|
||||
eevee_cryptomatte_extract_render_passes(rl,
|
||||
viewname,
|
||||
"CryptoAsset%02d",
|
||||
accum_buffer,
|
||||
num_cryptomatte_passes,
|
||||
num_cryptomatte_levels,
|
||||
accum_pixel_stride,
|
||||
layer_stride,
|
||||
layer_index,
|
||||
rect_width,
|
||||
rect_height,
|
||||
rect_offset_x,
|
||||
rect_offset_y,
|
||||
viewport_width);
|
||||
layer_index++;
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_cryptomatte_store_metadata(EEVEE_Data *vedata, RenderResult *render_result)
|
||||
{
|
||||
EEVEE_PrivateData *g_data = vedata->stl->g_data;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
BLI_assert(g_data->cryptomatte_session);
|
||||
|
||||
BKE_cryptomatte_store_metadata(g_data->cryptomatte_session, render_result, view_layer);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void EEVEE_cryptomatte_free(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PrivateData *g_data = vedata->stl->g_data;
|
||||
MEM_SAFE_FREE(g_data->cryptomatte_accum_buffer);
|
||||
MEM_SAFE_FREE(g_data->cryptomatte_download_buffer);
|
||||
if (g_data->cryptomatte_session) {
|
||||
BKE_cryptomatte_free(g_data->cryptomatte_session);
|
||||
g_data->cryptomatte_session = NULL;
|
||||
}
|
||||
}
|
||||
@@ -1,389 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*
|
||||
* All specific data handler for Objects, Lights, ViewLayers, ...
|
||||
*/
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_memblock.h"
|
||||
|
||||
#include "BKE_duplilist.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "GPU_vertex_buffer.h"
|
||||
|
||||
#include "eevee_lightcache.h"
|
||||
#include "eevee_private.h"
|
||||
|
||||
/* Motion Blur data. */
|
||||
|
||||
static void eevee_motion_blur_mesh_data_free(void *val)
|
||||
{
|
||||
EEVEE_GeometryMotionData *geom_mb = (EEVEE_GeometryMotionData *)val;
|
||||
EEVEE_HairMotionData *hair_mb = (EEVEE_HairMotionData *)val;
|
||||
switch (geom_mb->type) {
|
||||
case EEVEE_MOTION_DATA_HAIR:
|
||||
for (int j = 0; j < hair_mb->psys_len; j++) {
|
||||
for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) {
|
||||
GPU_VERTBUF_DISCARD_SAFE(hair_mb->psys[j].hair_pos[i]);
|
||||
}
|
||||
for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) {
|
||||
DRW_TEXTURE_FREE_SAFE(hair_mb->psys[j].hair_pos_tx[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EEVEE_MOTION_DATA_MESH:
|
||||
for (int i = 0; i < ARRAY_SIZE(geom_mb->vbo); i++) {
|
||||
GPU_VERTBUF_DISCARD_SAFE(geom_mb->vbo[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
MEM_freeN(val);
|
||||
}
|
||||
|
||||
static uint eevee_object_key_hash(const void *key)
|
||||
{
|
||||
EEVEE_ObjectKey *ob_key = (EEVEE_ObjectKey *)key;
|
||||
uint hash = BLI_ghashutil_ptrhash(ob_key->ob);
|
||||
hash = BLI_ghashutil_combine_hash(hash, BLI_ghashutil_ptrhash(ob_key->parent));
|
||||
for (int i = 0; i < MAX_DUPLI_RECUR; i++) {
|
||||
if (ob_key->id[i] != 0) {
|
||||
hash = BLI_ghashutil_combine_hash(hash, BLI_ghashutil_inthash(ob_key->id[i]));
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Return false if equal. */
|
||||
static bool eevee_object_key_cmp(const void *a, const void *b)
|
||||
{
|
||||
EEVEE_ObjectKey *key_a = (EEVEE_ObjectKey *)a;
|
||||
EEVEE_ObjectKey *key_b = (EEVEE_ObjectKey *)b;
|
||||
|
||||
if (key_a->ob != key_b->ob) {
|
||||
return true;
|
||||
}
|
||||
if (key_a->parent != key_b->parent) {
|
||||
return true;
|
||||
}
|
||||
if (memcmp(key_a->id, key_b->id, sizeof(key_a->id)) != 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb)
|
||||
{
|
||||
if (mb->object == NULL) {
|
||||
mb->object = BLI_ghash_new(eevee_object_key_hash, eevee_object_key_cmp, "EEVEE Object Motion");
|
||||
}
|
||||
if (mb->geom == NULL) {
|
||||
mb->geom = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE Mesh Motion");
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb)
|
||||
{
|
||||
if (mb->object) {
|
||||
BLI_ghash_free(mb->object, MEM_freeN, MEM_freeN);
|
||||
mb->object = NULL;
|
||||
}
|
||||
if (mb->geom) {
|
||||
BLI_ghash_free(mb->geom, NULL, eevee_motion_blur_mesh_data_free);
|
||||
mb->geom = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb,
|
||||
Object *ob,
|
||||
bool hair)
|
||||
{
|
||||
if (mb->object == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EEVEE_ObjectKey key, *key_p;
|
||||
/* Small hack to avoid another comparison. */
|
||||
key.ob = (Object *)((char *)ob + hair);
|
||||
DupliObject *dup = DRW_object_get_dupli(ob);
|
||||
if (dup) {
|
||||
key.parent = DRW_object_get_dupli_parent(ob);
|
||||
memcpy(key.id, dup->persistent_id, sizeof(key.id));
|
||||
}
|
||||
else {
|
||||
key.parent = key.ob;
|
||||
memset(key.id, 0, sizeof(key.id));
|
||||
}
|
||||
|
||||
EEVEE_ObjectMotionData *ob_step = BLI_ghash_lookup(mb->object, &key);
|
||||
if (ob_step == NULL) {
|
||||
key_p = MEM_mallocN(sizeof(*key_p), __func__);
|
||||
memcpy(key_p, &key, sizeof(*key_p));
|
||||
|
||||
ob_step = MEM_callocN(sizeof(EEVEE_ObjectMotionData), __func__);
|
||||
|
||||
BLI_ghash_insert(mb->object, key_p, ob_step);
|
||||
}
|
||||
return ob_step;
|
||||
}
|
||||
|
||||
static void *motion_blur_deform_data_get(EEVEE_MotionBlurData *mb, Object *ob, bool hair)
|
||||
{
|
||||
if (mb->geom == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
DupliObject *dup = DRW_object_get_dupli(ob);
|
||||
void *key;
|
||||
if (dup) {
|
||||
key = dup->ob;
|
||||
}
|
||||
else {
|
||||
key = ob;
|
||||
}
|
||||
/* Only use data for object that have no modifiers. */
|
||||
if (!BKE_object_is_modified(DRW_context_state_get()->scene, ob)) {
|
||||
key = ob->data;
|
||||
}
|
||||
key = (char *)key + (int)hair;
|
||||
EEVEE_GeometryMotionData *geom_step = BLI_ghash_lookup(mb->geom, key);
|
||||
if (geom_step == NULL) {
|
||||
if (hair) {
|
||||
EEVEE_HairMotionData *hair_step;
|
||||
/* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */
|
||||
int psys_len = (ob->type != OB_HAIR) ? BLI_listbase_count(&ob->modifiers) : 1;
|
||||
hair_step = MEM_callocN(sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len,
|
||||
__func__);
|
||||
hair_step->psys_len = psys_len;
|
||||
geom_step = (EEVEE_GeometryMotionData *)hair_step;
|
||||
geom_step->type = EEVEE_MOTION_DATA_HAIR;
|
||||
}
|
||||
else {
|
||||
geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__);
|
||||
geom_step->type = EEVEE_MOTION_DATA_MESH;
|
||||
}
|
||||
BLI_ghash_insert(mb->geom, key, geom_step);
|
||||
}
|
||||
return geom_step;
|
||||
}
|
||||
|
||||
EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb, Object *ob)
|
||||
{
|
||||
return motion_blur_deform_data_get(mb, ob, false);
|
||||
}
|
||||
|
||||
EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_MotionBlurData *mb, Object *ob)
|
||||
{
|
||||
return motion_blur_deform_data_get(mb, ob, true);
|
||||
}
|
||||
|
||||
/* View Layer data. */
|
||||
|
||||
void EEVEE_view_layer_data_free(void *storage)
|
||||
{
|
||||
EEVEE_ViewLayerData *sldata = (EEVEE_ViewLayerData *)storage;
|
||||
|
||||
/* Lights */
|
||||
MEM_SAFE_FREE(sldata->lights);
|
||||
DRW_UBO_FREE_SAFE(sldata->light_ubo);
|
||||
DRW_UBO_FREE_SAFE(sldata->shadow_ubo);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(sldata->shadow_fb);
|
||||
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_pool);
|
||||
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_pool);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
MEM_SAFE_FREE(sldata->shcasters_buffers[i].bbox);
|
||||
MEM_SAFE_FREE(sldata->shcasters_buffers[i].update);
|
||||
}
|
||||
|
||||
if (sldata->fallback_lightcache) {
|
||||
EEVEE_lightcache_free(sldata->fallback_lightcache);
|
||||
sldata->fallback_lightcache = NULL;
|
||||
}
|
||||
|
||||
/* Probes */
|
||||
MEM_SAFE_FREE(sldata->probes);
|
||||
DRW_UBO_FREE_SAFE(sldata->probe_ubo);
|
||||
DRW_UBO_FREE_SAFE(sldata->grid_ubo);
|
||||
DRW_UBO_FREE_SAFE(sldata->planar_ubo);
|
||||
DRW_UBO_FREE_SAFE(sldata->common_ubo);
|
||||
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.combined);
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.diff_color);
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.diff_light);
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.spec_color);
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.spec_light);
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.emit);
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.environment);
|
||||
for (int aov_index = 0; aov_index < MAX_AOVS; aov_index++) {
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.aovs[aov_index]);
|
||||
}
|
||||
|
||||
if (sldata->material_cache) {
|
||||
BLI_memblock_destroy(sldata->material_cache, NULL);
|
||||
sldata->material_cache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void)
|
||||
{
|
||||
return (EEVEE_ViewLayerData *)DRW_view_layer_engine_data_get(&draw_engine_eevee_type);
|
||||
}
|
||||
|
||||
static void eevee_view_layer_init(EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
sldata->common_ubo = GPU_uniformbuf_create(sizeof(sldata->common_data));
|
||||
}
|
||||
|
||||
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer)
|
||||
{
|
||||
EEVEE_ViewLayerData **sldata = (EEVEE_ViewLayerData **)DRW_view_layer_engine_data_ensure_ex(
|
||||
view_layer, &draw_engine_eevee_type, &EEVEE_view_layer_data_free);
|
||||
|
||||
if (*sldata == NULL) {
|
||||
*sldata = MEM_callocN(sizeof(**sldata), "EEVEE_ViewLayerData");
|
||||
eevee_view_layer_init(*sldata);
|
||||
}
|
||||
|
||||
return *sldata;
|
||||
}
|
||||
|
||||
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void)
|
||||
{
|
||||
EEVEE_ViewLayerData **sldata = (EEVEE_ViewLayerData **)DRW_view_layer_engine_data_ensure(
|
||||
&draw_engine_eevee_type, &EEVEE_view_layer_data_free);
|
||||
|
||||
if (*sldata == NULL) {
|
||||
*sldata = MEM_callocN(sizeof(**sldata), "EEVEE_ViewLayerData");
|
||||
eevee_view_layer_init(*sldata);
|
||||
}
|
||||
|
||||
return *sldata;
|
||||
}
|
||||
|
||||
/* Object data. */
|
||||
|
||||
static void eevee_object_data_init(DrawData *dd)
|
||||
{
|
||||
EEVEE_ObjectEngineData *eevee_data = (EEVEE_ObjectEngineData *)dd;
|
||||
eevee_data->shadow_caster_id = -1;
|
||||
eevee_data->need_update = false;
|
||||
eevee_data->geom_update = false;
|
||||
}
|
||||
|
||||
EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob)
|
||||
{
|
||||
if (ELEM(ob->type, OB_LIGHTPROBE, OB_LAMP)) {
|
||||
return NULL;
|
||||
}
|
||||
return (EEVEE_ObjectEngineData *)DRW_drawdata_get(&ob->id, &draw_engine_eevee_type);
|
||||
}
|
||||
|
||||
EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob)
|
||||
{
|
||||
BLI_assert(!ELEM(ob->type, OB_LIGHTPROBE, OB_LAMP));
|
||||
return (EEVEE_ObjectEngineData *)DRW_drawdata_ensure(&ob->id,
|
||||
&draw_engine_eevee_type,
|
||||
sizeof(EEVEE_ObjectEngineData),
|
||||
eevee_object_data_init,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* Light probe data. */
|
||||
|
||||
static void eevee_lightprobe_data_init(DrawData *dd)
|
||||
{
|
||||
EEVEE_LightProbeEngineData *ped = (EEVEE_LightProbeEngineData *)dd;
|
||||
ped->need_update = false;
|
||||
}
|
||||
|
||||
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob)
|
||||
{
|
||||
if (ob->type != OB_LIGHTPROBE) {
|
||||
return NULL;
|
||||
}
|
||||
return (EEVEE_LightProbeEngineData *)DRW_drawdata_get(&ob->id, &draw_engine_eevee_type);
|
||||
}
|
||||
|
||||
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_LIGHTPROBE);
|
||||
return (EEVEE_LightProbeEngineData *)DRW_drawdata_ensure(&ob->id,
|
||||
&draw_engine_eevee_type,
|
||||
sizeof(EEVEE_LightProbeEngineData),
|
||||
eevee_lightprobe_data_init,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* Light data. */
|
||||
|
||||
static void eevee_light_data_init(DrawData *dd)
|
||||
{
|
||||
EEVEE_LightEngineData *led = (EEVEE_LightEngineData *)dd;
|
||||
led->need_update = true;
|
||||
}
|
||||
|
||||
EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob)
|
||||
{
|
||||
if (ob->type != OB_LAMP) {
|
||||
return NULL;
|
||||
}
|
||||
return (EEVEE_LightEngineData *)DRW_drawdata_get(&ob->id, &draw_engine_eevee_type);
|
||||
}
|
||||
|
||||
EEVEE_LightEngineData *EEVEE_light_data_ensure(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_LAMP);
|
||||
return (EEVEE_LightEngineData *)DRW_drawdata_ensure(&ob->id,
|
||||
&draw_engine_eevee_type,
|
||||
sizeof(EEVEE_LightEngineData),
|
||||
eevee_light_data_init,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* World data. */
|
||||
|
||||
static void eevee_world_data_init(DrawData *dd)
|
||||
{
|
||||
EEVEE_WorldEngineData *wed = (EEVEE_WorldEngineData *)dd;
|
||||
wed->dd.recalc |= 1;
|
||||
}
|
||||
|
||||
EEVEE_WorldEngineData *EEVEE_world_data_get(World *wo)
|
||||
{
|
||||
return (EEVEE_WorldEngineData *)DRW_drawdata_get(&wo->id, &draw_engine_eevee_type);
|
||||
}
|
||||
|
||||
EEVEE_WorldEngineData *EEVEE_world_data_ensure(World *wo)
|
||||
{
|
||||
return (EEVEE_WorldEngineData *)DRW_drawdata_ensure(&wo->id,
|
||||
&draw_engine_eevee_type,
|
||||
sizeof(EEVEE_WorldEngineData),
|
||||
eevee_world_data_init,
|
||||
NULL);
|
||||
}
|
||||
47
source/blender/draw/engines/eevee/eevee_defines.hh
Normal file
47
source/blender/draw/engines/eevee/eevee_defines.hh
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* List of defines that are shared with the GPUShaderCreateInfos. We do this to avoid
|
||||
* dragging larger headers into the createInfo pipeline which would cause problems.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536. */
|
||||
/* Current limiting factor is the sorting phase which is single pass and only sort within a
|
||||
* threadgroup which maximum size is 1024. */
|
||||
#define CULLING_BATCH_SIZE 1024
|
||||
|
||||
/**
|
||||
* IMPORTANT: Some data packing are tweaked for these values.
|
||||
* Be sure to update them accordingly.
|
||||
* SHADOW_TILEMAP_RES max is 32 because of the shared bitmaps used for LOD tagging.
|
||||
* It is also limited by the maximum thread group size (1024).
|
||||
*/
|
||||
#define SHADOW_TILEMAP_RES 16
|
||||
#define SHADOW_TILEMAP_LOD 4 /* LOG2(SHADOW_TILEMAP_RES) */
|
||||
#define SHADOW_TILEMAP_PER_ROW 64
|
||||
#define SHADOW_PAGE_COPY_GROUP_SIZE 32
|
||||
#define SHADOW_DEPTH_SCAN_GROUP_SIZE 32
|
||||
#define SHADOW_AABB_TAG_GROUP_SIZE 64
|
||||
#define SHADOW_MAX_TILEMAP 4096
|
||||
#define SHADOW_MAX_PAGE 4096
|
||||
#define SHADOW_PAGE_PER_ROW 64
|
||||
File diff suppressed because it is too large
Load Diff
732
source/blender/draw/engines/eevee/eevee_depth_of_field.cc
Normal file
732
source/blender/draw/engines/eevee/eevee_depth_of_field.cc
Normal file
@@ -0,0 +1,732 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Depth of field post process effect.
|
||||
*
|
||||
* There are 2 methods to achieve this effect.
|
||||
* - The first uses projection matrix offsetting and sample accumulation to give
|
||||
* reference quality depth of field. But this needs many samples to hide the
|
||||
* under-sampling.
|
||||
* - The second one is a post-processing based one. It follows the
|
||||
* implementation described in the presentation "Life of a Bokeh - Siggraph
|
||||
* 2018" from Guillaume Abadie. There are some difference with our actual
|
||||
* implementation that prioritize quality.
|
||||
*/
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BKE_camera.h"
|
||||
#include "DNA_camera_types.h"
|
||||
|
||||
#include "GPU_texture.h"
|
||||
#include "GPU_uniform_buffer.h"
|
||||
|
||||
#include "eevee_camera.hh"
|
||||
#include "eevee_instance.hh"
|
||||
#include "eevee_sampling.hh"
|
||||
#include "eevee_shader.hh"
|
||||
#include "eevee_shader_shared.hh"
|
||||
|
||||
#include "eevee_depth_of_field.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Depth of field
|
||||
* \{ */
|
||||
|
||||
void DepthOfField::init(void)
|
||||
{
|
||||
const Instance &inst = inst_;
|
||||
const SceneEEVEE &sce_eevee = inst.scene->eevee;
|
||||
do_hq_slight_focus_ = (sce_eevee.flag & SCE_EEVEE_DOF_HQ_SLIGHT_FOCUS) != 0;
|
||||
do_jitter_ = (sce_eevee.flag & SCE_EEVEE_DOF_JITTER) != 0;
|
||||
user_overblur_ = sce_eevee.bokeh_overblur / 100.0f;
|
||||
fx_max_coc_ = sce_eevee.bokeh_max_size;
|
||||
data_.scatter_color_threshold = sce_eevee.bokeh_threshold;
|
||||
data_.scatter_neighbor_max_color = sce_eevee.bokeh_neighbor_max;
|
||||
data_.denoise_factor = sce_eevee.bokeh_denoise_fac;
|
||||
/* Default to no depth of field. */
|
||||
fx_radius_ = 0.0f;
|
||||
jitter_radius_ = 0.0f;
|
||||
}
|
||||
|
||||
void DepthOfField::sync(const float4x4 &winmat, int2 input_extent)
|
||||
{
|
||||
const Object *camera_object_eval = inst_.camera_eval_object;
|
||||
const ::Camera *cam = (camera_object_eval) ?
|
||||
reinterpret_cast<const ::Camera *>(camera_object_eval->data) :
|
||||
nullptr;
|
||||
|
||||
if (cam == nullptr || (cam->dof.flag & CAM_DOF_ENABLED) == 0) {
|
||||
fx_radius_ = 0.0f;
|
||||
jitter_radius_ = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
extent_ = input_extent;
|
||||
|
||||
data_.bokeh_blades = cam->dof.aperture_blades;
|
||||
data_.bokeh_rotation = cam->dof.aperture_rotation;
|
||||
data_.bokeh_anisotropic_scale.x = clamp_f(1.0f / cam->dof.aperture_ratio, 0.00001f, 1.0f);
|
||||
data_.bokeh_anisotropic_scale.y = clamp_f(cam->dof.aperture_ratio, 0.00001f, 1.0f);
|
||||
data_.bokeh_anisotropic_scale_inv = 1.0f / data_.bokeh_anisotropic_scale;
|
||||
|
||||
focus_distance_ = BKE_camera_object_dof_distance(camera_object_eval);
|
||||
float fstop = max_ff(cam->dof.aperture_fstop, 1e-5f);
|
||||
float aperture = 1.0f / (2.0f * fstop);
|
||||
if (cam->type == CAM_PERSP) {
|
||||
aperture *= cam->lens * 1e-3f;
|
||||
}
|
||||
|
||||
if (cam->type == CAM_ORTHO) {
|
||||
/* FIXME: Why is this needed? Some kind of implicit unit conversion? */
|
||||
aperture *= 0.04f;
|
||||
/* Really strange behavior from Cycles but replicating. */
|
||||
focus_distance_ += cam->clip_start;
|
||||
}
|
||||
|
||||
if (cam->type == CAM_PANO) {
|
||||
/* FIXME: Eyeballed. */
|
||||
aperture *= 0.185f;
|
||||
}
|
||||
|
||||
if (cam->dof.aperture_ratio < 1.0) {
|
||||
/* If ratio is scaling the bokeh outwards, we scale the aperture so that
|
||||
* the gather kernel size will encompass the maximum axis. */
|
||||
aperture /= max_ff(cam->dof.aperture_ratio, 1e-5f);
|
||||
}
|
||||
|
||||
/* Balance blur radius between fx dof and jitter dof. */
|
||||
if (do_jitter_ && (inst_.sampling.dof_ring_count_get() > 0) && (cam->type != CAM_PANO)) {
|
||||
/* Compute a minimal overblur radius to fill the gaps between the samples.
|
||||
* This is just the simplified form of dividing the area of the bokeh by
|
||||
* the number of samples. */
|
||||
float minimal_overblur = 1.0f / sqrtf(inst_.sampling.dof_sample_count_get());
|
||||
|
||||
fx_radius_ = (minimal_overblur + user_overblur_) * aperture;
|
||||
/* Avoid dilating the shape. Over-blur only soften. */
|
||||
jitter_radius_ = max_ff(0.0f, aperture - fx_radius_);
|
||||
}
|
||||
else {
|
||||
jitter_radius_ = 0.0f;
|
||||
fx_radius_ = aperture;
|
||||
}
|
||||
|
||||
if (fx_max_coc_ > 0.0f && fx_radius_ > 0.0f) {
|
||||
data_.camera_type = inst_.camera.data_get().type;
|
||||
/* OPTI(fclem) Could be optimized. */
|
||||
float jitter[3] = {fx_radius_, 0.0f, -focus_distance_};
|
||||
float center[3] = {0.0f, 0.0f, -focus_distance_};
|
||||
mul_project_m4_v3(winmat.ptr(), jitter);
|
||||
mul_project_m4_v3(winmat.ptr(), center);
|
||||
/* Simplify CoC calculation to a simple MADD. */
|
||||
if (data_.camera_type != CAMERA_ORTHO) {
|
||||
data_.coc_bias = -(center[0] - jitter[0]) * 0.5f * extent_[0];
|
||||
data_.coc_mul = focus_distance_ * data_.coc_bias;
|
||||
}
|
||||
else {
|
||||
data_.coc_mul = (center[0] - jitter[0]) * 0.5f * extent_[0];
|
||||
data_.coc_bias = focus_distance_ * data_.coc_mul;
|
||||
}
|
||||
|
||||
float min_fg_coc = coc_radius_from_camera_depth(data_, -cam->clip_start);
|
||||
float max_bg_coc = coc_radius_from_camera_depth(data_, -cam->clip_end);
|
||||
if (data_.camera_type != CAMERA_ORTHO) {
|
||||
/* Background is at infinity so maximum CoC is the limit of the function at -inf. */
|
||||
/* NOTE: we only do this for perspective camera since orthographic coc limit is inf. */
|
||||
max_bg_coc = data_.coc_bias;
|
||||
}
|
||||
/* Clamp with user defined max. */
|
||||
data_.coc_abs_max = min_ff(max_ff(fabsf(min_fg_coc), fabsf(max_bg_coc)), fx_max_coc_);
|
||||
|
||||
bokeh_lut_pass_sync();
|
||||
setup_pass_sync();
|
||||
tiles_prepare_pass_sync();
|
||||
reduce_pass_sync();
|
||||
convolve_pass_sync();
|
||||
resolve_pass_sync();
|
||||
|
||||
data_.push_update();
|
||||
}
|
||||
}
|
||||
|
||||
void DepthOfField::jitter_apply(float4x4 &winmat, float4x4 &viewmat)
|
||||
{
|
||||
if (jitter_radius_ == 0.0f) {
|
||||
return;
|
||||
}
|
||||
float radius, theta;
|
||||
inst_.sampling.dof_disk_sample_get(&radius, &theta);
|
||||
|
||||
if (data_.bokeh_blades >= 3.0f) {
|
||||
theta = circle_to_polygon_angle(data_.bokeh_blades, theta);
|
||||
radius *= circle_to_polygon_radius(data_.bokeh_blades, theta);
|
||||
}
|
||||
radius *= jitter_radius_;
|
||||
theta += data_.bokeh_rotation;
|
||||
|
||||
/* Sample in View Space. */
|
||||
float2 sample = float2(radius * cosf(theta), radius * sinf(theta));
|
||||
sample *= data_.bokeh_anisotropic_scale;
|
||||
/* Convert to NDC Space. */
|
||||
float3 jitter = float3(UNPACK2(sample), -focus_distance_);
|
||||
float3 center = float3(0.0f, 0.0f, -focus_distance_);
|
||||
mul_project_m4_v3(winmat.ptr(), jitter);
|
||||
mul_project_m4_v3(winmat.ptr(), center);
|
||||
|
||||
const bool is_ortho = (winmat[2][3] != -1.0f);
|
||||
if (is_ortho) {
|
||||
sample *= focus_distance_;
|
||||
}
|
||||
/* Translate origin. */
|
||||
sub_v2_v2(viewmat[3], sample);
|
||||
/* Skew winmat Z axis. */
|
||||
add_v2_v2(winmat[2], center - jitter);
|
||||
}
|
||||
|
||||
/** Will swap input and output texture if rendering happens. The actual output of this function
|
||||
* is in intput_tx. */
|
||||
void DepthOfField::render(GPUTexture *depth_tx, GPUTexture **input_tx, GPUTexture **output_tx)
|
||||
{
|
||||
if (fx_radius_ == 0.0f || fx_max_coc_ < 0.5f) {
|
||||
return;
|
||||
}
|
||||
|
||||
input_color_tx_ = *input_tx;
|
||||
input_depth_tx_ = depth_tx;
|
||||
|
||||
DRW_stats_group_start("Depth of Field");
|
||||
|
||||
bokeh_lut_pass_render();
|
||||
|
||||
setup_pass_render();
|
||||
tiles_prepare_pass_render();
|
||||
reduce_pass_render();
|
||||
convolve_pass_render();
|
||||
resolve_pass_render(*output_tx);
|
||||
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* Swap buffers so that next effect has the right input. */
|
||||
*input_tx = *output_tx;
|
||||
*output_tx = input_color_tx_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates bokeh texture.
|
||||
**/
|
||||
void DepthOfField::bokeh_lut_pass_sync(void)
|
||||
{
|
||||
const bool has_anisotropy = data_.bokeh_anisotropic_scale != float2(1.0f);
|
||||
if (has_anisotropy && (data_.bokeh_blades == 0.0)) {
|
||||
bokeh_gather_lut_tx_ = nullptr;
|
||||
bokeh_scatter_lut_tx_ = nullptr;
|
||||
bokeh_resolve_lut_tx_ = nullptr;
|
||||
bokeh_lut_ps_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
|
||||
int res[2] = {DOF_BOKEH_LUT_SIZE, DOF_BOKEH_LUT_SIZE};
|
||||
|
||||
DRW_PASS_CREATE(bokeh_lut_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_BOKEH_LUT);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, bokeh_lut_ps_);
|
||||
DRW_shgroup_uniform_block(grp, "dof_buf", data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
|
||||
bokeh_gather_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RG16F, owner);
|
||||
bokeh_scatter_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
|
||||
bokeh_resolve_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
|
||||
|
||||
bokeh_lut_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(bokeh_gather_lut_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(bokeh_scatter_lut_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(bokeh_resolve_lut_tx_));
|
||||
}
|
||||
|
||||
void DepthOfField::bokeh_lut_pass_render(void)
|
||||
{
|
||||
if (bokeh_lut_ps_) {
|
||||
GPU_framebuffer_bind(bokeh_lut_fb_);
|
||||
DRW_draw_pass(bokeh_lut_ps_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs halfResColorBuffer and halfResCocBuffer.
|
||||
**/
|
||||
void DepthOfField::setup_pass_sync(void)
|
||||
{
|
||||
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
|
||||
uint res[2] = {divide_ceil_u(extent_[0], 2), divide_ceil_u(extent_[1], 2)};
|
||||
|
||||
DRW_PASS_CREATE(setup_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_SETUP);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, setup_ps_);
|
||||
|
||||
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &input_depth_tx_, no_filter);
|
||||
DRW_shgroup_uniform_block(grp, "dof_buf", data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
|
||||
setup_color_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
|
||||
setup_coc_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RG16F, owner);
|
||||
|
||||
setup_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(setup_color_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(setup_coc_tx_));
|
||||
}
|
||||
|
||||
void DepthOfField::setup_pass_render(void)
|
||||
{
|
||||
GPU_framebuffer_bind(setup_fb_);
|
||||
DRW_draw_pass(setup_ps_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs min & max COC in each 8x8 half res pixel tiles (so 1/16th of full resolution).
|
||||
* Then dilates the min & max CoCs to cover maximum COC values.
|
||||
**/
|
||||
void DepthOfField::tiles_prepare_pass_sync(void)
|
||||
{
|
||||
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
|
||||
uint res[2] = {divide_ceil_u(extent_[0], DOF_TILE_DIVISOR),
|
||||
divide_ceil_u(extent_[1], DOF_TILE_DIVISOR)};
|
||||
/* WARNING: If you change this, make sure dof_tile_* GLSL constants can be properly encoded. */
|
||||
eGPUTextureFormat fg_tile_format = GPU_RGBA16F;
|
||||
eGPUTextureFormat bg_tile_format = GPU_R11F_G11F_B10F;
|
||||
|
||||
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
{
|
||||
DRW_PASS_CREATE(tiles_flatten_ps_, DRW_STATE_WRITE_COLOR);
|
||||
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_TILES_FLATTEN);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_flatten_ps_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
|
||||
tiles_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), fg_tile_format, owner);
|
||||
tiles_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), bg_tile_format, owner);
|
||||
|
||||
tiles_flatten_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(tiles_fg_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(tiles_bg_tx_));
|
||||
}
|
||||
{
|
||||
DRW_PASS_CREATE(tiles_dilate_minmax_ps_, DRW_STATE_WRITE_COLOR);
|
||||
DRW_PASS_CREATE(tiles_dilate_minabs_ps_, DRW_STATE_WRITE_COLOR);
|
||||
|
||||
for (int pass = 0; pass < 2; pass++) {
|
||||
DRWPass *drw_pass = (pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_;
|
||||
GPUShader *sh = inst_.shaders.static_shader_get((pass == 0) ? DOF_TILES_DILATE_MINMAX :
|
||||
DOF_TILES_DILATE_MINABS);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, drw_pass);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_fg_tx", &tiles_fg_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_bg_tx", &tiles_bg_tx_, no_filter);
|
||||
DRW_shgroup_uniform_bool(grp, "dilate_slight_focus", &tiles_dilate_slight_focus_, 1);
|
||||
DRW_shgroup_uniform_int(grp, "ring_count", &tiles_dilate_ring_count_, 1);
|
||||
DRW_shgroup_uniform_int(
|
||||
grp, "ring_width_multiplier", &tiles_dilate_ring_width_multiplier_, 1);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
|
||||
tiles_dilated_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), fg_tile_format, owner);
|
||||
tiles_dilated_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), bg_tile_format, owner);
|
||||
|
||||
tiles_dilate_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(tiles_dilated_fg_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(tiles_dilated_bg_tx_));
|
||||
}
|
||||
}
|
||||
|
||||
void DepthOfField::tiles_prepare_pass_render(void)
|
||||
{
|
||||
GPU_framebuffer_bind(tiles_flatten_fb_);
|
||||
DRW_draw_pass(tiles_flatten_ps_);
|
||||
|
||||
/* Run dilation twice. One for minmax and one for minabs. */
|
||||
for (int pass = 0; pass < 2; pass++) {
|
||||
/* Error introduced by gather center jittering. */
|
||||
const float error_multiplier = 1.0f + 1.0f / (DOF_GATHER_RING_COUNT + 0.5f);
|
||||
int dilation_end_radius = ceilf((fx_max_coc_ * error_multiplier) / DOF_TILE_DIVISOR);
|
||||
|
||||
/* This algorithm produce the exact dilation radius by dividing it in multiple passes. */
|
||||
int dilation_radius = 0;
|
||||
while (dilation_radius < dilation_end_radius) {
|
||||
/* Dilate slight focus only on first iteration. */
|
||||
tiles_dilate_slight_focus_ = (dilation_radius == 0) ? 1 : 0;
|
||||
|
||||
int remainder = dilation_end_radius - dilation_radius;
|
||||
/* Do not step over any unvisited tile. */
|
||||
int max_multiplier = dilation_radius + 1;
|
||||
|
||||
int ring_count = min_ii(DOF_DILATE_RING_COUNT, ceilf(remainder / (float)max_multiplier));
|
||||
int multiplier = min_ii(max_multiplier, floor(remainder / (float)ring_count));
|
||||
|
||||
dilation_radius += ring_count * multiplier;
|
||||
|
||||
tiles_dilate_ring_count_ = ring_count;
|
||||
tiles_dilate_ring_width_multiplier_ = multiplier;
|
||||
|
||||
GPU_framebuffer_bind(tiles_dilate_fb_);
|
||||
DRW_draw_pass((pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_);
|
||||
|
||||
Framebuffer::swap(tiles_dilate_fb_, tiles_flatten_fb_);
|
||||
SWAP(GPUTexture *, tiles_dilated_bg_tx_, tiles_bg_tx_);
|
||||
SWAP(GPUTexture *, tiles_dilated_fg_tx_, tiles_fg_tx_);
|
||||
}
|
||||
}
|
||||
/* Swap again so that final textures are tiles_dilated_*_tx_. */
|
||||
Framebuffer::swap(tiles_dilate_fb_, tiles_flatten_fb_);
|
||||
SWAP(GPUTexture *, tiles_dilated_bg_tx_, tiles_bg_tx_);
|
||||
SWAP(GPUTexture *, tiles_dilated_fg_tx_, tiles_fg_tx_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create mipmapped color & COC textures for gather passes.
|
||||
**/
|
||||
void DepthOfField::reduce_pass_sync(void)
|
||||
{
|
||||
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
|
||||
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
/* Divide by 2 because dof_fx_max_coc is in fullres CoC radius and the reduce
|
||||
* texture begins at half resolution. */
|
||||
float max_space_between_sample = fx_max_coc_ * 0.5f / DOF_GATHER_RING_COUNT;
|
||||
|
||||
int mip_count = max_ii(1, log2_ceil_u(max_space_between_sample));
|
||||
|
||||
reduce_steps_ = mip_count - 1;
|
||||
/* This ensure the mipmaps are aligned for the needed 4 mip levels.
|
||||
* Starts at 2 because already at half resolution. */
|
||||
int multiple = 2 << (mip_count - 1);
|
||||
int2 res = (int2(divide_ceil_u(extent_[0], multiple), divide_ceil_u(extent_[1], multiple)) *
|
||||
multiple) /
|
||||
2;
|
||||
|
||||
int2 quater_res = int2(divide_ceil_u(extent_[0], 4), divide_ceil_u(extent_[1], 4));
|
||||
|
||||
/* TODO(fclem): Make this dependent of the quality of the gather pass. */
|
||||
data_.scatter_coc_threshold = 4.0f;
|
||||
|
||||
/* Color needs to be signed format here. See note in shader for explanation. */
|
||||
/* Do not use texture pool because of needs mipmaps. */
|
||||
reduced_color_tx_.ensure_2d(GPU_RGBA16F, res, nullptr, mip_count);
|
||||
reduced_coc_tx_.ensure_2d(GPU_R16F, res, nullptr, mip_count);
|
||||
|
||||
{
|
||||
DRW_PASS_CREATE(reduce_downsample_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_DOWNSAMPLE);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_downsample_ps_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &setup_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
|
||||
reduce_downsample_tx_ = DRW_texture_pool_query_2d(UNPACK2(quater_res), GPU_RGBA16F, owner);
|
||||
|
||||
reduce_downsample_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(reduce_downsample_tx_));
|
||||
}
|
||||
{
|
||||
DRW_PASS_CREATE(reduce_copy_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_COPY);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_copy_ps_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &setup_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "downsampled_tx", reduce_downsample_tx_, no_filter);
|
||||
DRW_shgroup_uniform_block(grp, "dof_buf", data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
|
||||
scatter_src_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R11F_G11F_B10F, owner);
|
||||
}
|
||||
{
|
||||
DRW_PASS_CREATE(reduce_recursive_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_RECURSIVE);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_recursive_ps_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
|
||||
reduce_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(reduced_color_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(reduced_coc_tx_));
|
||||
|
||||
reduce_copy_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(reduced_color_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(reduced_coc_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(scatter_src_tx_));
|
||||
}
|
||||
|
||||
void DepthOfField::reduce_recusive(void *thunk, int UNUSED(level))
|
||||
{
|
||||
DepthOfField *dof = reinterpret_cast<DepthOfField *>(thunk);
|
||||
DRW_draw_pass(dof->reduce_recursive_ps_);
|
||||
}
|
||||
|
||||
void DepthOfField::reduce_pass_render(void)
|
||||
{
|
||||
GPU_framebuffer_bind(reduce_downsample_fb_);
|
||||
DRW_draw_pass(reduce_downsample_ps_);
|
||||
|
||||
/* First step is just a copy. */
|
||||
GPU_framebuffer_bind(reduce_copy_fb_);
|
||||
DRW_draw_pass(reduce_copy_ps_);
|
||||
|
||||
GPU_framebuffer_recursive_downsample(reduce_fb_, reduce_steps_, &reduce_recusive, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the gather & scatter convolution. For each pixels we gather multiple pixels in its
|
||||
* neighborhood depending on the min & max CoC tiles. We apply a median filter on the output.
|
||||
* We also scatter a sprite for very bright pixels for high quality bokeh.
|
||||
**/
|
||||
void DepthOfField::convolve_pass_sync(void)
|
||||
{
|
||||
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
eGPUSamplerState with_filter = (GPU_SAMPLER_MIPMAP | GPU_SAMPLER_FILTER);
|
||||
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
|
||||
uint res[2] = {divide_ceil_u(extent_[0], 2), divide_ceil_u(extent_[1], 2)};
|
||||
int input_size[2];
|
||||
GPU_texture_get_mipmap_size(reduced_color_tx_, 0, input_size);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
data_.gather_uv_fac[i] = 1.0f / (float)input_size[i];
|
||||
data_.texel_size[i] = 1.0f / res[i];
|
||||
}
|
||||
|
||||
/* Reuse textures from the setup pass. */
|
||||
/* NOTE: We could use the texture pool do that for us but it does not track
|
||||
* usage and it might backfire (it does in practice). */
|
||||
/* Since it is only used for scatter, and foreground is processed before background, we can
|
||||
* reuse the occlusion_tx for both field. */
|
||||
occlusion_tx_ = setup_coc_tx_;
|
||||
|
||||
{
|
||||
DRW_PASS_CREATE(gather_holefill_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_GATHER_HOLEFILL);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_holefill_ps_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
|
||||
DRW_shgroup_uniform_block(grp, "dof_buf", data_);
|
||||
DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
/* Reuse textures from the setup pass. */
|
||||
/* NOTE: We could use the texture pool do that for us but it does not track
|
||||
* usage and it might backfire (it does in practice). */
|
||||
color_holefill_tx_ = setup_color_tx_;
|
||||
weight_holefill_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
|
||||
|
||||
gather_holefill_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(color_holefill_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(weight_holefill_tx_));
|
||||
}
|
||||
{
|
||||
DRW_PASS_CREATE(gather_fg_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(
|
||||
bokeh_gather_lut_tx_ ? DOF_GATHER_FOREGROUND_LUT : DOF_GATHER_FOREGROUND);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_fg_ps_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
|
||||
if (bokeh_gather_lut_tx_) {
|
||||
DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_gather_lut_tx_);
|
||||
}
|
||||
DRW_shgroup_uniform_block(grp, "dof_buf", data_);
|
||||
DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
color_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
|
||||
weight_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
|
||||
}
|
||||
{
|
||||
DRW_PASS_CREATE(gather_bg_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(
|
||||
bokeh_gather_lut_tx_ ? DOF_GATHER_BACKGROUND_LUT : DOF_GATHER_BACKGROUND);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_bg_ps_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
|
||||
if (bokeh_gather_lut_tx_) {
|
||||
DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_gather_lut_tx_);
|
||||
}
|
||||
DRW_shgroup_uniform_block(grp, "dof_buf", data_);
|
||||
DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
color_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
|
||||
weight_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
|
||||
}
|
||||
|
||||
/* NOTE: First target is holefill texture so we can use the median filter on it and save some
|
||||
* texture memory. Both field use the same framebuffer. */
|
||||
gather_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(color_holefill_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(weight_holefill_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(occlusion_tx_));
|
||||
{
|
||||
/**
|
||||
* Filter an input buffer using a median filter to reduce noise.
|
||||
* NOTE: We use the holefill texture as our input to reduce memory usage.
|
||||
* Thus, the holefill pass cannot be filtered.
|
||||
**/
|
||||
DRW_PASS_CREATE(filter_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_FILTER);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_ps_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_tx", color_holefill_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "weight_tx", weight_holefill_tx_, no_filter);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
filter_fg_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(color_fg_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(weight_fg_tx_));
|
||||
|
||||
filter_bg_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(color_bg_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(weight_bg_tx_));
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the Scatter convolution. A sprite is emitted for every 4 pixels but is only expanded
|
||||
* if the pixels are bright enough to be scattered.
|
||||
**/
|
||||
data_.scatter_sprite_per_row = input_size[0] / 2;
|
||||
int sprite_count = data_.scatter_sprite_per_row * (input_size[1] / 2);
|
||||
{
|
||||
DRW_PASS_CREATE(scatter_fg_ps_, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(
|
||||
bokeh_gather_lut_tx_ ? DOF_SCATTER_FOREGROUND_LUT : DOF_SCATTER_FOREGROUND);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, scatter_fg_ps_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_tx", scatter_src_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture(grp, "occlusion_tx", occlusion_tx_);
|
||||
if (bokeh_scatter_lut_tx_) {
|
||||
DRW_shgroup_uniform_texture(grp, "bokehLut", bokeh_scatter_lut_tx_);
|
||||
}
|
||||
DRW_shgroup_uniform_block(grp, "dof_buf", data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, sprite_count);
|
||||
|
||||
scatter_fg_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_fg_tx_));
|
||||
}
|
||||
{
|
||||
DRW_PASS_CREATE(scatter_bg_ps_, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(
|
||||
bokeh_gather_lut_tx_ ? DOF_SCATTER_BACKGROUND_LUT : DOF_SCATTER_BACKGROUND);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, scatter_bg_ps_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_tx", scatter_src_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture(grp, "occlusion_tx", occlusion_tx_);
|
||||
if (bokeh_scatter_lut_tx_) {
|
||||
DRW_shgroup_uniform_texture(grp, "bokehLut", bokeh_scatter_lut_tx_);
|
||||
}
|
||||
DRW_shgroup_uniform_block(grp, "dof_buf", data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, sprite_count);
|
||||
|
||||
scatter_bg_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_bg_tx_));
|
||||
}
|
||||
}
|
||||
|
||||
void DepthOfField::convolve_pass_render(void)
|
||||
{
|
||||
DRW_stats_group_start("Foreground convolution");
|
||||
GPU_framebuffer_bind(gather_fb_);
|
||||
DRW_draw_pass(gather_fg_ps_);
|
||||
|
||||
GPU_framebuffer_bind(filter_fg_fb_);
|
||||
DRW_draw_pass(filter_ps_);
|
||||
|
||||
GPU_framebuffer_bind(scatter_fg_fb_);
|
||||
DRW_draw_pass(scatter_fg_ps_);
|
||||
DRW_stats_group_end();
|
||||
|
||||
DRW_stats_group_start("Background convolution");
|
||||
GPU_framebuffer_bind(gather_fb_);
|
||||
DRW_draw_pass(gather_bg_ps_);
|
||||
|
||||
GPU_framebuffer_bind(filter_bg_fb_);
|
||||
DRW_draw_pass(filter_ps_);
|
||||
|
||||
GPU_framebuffer_bind(scatter_bg_fb_);
|
||||
DRW_draw_pass(scatter_bg_ps_);
|
||||
DRW_stats_group_end();
|
||||
|
||||
DRW_stats_group_start("Background convolution");
|
||||
/* Hole-fill convolution. */
|
||||
GPU_framebuffer_bind(gather_holefill_fb_);
|
||||
DRW_draw_pass(gather_holefill_ps_);
|
||||
/* NOTE: We do not filter the hole-fill pass as we use it as out filter input
|
||||
* buffer. Also effect is likely to not be noticeable. */
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recombine the result of the foreground and background processing.
|
||||
* Also perform a slight out of focus gather to improve geometric continuity.
|
||||
**/
|
||||
void DepthOfField::resolve_pass_sync(void)
|
||||
{
|
||||
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
eGPUSamplerState with_filter = GPU_SAMPLER_FILTER;
|
||||
eShaderType sh_type = (bokeh_resolve_lut_tx_) ?
|
||||
(do_hq_slight_focus_ ? DOF_RESOLVE_LUT_HQ : DOF_RESOLVE_LUT) :
|
||||
(do_hq_slight_focus_ ? DOF_RESOLVE_HQ : DOF_RESOLVE);
|
||||
|
||||
DRW_PASS_CREATE(resolve_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(sh_type);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &input_depth_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_bg_tx", color_bg_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_fg_tx", color_fg_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_holefill_tx", color_holefill_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "weight_bg_tx", weight_bg_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "weight_fg_tx", weight_fg_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "weight_holefill_tx", weight_holefill_tx_, with_filter);
|
||||
DRW_shgroup_uniform_block(grp, "dof_buf", data_);
|
||||
DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
|
||||
if (bokeh_resolve_lut_tx_) {
|
||||
DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_resolve_lut_tx_);
|
||||
}
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
}
|
||||
|
||||
void DepthOfField::resolve_pass_render(GPUTexture *output_tx)
|
||||
{
|
||||
resolve_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(output_tx));
|
||||
|
||||
GPU_framebuffer_bind(resolve_fb_);
|
||||
DRW_draw_pass(resolve_ps_);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
176
source/blender/draw/engines/eevee/eevee_depth_of_field.hh
Normal file
176
source/blender/draw/engines/eevee/eevee_depth_of_field.hh
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Depth of field post process effect.
|
||||
*
|
||||
* There are 2 methods to achieve this effect.
|
||||
* - The first uses projection matrix offsetting and sample accumulation to give
|
||||
* reference quality depth of field. But this needs many samples to hide the
|
||||
* under-sampling.
|
||||
* - The second one is a post-processing based one. It follows the
|
||||
* implementation described in the presentation "Life of a Bokeh - Siggraph
|
||||
* 2018" from Guillaume Abadie. There are some difference with our actual
|
||||
* implementation that prioritize quality.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "eevee_shader_shared.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Depth of field
|
||||
* \{ */
|
||||
|
||||
class DepthOfField {
|
||||
private:
|
||||
class Instance &inst_;
|
||||
|
||||
DepthOfFieldDataBuf data_;
|
||||
|
||||
/** Textures from pool. Not owned. */
|
||||
GPUTexture *bokeh_gather_lut_tx_ = nullptr;
|
||||
GPUTexture *bokeh_resolve_lut_tx_ = nullptr;
|
||||
GPUTexture *bokeh_scatter_lut_tx_ = nullptr;
|
||||
GPUTexture *color_bg_tx_ = nullptr;
|
||||
GPUTexture *color_fg_tx_ = nullptr;
|
||||
GPUTexture *color_holefill_tx_ = nullptr;
|
||||
GPUTexture *occlusion_tx_ = nullptr;
|
||||
GPUTexture *reduce_downsample_tx_ = nullptr;
|
||||
GPUTexture *scatter_src_tx_ = nullptr;
|
||||
GPUTexture *setup_coc_tx_ = nullptr;
|
||||
GPUTexture *setup_color_tx_ = nullptr;
|
||||
GPUTexture *tiles_bg_tx_ = nullptr;
|
||||
GPUTexture *tiles_fg_tx_ = nullptr;
|
||||
GPUTexture *tiles_dilated_bg_tx_ = nullptr;
|
||||
GPUTexture *tiles_dilated_fg_tx_ = nullptr;
|
||||
GPUTexture *weight_bg_tx_ = nullptr;
|
||||
GPUTexture *weight_fg_tx_ = nullptr;
|
||||
GPUTexture *weight_holefill_tx_ = nullptr;
|
||||
/** Allocated textures. Owned. */
|
||||
Texture reduced_coc_tx_ = {"dof_reduced_coc"};
|
||||
Texture reduced_color_tx_ = {"dof_reduced_color"};
|
||||
/** Input texture. Not owned. */
|
||||
GPUTexture *input_color_tx_;
|
||||
GPUTexture *input_depth_tx_;
|
||||
/** Passes. Not owned. */
|
||||
DRWPass *bokeh_lut_ps_ = nullptr;
|
||||
DRWPass *gather_bg_ps_ = nullptr;
|
||||
DRWPass *gather_fg_ps_ = nullptr;
|
||||
DRWPass *filter_ps_ = nullptr;
|
||||
DRWPass *gather_holefill_ps_ = nullptr;
|
||||
DRWPass *reduce_copy_ps_ = nullptr;
|
||||
DRWPass *reduce_downsample_ps_ = nullptr;
|
||||
DRWPass *reduce_recursive_ps_ = nullptr;
|
||||
DRWPass *resolve_ps_ = nullptr;
|
||||
DRWPass *scatter_bg_ps_ = nullptr;
|
||||
DRWPass *scatter_fg_ps_ = nullptr;
|
||||
DRWPass *setup_ps_ = nullptr;
|
||||
DRWPass *tiles_dilate_minabs_ps_ = nullptr;
|
||||
DRWPass *tiles_dilate_minmax_ps_ = nullptr;
|
||||
DRWPass *tiles_flatten_ps_ = nullptr;
|
||||
/** Framebuffers. Owned. */
|
||||
Framebuffer bokeh_lut_fb_ = {"bokeh_lut_fb_"};
|
||||
Framebuffer filter_bg_fb_ = {"filter_bg_fb_"};
|
||||
Framebuffer filter_fg_fb_ = {"filter_fg_fb_"};
|
||||
Framebuffer gather_fb_ = {"gather_fb_"};
|
||||
Framebuffer gather_filter_bg_fb_ = {"gather_filter_bg_fb_"};
|
||||
Framebuffer gather_holefill_fb_ = {"gather_holefill_fb_"};
|
||||
Framebuffer reduce_copy_fb_ = {"reduce_copy_fb_"};
|
||||
Framebuffer reduce_downsample_fb_ = {"reduce_downsample_fb_"};
|
||||
Framebuffer reduce_fb_ = {"reduce_fb_"};
|
||||
Framebuffer resolve_fb_ = {"resolve_fb_"};
|
||||
Framebuffer scatter_bg_fb_ = {"scatter_bg_fb_"};
|
||||
Framebuffer scatter_fg_fb_ = {"scatter_fg_fb_"};
|
||||
Framebuffer setup_fb_ = {"setup_fb_"};
|
||||
Framebuffer tiles_dilate_fb_ = {"tiles_dilate_fb_"};
|
||||
Framebuffer tiles_flatten_fb_ = {"tiles_flatten_fb_"};
|
||||
|
||||
/** Scene settings that are immutable. */
|
||||
float user_overblur_;
|
||||
float fx_max_coc_;
|
||||
/** Use Hiqh Quality (expensive) in-focus gather pass. */
|
||||
bool do_hq_slight_focus_;
|
||||
/** Use jittered depth of field where we randomize camera location. */
|
||||
bool do_jitter_;
|
||||
|
||||
/** Circle of Confusion radius for FX DoF passes. Is in view X direction in [0..1] range. */
|
||||
float fx_radius_;
|
||||
/** Circle of Confusion radius for jittered DoF. Is in view X direction in [0..1] range. */
|
||||
float jitter_radius_;
|
||||
/** Focus distance in view space. */
|
||||
float focus_distance_;
|
||||
/** Extent of the input buffer. */
|
||||
int2 extent_;
|
||||
|
||||
/** Tile dilation uniforms. */
|
||||
int tiles_dilate_slight_focus_;
|
||||
int tiles_dilate_ring_count_;
|
||||
int tiles_dilate_ring_width_multiplier_;
|
||||
|
||||
/** Reduce pass info. */
|
||||
int reduce_steps_;
|
||||
|
||||
/** Static string pointer. Used as debug name and as UUID for texture pool. */
|
||||
StringRefNull view_name_;
|
||||
|
||||
public:
|
||||
DepthOfField(Instance &inst, StringRefNull view_name) : inst_(inst), view_name_(view_name){};
|
||||
~DepthOfField(){};
|
||||
|
||||
void init();
|
||||
|
||||
void sync(const float4x4 &winmat, int2 input_extent);
|
||||
|
||||
/** Apply Depth Of Field jittering to the view and projection matrices.. */
|
||||
void jitter_apply(float4x4 &winmat, float4x4 &viewmat);
|
||||
|
||||
/** Will swap input and output texture if rendering happens. The actual output of this function
|
||||
* is in intput_tx. */
|
||||
void render(GPUTexture *depth_tx, GPUTexture **input_tx, GPUTexture **output_tx);
|
||||
|
||||
private:
|
||||
void bokeh_lut_pass_sync(void);
|
||||
void bokeh_lut_pass_render(void);
|
||||
|
||||
void setup_pass_sync(void);
|
||||
void setup_pass_render(void);
|
||||
|
||||
void tiles_prepare_pass_sync(void);
|
||||
void tiles_prepare_pass_render(void);
|
||||
|
||||
static void reduce_recusive(void *thunk, int level);
|
||||
void reduce_pass_sync(void);
|
||||
void reduce_pass_render(void);
|
||||
|
||||
void convolve_pass_sync(void);
|
||||
void convolve_pass_render(void);
|
||||
|
||||
void resolve_pass_sync(void);
|
||||
void resolve_pass_render(GPUTexture *output_tx);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
@@ -1,524 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*
|
||||
* Gather all screen space effects technique such as Bloom, Motion Blur, DoF, SSAO, SSR, ...
|
||||
*/
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BKE_global.h" /* for G.debug_value */
|
||||
|
||||
#include "GPU_capabilities.h"
|
||||
#include "GPU_platform.h"
|
||||
#include "GPU_state.h"
|
||||
#include "GPU_texture.h"
|
||||
#include "eevee_private.h"
|
||||
|
||||
static struct {
|
||||
/* These are just references, not actually allocated */
|
||||
struct GPUTexture *depth_src;
|
||||
struct GPUTexture *color_src;
|
||||
|
||||
int depth_src_layer;
|
||||
/* Size can be vec3. But we only use 2 components in the shader. */
|
||||
float texel_size[2];
|
||||
} e_data = {NULL}; /* Engine data */
|
||||
|
||||
#define SETUP_BUFFER(tex, fb, fb_color) \
|
||||
{ \
|
||||
eGPUTextureFormat format = (DRW_state_is_scene_render()) ? GPU_RGBA32F : GPU_RGBA16F; \
|
||||
DRW_texture_ensure_fullscreen_2d(&tex, format, DRW_TEX_FILTER); \
|
||||
GPU_framebuffer_ensure_config(&fb, \
|
||||
{ \
|
||||
GPU_ATTACHMENT_TEXTURE(dtxl->depth), \
|
||||
GPU_ATTACHMENT_TEXTURE(tex), \
|
||||
}); \
|
||||
GPU_framebuffer_ensure_config(&fb_color, \
|
||||
{ \
|
||||
GPU_ATTACHMENT_NONE, \
|
||||
GPU_ATTACHMENT_TEXTURE(tex), \
|
||||
}); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
#define CLEANUP_BUFFER(tex, fb, fb_color) \
|
||||
{ \
|
||||
/* Cleanup to release memory */ \
|
||||
DRW_TEXTURE_FREE_SAFE(tex); \
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fb); \
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fb_color); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
void EEVEE_effects_init(EEVEE_ViewLayerData *sldata,
|
||||
EEVEE_Data *vedata,
|
||||
Object *camera,
|
||||
const bool minimal)
|
||||
{
|
||||
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_EffectsInfo *effects;
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
const int size_fs[2] = {(int)viewport_size[0], (int)viewport_size[1]};
|
||||
|
||||
if (!stl->effects) {
|
||||
stl->effects = MEM_callocN(sizeof(EEVEE_EffectsInfo), "EEVEE_EffectsInfo");
|
||||
stl->effects->taa_render_sample = 1;
|
||||
}
|
||||
|
||||
/* WORKAROUND: EEVEE_lookdev_init can reset TAA and needs a stl->effect.
|
||||
* So putting this before EEVEE_temporal_sampling_init for now. */
|
||||
EEVEE_lookdev_init(vedata);
|
||||
|
||||
effects = stl->effects;
|
||||
|
||||
int div = 1 << MAX_SCREEN_BUFFERS_LOD_LEVEL;
|
||||
effects->hiz_size[0] = divide_ceil_u(size_fs[0], div) * div;
|
||||
effects->hiz_size[1] = divide_ceil_u(size_fs[1], div) * div;
|
||||
|
||||
effects->enabled_effects = 0;
|
||||
effects->enabled_effects |= (G.debug_value == 9) ? EFFECT_VELOCITY_BUFFER : 0;
|
||||
effects->enabled_effects |= EEVEE_motion_blur_init(sldata, vedata);
|
||||
effects->enabled_effects |= EEVEE_bloom_init(sldata, vedata);
|
||||
effects->enabled_effects |= EEVEE_depth_of_field_init(sldata, vedata, camera);
|
||||
effects->enabled_effects |= EEVEE_temporal_sampling_init(sldata, vedata);
|
||||
effects->enabled_effects |= EEVEE_occlusion_init(sldata, vedata);
|
||||
effects->enabled_effects |= EEVEE_screen_raytrace_init(sldata, vedata);
|
||||
|
||||
/* Update matrices here because EEVEE_screen_raytrace_init can have reset the
|
||||
* taa_current_sample. (See T66811) */
|
||||
EEVEE_temporal_sampling_update_matrices(vedata);
|
||||
|
||||
EEVEE_volumes_init(sldata, vedata);
|
||||
EEVEE_subsurface_init(sldata, vedata);
|
||||
|
||||
/* Force normal buffer creation. */
|
||||
if (!minimal && (stl->g_data->render_passes & EEVEE_RENDER_PASS_NORMAL) != 0) {
|
||||
effects->enabled_effects |= EFFECT_NORMAL_BUFFER;
|
||||
}
|
||||
|
||||
/**
|
||||
* MinMax Pyramid
|
||||
*/
|
||||
|
||||
if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
/* Intel gpu seems to have problem rendering to only depth hiz_format */
|
||||
DRW_texture_ensure_2d(&txl->maxzbuffer, UNPACK2(effects->hiz_size), GPU_R32F, DRW_TEX_MIPMAP);
|
||||
GPU_framebuffer_ensure_config(&fbl->maxzbuffer_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(txl->maxzbuffer),
|
||||
});
|
||||
}
|
||||
else {
|
||||
DRW_texture_ensure_2d(
|
||||
&txl->maxzbuffer, UNPACK2(effects->hiz_size), GPU_DEPTH_COMPONENT24, DRW_TEX_MIPMAP);
|
||||
GPU_framebuffer_ensure_config(&fbl->maxzbuffer_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_TEXTURE(txl->maxzbuffer),
|
||||
GPU_ATTACHMENT_NONE,
|
||||
});
|
||||
}
|
||||
|
||||
if (fbl->downsample_fb == NULL) {
|
||||
fbl->downsample_fb = GPU_framebuffer_create("downsample_fb");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute hiZ texel alignment.
|
||||
*/
|
||||
common_data->hiz_uv_scale[0] = viewport_size[0] / effects->hiz_size[0];
|
||||
common_data->hiz_uv_scale[1] = viewport_size[1] / effects->hiz_size[1];
|
||||
|
||||
/* Compute pixel size. Size is multiplied by 2 because it is applied in NDC [-1..1] range. */
|
||||
sldata->common_data.ssr_pixelsize[0] = 2.0f / size_fs[0];
|
||||
sldata->common_data.ssr_pixelsize[1] = 2.0f / size_fs[1];
|
||||
|
||||
/**
|
||||
* Color buffer with correct down-sampling alignment.
|
||||
* Used for SSReflections & SSRefractions.
|
||||
*/
|
||||
if ((effects->enabled_effects & EFFECT_RADIANCE_BUFFER) != 0) {
|
||||
DRW_texture_ensure_2d(&txl->filtered_radiance,
|
||||
UNPACK2(effects->hiz_size),
|
||||
GPU_R11F_G11F_B10F,
|
||||
DRW_TEX_FILTER | DRW_TEX_MIPMAP);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->radiance_filtered_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(txl->filtered_radiance),
|
||||
});
|
||||
}
|
||||
else {
|
||||
DRW_TEXTURE_FREE_SAFE(txl->filtered_radiance);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fbl->radiance_filtered_fb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal buffer for deferred passes.
|
||||
*/
|
||||
if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0) {
|
||||
effects->ssr_normal_input = DRW_texture_pool_query_2d(
|
||||
size_fs[0], size_fs[1], GPU_RG16, &draw_engine_eevee_type);
|
||||
|
||||
GPU_framebuffer_texture_attach(fbl->main_fb, effects->ssr_normal_input, 1, 0);
|
||||
}
|
||||
else {
|
||||
effects->ssr_normal_input = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Motion vector buffer for correct TAA / motion blur.
|
||||
*/
|
||||
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
|
||||
effects->velocity_tx = DRW_texture_pool_query_2d(
|
||||
size_fs[0], size_fs[1], GPU_RGBA16, &draw_engine_eevee_type);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->velocity_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_TEXTURE(dtxl->depth),
|
||||
GPU_ATTACHMENT_TEXTURE(effects->velocity_tx),
|
||||
});
|
||||
|
||||
GPU_framebuffer_ensure_config(
|
||||
&fbl->velocity_resolve_fb,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->velocity_tx)});
|
||||
}
|
||||
else {
|
||||
effects->velocity_tx = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup depth double buffer.
|
||||
*/
|
||||
if ((effects->enabled_effects & EFFECT_DEPTH_DOUBLE_BUFFER) != 0) {
|
||||
DRW_texture_ensure_fullscreen_2d(&txl->depth_double_buffer, GPU_DEPTH24_STENCIL8, 0);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->double_buffer_depth_fb,
|
||||
{GPU_ATTACHMENT_TEXTURE(txl->depth_double_buffer)});
|
||||
}
|
||||
else {
|
||||
/* Cleanup to release memory */
|
||||
DRW_TEXTURE_FREE_SAFE(txl->depth_double_buffer);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fbl->double_buffer_depth_fb);
|
||||
}
|
||||
|
||||
if ((effects->enabled_effects & (EFFECT_TAA | EFFECT_TAA_REPROJECT)) != 0) {
|
||||
SETUP_BUFFER(txl->taa_history, fbl->taa_history_fb, fbl->taa_history_color_fb);
|
||||
}
|
||||
else {
|
||||
CLEANUP_BUFFER(txl->taa_history, fbl->taa_history_fb, fbl->taa_history_color_fb);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
DRWState downsample_write = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS;
|
||||
DRWShadingGroup *grp;
|
||||
|
||||
/* Intel gpu seems to have problem rendering to only depth format.
|
||||
* Use color texture instead. */
|
||||
if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
downsample_write = DRW_STATE_WRITE_COLOR;
|
||||
}
|
||||
|
||||
struct GPUBatch *quad = DRW_cache_fullscreen_quad_get();
|
||||
|
||||
if (effects->enabled_effects & EFFECT_RADIANCE_BUFFER) {
|
||||
DRW_PASS_CREATE(psl->color_copy_ps, DRW_STATE_WRITE_COLOR);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_color_copy_sh_get(), psl->color_copy_ps);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "source", &e_data.color_src, GPU_SAMPLER_DEFAULT);
|
||||
DRW_shgroup_uniform_float(grp, "fireflyFactor", &sldata->common_data.ssr_firefly_fac, 1);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
DRW_PASS_CREATE(psl->color_downsample_ps, DRW_STATE_WRITE_COLOR);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_downsample_sh_get(), psl->color_downsample_ps);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "source", txl->filtered_radiance, GPU_SAMPLER_FILTER);
|
||||
DRW_shgroup_uniform_vec2(grp, "texelSize", e_data.texel_size, 1);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
}
|
||||
|
||||
{
|
||||
DRW_PASS_CREATE(psl->color_downsample_cube_ps, DRW_STATE_WRITE_COLOR);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_downsample_cube_sh_get(),
|
||||
psl->color_downsample_cube_ps);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "source", &e_data.color_src);
|
||||
DRW_shgroup_uniform_float(grp, "texelSize", e_data.texel_size, 1);
|
||||
DRW_shgroup_uniform_int_copy(grp, "Layer", 0);
|
||||
DRW_shgroup_call_instances(grp, NULL, quad, 6);
|
||||
}
|
||||
|
||||
{
|
||||
/* Perform min/max down-sample. */
|
||||
DRW_PASS_CREATE(psl->maxz_downlevel_ps, downsample_write);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_maxz_downlevel_sh_get(), psl->maxz_downlevel_ps);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &txl->maxzbuffer, GPU_SAMPLER_DEFAULT);
|
||||
DRW_shgroup_uniform_vec2(grp, "texelSize", e_data.texel_size, 1);
|
||||
DRW_shgroup_call(grp, quad, NULL);
|
||||
|
||||
/* Copy depth buffer to top level of HiZ */
|
||||
DRW_PASS_CREATE(psl->maxz_copydepth_ps, downsample_write);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_maxz_copydepth_sh_get(), psl->maxz_copydepth_ps);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &e_data.depth_src, GPU_SAMPLER_DEFAULT);
|
||||
DRW_shgroup_call(grp, quad, NULL);
|
||||
|
||||
DRW_PASS_CREATE(psl->maxz_copydepth_layer_ps, downsample_write);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_maxz_copydepth_layer_sh_get(),
|
||||
psl->maxz_copydepth_layer_ps);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &e_data.depth_src, GPU_SAMPLER_DEFAULT);
|
||||
DRW_shgroup_uniform_int(grp, "depthLayer", &e_data.depth_src_layer, 1);
|
||||
DRW_shgroup_call(grp, quad, NULL);
|
||||
}
|
||||
|
||||
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
|
||||
EEVEE_MotionBlurData *mb_data = &effects->motion_blur;
|
||||
|
||||
/* This pass compute camera motions to the non moving objects. */
|
||||
DRW_PASS_CREATE(psl->velocity_resolve, DRW_STATE_WRITE_COLOR);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_velocity_resolve_sh_get(), psl->velocity_resolve);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
|
||||
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
|
||||
|
||||
DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat);
|
||||
DRW_shgroup_uniform_mat4(grp, "currViewProjMatrixInv", mb_data->camera[MB_CURR].persinv);
|
||||
DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat);
|
||||
DRW_shgroup_call(grp, quad, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_effects_draw_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_EffectsInfo *effects = vedata->stl->effects;
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
/**
|
||||
* Setup double buffer so we can access last frame as it was before post processes.
|
||||
*/
|
||||
if ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0) {
|
||||
SETUP_BUFFER(txl->color_double_buffer, fbl->double_buffer_fb, fbl->double_buffer_color_fb);
|
||||
}
|
||||
else {
|
||||
CLEANUP_BUFFER(txl->color_double_buffer, fbl->double_buffer_fb, fbl->double_buffer_color_fb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ping Pong buffer
|
||||
*/
|
||||
if ((effects->enabled_effects & EFFECT_POST_BUFFER) != 0) {
|
||||
SETUP_BUFFER(txl->color_post, fbl->effect_fb, fbl->effect_color_fb);
|
||||
}
|
||||
else {
|
||||
CLEANUP_BUFFER(txl->color_post, fbl->effect_fb, fbl->effect_color_fb);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 /* Not required for now */
|
||||
static void min_downsample_cb(void *vedata, int UNUSED(level))
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
DRW_draw_pass(psl->minz_downlevel_ps);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void max_downsample_cb(void *vedata, int level)
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl;
|
||||
int texture_size[3];
|
||||
GPU_texture_get_mipmap_size(txl->maxzbuffer, level - 1, texture_size);
|
||||
e_data.texel_size[0] = 1.0f / texture_size[0];
|
||||
e_data.texel_size[1] = 1.0f / texture_size[1];
|
||||
DRW_draw_pass(psl->maxz_downlevel_ps);
|
||||
}
|
||||
|
||||
static void simple_downsample_cube_cb(void *vedata, int level)
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
e_data.texel_size[0] = (float)(1 << level) / (float)GPU_texture_width(e_data.color_src);
|
||||
e_data.texel_size[1] = e_data.texel_size[0];
|
||||
DRW_draw_pass(psl->color_downsample_cube_ps);
|
||||
}
|
||||
|
||||
void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, GPUTexture *depth_src, int layer)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
|
||||
e_data.depth_src = depth_src;
|
||||
e_data.depth_src_layer = layer;
|
||||
|
||||
DRW_stats_group_start("Max buffer");
|
||||
/* Copy depth buffer to max texture top level */
|
||||
GPU_framebuffer_bind(fbl->maxzbuffer_fb);
|
||||
if (layer >= 0) {
|
||||
DRW_draw_pass(psl->maxz_copydepth_layer_ps);
|
||||
}
|
||||
else {
|
||||
DRW_draw_pass(psl->maxz_copydepth_ps);
|
||||
}
|
||||
/* Create lower levels */
|
||||
GPU_framebuffer_recursive_downsample(
|
||||
fbl->maxzbuffer_fb, MAX_SCREEN_BUFFERS_LOD_LEVEL, &max_downsample_cb, vedata);
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* Restore */
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
|
||||
if (GPU_mip_render_workaround() ||
|
||||
GPU_type_matches(GPU_DEVICE_INTEL_UHD, GPU_OS_WIN, GPU_DRIVER_ANY)) {
|
||||
/* Fix dot corruption on intel HD5XX/HD6XX series. */
|
||||
GPU_flush();
|
||||
}
|
||||
}
|
||||
|
||||
static void downsample_radiance_cb(void *vedata, int level)
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl;
|
||||
int texture_size[3];
|
||||
GPU_texture_get_mipmap_size(txl->filtered_radiance, level - 1, texture_size);
|
||||
e_data.texel_size[0] = 1.0f / texture_size[0];
|
||||
e_data.texel_size[1] = 1.0f / texture_size[1];
|
||||
DRW_draw_pass(psl->color_downsample_ps);
|
||||
}
|
||||
|
||||
void EEVEE_effects_downsample_radiance_buffer(EEVEE_Data *vedata, GPUTexture *texture_src)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
|
||||
e_data.color_src = texture_src;
|
||||
DRW_stats_group_start("Downsample Radiance");
|
||||
|
||||
GPU_framebuffer_bind(fbl->radiance_filtered_fb);
|
||||
DRW_draw_pass(psl->color_copy_ps);
|
||||
|
||||
GPU_framebuffer_recursive_downsample(
|
||||
fbl->radiance_filtered_fb, MAX_SCREEN_BUFFERS_LOD_LEVEL, &downsample_radiance_cb, vedata);
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, GPUTexture *texture_src, int level)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
e_data.color_src = texture_src;
|
||||
|
||||
/* Create lower levels */
|
||||
DRW_stats_group_start("Downsample Cube buffer");
|
||||
GPU_framebuffer_texture_attach(fbl->downsample_fb, texture_src, 0, 0);
|
||||
GPU_framebuffer_recursive_downsample(
|
||||
fbl->downsample_fb, level, &simple_downsample_cube_cb, vedata);
|
||||
GPU_framebuffer_texture_detach(fbl->downsample_fb, texture_src);
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
static void EEVEE_velocity_resolve(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
e_data.depth_src = dtxl->depth;
|
||||
|
||||
GPU_framebuffer_bind(fbl->velocity_resolve_fb);
|
||||
DRW_draw_pass(psl->velocity_resolve);
|
||||
|
||||
if (psl->velocity_object) {
|
||||
GPU_framebuffer_bind(fbl->velocity_fb);
|
||||
DRW_draw_pass(psl->velocity_object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
/* only once per frame after the first post process */
|
||||
effects->swap_double_buffer = ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0);
|
||||
|
||||
/* Init pointers */
|
||||
effects->source_buffer = txl->color; /* latest updated texture */
|
||||
effects->target_buffer = fbl->effect_color_fb; /* next target to render to */
|
||||
|
||||
/* Post process stack (order matters) */
|
||||
EEVEE_velocity_resolve(vedata);
|
||||
EEVEE_motion_blur_draw(vedata);
|
||||
EEVEE_depth_of_field_draw(vedata);
|
||||
|
||||
/* NOTE: Lookdev drawing happens before TAA but after
|
||||
* motion blur and DOF to avoid distortions.
|
||||
* Velocity resolve use a hack to exclude lookdev
|
||||
* spheres from creating shimmering re-projection vectors. */
|
||||
EEVEE_lookdev_draw(vedata);
|
||||
|
||||
EEVEE_temporal_sampling_draw(vedata);
|
||||
EEVEE_bloom_draw(vedata);
|
||||
|
||||
/* Post effect render passes are done here just after the drawing of the effects and just before
|
||||
* the swapping of the buffers. */
|
||||
EEVEE_renderpasses_output_accumulate(sldata, vedata, true);
|
||||
|
||||
/* Save the final texture and frame-buffer for final transformation or read. */
|
||||
effects->final_tx = effects->source_buffer;
|
||||
effects->final_fb = (effects->target_buffer != fbl->main_color_fb) ? fbl->main_fb :
|
||||
fbl->effect_fb;
|
||||
if ((effects->enabled_effects & EFFECT_TAA) && (effects->source_buffer == txl->taa_history)) {
|
||||
effects->final_fb = fbl->taa_history_fb;
|
||||
}
|
||||
|
||||
/* If no post processes is enabled, buffers are still not swapped, do it now. */
|
||||
SWAP_DOUBLE_BUFFERS();
|
||||
|
||||
if (!stl->g_data->valid_double_buffer &&
|
||||
((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0) &&
|
||||
(DRW_state_is_image_render() == false)) {
|
||||
/* If history buffer is not valid request another frame.
|
||||
* This fix black reflections on area resize. */
|
||||
DRW_viewport_request_redraw();
|
||||
}
|
||||
|
||||
/* Record perspective matrix for the next frame. */
|
||||
DRW_view_persmat_get(effects->taa_view, effects->prev_persmat, false);
|
||||
|
||||
/* Update double buffer status if render mode. */
|
||||
if (DRW_state_is_image_render()) {
|
||||
stl->g_data->valid_double_buffer = (txl->color_double_buffer != NULL);
|
||||
stl->g_data->valid_taa_history = (txl->taa_history != NULL);
|
||||
}
|
||||
}
|
||||
@@ -1,662 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "draw_color_management.h" /* TODO: remove dependency. */
|
||||
|
||||
#include "BLI_rand.h"
|
||||
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
|
||||
#include "eevee_private.h"
|
||||
|
||||
#include "eevee_engine.h" /* own include */
|
||||
|
||||
#define EEVEE_ENGINE "BLENDER_EEVEE"
|
||||
|
||||
/* *********** FUNCTIONS *********** */
|
||||
|
||||
static void eevee_engine_init(void *ved)
|
||||
{
|
||||
EEVEE_Data *vedata = (EEVEE_Data *)ved;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
View3D *v3d = draw_ctx->v3d;
|
||||
RegionView3D *rv3d = draw_ctx->rv3d;
|
||||
Object *camera = (rv3d->persp == RV3D_CAMOB) ? v3d->camera : NULL;
|
||||
|
||||
if (!stl->g_data) {
|
||||
/* Alloc transient pointers */
|
||||
stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__);
|
||||
}
|
||||
stl->g_data->use_color_render_settings = USE_SCENE_LIGHT(v3d) ||
|
||||
!LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d);
|
||||
stl->g_data->background_alpha = DRW_state_draw_background() ? 1.0f : 0.0f;
|
||||
stl->g_data->valid_double_buffer = (txl->color_double_buffer != NULL);
|
||||
stl->g_data->valid_taa_history = (txl->taa_history != NULL);
|
||||
stl->g_data->queued_shaders_count = 0;
|
||||
stl->g_data->render_timesteps = 1;
|
||||
|
||||
/* Main Buffer */
|
||||
DRW_texture_ensure_fullscreen_2d(&txl->color, GPU_RGBA16F, DRW_TEX_FILTER);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->main_fb,
|
||||
{GPU_ATTACHMENT_TEXTURE(dtxl->depth),
|
||||
GPU_ATTACHMENT_TEXTURE(txl->color),
|
||||
GPU_ATTACHMENT_LEAVE,
|
||||
GPU_ATTACHMENT_LEAVE,
|
||||
GPU_ATTACHMENT_LEAVE,
|
||||
GPU_ATTACHMENT_LEAVE});
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->main_color_fb,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->color)});
|
||||
|
||||
/* `EEVEE_renderpasses_init` will set the active render passes used by `EEVEE_effects_init`.
|
||||
* `EEVEE_effects_init` needs to go second for TAA. */
|
||||
EEVEE_renderpasses_init(vedata);
|
||||
EEVEE_effects_init(sldata, vedata, camera, false);
|
||||
EEVEE_materials_init(sldata, vedata, stl, fbl);
|
||||
EEVEE_shadows_init(sldata);
|
||||
EEVEE_lightprobes_init(sldata, vedata);
|
||||
}
|
||||
|
||||
static void eevee_cache_init(void *vedata)
|
||||
{
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
|
||||
EEVEE_bloom_cache_init(sldata, vedata);
|
||||
EEVEE_depth_of_field_cache_init(sldata, vedata);
|
||||
EEVEE_effects_cache_init(sldata, vedata);
|
||||
EEVEE_lightprobes_cache_init(sldata, vedata);
|
||||
EEVEE_lights_cache_init(sldata, vedata);
|
||||
EEVEE_materials_cache_init(sldata, vedata);
|
||||
EEVEE_motion_blur_cache_init(sldata, vedata);
|
||||
EEVEE_occlusion_cache_init(sldata, vedata);
|
||||
EEVEE_screen_raytrace_cache_init(sldata, vedata);
|
||||
EEVEE_subsurface_cache_init(sldata, vedata);
|
||||
EEVEE_temporal_sampling_cache_init(sldata, vedata);
|
||||
EEVEE_volumes_cache_init(sldata, vedata);
|
||||
}
|
||||
|
||||
void EEVEE_cache_populate(void *vedata, Object *ob)
|
||||
{
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const int ob_visibility = DRW_object_visibility_in_active_context(ob);
|
||||
bool cast_shadow = false;
|
||||
|
||||
if (ob_visibility & OB_VISIBLE_PARTICLES) {
|
||||
EEVEE_particle_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
|
||||
}
|
||||
|
||||
if (DRW_object_is_renderable(ob) && (ob_visibility & OB_VISIBLE_SELF)) {
|
||||
if (ELEM(ob->type, OB_MESH, OB_SURF, OB_MBALL)) {
|
||||
EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
|
||||
}
|
||||
else if (ob->type == OB_HAIR) {
|
||||
EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
|
||||
}
|
||||
else if (ob->type == OB_VOLUME) {
|
||||
EEVEE_volumes_cache_object_add(sldata, vedata, draw_ctx->scene, ob);
|
||||
}
|
||||
else if (!USE_SCENE_LIGHT(draw_ctx->v3d)) {
|
||||
/* do not add any scene light sources to the cache */
|
||||
}
|
||||
else if (ob->type == OB_LIGHTPROBE) {
|
||||
if ((ob->base_flag & BASE_FROM_DUPLI) != 0) {
|
||||
/* TODO: Special case for dupli objects because we cannot save the object pointer. */
|
||||
}
|
||||
else {
|
||||
EEVEE_lightprobes_cache_add(sldata, vedata, ob);
|
||||
}
|
||||
}
|
||||
else if (ob->type == OB_LAMP) {
|
||||
EEVEE_lights_cache_add(sldata, ob);
|
||||
}
|
||||
}
|
||||
|
||||
if (cast_shadow) {
|
||||
EEVEE_shadows_caster_register(sldata, ob);
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_cache_finish(void *vedata)
|
||||
{
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
|
||||
|
||||
EEVEE_volumes_cache_finish(sldata, vedata);
|
||||
EEVEE_materials_cache_finish(sldata, vedata);
|
||||
EEVEE_lights_cache_finish(sldata, vedata);
|
||||
EEVEE_lightprobes_cache_finish(sldata, vedata);
|
||||
EEVEE_renderpasses_cache_finish(sldata, vedata);
|
||||
|
||||
EEVEE_subsurface_draw_init(sldata, vedata);
|
||||
EEVEE_effects_draw_init(sldata, vedata);
|
||||
EEVEE_volumes_draw_init(sldata, vedata);
|
||||
|
||||
uint tot_samples = scene_eval->eevee.taa_render_samples;
|
||||
if (tot_samples == 0) {
|
||||
/* Use a high number of samples so the outputs accumulation buffers
|
||||
* will have the highest possible precision. */
|
||||
tot_samples = 1024;
|
||||
}
|
||||
EEVEE_renderpasses_output_init(sldata, vedata, tot_samples);
|
||||
|
||||
/* Restart TAA if a shader has finish compiling. */
|
||||
/* HACK: We should use notification of some sort from the compilation job instead. */
|
||||
if (g_data->queued_shaders_count != g_data->queued_shaders_count_prev) {
|
||||
g_data->queued_shaders_count_prev = g_data->queued_shaders_count;
|
||||
EEVEE_temporal_sampling_reset(vedata);
|
||||
}
|
||||
}
|
||||
|
||||
/* As renders in an HDR off-screen buffer, we need draw everything once
|
||||
* during the background pass. This way the other drawing callback between
|
||||
* the background and the scene pass are visible.
|
||||
* NOTE: we could break it up in two passes using some depth test
|
||||
* to reduce the fill-rate. */
|
||||
static void eevee_draw_scene(void *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
EEVEE_FramebufferList *fbl = ((EEVEE_Data *)vedata)->fbl;
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
|
||||
/* Default framebuffer and texture */
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
|
||||
|
||||
/* Sort transparents before the loop. */
|
||||
DRW_pass_sort_shgroup_z(psl->transparent_pass);
|
||||
|
||||
/* Number of iteration: Use viewport taa_samples when using viewport rendering */
|
||||
int loop_len = 1;
|
||||
if (DRW_state_is_image_render()) {
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const Scene *scene = draw_ctx->scene;
|
||||
loop_len = MAX2(1, scene->eevee.taa_samples);
|
||||
}
|
||||
|
||||
if (stl->effects->bypass_drawing) {
|
||||
loop_len = 0;
|
||||
}
|
||||
|
||||
while (loop_len--) {
|
||||
const float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
float clear_depth = 1.0f;
|
||||
uint clear_stencil = 0x0;
|
||||
const uint primes[3] = {2, 3, 7};
|
||||
double offset[3] = {0.0, 0.0, 0.0};
|
||||
double r[3];
|
||||
|
||||
bool taa_use_reprojection = (stl->effects->enabled_effects & EFFECT_TAA_REPROJECT) != 0;
|
||||
|
||||
if (DRW_state_is_image_render() || taa_use_reprojection ||
|
||||
((stl->effects->enabled_effects & EFFECT_TAA) != 0)) {
|
||||
int samp = taa_use_reprojection ? stl->effects->taa_reproject_sample + 1 :
|
||||
stl->effects->taa_current_sample;
|
||||
BLI_halton_3d(primes, offset, samp, r);
|
||||
EEVEE_update_noise(psl, fbl, r);
|
||||
EEVEE_volumes_set_jitter(sldata, samp - 1);
|
||||
EEVEE_materials_init(sldata, vedata, stl, fbl);
|
||||
}
|
||||
/* Copy previous persmat to UBO data */
|
||||
copy_m4_m4(sldata->common_data.prev_persmat, stl->effects->prev_persmat);
|
||||
|
||||
/* Refresh Probes
|
||||
* Shadows needs to be updated for correct probes */
|
||||
DRW_stats_group_start("Probes Refresh");
|
||||
EEVEE_shadows_update(sldata, vedata);
|
||||
EEVEE_lightprobes_refresh(sldata, vedata);
|
||||
EEVEE_lightprobes_refresh_planar(sldata, vedata);
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* Refresh shadows */
|
||||
DRW_stats_group_start("Shadows");
|
||||
EEVEE_shadows_draw(sldata, vedata, stl->effects->taa_view);
|
||||
DRW_stats_group_end();
|
||||
|
||||
if (((stl->effects->enabled_effects & EFFECT_TAA) != 0) &&
|
||||
(stl->effects->taa_current_sample > 1) && !DRW_state_is_image_render() &&
|
||||
!taa_use_reprojection) {
|
||||
DRW_view_set_active(stl->effects->taa_view);
|
||||
}
|
||||
/* when doing viewport rendering the overrides needs to be recalculated for
|
||||
* every loop as this normally happens once inside
|
||||
* `EEVEE_temporal_sampling_init` */
|
||||
else if (((stl->effects->enabled_effects & EFFECT_TAA) != 0) &&
|
||||
(stl->effects->taa_current_sample > 1) && DRW_state_is_image_render()) {
|
||||
EEVEE_temporal_sampling_update_matrices(vedata);
|
||||
}
|
||||
|
||||
/* Set ray type. */
|
||||
sldata->common_data.ray_type = EEVEE_RAY_CAMERA;
|
||||
sldata->common_data.ray_depth = 0.0f;
|
||||
GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
|
||||
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
eGPUFrameBufferBits clear_bits = GPU_DEPTH_BIT;
|
||||
SET_FLAG_FROM_TEST(clear_bits, !DRW_state_draw_background(), GPU_COLOR_BIT);
|
||||
SET_FLAG_FROM_TEST(clear_bits, (stl->effects->enabled_effects & EFFECT_SSS), GPU_STENCIL_BIT);
|
||||
GPU_framebuffer_clear(fbl->main_fb, clear_bits, clear_col, clear_depth, clear_stencil);
|
||||
|
||||
/* Depth prepass */
|
||||
DRW_stats_group_start("Prepass");
|
||||
DRW_draw_pass(psl->depth_ps);
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* Create minmax texture */
|
||||
DRW_stats_group_start("Main MinMax buffer");
|
||||
EEVEE_create_minmax_buffer(vedata, dtxl->depth, -1);
|
||||
DRW_stats_group_end();
|
||||
|
||||
EEVEE_occlusion_compute(sldata, vedata);
|
||||
EEVEE_volumes_compute(sldata, vedata);
|
||||
|
||||
/* Shading pass */
|
||||
DRW_stats_group_start("Shading");
|
||||
if (DRW_state_draw_background()) {
|
||||
DRW_draw_pass(psl->background_ps);
|
||||
}
|
||||
DRW_draw_pass(psl->material_ps);
|
||||
EEVEE_subsurface_data_render(sldata, vedata);
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* Effects pre-transparency */
|
||||
EEVEE_subsurface_compute(sldata, vedata);
|
||||
EEVEE_reflection_compute(sldata, vedata);
|
||||
EEVEE_occlusion_draw_debug(sldata, vedata);
|
||||
if (psl->probe_display) {
|
||||
DRW_draw_pass(psl->probe_display);
|
||||
}
|
||||
EEVEE_refraction_compute(sldata, vedata);
|
||||
|
||||
/* Opaque refraction */
|
||||
DRW_stats_group_start("Opaque Refraction");
|
||||
DRW_draw_pass(psl->depth_refract_ps);
|
||||
DRW_draw_pass(psl->material_refract_ps);
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* Volumetrics Resolve Opaque */
|
||||
EEVEE_volumes_resolve(sldata, vedata);
|
||||
|
||||
/* Renderpasses */
|
||||
EEVEE_renderpasses_output_accumulate(sldata, vedata, false);
|
||||
|
||||
/* Transparent */
|
||||
/* TODO(fclem): should be its own Frame-buffer.
|
||||
* This is needed because dualsource blending only works with 1 color buffer. */
|
||||
GPU_framebuffer_texture_attach(fbl->main_color_fb, dtxl->depth, 0, 0);
|
||||
GPU_framebuffer_bind(fbl->main_color_fb);
|
||||
DRW_draw_pass(psl->transparent_pass);
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
GPU_framebuffer_texture_detach(fbl->main_color_fb, dtxl->depth);
|
||||
|
||||
/* Post Process */
|
||||
DRW_stats_group_start("Post FX");
|
||||
EEVEE_draw_effects(sldata, vedata);
|
||||
DRW_stats_group_end();
|
||||
|
||||
DRW_view_set_active(NULL);
|
||||
|
||||
if (DRW_state_is_image_render() && (stl->effects->enabled_effects & EFFECT_SSR) &&
|
||||
!stl->effects->ssr_was_valid_double_buffer) {
|
||||
/* SSR needs one iteration to start properly. */
|
||||
loop_len++;
|
||||
/* Reset sampling (and accumulation) after the first sample to avoid
|
||||
* washed out first bounce for SSR. */
|
||||
EEVEE_temporal_sampling_reset(vedata);
|
||||
stl->effects->ssr_was_valid_double_buffer = stl->g_data->valid_double_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
if ((stl->g_data->render_passes & EEVEE_RENDER_PASS_COMBINED) != 0) {
|
||||
/* Transfer result to default framebuffer. */
|
||||
GPU_framebuffer_bind(dfbl->default_fb);
|
||||
DRW_transform_none(stl->effects->final_tx);
|
||||
}
|
||||
else {
|
||||
EEVEE_renderpasses_draw(sldata, vedata);
|
||||
}
|
||||
|
||||
if (stl->effects->bypass_drawing) {
|
||||
/* Restore the depth from sample 1. */
|
||||
GPU_framebuffer_blit(fbl->double_buffer_depth_fb, 0, dfbl->default_fb, 0, GPU_DEPTH_BIT);
|
||||
}
|
||||
|
||||
EEVEE_renderpasses_draw_debug(vedata);
|
||||
|
||||
EEVEE_volumes_free_smoke_textures();
|
||||
|
||||
stl->g_data->view_updated = false;
|
||||
|
||||
DRW_view_set_active(NULL);
|
||||
}
|
||||
|
||||
static void eevee_view_update(void *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
if (stl->g_data) {
|
||||
stl->g_data->view_updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_id_object_update(void *UNUSED(vedata), Object *object)
|
||||
{
|
||||
EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(object);
|
||||
if (ped != NULL && ped->dd.recalc != 0) {
|
||||
ped->need_update = (ped->dd.recalc & ID_RECALC_TRANSFORM) != 0;
|
||||
ped->dd.recalc = 0;
|
||||
}
|
||||
EEVEE_LightEngineData *led = EEVEE_light_data_get(object);
|
||||
if (led != NULL && led->dd.recalc != 0) {
|
||||
led->need_update = true;
|
||||
led->dd.recalc = 0;
|
||||
}
|
||||
EEVEE_ObjectEngineData *oedata = EEVEE_object_data_get(object);
|
||||
if (oedata != NULL && oedata->dd.recalc != 0) {
|
||||
oedata->need_update = true;
|
||||
oedata->geom_update = (oedata->dd.recalc & (ID_RECALC_GEOMETRY)) != 0;
|
||||
oedata->dd.recalc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_id_world_update(void *vedata, World *wo)
|
||||
{
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
LightCache *lcache = stl->g_data->light_cache;
|
||||
|
||||
if (ELEM(lcache, NULL, stl->lookdev_lightcache)) {
|
||||
/* Avoid Lookdev viewport clearing the update flag (see T67741). */
|
||||
return;
|
||||
}
|
||||
|
||||
EEVEE_WorldEngineData *wedata = EEVEE_world_data_ensure(wo);
|
||||
|
||||
if (wedata != NULL && wedata->dd.recalc != 0) {
|
||||
if ((lcache->flag & LIGHTCACHE_BAKING) == 0) {
|
||||
lcache->flag |= LIGHTCACHE_UPDATE_WORLD;
|
||||
}
|
||||
wedata->dd.recalc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void eevee_id_update(void *vedata, ID *id)
|
||||
{
|
||||
/* Handle updates based on ID type. */
|
||||
switch (GS(id->name)) {
|
||||
case ID_WO:
|
||||
eevee_id_world_update(vedata, (World *)id);
|
||||
break;
|
||||
case ID_OB:
|
||||
eevee_id_object_update(vedata, (Object *)id);
|
||||
break;
|
||||
default:
|
||||
/* pass */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_render_reset_passes(EEVEE_Data *vedata)
|
||||
{
|
||||
/* Reset passlist. This is safe as they are stored into managed memory chunks. */
|
||||
memset(vedata->psl, 0, sizeof(*vedata->psl));
|
||||
}
|
||||
|
||||
static void eevee_render_to_image(void *vedata,
|
||||
RenderEngine *engine,
|
||||
struct RenderLayer *render_layer,
|
||||
const rcti *rect)
|
||||
{
|
||||
EEVEE_Data *ved = (EEVEE_Data *)vedata;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
Depsgraph *depsgraph = draw_ctx->depsgraph;
|
||||
Scene *scene = DEG_get_evaluated_scene(depsgraph);
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
const bool do_motion_blur = (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0;
|
||||
const bool do_motion_blur_fx = do_motion_blur && (scene->eevee.motion_blur_max > 0);
|
||||
|
||||
if (!EEVEE_render_init(vedata, engine, depsgraph)) {
|
||||
return;
|
||||
}
|
||||
EEVEE_PrivateData *g_data = ved->stl->g_data;
|
||||
|
||||
int initial_frame = CFRA;
|
||||
float initial_subframe = SUBFRA;
|
||||
float shuttertime = (do_motion_blur) ? scene->eevee.motion_blur_shutter : 0.0f;
|
||||
int time_steps_tot = (do_motion_blur) ? max_ii(1, scene->eevee.motion_blur_steps) : 1;
|
||||
g_data->render_timesteps = time_steps_tot;
|
||||
|
||||
EEVEE_render_modules_init(vedata, engine, depsgraph);
|
||||
|
||||
g_data->render_sample_count_per_timestep = EEVEE_temporal_sampling_sample_count_get(scene,
|
||||
ved->stl);
|
||||
|
||||
/* Reset in case the same engine is used on multiple views. */
|
||||
EEVEE_temporal_sampling_reset(vedata);
|
||||
|
||||
/* Compute start time. The motion blur will cover `[time ...time + shuttertime]`. */
|
||||
float time = initial_frame + initial_subframe;
|
||||
switch (scene->eevee.motion_blur_position) {
|
||||
case SCE_EEVEE_MB_START:
|
||||
/* No offset. */
|
||||
break;
|
||||
case SCE_EEVEE_MB_CENTER:
|
||||
time -= shuttertime * 0.5f;
|
||||
break;
|
||||
case SCE_EEVEE_MB_END:
|
||||
time -= shuttertime;
|
||||
break;
|
||||
default:
|
||||
BLI_assert_msg(0, "Invalid motion blur position enum!");
|
||||
break;
|
||||
}
|
||||
|
||||
float time_step = shuttertime / time_steps_tot;
|
||||
for (int i = 0; i < time_steps_tot && !RE_engine_test_break(engine); i++) {
|
||||
float time_prev = time;
|
||||
float time_curr = time + time_step * 0.5f;
|
||||
float time_next = time + time_step;
|
||||
time += time_step;
|
||||
|
||||
/* Previous motion step. */
|
||||
if (do_motion_blur_fx) {
|
||||
if (i == 0) {
|
||||
EEVEE_motion_blur_step_set(ved, MB_PREV);
|
||||
DRW_render_set_time(engine, depsgraph, floorf(time_prev), fractf(time_prev));
|
||||
EEVEE_render_modules_init(vedata, engine, depsgraph);
|
||||
sldata = EEVEE_view_layer_data_ensure();
|
||||
|
||||
EEVEE_render_cache_init(sldata, vedata);
|
||||
|
||||
DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
|
||||
|
||||
EEVEE_motion_blur_cache_finish(vedata);
|
||||
EEVEE_materials_cache_finish(sldata, vedata);
|
||||
eevee_render_reset_passes(vedata);
|
||||
}
|
||||
}
|
||||
|
||||
/* Next motion step. */
|
||||
if (do_motion_blur_fx) {
|
||||
EEVEE_motion_blur_step_set(ved, MB_NEXT);
|
||||
DRW_render_set_time(engine, depsgraph, floorf(time_next), fractf(time_next));
|
||||
EEVEE_render_modules_init(vedata, engine, depsgraph);
|
||||
sldata = EEVEE_view_layer_data_ensure();
|
||||
|
||||
EEVEE_render_cache_init(sldata, vedata);
|
||||
|
||||
DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
|
||||
|
||||
EEVEE_motion_blur_cache_finish(vedata);
|
||||
EEVEE_materials_cache_finish(sldata, vedata);
|
||||
eevee_render_reset_passes(vedata);
|
||||
}
|
||||
|
||||
/* Current motion step. */
|
||||
{
|
||||
if (do_motion_blur) {
|
||||
EEVEE_motion_blur_step_set(ved, MB_CURR);
|
||||
DRW_render_set_time(engine, depsgraph, floorf(time_curr), fractf(time_curr));
|
||||
EEVEE_render_modules_init(vedata, engine, depsgraph);
|
||||
sldata = EEVEE_view_layer_data_ensure();
|
||||
}
|
||||
|
||||
EEVEE_render_cache_init(sldata, vedata);
|
||||
|
||||
DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
|
||||
|
||||
EEVEE_motion_blur_cache_finish(vedata);
|
||||
EEVEE_volumes_cache_finish(sldata, vedata);
|
||||
EEVEE_materials_cache_finish(sldata, vedata);
|
||||
EEVEE_lights_cache_finish(sldata, vedata);
|
||||
EEVEE_lightprobes_cache_finish(sldata, vedata);
|
||||
EEVEE_renderpasses_cache_finish(sldata, vedata);
|
||||
|
||||
EEVEE_subsurface_draw_init(sldata, vedata);
|
||||
EEVEE_effects_draw_init(sldata, vedata);
|
||||
EEVEE_volumes_draw_init(sldata, vedata);
|
||||
}
|
||||
|
||||
/* Actual drawing. */
|
||||
{
|
||||
EEVEE_renderpasses_output_init(
|
||||
sldata, vedata, g_data->render_sample_count_per_timestep * time_steps_tot);
|
||||
|
||||
if (scene->world) {
|
||||
/* Update world in case of animated world material. */
|
||||
eevee_id_world_update(vedata, scene->world);
|
||||
}
|
||||
|
||||
EEVEE_temporal_sampling_create_view(vedata);
|
||||
EEVEE_render_draw(vedata, engine, render_layer, rect);
|
||||
|
||||
if (i < time_steps_tot - 1) {
|
||||
/* Don't reset after the last loop. Since EEVEE_render_read_result
|
||||
* might need some DRWPasses. */
|
||||
DRW_cache_restart();
|
||||
}
|
||||
}
|
||||
|
||||
if (do_motion_blur_fx) {
|
||||
/* The previous step of next iteration N is exactly the next step of this iteration N - 1.
|
||||
* So we just swap the resources to avoid too much re-evaluation.
|
||||
* Note that this also clears the VBO references from the GPUBatches of deformed
|
||||
* geometries. */
|
||||
EEVEE_motion_blur_swap_data(vedata);
|
||||
}
|
||||
}
|
||||
|
||||
EEVEE_volumes_free_smoke_textures();
|
||||
EEVEE_motion_blur_data_free(&ved->stl->effects->motion_blur);
|
||||
|
||||
if (RE_engine_test_break(engine)) {
|
||||
return;
|
||||
}
|
||||
|
||||
EEVEE_render_read_result(vedata, engine, render_layer, rect);
|
||||
|
||||
/* Restore original viewport size. */
|
||||
DRW_render_viewport_size_set((int[2]){g_data->size_orig[0], g_data->size_orig[1]});
|
||||
|
||||
if (CFRA != initial_frame || SUBFRA != initial_subframe) {
|
||||
/* Restore original frame number. This is because the render pipeline expects it. */
|
||||
RE_engine_frame_set(engine, initial_frame, initial_subframe);
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_store_metadata(void *vedata, struct RenderResult *render_result)
|
||||
{
|
||||
EEVEE_Data *ved = (EEVEE_Data *)vedata;
|
||||
EEVEE_PrivateData *g_data = ved->stl->g_data;
|
||||
if (g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) {
|
||||
EEVEE_cryptomatte_store_metadata(ved, render_result);
|
||||
EEVEE_cryptomatte_free(ved);
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_engine_free(void)
|
||||
{
|
||||
EEVEE_shaders_free();
|
||||
EEVEE_lightprobes_free();
|
||||
EEVEE_materials_free();
|
||||
EEVEE_occlusion_free();
|
||||
EEVEE_volumes_free();
|
||||
}
|
||||
|
||||
static const DrawEngineDataSize eevee_data_size = DRW_VIEWPORT_DATA_SIZE(EEVEE_Data);
|
||||
|
||||
DrawEngineType draw_engine_eevee_type = {
|
||||
NULL,
|
||||
NULL,
|
||||
N_("Eevee"),
|
||||
&eevee_data_size,
|
||||
&eevee_engine_init,
|
||||
&eevee_engine_free,
|
||||
NULL, /* instance_free */
|
||||
&eevee_cache_init,
|
||||
&EEVEE_cache_populate,
|
||||
&eevee_cache_finish,
|
||||
&eevee_draw_scene,
|
||||
&eevee_view_update,
|
||||
&eevee_id_update,
|
||||
&eevee_render_to_image,
|
||||
&eevee_store_metadata,
|
||||
};
|
||||
|
||||
RenderEngineType DRW_engine_viewport_eevee_type = {
|
||||
NULL,
|
||||
NULL,
|
||||
EEVEE_ENGINE,
|
||||
N_("Eevee"),
|
||||
RE_INTERNAL | RE_USE_PREVIEW | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT,
|
||||
NULL,
|
||||
&DRW_render_to_image,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&EEVEE_render_update_passes,
|
||||
&draw_engine_eevee_type,
|
||||
{NULL, NULL, NULL},
|
||||
};
|
||||
|
||||
#undef EEVEE_ENGINE
|
||||
247
source/blender/draw/engines/eevee/eevee_engine.cc
Normal file
247
source/blender/draw/engines/eevee/eevee_engine.cc
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
#include "BKE_global.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "GPU_framebuffer.h"
|
||||
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
using namespace blender::eevee;
|
||||
|
||||
typedef struct EEVEE_Data {
|
||||
DrawEngineType *engine_type;
|
||||
DRWViewportEmptyList *fbl;
|
||||
DRWViewportEmptyList *txl;
|
||||
DRWViewportEmptyList *psl;
|
||||
DRWViewportEmptyList *stl;
|
||||
Instance *instance;
|
||||
} EEVEE_Data;
|
||||
|
||||
static void eevee_engine_init(void *vedata)
|
||||
{
|
||||
EEVEE_Data *ved = (EEVEE_Data *)vedata;
|
||||
|
||||
if (ved->instance == NULL) {
|
||||
ved->instance = new Instance();
|
||||
}
|
||||
|
||||
const DRWContextState *ctx_state = DRW_context_state_get();
|
||||
Depsgraph *depsgraph = ctx_state->depsgraph;
|
||||
Scene *scene = ctx_state->scene;
|
||||
View3D *v3d = ctx_state->v3d;
|
||||
const ARegion *region = ctx_state->region;
|
||||
RegionView3D *rv3d = ctx_state->rv3d;
|
||||
|
||||
/* Scaling output to better see what happens with accumulation. */
|
||||
int resolution_divider = (ELEM(G.debug_value, 1, 2)) ? 16 : 1;
|
||||
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
int size[2];
|
||||
size[0] = divide_ceil_u(GPU_texture_width(dtxl->color), resolution_divider);
|
||||
size[1] = divide_ceil_u(GPU_texture_height(dtxl->color), resolution_divider);
|
||||
|
||||
const DRWView *default_view = DRW_view_default_get();
|
||||
|
||||
Object *camera = nullptr;
|
||||
/* Get render borders. */
|
||||
rcti rect;
|
||||
BLI_rcti_init(&rect, 0, size[0], 0, size[1]);
|
||||
if (v3d) {
|
||||
if (rv3d && (rv3d->persp == RV3D_CAMOB)) {
|
||||
camera = v3d->camera;
|
||||
}
|
||||
|
||||
if (v3d->flag2 & V3D_RENDER_BORDER) {
|
||||
if (camera) {
|
||||
rctf viewborder;
|
||||
/* TODO(fclem) Might be better to get it from DRW. */
|
||||
ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &viewborder, false);
|
||||
float viewborder_sizex = BLI_rctf_size_x(&viewborder);
|
||||
float viewborder_sizey = BLI_rctf_size_y(&viewborder);
|
||||
rect.xmin = floorf(viewborder.xmin + (scene->r.border.xmin * viewborder_sizex));
|
||||
rect.ymin = floorf(viewborder.ymin + (scene->r.border.ymin * viewborder_sizey));
|
||||
rect.xmax = floorf(viewborder.xmin + (scene->r.border.xmax * viewborder_sizex));
|
||||
rect.ymax = floorf(viewborder.ymin + (scene->r.border.ymax * viewborder_sizey));
|
||||
}
|
||||
else {
|
||||
rect.xmin = v3d->render_border.xmin * size[0];
|
||||
rect.ymin = v3d->render_border.ymin * size[1];
|
||||
rect.xmax = v3d->render_border.xmax * size[0];
|
||||
rect.ymax = v3d->render_border.ymax * size[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ved->instance->init(
|
||||
size, &rect, nullptr, depsgraph, nullptr, camera, nullptr, default_view, v3d, rv3d);
|
||||
}
|
||||
|
||||
static void eevee_draw_scene(void *vedata)
|
||||
{
|
||||
((EEVEE_Data *)vedata)->instance->draw_viewport(DRW_viewport_framebuffer_list_get());
|
||||
}
|
||||
|
||||
static void eevee_cache_init(void *vedata)
|
||||
{
|
||||
((EEVEE_Data *)vedata)->instance->begin_sync();
|
||||
}
|
||||
|
||||
static void eevee_cache_populate(void *vedata, Object *object)
|
||||
{
|
||||
((EEVEE_Data *)vedata)->instance->object_sync(object);
|
||||
}
|
||||
|
||||
static void eevee_cache_finish(void *vedata)
|
||||
{
|
||||
((EEVEE_Data *)vedata)->instance->end_sync();
|
||||
}
|
||||
|
||||
static void eevee_engine_free(void)
|
||||
{
|
||||
ShaderModule::module_free();
|
||||
}
|
||||
|
||||
static void eevee_instance_free(void *instance)
|
||||
{
|
||||
delete reinterpret_cast<Instance *>(instance);
|
||||
}
|
||||
|
||||
static void eevee_render_to_image(void *UNUSED(vedata),
|
||||
struct RenderEngine *engine,
|
||||
struct RenderLayer *layer,
|
||||
const struct rcti *UNUSED(rect))
|
||||
{
|
||||
Instance *instance = new Instance();
|
||||
|
||||
Render *render = engine->re;
|
||||
Depsgraph *depsgraph = DRW_context_state_get()->depsgraph;
|
||||
Object *camera_original_ob = RE_GetCamera(engine->re);
|
||||
const char *viewname = RE_GetActiveRenderView(engine->re);
|
||||
int size[2] = {engine->resolution_x, engine->resolution_y};
|
||||
|
||||
rctf view_rect;
|
||||
rcti rect;
|
||||
RE_GetViewPlane(render, &view_rect, &rect);
|
||||
|
||||
instance->init(size, &rect, engine, depsgraph, nullptr, camera_original_ob, layer);
|
||||
instance->render_frame(layer, viewname);
|
||||
|
||||
delete instance;
|
||||
}
|
||||
|
||||
static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
|
||||
{
|
||||
RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
|
||||
|
||||
#define CHECK_PASS_LEGACY(name, type, channels, chanid) \
|
||||
if (view_layer->passflag & (SCE_PASS_##name)) { \
|
||||
RE_engine_register_pass( \
|
||||
engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
|
||||
} \
|
||||
((void)0)
|
||||
#define CHECK_PASS_EEVEE(name, type, channels, chanid) \
|
||||
if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \
|
||||
RE_engine_register_pass( \
|
||||
engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z");
|
||||
CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z");
|
||||
CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ");
|
||||
CHECK_PASS_LEGACY(VECTOR, SOCK_RGBA, 4, "RGBA");
|
||||
CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_EEVEE(BLOOM, SOCK_RGBA, 3, "RGB");
|
||||
|
||||
LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
|
||||
if ((aov->flag & AOV_CONFLICT) != 0) {
|
||||
continue;
|
||||
}
|
||||
switch (aov->type) {
|
||||
case AOV_TYPE_COLOR:
|
||||
RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA);
|
||||
break;
|
||||
case AOV_TYPE_VALUE:
|
||||
RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// EEVEE_cryptomatte_update_passes(engine, scene, view_layer);
|
||||
|
||||
#undef CHECK_PASS_LEGACY
|
||||
#undef CHECK_PASS_EEVEE
|
||||
}
|
||||
|
||||
static const DrawEngineDataSize eevee_data_size = DRW_VIEWPORT_DATA_SIZE(EEVEE_Data);
|
||||
|
||||
DrawEngineType draw_engine_eevee_type = {
|
||||
NULL,
|
||||
NULL,
|
||||
N_("Eevee"),
|
||||
&eevee_data_size,
|
||||
&eevee_engine_init,
|
||||
&eevee_engine_free,
|
||||
&eevee_instance_free,
|
||||
&eevee_cache_init,
|
||||
&eevee_cache_populate,
|
||||
&eevee_cache_finish,
|
||||
&eevee_draw_scene,
|
||||
NULL,
|
||||
NULL,
|
||||
&eevee_render_to_image,
|
||||
NULL,
|
||||
};
|
||||
|
||||
#define EEVEE_ENGINE "BLENDER_EEVEE"
|
||||
|
||||
RenderEngineType DRW_engine_viewport_eevee_type = {
|
||||
NULL,
|
||||
NULL,
|
||||
EEVEE_ENGINE,
|
||||
N_("Eevee"),
|
||||
RE_INTERNAL | RE_USE_PREVIEW | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT,
|
||||
NULL,
|
||||
&DRW_render_to_image,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&eevee_render_update_passes,
|
||||
&draw_engine_eevee_type,
|
||||
{NULL, NULL, NULL},
|
||||
};
|
||||
|
||||
#undef EEVEE_ENGINE
|
||||
@@ -22,4 +22,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
extern RenderEngineType DRW_engine_viewport_eevee_type;
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern RenderEngineType DRW_engine_viewport_eevee_type;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
253
source/blender/draw/engines/eevee/eevee_film.cc
Normal file
253
source/blender/draw/engines/eevee/eevee_film.cc
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* A film is a fullscreen buffer (usually at output extent)
|
||||
* that will be able to accumulate sample in any distorted camera_type
|
||||
* using a pixel filter.
|
||||
*
|
||||
* Input needs to be jittered so that the filter converges to the right result.
|
||||
*/
|
||||
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "GPU_framebuffer.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "eevee_film.hh"
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name FilmData
|
||||
* \{ */
|
||||
|
||||
static eGPUTextureFormat to_gpu_texture_format(eFilmDataType film_type)
|
||||
{
|
||||
switch (film_type) {
|
||||
default:
|
||||
case FILM_DATA_COLOR_LOG:
|
||||
case FILM_DATA_COLOR:
|
||||
case FILM_DATA_MOTION:
|
||||
case FILM_DATA_VEC4:
|
||||
return GPU_RGBA16F;
|
||||
case FILM_DATA_FLOAT:
|
||||
return GPU_R16F;
|
||||
case FILM_DATA_VEC2:
|
||||
return GPU_RG16F;
|
||||
case FILM_DATA_NORMAL:
|
||||
return GPU_RGB10_A2;
|
||||
case FILM_DATA_DEPTH:
|
||||
return GPU_R32F;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool operator==(const FilmData &a, const FilmData &b)
|
||||
{
|
||||
return (a.extent == b.extent) && (a.offset == b.offset);
|
||||
}
|
||||
|
||||
inline bool operator!=(const FilmData &a, const FilmData &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Film
|
||||
* \{ */
|
||||
|
||||
void Film::init(const int2 &full_extent, const rcti *output_rect)
|
||||
{
|
||||
FilmData data = data_;
|
||||
data.extent = int2(BLI_rcti_size_x(output_rect), BLI_rcti_size_y(output_rect));
|
||||
data.offset = int2(output_rect->xmin, output_rect->ymin);
|
||||
|
||||
has_changed_ = data_ != data;
|
||||
|
||||
if (has_changed_) {
|
||||
data_ = data;
|
||||
inst_.sampling.reset();
|
||||
}
|
||||
|
||||
data_.opacity = 1.0f;
|
||||
data_.uv_scale_inv = float2(full_extent);
|
||||
data_.uv_scale = 1.0f / data_.uv_scale_inv;
|
||||
data_.uv_bias = float2(data_.offset) * data_.uv_scale;
|
||||
}
|
||||
|
||||
void Film::sync(void)
|
||||
{
|
||||
char full_name[32];
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (data_tx_[i].is_valid() == false) {
|
||||
eGPUTextureFormat tex_format = to_gpu_texture_format(data_.data_type);
|
||||
data_tx_[i].ensure_2d(tex_format, data_.extent);
|
||||
/* TODO(fclem) The weight texture could be shared between all similar accumulators. */
|
||||
weight_tx_[i].ensure_2d(GPU_R16F, data_.extent);
|
||||
|
||||
accumulation_fb_[i].ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(data_tx_[i]),
|
||||
GPU_ATTACHMENT_TEXTURE(weight_tx_[i]));
|
||||
}
|
||||
}
|
||||
|
||||
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
{
|
||||
SNPRINTF(full_name, "Film.%s.Accumulate", name_.c_str());
|
||||
accumulate_ps_ = DRW_pass_create(full_name, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(FILM_FILTER);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, accumulate_ps_);
|
||||
DRW_shgroup_uniform_block(grp, "film", data_);
|
||||
DRW_shgroup_uniform_block(grp, "camera", inst_.camera.ubo_get());
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "input_tx", &input_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "data_tx", &data_tx_[0], no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "weight_tx", &weight_tx_[0], no_filter);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
}
|
||||
{
|
||||
SNPRINTF(full_name, "Film.%s.Resolve", name_.c_str());
|
||||
DRWState state = DRW_STATE_WRITE_COLOR;
|
||||
eShaderType sh_type = FILM_RESOLVE;
|
||||
if (data_.data_type == FILM_DATA_DEPTH) {
|
||||
state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS;
|
||||
sh_type = FILM_RESOLVE_DEPTH;
|
||||
}
|
||||
resolve_ps_ = DRW_pass_create(full_name, state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(sh_type);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_);
|
||||
DRW_shgroup_uniform_block(grp, "film", data_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "first_sample_tx", &first_sample_ref_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "data_tx", &data_tx_[0], no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "weight_tx", &weight_tx_[0], no_filter);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Film::end_sync()
|
||||
{
|
||||
/* TODO reprojection. */
|
||||
if (inst_.sampling.is_reset()) {
|
||||
data_.use_history = 0;
|
||||
}
|
||||
|
||||
if (inst_.is_viewport()) {
|
||||
data_.opacity = inst_.sampling.viewport_smoothing_opacity_factor_get();
|
||||
}
|
||||
|
||||
if (data_.use_history == 0 || inst_.is_viewport()) {
|
||||
data_.push_update();
|
||||
}
|
||||
|
||||
const bool is_first_sample = (inst_.sampling.sample_get() == 1);
|
||||
if (do_smooth_viewport_smooth_transition() && (data_.opacity < 1.0f || is_first_sample)) {
|
||||
GPUTexture *dtxl_color = DRW_viewport_texture_list_get()->color;
|
||||
eGPUTextureFormat tex_format = GPU_texture_format(dtxl_color);
|
||||
int extent[2] = {GPU_texture_width(dtxl_color), GPU_texture_height(dtxl_color)};
|
||||
first_sample_tx_.ensure_2d(tex_format, extent);
|
||||
first_sample_ref_ = first_sample_tx_;
|
||||
}
|
||||
else {
|
||||
/* Reuse the data_tx since there is no need to blend. */
|
||||
first_sample_tx_.free();
|
||||
first_sample_ref_ = data_tx_[0];
|
||||
}
|
||||
}
|
||||
|
||||
void Film::accumulate(GPUTexture *input, const DRWView *view)
|
||||
{
|
||||
input_tx_ = input;
|
||||
|
||||
DRW_view_set_active(view);
|
||||
|
||||
GPU_framebuffer_bind(accumulation_fb_[1]);
|
||||
DRW_draw_pass(accumulate_ps_);
|
||||
|
||||
Framebuffer::swap(accumulation_fb_[0], accumulation_fb_[1]);
|
||||
Texture::swap(data_tx_[0], data_tx_[1]);
|
||||
Texture::swap(weight_tx_[0], weight_tx_[1]);
|
||||
|
||||
/* Use history after first sample. */
|
||||
if (data_.use_history == 0) {
|
||||
data_.use_history = 1;
|
||||
data_.push_update();
|
||||
}
|
||||
}
|
||||
|
||||
void Film::resolve_viewport(GPUFrameBuffer *target)
|
||||
{
|
||||
int viewport[4];
|
||||
|
||||
GPU_framebuffer_bind(target);
|
||||
GPU_framebuffer_viewport_get(target, viewport);
|
||||
|
||||
const bool use_render_border = (data_.offset[0] > 0) || (data_.offset[1] > 0) ||
|
||||
(data_.extent[0] < viewport[2]) ||
|
||||
(data_.extent[1] < viewport[3]);
|
||||
if (use_render_border) {
|
||||
if (has_changed_) {
|
||||
/* Film is cropped and does not fill the view completely. Clear the background. */
|
||||
if (data_.data_type == FILM_DATA_DEPTH) {
|
||||
GPU_framebuffer_clear_depth(target, 1.0f);
|
||||
}
|
||||
else {
|
||||
float color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
GPU_framebuffer_clear_color(target, color);
|
||||
}
|
||||
}
|
||||
GPU_framebuffer_viewport_set(target, UNPACK2(data_.offset), UNPACK2(data_.extent));
|
||||
}
|
||||
|
||||
DRW_draw_pass(resolve_ps_);
|
||||
|
||||
/* Minus one because we already incremented it in step() which is the first
|
||||
* thing to happen in the sample loop. */
|
||||
const bool is_first_sample = (inst_.sampling.sample_get() - 1 == 1);
|
||||
const bool is_only_one_sample = is_first_sample && inst_.sampling.finished();
|
||||
if (is_first_sample && !is_only_one_sample && do_smooth_viewport_smooth_transition()) {
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
GPU_texture_copy(first_sample_tx_, dtxl->color);
|
||||
}
|
||||
|
||||
if (use_render_border) {
|
||||
GPU_framebuffer_viewport_reset(target);
|
||||
}
|
||||
}
|
||||
|
||||
void Film::read_result(float *data)
|
||||
{
|
||||
/* Resolve onto the next data texture. */
|
||||
read_result_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(data_tx_[1]));
|
||||
GPU_framebuffer_bind(read_result_fb_);
|
||||
DRW_draw_pass(resolve_ps_);
|
||||
|
||||
eGPUTextureFormat format = to_gpu_texture_format(data_.data_type);
|
||||
int channel_count = GPU_texture_component_len(format);
|
||||
GPU_framebuffer_read_color(
|
||||
read_result_fb_, 0, 0, UNPACK2(data_.extent), channel_count, 0, GPU_DATA_FLOAT, data);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
113
source/blender/draw/engines/eevee/eevee_film.hh
Normal file
113
source/blender/draw/engines/eevee/eevee_film.hh
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* A film is a fullscreen buffer (usually at output extent)
|
||||
* that will be able to accumulate sample in any distorted camera_type
|
||||
* using a pixel filter.
|
||||
*
|
||||
* Input needs to be jittered so that the filter converges to the right result.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "eevee_camera.hh"
|
||||
#include "eevee_sampling.hh"
|
||||
#include "eevee_shader.hh"
|
||||
#include "eevee_wrapper.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Film
|
||||
* \{ */
|
||||
|
||||
class Film {
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
/** Owned resources. */
|
||||
Framebuffer read_result_fb_;
|
||||
Framebuffer accumulation_fb_[2];
|
||||
Texture data_tx_[2];
|
||||
Texture weight_tx_[2];
|
||||
/** First sample in case we need to blend using it or just reuse it. */
|
||||
Texture first_sample_tx_ = {"first_sample_tx_"};
|
||||
|
||||
/** Reference to first_sample_tx_ or data_tx_ depending on the context. */
|
||||
GPUTexture *first_sample_ref_;
|
||||
|
||||
// DRWPass *clear_ps_ = nullptr;
|
||||
DRWPass *accumulate_ps_ = nullptr;
|
||||
DRWPass *resolve_ps_ = nullptr;
|
||||
|
||||
/** Shader parameter, not allocated. */
|
||||
GPUTexture *input_tx_;
|
||||
/** ViewProjection matrix used to render the input. */
|
||||
// float src_persmat_[4][4];
|
||||
/** ViewProjection matrix Inverse used to render the input. */
|
||||
// float src_persinv_[4][4];
|
||||
|
||||
draw::UniformBuffer<FilmData> data_;
|
||||
|
||||
/** True if offset or size changed. */
|
||||
bool has_changed_ = true;
|
||||
|
||||
/** Debug static name. */
|
||||
StringRefNull name_;
|
||||
|
||||
public:
|
||||
/* NOTE: name needs to be static. */
|
||||
Film(Instance &inst, eFilmDataType data_type, const char *name)
|
||||
: inst_(inst), data_tx_{{name}, {name}}, weight_tx_{{name}, {name}}, name_(name)
|
||||
{
|
||||
data_.extent[0] = data_.extent[1] = -1;
|
||||
data_.data_type = data_type;
|
||||
data_.use_history = 0;
|
||||
}
|
||||
|
||||
~Film(){};
|
||||
|
||||
void init(const int2 &full_extent, const rcti *output_rect);
|
||||
|
||||
void sync(void);
|
||||
void end_sync(void);
|
||||
|
||||
void accumulate(GPUTexture *input, const DRWView *view);
|
||||
|
||||
void resolve_viewport(GPUFrameBuffer *target);
|
||||
|
||||
void read_result(float *data);
|
||||
|
||||
private:
|
||||
bool do_smooth_viewport_smooth_transition(void)
|
||||
{
|
||||
return ELEM(data_.data_type, FILM_DATA_COLOR, FILM_DATA_COLOR_LOG) &&
|
||||
!DRW_state_is_image_render();
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
258
source/blender/draw/engines/eevee/eevee_gbuffer.hh
Normal file
258
source/blender/draw/engines/eevee/eevee_gbuffer.hh
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Gbuffer layout used for deferred shading pipeline.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "eevee_wrapper.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Gbuffer
|
||||
*
|
||||
* Fullscreen textures containing geometric, surface and volume data.
|
||||
* Used by deferred shading layers. Only one gbuffer is allocated per view
|
||||
* and is reused for each deferred layer. This is why there can only be temporary
|
||||
* texture inside it.
|
||||
* \{ */
|
||||
|
||||
/** NOTE: Theses are used as stencil bits. So we are limited to 8bits. */
|
||||
enum eClosureBits {
|
||||
CLOSURE_DIFFUSE = 1 << 0,
|
||||
CLOSURE_SSS = 1 << 1,
|
||||
CLOSURE_REFLECTION = 1 << 2,
|
||||
CLOSURE_REFRACTION = 1 << 3,
|
||||
CLOSURE_VOLUME = 1 << 4,
|
||||
CLOSURE_EMISSION = 1 << 5,
|
||||
CLOSURE_TRANSPARENCY = 1 << 6,
|
||||
};
|
||||
|
||||
struct GBuffer {
|
||||
TextureFromPool transmit_color_tx = {"GbufferTransmitColor"};
|
||||
TextureFromPool transmit_normal_tx = {"GbufferTransmitNormal"};
|
||||
TextureFromPool transmit_data_tx = {"GbufferTransmitData"};
|
||||
TextureFromPool reflect_color_tx = {"GbufferReflectionColor"};
|
||||
TextureFromPool reflect_normal_tx = {"GbufferReflectionNormal"};
|
||||
TextureFromPool volume_tx = {"GbufferVolume"};
|
||||
TextureFromPool emission_tx = {"GbufferEmission"};
|
||||
TextureFromPool transparency_tx = {"GbufferTransparency"};
|
||||
|
||||
Framebuffer gbuffer_fb = {"Gbuffer"};
|
||||
Framebuffer volume_fb = {"VolumeHeterogeneous"};
|
||||
|
||||
TextureFromPool holdout_tx = {"HoldoutRadiance"};
|
||||
TextureFromPool diffuse_tx = {"DiffuseRadiance"};
|
||||
|
||||
Framebuffer radiance_fb = {"Radiance"};
|
||||
Framebuffer radiance_clear_fb = {"RadianceClear"};
|
||||
|
||||
Framebuffer holdout_fb = {"Holdout"};
|
||||
|
||||
TextureFromPool depth_behind_tx = {"DepthBehind"};
|
||||
|
||||
Framebuffer depth_behind_fb = {"DepthCopy"};
|
||||
|
||||
/** Raytracing. */
|
||||
TextureFromPool ray_data_tx = {"RayData"};
|
||||
TextureFromPool ray_radiance_tx = {"RayRadiance"};
|
||||
TextureFromPool ray_variance_tx = {"RayVariance"};
|
||||
Framebuffer ray_data_fb = {"RayData"};
|
||||
Framebuffer ray_denoise_fb = {"RayDenoise"};
|
||||
|
||||
/* Owner of this GBuffer. Used to query temp textures. */
|
||||
void *owner;
|
||||
|
||||
/* Pointer to the view's buffers. */
|
||||
GPUTexture *depth_tx = nullptr;
|
||||
GPUTexture *combined_tx = nullptr;
|
||||
int layer = -1;
|
||||
|
||||
void sync(GPUTexture *depth_tx_, GPUTexture *combined_tx_, void *owner_, int layer_ = -1)
|
||||
{
|
||||
owner = owner_;
|
||||
depth_tx = depth_tx_;
|
||||
combined_tx = combined_tx_;
|
||||
layer = layer_;
|
||||
transmit_color_tx.sync();
|
||||
transmit_normal_tx.sync();
|
||||
transmit_data_tx.sync();
|
||||
reflect_color_tx.sync();
|
||||
reflect_normal_tx.sync();
|
||||
volume_tx.sync();
|
||||
emission_tx.sync();
|
||||
transparency_tx.sync();
|
||||
holdout_tx.sync();
|
||||
diffuse_tx.sync();
|
||||
depth_behind_tx.sync();
|
||||
ray_data_tx.sync();
|
||||
ray_radiance_tx.sync();
|
||||
ray_variance_tx.sync();
|
||||
}
|
||||
|
||||
void prepare(eClosureBits closures_used)
|
||||
{
|
||||
int2 extent = {GPU_texture_width(depth_tx), GPU_texture_height(depth_tx)};
|
||||
|
||||
/* TODO Reuse for different config. */
|
||||
if (closures_used & (CLOSURE_DIFFUSE | CLOSURE_SSS | CLOSURE_REFRACTION)) {
|
||||
transmit_color_tx.acquire(extent, GPU_R11F_G11F_B10F, owner);
|
||||
}
|
||||
if (closures_used & (CLOSURE_SSS | CLOSURE_REFRACTION)) {
|
||||
transmit_normal_tx.acquire(extent, GPU_RGBA16F, owner);
|
||||
transmit_data_tx.acquire(extent, GPU_R11F_G11F_B10F, owner);
|
||||
}
|
||||
else if (closures_used & CLOSURE_DIFFUSE) {
|
||||
transmit_normal_tx.acquire(extent, GPU_RG16F, owner);
|
||||
}
|
||||
if (closures_used & CLOSURE_SSS) {
|
||||
diffuse_tx.acquire(extent, GPU_RGBA16F, owner);
|
||||
}
|
||||
|
||||
if (closures_used & CLOSURE_REFLECTION) {
|
||||
reflect_color_tx.acquire(extent, GPU_R11F_G11F_B10F, owner);
|
||||
reflect_normal_tx.acquire(extent, GPU_RGBA16F, owner);
|
||||
}
|
||||
|
||||
if (closures_used & CLOSURE_VOLUME) {
|
||||
/* TODO(fclem): This is killing performance.
|
||||
* Idea: use interleaved data pattern to fill only a 32bpp buffer. */
|
||||
volume_tx.acquire(extent, GPU_RGBA32UI, owner);
|
||||
}
|
||||
|
||||
if (closures_used & CLOSURE_EMISSION) {
|
||||
emission_tx.acquire(extent, GPU_R11F_G11F_B10F, owner);
|
||||
}
|
||||
|
||||
if (closures_used & CLOSURE_TRANSPARENCY) {
|
||||
/* TODO(fclem): Speedup by using Dithered holdout and GPU_RGB10_A2. */
|
||||
transparency_tx.acquire(extent, GPU_RGBA16, owner);
|
||||
}
|
||||
|
||||
if (closures_used & (CLOSURE_DIFFUSE | CLOSURE_REFLECTION | CLOSURE_REFRACTION)) {
|
||||
ray_data_tx.acquire(extent, GPU_RGBA16F, owner);
|
||||
ray_radiance_tx.acquire(extent, GPU_RGBA16F, owner);
|
||||
ray_variance_tx.acquire(extent, GPU_R8, owner);
|
||||
}
|
||||
|
||||
holdout_tx.acquire(extent, GPU_R11F_G11F_B10F, owner);
|
||||
depth_behind_tx.acquire(extent, GPU_DEPTH24_STENCIL8, owner);
|
||||
|
||||
/* Layer attachement also works with cubemap. */
|
||||
gbuffer_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer),
|
||||
GPU_ATTACHMENT_TEXTURE(transmit_color_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(transmit_normal_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(transmit_data_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(reflect_color_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(reflect_normal_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(volume_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(emission_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(transparency_tx));
|
||||
}
|
||||
|
||||
void bind(void)
|
||||
{
|
||||
GPU_framebuffer_bind(gbuffer_fb);
|
||||
GPU_framebuffer_clear_stencil(gbuffer_fb, 0x0);
|
||||
}
|
||||
|
||||
void bind_radiance(void)
|
||||
{
|
||||
/* Layer attachement also works with cubemap. */
|
||||
radiance_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer),
|
||||
GPU_ATTACHMENT_TEXTURE(combined_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(diffuse_tx));
|
||||
GPU_framebuffer_bind(radiance_fb);
|
||||
}
|
||||
|
||||
void bind_volume(void)
|
||||
{
|
||||
/* Layer attachement also works with cubemap. */
|
||||
volume_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer),
|
||||
GPU_ATTACHMENT_TEXTURE(volume_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(transparency_tx));
|
||||
GPU_framebuffer_bind(volume_fb);
|
||||
}
|
||||
|
||||
void bind_tracing(void)
|
||||
{
|
||||
/* Layer attachement also works with cubemap. */
|
||||
/* Attach depth_stencil buffer to only trace the surfaces that need it. */
|
||||
ray_data_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer),
|
||||
GPU_ATTACHMENT_TEXTURE(ray_data_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(ray_radiance_tx));
|
||||
GPU_framebuffer_bind(ray_data_fb);
|
||||
|
||||
float color[4] = {0.0f};
|
||||
GPU_framebuffer_clear_color(ray_data_fb, color);
|
||||
}
|
||||
|
||||
void bind_holdout(void)
|
||||
{
|
||||
holdout_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(holdout_tx));
|
||||
GPU_framebuffer_bind(holdout_fb);
|
||||
}
|
||||
|
||||
void copy_depth_behind(void)
|
||||
{
|
||||
depth_behind_fb.ensure(GPU_ATTACHMENT_TEXTURE(depth_behind_tx));
|
||||
GPU_framebuffer_bind(depth_behind_fb);
|
||||
|
||||
GPU_framebuffer_blit(gbuffer_fb, 0, depth_behind_fb, 0, GPU_DEPTH_BIT);
|
||||
}
|
||||
|
||||
void clear_radiance(void)
|
||||
{
|
||||
radiance_clear_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(diffuse_tx));
|
||||
GPU_framebuffer_bind(radiance_clear_fb);
|
||||
|
||||
float color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
GPU_framebuffer_clear_color(radiance_clear_fb, color);
|
||||
}
|
||||
|
||||
void render_end(void)
|
||||
{
|
||||
transmit_color_tx.release();
|
||||
transmit_normal_tx.release();
|
||||
transmit_data_tx.release();
|
||||
reflect_color_tx.release();
|
||||
reflect_normal_tx.release();
|
||||
volume_tx.release();
|
||||
emission_tx.release();
|
||||
transparency_tx.release();
|
||||
holdout_tx.release();
|
||||
diffuse_tx.release();
|
||||
depth_behind_tx.release();
|
||||
ray_data_tx.release();
|
||||
ray_radiance_tx.release();
|
||||
ray_variance_tx.release();
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
155
source/blender/draw/engines/eevee/eevee_gpencil.cc
Normal file
155
source/blender/draw/engines/eevee/eevee_gpencil.cc
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_object.h"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
#define DO_BATCHING true
|
||||
|
||||
struct gpIterData {
|
||||
Instance &inst;
|
||||
Object *ob;
|
||||
MaterialArray &material_array;
|
||||
int cfra;
|
||||
|
||||
/* Drawcall batching. */
|
||||
GPUBatch *geom = nullptr;
|
||||
Material *material = nullptr;
|
||||
int vfirst = 0;
|
||||
int vcount = 0;
|
||||
bool instancing = false;
|
||||
|
||||
gpIterData(Instance &inst_, Object *ob_)
|
||||
: inst(inst_), ob(ob_), material_array(inst_.materials.material_array_get(ob_))
|
||||
{
|
||||
cfra = DEG_get_ctime(inst.depsgraph);
|
||||
};
|
||||
};
|
||||
|
||||
static void gpencil_drawcall_flush(gpIterData &iter)
|
||||
{
|
||||
if (iter.geom != NULL) {
|
||||
shgroup_geometry_call(iter.material->shading.shgrp,
|
||||
iter.ob,
|
||||
iter.geom,
|
||||
iter.vfirst,
|
||||
iter.vcount,
|
||||
iter.instancing);
|
||||
shgroup_geometry_call(iter.material->prepass.shgrp,
|
||||
iter.ob,
|
||||
iter.geom,
|
||||
iter.vfirst,
|
||||
iter.vcount,
|
||||
iter.instancing);
|
||||
shgroup_geometry_call(iter.material->shadow.shgrp,
|
||||
iter.ob,
|
||||
iter.geom,
|
||||
iter.vfirst,
|
||||
iter.vcount,
|
||||
iter.instancing);
|
||||
}
|
||||
iter.geom = NULL;
|
||||
iter.vfirst = -1;
|
||||
iter.vcount = 0;
|
||||
}
|
||||
|
||||
/* Group draw-calls that are consecutive and with the same type. Reduces GPU driver overhead. */
|
||||
static void gpencil_drawcall_add(gpIterData &iter,
|
||||
GPUBatch *geom,
|
||||
Material *material,
|
||||
int v_first,
|
||||
int v_count,
|
||||
bool instancing)
|
||||
{
|
||||
int last = iter.vfirst + iter.vcount;
|
||||
/* Interrupt draw-call grouping if the sequence is not consecutive. */
|
||||
if (!DO_BATCHING || (geom != iter.geom) || (material != iter.material) || (v_first - last > 3)) {
|
||||
gpencil_drawcall_flush(iter);
|
||||
}
|
||||
iter.geom = geom;
|
||||
iter.material = material;
|
||||
iter.instancing = instancing;
|
||||
if (iter.vfirst == -1) {
|
||||
iter.vfirst = v_first;
|
||||
}
|
||||
iter.vcount = v_first + v_count - iter.vfirst;
|
||||
}
|
||||
|
||||
static void gpencil_stroke_sync(bGPDlayer *UNUSED(gpl),
|
||||
bGPDframe *UNUSED(gpf),
|
||||
bGPDstroke *gps,
|
||||
void *thunk)
|
||||
{
|
||||
gpIterData &iter = *(gpIterData *)thunk;
|
||||
|
||||
Material *material = iter.material_array.materials[gps->mat_nr];
|
||||
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter.ob, gps->mat_nr + 1);
|
||||
|
||||
bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
|
||||
bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0) ||
|
||||
(!DRW_state_is_image_render() && ((gps->flag & GP_STROKE_NOFILL) != 0));
|
||||
bool show_fill = (gps->tot_triangles > 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0);
|
||||
|
||||
if (hide_material) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (show_fill) {
|
||||
GPUBatch *geom = DRW_cache_gpencil_fills_get(iter.ob, iter.cfra);
|
||||
int vfirst = gps->runtime.fill_start * 3;
|
||||
int vcount = gps->tot_triangles * 3;
|
||||
gpencil_drawcall_add(iter, geom, material, vfirst, vcount, false);
|
||||
}
|
||||
|
||||
if (show_stroke) {
|
||||
GPUBatch *geom = DRW_cache_gpencil_strokes_get(iter.ob, iter.cfra);
|
||||
/* Start one vert before to have gl_InstanceID > 0 (see shader). */
|
||||
int vfirst = gps->runtime.stroke_start - 1;
|
||||
/* Include "potential" cyclic vertex and start adj vertex (see shader). */
|
||||
int vcount = gps->totpoints + 1 + 1;
|
||||
gpencil_drawcall_add(iter, geom, material, vfirst, vcount, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::gpencil_sync(Object *ob, ObjectHandle &ob_handle)
|
||||
{
|
||||
gpIterData iter(*this, ob);
|
||||
|
||||
BKE_gpencil_visible_stroke_iter((bGPdata *)ob->data, nullptr, gpencil_stroke_sync, &iter);
|
||||
|
||||
gpencil_drawcall_flush(iter);
|
||||
|
||||
/* TODO(fclem) Gpencil velocity. */
|
||||
// shading_passes.velocity.gpencil_add(ob, ob_handle);
|
||||
|
||||
bool is_caster = true; /* TODO material.shadow.shgrp. */
|
||||
bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */
|
||||
shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend);
|
||||
}
|
||||
|
||||
} // namespace blender::eevee
|
||||
74
source/blender/draw/engines/eevee/eevee_hair.cc
Normal file
74
source/blender/draw/engines/eevee/eevee_hair.cc
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_particle_types.h"
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
static void shgroup_hair_call(MaterialPass &matpass,
|
||||
Object *ob,
|
||||
ParticleSystem *part_sys = nullptr,
|
||||
ModifierData *modifier_data = nullptr)
|
||||
{
|
||||
if (matpass.shgrp == nullptr) {
|
||||
return;
|
||||
}
|
||||
DRW_shgroup_hair_create_sub(ob, part_sys, modifier_data, matpass.shgrp, matpass.gpumat);
|
||||
}
|
||||
|
||||
void Instance::hair_sync(Object *ob, ObjectHandle &ob_handle, ModifierData *modifier_data)
|
||||
{
|
||||
int mat_nr = HAIR_MATERIAL_NR;
|
||||
|
||||
ParticleSystem *part_sys = nullptr;
|
||||
if (modifier_data != nullptr) {
|
||||
part_sys = reinterpret_cast<ParticleSystemModifierData *>(modifier_data)->psys;
|
||||
if (!DRW_object_is_visible_psys_in_active_context(ob, part_sys)) {
|
||||
return;
|
||||
}
|
||||
ParticleSettings *part_settings = part_sys->part;
|
||||
const int draw_as = (part_settings->draw_as == PART_DRAW_REND) ? part_settings->ren_as :
|
||||
part_settings->draw_as;
|
||||
if (draw_as != PART_DRAW_PATH) {
|
||||
return;
|
||||
}
|
||||
mat_nr = part_settings->omat;
|
||||
}
|
||||
|
||||
Material &material = materials.material_get(ob, mat_nr - 1, MAT_GEOM_HAIR);
|
||||
|
||||
shgroup_hair_call(material.shading, ob, part_sys, modifier_data);
|
||||
shgroup_hair_call(material.prepass, ob, part_sys, modifier_data);
|
||||
shgroup_hair_call(material.shadow, ob, part_sys, modifier_data);
|
||||
/* TODO(fclem) Hair velocity. */
|
||||
// shading_passes.velocity.gpencil_add(ob, ob_handle);
|
||||
|
||||
bool is_caster = material.shadow.shgrp != nullptr;
|
||||
bool is_alpha_blend = material.is_alpha_blend_transparent;
|
||||
shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend);
|
||||
}
|
||||
|
||||
} // namespace blender::eevee
|
||||
102
source/blender/draw/engines/eevee/eevee_hizbuffer.cc
Normal file
102
source/blender/draw/engines/eevee/eevee_hizbuffer.cc
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* The Hierarchical-Z buffer is texture containing a copy of the depth buffer with mipmaps.
|
||||
* Each mip contains the maximum depth of each 4 pixels on the upper level.
|
||||
* The size of the texture is padded to avoid messing with the mipmap pixels alignments.
|
||||
*/
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
#include "eevee_hizbuffer.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Hierarchical-Z buffer
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void HiZBufferModule::sync(void)
|
||||
{
|
||||
{
|
||||
hiz_copy_ps_ = DRW_pass_create("HizCopy", DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(HIZ_COPY);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, hiz_copy_ps_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
{
|
||||
hiz_downsample_ps_ = DRW_pass_create("HizDownsample", DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(HIZ_DOWNSAMPLE);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, hiz_downsample_ps_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
|
||||
DRW_shgroup_uniform_vec2(grp, "texel_size", texel_size_, 1);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void HiZBuffer::prepare(GPUTexture *depth_src_tx)
|
||||
{
|
||||
int div = 1 << mip_count_;
|
||||
float2 extent_src(GPU_texture_width(depth_src_tx), GPU_texture_height(depth_src_tx));
|
||||
int2 extent_hiz(divide_ceil_u(extent_src.x, div) * div, divide_ceil_u(extent_src.y, div) * div);
|
||||
|
||||
inst_.hiz.data_.pixel_to_ndc = 2.0f / extent_src;
|
||||
inst_.hiz.texel_size_ = 1.0f / float2(extent_hiz);
|
||||
inst_.hiz.data_.uv_scale = extent_src / float2(extent_hiz);
|
||||
|
||||
inst_.hiz.data_.push_update();
|
||||
|
||||
/* TODO/OPTI(fclem): Share it between similar views.
|
||||
* Not possible right now because request_tmp does not support mipmaps. */
|
||||
hiz_tx_.ensure_2d(GPU_R32F, extent_hiz, nullptr, mip_count_);
|
||||
hiz_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(hiz_tx_));
|
||||
|
||||
GPU_texture_mipmap_mode(hiz_tx_, true, false);
|
||||
}
|
||||
|
||||
void HiZBuffer::recursive_downsample(void *thunk, int UNUSED(lvl))
|
||||
{
|
||||
HiZBufferModule &hiz = *reinterpret_cast<HiZBufferModule *>(thunk);
|
||||
hiz.texel_size_ *= 2.0f;
|
||||
DRW_draw_pass(hiz.hiz_downsample_ps_);
|
||||
}
|
||||
|
||||
void HiZBuffer::update(GPUTexture *depth_src_tx)
|
||||
{
|
||||
DRW_stats_group_start("Hiz");
|
||||
|
||||
inst_.hiz.texel_size_ = 1.0f / float2(GPU_texture_width(hiz_tx_), GPU_texture_height(hiz_tx_));
|
||||
|
||||
inst_.hiz.input_depth_tx_ = depth_src_tx;
|
||||
GPU_framebuffer_bind(hiz_fb_);
|
||||
DRW_draw_pass(inst_.hiz.hiz_copy_ps_);
|
||||
|
||||
inst_.hiz.input_depth_tx_ = hiz_tx_;
|
||||
GPU_framebuffer_recursive_downsample(hiz_fb_, mip_count_, &recursive_downsample, &inst_.hiz);
|
||||
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
103
source/blender/draw/engines/eevee/eevee_hizbuffer.hh
Normal file
103
source/blender/draw/engines/eevee/eevee_hizbuffer.hh
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* The Hierarchical-Z buffer is texture containing a copy of the depth buffer with mipmaps.
|
||||
* Each mip contains the maximum depth of each 4 pixels on the upper level.
|
||||
* The size of the texture is padded to avoid messing with the mipmap pixels alignments.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "eevee_shader_shared.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Hierarchical-Z buffer
|
||||
* \{ */
|
||||
|
||||
class HiZBuffer {
|
||||
private:
|
||||
Instance &inst_;
|
||||
/** Framebuffer use for recursive downsampling. */
|
||||
/* TODO(fclem) Remove this and use a compute shader instead. */
|
||||
Framebuffer hiz_fb_ = draw::Framebuffer("DepthHiz");
|
||||
/** Max mip to downsample to. We ensure the hiz has enough padding to never
|
||||
* have to compensate the mipmap alignments. */
|
||||
constexpr static int mip_count_ = 6;
|
||||
/** TODO/OPTI(fclem): Share it between similar views. */
|
||||
Texture hiz_tx_ = {"hiz_tx_"};
|
||||
|
||||
public:
|
||||
HiZBuffer(Instance &inst) : inst_(inst){};
|
||||
|
||||
void prepare(GPUTexture *depth_src);
|
||||
void update(GPUTexture *depth_src);
|
||||
|
||||
GPUTexture *texture_get(void) const
|
||||
{
|
||||
return hiz_tx_;
|
||||
}
|
||||
|
||||
private:
|
||||
static void recursive_downsample(void *thunk, int lvl);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Hierarchical-Z buffer Module
|
||||
* \{ */
|
||||
|
||||
class HiZBufferModule {
|
||||
friend HiZBuffer;
|
||||
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
HiZDataBuf data_;
|
||||
/** Copy input depth to hiz-buffer with border padding. */
|
||||
DRWPass *hiz_copy_ps_;
|
||||
/** Downsample one mipmap level. */
|
||||
DRWPass *hiz_downsample_ps_;
|
||||
/** References only. */
|
||||
GPUTexture *input_depth_tx_ = nullptr;
|
||||
/** Pixel size of the render target during hiz downsampling. */
|
||||
float2 texel_size_;
|
||||
|
||||
public:
|
||||
HiZBufferModule(Instance &inst) : inst_(inst){};
|
||||
|
||||
void sync(void);
|
||||
|
||||
const GPUUniformBuf *ubo_get(void) const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
76
source/blender/draw/engines/eevee/eevee_id_map.cc
Normal file
76
source/blender/draw/engines/eevee/eevee_id_map.cc
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Structures to identify unique data blocks. The keys are unique so we are able to
|
||||
* match ids across frame updates.
|
||||
*/
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Draw Data
|
||||
*
|
||||
* \{ */
|
||||
|
||||
static void draw_data_init_cb(struct DrawData *dd)
|
||||
{
|
||||
/* Object has just been created or was never evaluated by the engine. */
|
||||
dd->recalc = ID_RECALC_ALL;
|
||||
}
|
||||
|
||||
ObjectHandle &SyncModule::sync_object(Object *ob)
|
||||
{
|
||||
DrawEngineType *owner = (DrawEngineType *)&DRW_engine_viewport_eevee_type;
|
||||
struct DrawData *dd = DRW_drawdata_ensure(
|
||||
(ID *)ob, owner, sizeof(eevee::ObjectHandle), draw_data_init_cb, nullptr);
|
||||
ObjectHandle &eevee_dd = *reinterpret_cast<ObjectHandle *>(dd);
|
||||
|
||||
if (eevee_dd.object_key.ob == nullptr) {
|
||||
eevee_dd.object_key = ObjectKey(ob);
|
||||
}
|
||||
|
||||
const int recalc_flags = ID_RECALC_COPY_ON_WRITE | ID_RECALC_TRANSFORM | ID_RECALC_SHADING |
|
||||
ID_RECALC_GEOMETRY;
|
||||
if ((eevee_dd.recalc & recalc_flags) != 0) {
|
||||
inst_.sampling.reset();
|
||||
}
|
||||
|
||||
return eevee_dd;
|
||||
}
|
||||
|
||||
WorldHandle &SyncModule::sync_world(::World *world)
|
||||
{
|
||||
DrawEngineType *owner = (DrawEngineType *)&DRW_engine_viewport_eevee_type;
|
||||
struct DrawData *dd = DRW_drawdata_ensure(
|
||||
(ID *)world, owner, sizeof(eevee::WorldHandle), draw_data_init_cb, nullptr);
|
||||
WorldHandle &eevee_dd = *reinterpret_cast<WorldHandle *>(dd);
|
||||
|
||||
const int recalc_flags = ID_RECALC_ALL;
|
||||
if ((eevee_dd.recalc & recalc_flags) != 0) {
|
||||
inst_.sampling.reset();
|
||||
}
|
||||
return eevee_dd;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
} // namespace blender::eevee
|
||||
284
source/blender/draw/engines/eevee/eevee_id_map.hh
Normal file
284
source/blender/draw/engines/eevee/eevee_id_map.hh
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Structures to identify unique data blocks. The keys are unique so we are able to
|
||||
* match ids across frame updates.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_duplilist.h"
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_map.hh"
|
||||
#include "DNA_object_types.h"
|
||||
#include "GPU_material.h"
|
||||
|
||||
#include "eevee_engine.h"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name ObjectKey
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/** Unique key to identify each object in the hashmap. */
|
||||
struct ObjectKey {
|
||||
/** Hash value of the key. */
|
||||
uint64_t hash_value;
|
||||
/** Original Object or source object for duplis. */
|
||||
Object *ob;
|
||||
/** Original Parent object for duplis. */
|
||||
Object *parent;
|
||||
/** Dupli objects recursive unique identifier */
|
||||
int id[MAX_DUPLI_RECUR];
|
||||
/** If object uses particle system hair. */
|
||||
bool use_particle_hair;
|
||||
#ifdef DEBUG
|
||||
char name[64];
|
||||
#endif
|
||||
ObjectKey() : ob(nullptr), parent(nullptr){};
|
||||
|
||||
ObjectKey(Object *ob_, Object *parent_, int id_[MAX_DUPLI_RECUR], bool use_particle_hair_)
|
||||
: ob(ob_), parent(parent_), use_particle_hair(use_particle_hair_)
|
||||
{
|
||||
if (id_) {
|
||||
memcpy(id, id_, sizeof(id));
|
||||
}
|
||||
else {
|
||||
memset(id, 0, sizeof(id));
|
||||
}
|
||||
/* Compute hash on creation so we avoid the cost of it for every sync. */
|
||||
hash_value = BLI_ghashutil_ptrhash(ob);
|
||||
hash_value = BLI_ghashutil_combine_hash(hash_value, BLI_ghashutil_ptrhash(parent));
|
||||
for (int i = 0; i < MAX_DUPLI_RECUR; i++) {
|
||||
if (id[i] != 0) {
|
||||
hash_value = BLI_ghashutil_combine_hash(hash_value, BLI_ghashutil_inthash(id[i]));
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
STRNCPY(name, ob->id.name);
|
||||
#endif
|
||||
}
|
||||
|
||||
ObjectKey(Object *ob, DupliObject *dupli, Object *parent)
|
||||
: ObjectKey(ob, parent, dupli ? dupli->persistent_id : nullptr, false){};
|
||||
|
||||
ObjectKey(Object *ob)
|
||||
: ObjectKey(ob, DRW_object_get_dupli(ob), DRW_object_get_dupli_parent(ob)){};
|
||||
|
||||
uint64_t hash(void) const
|
||||
{
|
||||
return hash_value;
|
||||
}
|
||||
|
||||
bool operator<(const ObjectKey &k) const
|
||||
{
|
||||
if (ob != k.ob) {
|
||||
return (ob < k.ob);
|
||||
}
|
||||
if (parent != k.parent) {
|
||||
return (parent < k.parent);
|
||||
}
|
||||
if (use_particle_hair != k.use_particle_hair) {
|
||||
return (use_particle_hair < k.use_particle_hair);
|
||||
}
|
||||
return memcmp(id, k.id, sizeof(id)) < 0;
|
||||
}
|
||||
|
||||
bool operator==(const ObjectKey &k) const
|
||||
{
|
||||
if (ob != k.ob) {
|
||||
return false;
|
||||
}
|
||||
if (parent != k.parent) {
|
||||
return false;
|
||||
}
|
||||
if (use_particle_hair != k.use_particle_hair) {
|
||||
return false;
|
||||
}
|
||||
return memcmp(id, k.id, sizeof(id)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Draw Data
|
||||
*
|
||||
* \{ */
|
||||
|
||||
struct ObjectHandle : public DrawData {
|
||||
ObjectKey object_key;
|
||||
|
||||
void reset_recalc_flag(void)
|
||||
{
|
||||
if (recalc != 0) {
|
||||
recalc = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldHandle : public DrawData {
|
||||
void reset_recalc_flag(void)
|
||||
{
|
||||
if (recalc != 0) {
|
||||
recalc = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SyncModule {
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
public:
|
||||
SyncModule(Instance &inst) : inst_(inst){};
|
||||
~SyncModule(){};
|
||||
|
||||
ObjectHandle &sync_object(Object *ob);
|
||||
WorldHandle &sync_world(::World *world);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name MaterialKey
|
||||
*
|
||||
* \{ */
|
||||
|
||||
enum eMaterialPipeline {
|
||||
MAT_PIPE_DEFERRED = 0,
|
||||
MAT_PIPE_FORWARD = 1,
|
||||
MAT_PIPE_DEFERRED_PREPASS = 2,
|
||||
MAT_PIPE_FORWARD_PREPASS = 3,
|
||||
MAT_PIPE_VOLUME = 4,
|
||||
MAT_PIPE_SHADOW = 5,
|
||||
};
|
||||
|
||||
enum eMaterialGeometry {
|
||||
MAT_GEOM_MESH = 0,
|
||||
MAT_GEOM_HAIR = 1,
|
||||
MAT_GEOM_GPENCIL = 2,
|
||||
MAT_GEOM_VOLUME = 3,
|
||||
MAT_GEOM_WORLD = 4,
|
||||
MAT_GEOM_LOOKDEV = 5,
|
||||
};
|
||||
|
||||
static inline void material_type_from_shader_uuid(uint64_t shader_uuid,
|
||||
eMaterialPipeline &pipeline_type,
|
||||
eMaterialGeometry &geometry_type)
|
||||
{
|
||||
const uint64_t geometry_mask = ((1u << 3u) - 1u);
|
||||
const uint64_t pipeline_mask = ((1u << 3u) - 1u);
|
||||
geometry_type = static_cast<eMaterialGeometry>(shader_uuid & geometry_mask);
|
||||
pipeline_type = static_cast<eMaterialPipeline>((shader_uuid >> 3u) & pipeline_mask);
|
||||
}
|
||||
|
||||
static inline uint64_t shader_uuid_from_material_type(eMaterialPipeline pipeline_type,
|
||||
eMaterialGeometry geometry_type)
|
||||
{
|
||||
return geometry_type | (pipeline_type << 3);
|
||||
}
|
||||
|
||||
static inline eMaterialGeometry to_material_geometry(const Object *ob)
|
||||
{
|
||||
switch (ob->type) {
|
||||
case OB_HAIR:
|
||||
return MAT_GEOM_HAIR;
|
||||
case OB_VOLUME:
|
||||
return MAT_GEOM_VOLUME;
|
||||
case OB_GPENCIL:
|
||||
return MAT_GEOM_GPENCIL;
|
||||
default:
|
||||
return MAT_GEOM_MESH;
|
||||
}
|
||||
}
|
||||
|
||||
/** Unique key to identify each material in the hashmap. */
|
||||
struct MaterialKey {
|
||||
Material *mat;
|
||||
uint64_t options;
|
||||
|
||||
MaterialKey(::Material *mat_, eMaterialGeometry geometry, eMaterialPipeline surface_pipeline)
|
||||
: mat(mat_)
|
||||
{
|
||||
options = shader_uuid_from_material_type(surface_pipeline, geometry);
|
||||
}
|
||||
|
||||
uint64_t hash(void) const
|
||||
{
|
||||
BLI_assert(options < sizeof(*mat));
|
||||
return (uint64_t)mat + options;
|
||||
}
|
||||
|
||||
bool operator<(const MaterialKey &k) const
|
||||
{
|
||||
return (mat < k.mat) || (options < k.options);
|
||||
}
|
||||
|
||||
bool operator==(const MaterialKey &k) const
|
||||
{
|
||||
return (mat == k.mat) && (options == k.options);
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name ShaderKey
|
||||
*
|
||||
* \{ */
|
||||
|
||||
struct ShaderKey {
|
||||
GPUShader *shader;
|
||||
uint64_t options;
|
||||
|
||||
ShaderKey(GPUMaterial *gpumat, eMaterialGeometry geometry, eMaterialPipeline pipeline)
|
||||
{
|
||||
shader = GPU_material_get_shader(gpumat);
|
||||
options = shader_uuid_from_material_type(pipeline, geometry);
|
||||
}
|
||||
|
||||
uint64_t hash(void) const
|
||||
{
|
||||
return (uint64_t)shader + options;
|
||||
}
|
||||
|
||||
bool operator<(const ShaderKey &k) const
|
||||
{
|
||||
return (shader < k.shader) || (options < k.options);
|
||||
}
|
||||
|
||||
bool operator==(const ShaderKey &k) const
|
||||
{
|
||||
return (shader == k.shader) && (options == k.options);
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
316
source/blender/draw/engines/eevee/eevee_instance.cc
Normal file
316
source/blender/draw/engines/eevee/eevee_instance.cc
Normal file
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* An instance contains all structures needed to do a complete render.
|
||||
*/
|
||||
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BLI_rect.h"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_lightprobe_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Init
|
||||
*
|
||||
* Init funcions need to be called once at the start of a frame.
|
||||
* Active camera, render extent and enabled render passes are immutable until next init.
|
||||
* This takes care of resizing output buffers and view in case a parameter changed.
|
||||
* IMPORTANT: xxx.init() functions are NOT meant to acquire and allocate DRW resources.
|
||||
* Any attempt to do so will likely produce use after free situations.
|
||||
* \{ */
|
||||
|
||||
void Instance::init(const int2 &output_res,
|
||||
const rcti *output_rect,
|
||||
RenderEngine *render_,
|
||||
Depsgraph *depsgraph_,
|
||||
const struct LightProbe *light_probe_,
|
||||
Object *camera_object_,
|
||||
const RenderLayer *render_layer_,
|
||||
const DRWView *drw_view_,
|
||||
const View3D *v3d_,
|
||||
const RegionView3D *rv3d_)
|
||||
{
|
||||
render = render_;
|
||||
depsgraph = depsgraph_;
|
||||
render_layer = render_layer_;
|
||||
camera_orig_object = camera_object_;
|
||||
drw_view = drw_view_;
|
||||
v3d = v3d_;
|
||||
rv3d = rv3d_;
|
||||
baking_probe = light_probe_;
|
||||
|
||||
debug_mode = (eDebugMode)G.debug_value;
|
||||
|
||||
update_eval_members();
|
||||
|
||||
rcti render_border = output_crop(output_res, output_rect);
|
||||
|
||||
/* Needs to be first. */
|
||||
sampling.init(scene);
|
||||
|
||||
camera.init();
|
||||
motion_blur.init();
|
||||
render_passes.init(output_res, &render_border);
|
||||
main_view.init(output_res);
|
||||
velocity.init();
|
||||
shadows.init();
|
||||
lightprobes.init();
|
||||
lookdev.init(output_res, &render_border);
|
||||
}
|
||||
|
||||
rcti Instance::output_crop(const int res[2], const rcti *crop)
|
||||
{
|
||||
rcti rect;
|
||||
BLI_rcti_init(&rect, 0, res[0], 0, res[1]);
|
||||
/* Clip the render border to region bounds. */
|
||||
BLI_rcti_isect(crop, &rect, &rect);
|
||||
if (BLI_rcti_is_empty(&rect)) {
|
||||
BLI_rcti_init(&rect, 0, res[0], 0, res[1]);
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
void Instance::set_time(float time)
|
||||
{
|
||||
BLI_assert(render);
|
||||
DRW_render_set_time(render, depsgraph, floorf(time), fractf(time));
|
||||
update_eval_members();
|
||||
}
|
||||
|
||||
void Instance::update_eval_members(void)
|
||||
{
|
||||
scene = DEG_get_evaluated_scene(depsgraph);
|
||||
view_layer = DEG_get_evaluated_view_layer(depsgraph);
|
||||
camera_eval_object = (camera_orig_object) ?
|
||||
DEG_get_evaluated_object(depsgraph, camera_orig_object) :
|
||||
nullptr;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Sync
|
||||
*
|
||||
* Sync will gather data from the scene that can change over a time step (i.e: motion steps).
|
||||
* IMPORTANT: xxx.sync() functions area responsible for creating DRW resources (i.e: DRWView) as
|
||||
* well as querying temp texture pool. All DRWPasses should be ready by the end end_sync().
|
||||
* \{ */
|
||||
|
||||
void Instance::begin_sync()
|
||||
{
|
||||
camera.sync();
|
||||
render_passes.sync();
|
||||
shading_passes.sync();
|
||||
main_view.sync();
|
||||
world.sync();
|
||||
raytracing.sync();
|
||||
hiz.sync();
|
||||
|
||||
lookdev.sync_background();
|
||||
lookdev.sync_overlay();
|
||||
|
||||
materials.begin_sync();
|
||||
velocity.begin_sync();
|
||||
lights.begin_sync();
|
||||
shadows.begin_sync();
|
||||
lightprobes.begin_sync();
|
||||
}
|
||||
|
||||
void Instance::object_sync(Object *ob)
|
||||
{
|
||||
const bool is_renderable_type = ELEM(
|
||||
ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_LAMP, OB_VOLUME, OB_GPENCIL);
|
||||
const int ob_visibility = DRW_object_visibility_in_active_context(ob);
|
||||
const bool partsys_is_visible = (ob_visibility & OB_VISIBLE_PARTICLES) != 0 &&
|
||||
(ob->type == OB_MESH);
|
||||
const bool object_is_visible = DRW_object_is_renderable(ob) &&
|
||||
(ob_visibility & OB_VISIBLE_SELF) != 0;
|
||||
|
||||
if (!is_renderable_type || (!partsys_is_visible && !object_is_visible)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectHandle &ob_handle = sync.sync_object(ob);
|
||||
|
||||
if (partsys_is_visible && ob != DRW_context_state_get()->object_edit) {
|
||||
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
||||
if (md->type == eModifierType_ParticleSystem) {
|
||||
hair_sync(ob, ob_handle, md);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (object_is_visible) {
|
||||
switch (ob->type) {
|
||||
case OB_LAMP:
|
||||
lights.sync_light(ob, ob_handle);
|
||||
break;
|
||||
case OB_MESH:
|
||||
case OB_CURVE:
|
||||
case OB_SURF:
|
||||
case OB_FONT:
|
||||
case OB_MBALL: {
|
||||
mesh_sync(ob, ob_handle);
|
||||
break;
|
||||
}
|
||||
case OB_VOLUME:
|
||||
shading_passes.deferred.volume_add(ob);
|
||||
break;
|
||||
case OB_HAIR:
|
||||
hair_sync(ob, ob_handle);
|
||||
break;
|
||||
case OB_GPENCIL:
|
||||
gpencil_sync(ob, ob_handle);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ob_handle.reset_recalc_flag();
|
||||
}
|
||||
|
||||
/* Wrapper to use with DRW_render_object_iter. */
|
||||
void Instance::object_sync_render(void *instance_,
|
||||
Object *ob,
|
||||
RenderEngine *engine,
|
||||
Depsgraph *depsgraph)
|
||||
{
|
||||
UNUSED_VARS(engine, depsgraph);
|
||||
|
||||
Instance &inst = *reinterpret_cast<Instance *>(instance_);
|
||||
|
||||
if (inst.baking_probe != nullptr) {
|
||||
if (inst.baking_probe->visibility_grp != nullptr) {
|
||||
bool test = BKE_collection_has_object_recursive(inst.baking_probe->visibility_grp, ob);
|
||||
test = (inst.baking_probe->flag & LIGHTPROBE_FLAG_INVERT_GROUP) ? !test : test;
|
||||
if (!test) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Exclude planar lightprobes. */
|
||||
if (ob->type == OB_LIGHTPROBE) {
|
||||
LightProbe *prb = (LightProbe *)ob->data;
|
||||
if (prb->type == LIGHTPROBE_TYPE_PLANAR) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inst.object_sync(ob);
|
||||
}
|
||||
|
||||
void Instance::end_sync(void)
|
||||
{
|
||||
velocity.end_sync();
|
||||
lights.end_sync();
|
||||
sampling.end_sync();
|
||||
render_passes.end_sync();
|
||||
lightprobes.end_sync();
|
||||
subsurface.end_sync();
|
||||
}
|
||||
|
||||
void Instance::render_sync(void)
|
||||
{
|
||||
DRW_cache_restart();
|
||||
|
||||
this->begin_sync();
|
||||
DRW_render_object_iter(this, render, depsgraph, object_sync_render);
|
||||
this->end_sync();
|
||||
|
||||
DRW_render_instance_buffer_finish();
|
||||
/* Also we weed to have a correct fbo bound for DRW_hair_update */
|
||||
// GPU_framebuffer_bind();
|
||||
// DRW_hair_update();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Rendering
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Conceptually renders one sample per pixel.
|
||||
* Everything based on random sampling should be done here (i.e: DRWViews jitter)
|
||||
**/
|
||||
void Instance::render_sample(void)
|
||||
{
|
||||
if (sampling.finished()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Motion blur may need to do re-sync after a certain number of sample. */
|
||||
if (sampling.do_render_sync()) {
|
||||
this->render_sync();
|
||||
}
|
||||
|
||||
sampling.step();
|
||||
|
||||
/* TODO update shadowmaps, planars, etc... */
|
||||
// shadow_view_.render();
|
||||
|
||||
main_view.render();
|
||||
|
||||
motion_blur.step();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Interface
|
||||
* \{ */
|
||||
|
||||
void Instance::render_frame(RenderLayer *render_layer, const char *view_name)
|
||||
{
|
||||
while (!sampling.finished()) {
|
||||
this->render_sample();
|
||||
/* TODO(fclem) print progression. */
|
||||
}
|
||||
|
||||
render_passes.read_result(render_layer, view_name);
|
||||
}
|
||||
|
||||
void Instance::draw_viewport(DefaultFramebufferList *dfbl)
|
||||
{
|
||||
this->render_sample();
|
||||
|
||||
render_passes.resolve_viewport(dfbl);
|
||||
|
||||
if (!sampling.finished_viewport()) {
|
||||
DRW_viewport_request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
bool Instance::finished(void) const
|
||||
{
|
||||
return sampling.finished();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
190
source/blender/draw/engines/eevee/eevee_instance.hh
Normal file
190
source/blender/draw/engines/eevee/eevee_instance.hh
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* An instance contains all structures needed to do a complete render.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_object.h"
|
||||
#include "DEG_depsgraph.h"
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "eevee_film.hh"
|
||||
#include "eevee_hizbuffer.hh"
|
||||
#include "eevee_id_map.hh"
|
||||
#include "eevee_light.hh"
|
||||
#include "eevee_lightprobe.hh"
|
||||
#include "eevee_lookdev.hh"
|
||||
#include "eevee_material.hh"
|
||||
#include "eevee_motion_blur.hh"
|
||||
#include "eevee_raytracing.hh"
|
||||
#include "eevee_renderpasses.hh"
|
||||
#include "eevee_sampling.hh"
|
||||
#include "eevee_shader.hh"
|
||||
#include "eevee_shading.hh"
|
||||
#include "eevee_shadow.hh"
|
||||
#include "eevee_subsurface.hh"
|
||||
#include "eevee_view.hh"
|
||||
#include "eevee_world.hh"
|
||||
|
||||
#include "eevee_engine.h"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/**
|
||||
* \class Instance
|
||||
* \brief A running instance of the engine.
|
||||
*/
|
||||
class Instance {
|
||||
friend MotionBlur;
|
||||
friend MotionBlurModule;
|
||||
friend VelocityModule;
|
||||
|
||||
public:
|
||||
ShaderModule &shaders;
|
||||
Sampling sampling;
|
||||
RenderPasses render_passes;
|
||||
ShadingPasses shading_passes;
|
||||
MainView main_view;
|
||||
Camera camera;
|
||||
World world;
|
||||
VelocityModule velocity;
|
||||
MotionBlurModule motion_blur;
|
||||
LightModule lights;
|
||||
LightProbeModule lightprobes;
|
||||
RaytracingModule raytracing;
|
||||
HiZBufferModule hiz;
|
||||
/* TODO(fclem) Move it to scene layer data. */
|
||||
ShadowModule shadows;
|
||||
SubsurfaceModule subsurface;
|
||||
SyncModule sync;
|
||||
MaterialModule materials;
|
||||
/** Lookdev own lightweight instance. May not be allocated. */
|
||||
LookDev lookdev;
|
||||
|
||||
/** Input data. */
|
||||
Depsgraph *depsgraph;
|
||||
/** Evaluated IDs. */
|
||||
Scene *scene;
|
||||
ViewLayer *view_layer;
|
||||
Object *camera_eval_object;
|
||||
Object *camera_orig_object;
|
||||
/** Only available when rendering for final render. */
|
||||
const RenderLayer *render_layer;
|
||||
RenderEngine *render;
|
||||
/** Only available when rendering for viewport. */
|
||||
const DRWView *drw_view;
|
||||
const View3D *v3d;
|
||||
const RegionView3D *rv3d;
|
||||
|
||||
/** Can be null. Used to exclude objects during baking. */
|
||||
const struct LightProbe *baking_probe = nullptr;
|
||||
|
||||
eDebugMode debug_mode = SHADOW_DEBUG_NONE;
|
||||
|
||||
/* Info string displayed at the top of the render / viewport. */
|
||||
char info[64];
|
||||
|
||||
public:
|
||||
Instance()
|
||||
: shaders(*ShaderModule::module_get()),
|
||||
render_passes(*this),
|
||||
shading_passes(*this),
|
||||
main_view(*this),
|
||||
camera(*this),
|
||||
world(*this),
|
||||
velocity(*this),
|
||||
motion_blur(*this),
|
||||
lights(*this),
|
||||
lightprobes(*this),
|
||||
raytracing(*this),
|
||||
hiz(*this),
|
||||
shadows(*this),
|
||||
subsurface(*this),
|
||||
sync(*this),
|
||||
materials(*this),
|
||||
lookdev(*this){};
|
||||
~Instance(){};
|
||||
|
||||
void init(const int2 &output_res,
|
||||
const rcti *output_rect,
|
||||
RenderEngine *render,
|
||||
Depsgraph *depsgraph,
|
||||
const struct LightProbe *light_probe_ = nullptr,
|
||||
Object *camera_object = nullptr,
|
||||
const RenderLayer *render_layer = nullptr,
|
||||
const DRWView *drw_view = nullptr,
|
||||
const View3D *v3d = nullptr,
|
||||
const RegionView3D *rv3d = nullptr);
|
||||
|
||||
void begin_sync(void);
|
||||
void object_sync(Object *ob);
|
||||
void end_sync(void);
|
||||
|
||||
void render_sync(void);
|
||||
void render_frame(RenderLayer *render_layer, const char *view_name);
|
||||
|
||||
void draw_viewport(DefaultFramebufferList *dfbl);
|
||||
|
||||
bool finished(void) const;
|
||||
|
||||
bool is_viewport(void)
|
||||
{
|
||||
return !DRW_state_is_scene_render();
|
||||
}
|
||||
|
||||
bool use_scene_light(void) const
|
||||
{
|
||||
return (!v3d) ||
|
||||
((v3d->shading.type == OB_MATERIAL) &&
|
||||
(v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS)) ||
|
||||
((v3d->shading.type == OB_RENDER) &&
|
||||
(v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER));
|
||||
}
|
||||
|
||||
/* Do we light the scene using the HDRI setup in the viewport settings. */
|
||||
bool use_studio_light(void) const
|
||||
{
|
||||
return (v3d) && (((v3d->shading.type == OB_MATERIAL) &&
|
||||
((v3d->shading.flag & V3D_SHADING_SCENE_WORLD) == 0)) ||
|
||||
((v3d->shading.type == OB_RENDER) &&
|
||||
((v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER) == 0)));
|
||||
}
|
||||
|
||||
private:
|
||||
void render_sample(void);
|
||||
static void object_sync_render(void *instance_,
|
||||
Object *ob,
|
||||
RenderEngine *engine,
|
||||
Depsgraph *depsgraph);
|
||||
|
||||
void mesh_sync(Object *ob, ObjectHandle &ob_handle);
|
||||
void gpencil_sync(Object *ob, ObjectHandle &ob_handle);
|
||||
void hair_sync(Object *ob, ObjectHandle &ob_handle, ModifierData *modifier_data = nullptr);
|
||||
|
||||
rcti output_crop(const int output_res[2], const rcti *crop);
|
||||
|
||||
void set_time(float time);
|
||||
void update_eval_members(void);
|
||||
};
|
||||
|
||||
} // namespace blender::eevee
|
||||
483
source/blender/draw/engines/eevee/eevee_light.cc
Normal file
483
source/blender/draw/engines/eevee/eevee_light.cc
Normal file
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* The light module manages light data buffers and light culling system.
|
||||
*/
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
#include "eevee_light.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name LightData
|
||||
* \{ */
|
||||
|
||||
static eLightType to_light_type(short blender_light_type, short blender_area_type)
|
||||
{
|
||||
switch (blender_light_type) {
|
||||
default:
|
||||
case LA_LOCAL:
|
||||
return LIGHT_POINT;
|
||||
case LA_SUN:
|
||||
return LIGHT_SUN;
|
||||
case LA_SPOT:
|
||||
return LIGHT_SPOT;
|
||||
case LA_AREA:
|
||||
return ELEM(blender_area_type, LA_AREA_DISK, LA_AREA_ELLIPSE) ? LIGHT_ELLIPSE : LIGHT_RECT;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Light Object
|
||||
* \{ */
|
||||
|
||||
void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
|
||||
{
|
||||
const ::Light *la = (const ::Light *)ob->data;
|
||||
float scale[3];
|
||||
|
||||
float max_power = max_fff(la->r, la->g, la->b) * fabsf(la->energy / 100.0f);
|
||||
float surface_max_power = max_ff(la->diff_fac, la->spec_fac) * max_power;
|
||||
float volume_max_power = la->volume_fac * max_power;
|
||||
|
||||
float influence_radius_surface = attenuation_radius_get(la, threshold, surface_max_power);
|
||||
float influence_radius_volume = attenuation_radius_get(la, threshold, volume_max_power);
|
||||
|
||||
this->influence_radius_max = max_ff(influence_radius_surface, influence_radius_volume);
|
||||
this->influence_radius_invsqr_surface = (influence_radius_surface > 1e-8f) ?
|
||||
(1.0f / square_f(influence_radius_surface)) :
|
||||
0.0f;
|
||||
this->influence_radius_invsqr_volume = (influence_radius_volume > 1e-8f) ?
|
||||
(1.0f / square_f(influence_radius_volume)) :
|
||||
0.0f;
|
||||
|
||||
this->color = float3(&la->r) * la->energy;
|
||||
normalize_m4_m4_ex(this->object_mat.ptr(), ob->obmat, scale);
|
||||
/* Make sure we have consistent handedness (in case of negatively scaled Z axis). */
|
||||
float3 cross = math::cross(float3(this->_right), float3(this->_up));
|
||||
if (math::dot(cross, float3(this->_back)) < 0.0f) {
|
||||
negate_v3(this->_up);
|
||||
}
|
||||
|
||||
shape_parameters_set(la, scale);
|
||||
|
||||
float shape_power = shape_power_get(la);
|
||||
float point_power = point_power_get(la);
|
||||
this->diffuse_power = la->diff_fac * shape_power;
|
||||
this->transmit_power = la->diff_fac * point_power;
|
||||
this->specular_power = la->spec_fac * shape_power;
|
||||
this->volume_power = la->volume_fac * point_power;
|
||||
|
||||
eLightType new_type = to_light_type(la->type, la->area_shape);
|
||||
if (this->type != new_type) {
|
||||
shadow_discard_safe(shadows);
|
||||
this->type = new_type;
|
||||
}
|
||||
|
||||
if (la->mode & LA_SHADOW) {
|
||||
if (la->type == LA_SUN) {
|
||||
if (this->shadow_id == LIGHT_NO_SHADOW) {
|
||||
this->shadow_id = shadows.directionals.alloc();
|
||||
}
|
||||
|
||||
ShadowDirectional &shadow = shadows.directionals[this->shadow_id];
|
||||
shadow.sync(this->object_mat, la->bias * 0.05f, 1.0f);
|
||||
}
|
||||
else {
|
||||
float cone_aperture = DEG2RAD(360.0);
|
||||
if (la->type == LA_SPOT) {
|
||||
cone_aperture = min_ff(DEG2RAD(179.9), la->spotsize);
|
||||
}
|
||||
else if (la->type == LA_AREA) {
|
||||
cone_aperture = DEG2RAD(179.9);
|
||||
}
|
||||
|
||||
if (this->shadow_id == LIGHT_NO_SHADOW) {
|
||||
this->shadow_id = shadows.punctuals.alloc();
|
||||
}
|
||||
|
||||
ShadowPunctual &shadow = shadows.punctuals[this->shadow_id];
|
||||
shadow.sync(this->type,
|
||||
this->object_mat,
|
||||
cone_aperture,
|
||||
la->clipsta,
|
||||
this->influence_radius_max,
|
||||
la->bias * 0.05f);
|
||||
}
|
||||
}
|
||||
else {
|
||||
shadow_discard_safe(shadows);
|
||||
}
|
||||
|
||||
this->initialized = true;
|
||||
}
|
||||
|
||||
void Light::shadow_discard_safe(ShadowModule &shadows)
|
||||
{
|
||||
if (shadow_id != LIGHT_NO_SHADOW) {
|
||||
if (this->type != LIGHT_SUN) {
|
||||
shadows.punctuals.free(shadow_id);
|
||||
}
|
||||
else {
|
||||
shadows.directionals.free(shadow_id);
|
||||
}
|
||||
shadow_id = LIGHT_NO_SHADOW;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns attenuation radius inversed & squared for easy bound checking inside the shader. */
|
||||
float Light::attenuation_radius_get(const ::Light *la, float light_threshold, float light_power)
|
||||
{
|
||||
if (la->type == LA_SUN) {
|
||||
return (light_power > 1e-5f) ? 1e16f : 0.0f;
|
||||
}
|
||||
|
||||
if (la->mode & LA_CUSTOM_ATTENUATION) {
|
||||
return la->att_dist;
|
||||
}
|
||||
/* Compute the distance (using the inverse square law)
|
||||
* at which the light power reaches the light_threshold. */
|
||||
/* TODO take area light scale into account. */
|
||||
return sqrtf(light_power / light_threshold);
|
||||
}
|
||||
|
||||
void Light::shape_parameters_set(const ::Light *la, const float scale[3])
|
||||
{
|
||||
if (la->type == LA_AREA) {
|
||||
float area_size_y = (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE)) ? la->area_sizey :
|
||||
la->area_size;
|
||||
_area_size_x = max_ff(0.003f, la->area_size * scale[0] * 0.5f);
|
||||
_area_size_y = max_ff(0.003f, area_size_y * scale[1] * 0.5f);
|
||||
/* For volume point lighting. */
|
||||
radius_squared = max_ff(0.001f, hypotf(_area_size_x, _area_size_y) * 0.5f);
|
||||
radius_squared = square_f(radius_squared);
|
||||
}
|
||||
else {
|
||||
if (la->type == LA_SPOT) {
|
||||
/* Spot size & blend */
|
||||
spot_size_inv[0] = scale[2] / scale[0];
|
||||
spot_size_inv[1] = scale[2] / scale[1];
|
||||
float spot_size = cosf(la->spotsize * 0.5f);
|
||||
float spot_blend = (1.0f - spot_size) * la->spotblend;
|
||||
_spot_mul = 1.0f / max_ff(1e-8f, spot_blend);
|
||||
_spot_bias = -spot_size * _spot_mul;
|
||||
}
|
||||
|
||||
if (la->type == LA_SUN) {
|
||||
_area_size_x = max_ff(0.001f, tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f));
|
||||
_area_size_y = _area_size_x;
|
||||
}
|
||||
else {
|
||||
_area_size_x = _area_size_y = max_ff(0.001f, la->area_size);
|
||||
}
|
||||
radius_squared = square_f(_area_size_x);
|
||||
}
|
||||
}
|
||||
|
||||
float Light::shape_power_get(const ::Light *la)
|
||||
{
|
||||
float power;
|
||||
/* Make illumination power constant */
|
||||
if (la->type == LA_AREA) {
|
||||
float area = _area_size_x * _area_size_y;
|
||||
power = 1.0f / (area * 4.0f * float(M_PI));
|
||||
/* FIXME : Empirical, Fit cycles power */
|
||||
power *= 0.8f;
|
||||
if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
|
||||
/* Scale power to account for the lower area of the ellipse compared to the surrounding
|
||||
* rectangle. */
|
||||
power *= 4.0f / M_PI;
|
||||
}
|
||||
}
|
||||
else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
|
||||
power = 1.0f / (4.0f * square_f(_radius) * float(M_PI * M_PI));
|
||||
}
|
||||
else { /* LA_SUN */
|
||||
power = 1.0f / (square_f(_radius) * float(M_PI));
|
||||
/* Make illumination power closer to cycles for bigger radii. Cycles uses a cos^3 term that
|
||||
* we cannot reproduce so we account for that by scaling the light power. This function is
|
||||
* the result of a rough manual fitting. */
|
||||
/* Simplification of:
|
||||
* power *= 1 + r²/2 */
|
||||
power += 1.0f / (2.0f * M_PI);
|
||||
}
|
||||
return power;
|
||||
}
|
||||
|
||||
float Light::point_power_get(const ::Light *la)
|
||||
{
|
||||
/* Volume light is evaluated as point lights. Remove the shape power. */
|
||||
if (la->type == LA_AREA) {
|
||||
/* Match cycles. Empirical fit... must correspond to some constant. */
|
||||
float power = 0.0792f * M_PI;
|
||||
|
||||
/* This corrects for area light most representative point trick. The fit was found by
|
||||
* reducing the average error compared to cycles. */
|
||||
float area = _area_size_x * _area_size_y;
|
||||
float tmp = M_PI_2 / (M_PI_2 + sqrtf(area));
|
||||
/* Lerp between 1.0 and the limit (1 / pi). */
|
||||
power *= tmp + (1.0f - tmp) * M_1_PI;
|
||||
|
||||
return power;
|
||||
}
|
||||
else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
|
||||
/* Match cycles. Empirical fit... must correspond to some constant. */
|
||||
return 0.0792f;
|
||||
}
|
||||
else { /* LA_SUN */
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void Light::debug_draw(void)
|
||||
{
|
||||
const float color[4] = {0.8, 0.3, 0, 1};
|
||||
DRW_debug_sphere(_position, influence_radius_max, color);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name LightModule
|
||||
* \{ */
|
||||
|
||||
void LightModule::begin_sync(void)
|
||||
{
|
||||
/* In begin_sync so it can be aninated. */
|
||||
float light_threshold = max_ff(1e-16f, inst_.scene->eevee.light_threshold);
|
||||
if (light_threshold != light_threshold_) {
|
||||
light_threshold_ = light_threshold;
|
||||
inst_.sampling.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void LightModule::sync_light(const Object *ob, ObjectHandle &handle)
|
||||
{
|
||||
Light &light = lights_.lookup_or_add_default(handle.object_key);
|
||||
light.used = true;
|
||||
if (handle.recalc != 0 || !light.initialized) {
|
||||
light.sync(inst_.shadows, ob, light_threshold_);
|
||||
}
|
||||
}
|
||||
|
||||
void LightModule::end_sync(void)
|
||||
{
|
||||
Vector<ObjectKey, 0> deleted_keys;
|
||||
|
||||
light_refs_.clear();
|
||||
|
||||
/* Detect light deletion. */
|
||||
culling_data.items_no_cull_count = 0;
|
||||
for (auto item : lights_.items()) {
|
||||
Light &light = item.value;
|
||||
if (!light.used) {
|
||||
deleted_keys.append(item.key);
|
||||
light.shadow_discard_safe(inst_.shadows);
|
||||
}
|
||||
else {
|
||||
light.used = false;
|
||||
light_refs_.append(&light);
|
||||
|
||||
if (light.type == LIGHT_SUN) {
|
||||
culling_data.items_no_cull_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deleted_keys.size() > 0) {
|
||||
inst_.sampling.reset();
|
||||
}
|
||||
for (auto key : deleted_keys) {
|
||||
lights_.remove(key);
|
||||
}
|
||||
|
||||
if (light_refs_.size() > CULLING_MAX_ITEM) {
|
||||
/* TODO(fclem) Print error to user. */
|
||||
light_refs_.resize(CULLING_MAX_ITEM);
|
||||
}
|
||||
|
||||
batch_len_ = divide_ceil_u(max_ii(light_refs_.size(), 1), CULLING_BATCH_SIZE);
|
||||
lights_data.resize(batch_len_ * CULLING_BATCH_SIZE);
|
||||
culling_key_buf.resize(batch_len_ * CULLING_BATCH_SIZE);
|
||||
culling_light_buf.resize(batch_len_ * CULLING_BATCH_SIZE);
|
||||
culling_zbin_buf.resize(batch_len_ * CULLING_ZBIN_COUNT);
|
||||
culling_data.items_count = light_refs_.size();
|
||||
culling_data.tile_word_len = divide_ceil_u(max_ii(culling_data.items_count, 1), 32);
|
||||
|
||||
/* Call shadows.end_sync after light pruning to avoid packing deleted shadows. */
|
||||
inst_.shadows.end_sync();
|
||||
|
||||
int direc_idx = 0;
|
||||
int punct_idx = culling_data.items_no_cull_count;
|
||||
for (auto l_idx : light_refs_.index_range()) {
|
||||
Light &light = *light_refs_[l_idx];
|
||||
int dst_idx = (light.type == LIGHT_SUN) ? direc_idx++ : punct_idx++;
|
||||
lights_data[dst_idx] = light;
|
||||
|
||||
if (light.shadow_id != LIGHT_NO_SHADOW) {
|
||||
if (light.type == LIGHT_SUN) {
|
||||
lights_data[dst_idx].shadow_data = this->inst_.shadows.directionals[light.shadow_id];
|
||||
}
|
||||
else {
|
||||
lights_data[dst_idx].shadow_data = this->inst_.shadows.punctuals[light.shadow_id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lights_data.push_update();
|
||||
|
||||
{
|
||||
culling_ps_ = DRW_pass_create("CullingLight", (DRWState)0);
|
||||
|
||||
uint lights_len = light_refs_.size();
|
||||
uint batch_len = divide_ceil_u(lights_len, CULLING_BATCH_SIZE);
|
||||
|
||||
if (batch_len > 0) {
|
||||
/* NOTE: We reference the buffers that may be resized or updated later. */
|
||||
{
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(CULLING_SELECT);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_ps_);
|
||||
DRW_shgroup_storage_block(grp, "lights_buf", lights_data);
|
||||
DRW_shgroup_storage_block_ref(grp, "lights_cull_buf", &culling_data);
|
||||
DRW_shgroup_storage_block(grp, "keys_buf", culling_key_buf);
|
||||
DRW_shgroup_call_compute(grp, batch_len, 1, 1);
|
||||
DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE);
|
||||
}
|
||||
{
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(CULLING_SORT);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_ps_);
|
||||
DRW_shgroup_storage_block(grp, "lights_buf", lights_data);
|
||||
DRW_shgroup_storage_block_ref(grp, "lights_cull_buf", &culling_data);
|
||||
DRW_shgroup_storage_block(grp, "keys_buf", culling_key_buf);
|
||||
DRW_shgroup_storage_block_ref(grp, "lights_zbin_buf", &culling_zbin_buf);
|
||||
DRW_shgroup_storage_block_ref(grp, "out_lights_buf", &culling_light_buf);
|
||||
DRW_shgroup_call_compute(grp, batch_len, 1, 1);
|
||||
DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE);
|
||||
}
|
||||
{
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(CULLING_TILE);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_ps_);
|
||||
DRW_shgroup_storage_block(grp, "lights_buf", culling_light_buf);
|
||||
DRW_shgroup_storage_block_ref(grp, "lights_cull_buf", &culling_data);
|
||||
DRW_shgroup_storage_block_ref(grp, "lights_tile_buf", &culling_tile_buf);
|
||||
DRW_shgroup_call_compute_ref(grp, culling_tile_dispatch_size_);
|
||||
DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug_end_sync();
|
||||
}
|
||||
|
||||
void LightModule::debug_end_sync(void)
|
||||
{
|
||||
if (inst_.debug_mode != eDebugMode::DEBUG_LIGHT_CULLING) {
|
||||
debug_draw_ps_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
debug_draw_ps_ = DRW_pass_create("CullingDebug", DRW_STATE_WRITE_COLOR);
|
||||
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(CULLING_DEBUG);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, debug_draw_ps_);
|
||||
DRW_shgroup_storage_block_ref(grp, "lights_buf", &culling_light_buf);
|
||||
DRW_shgroup_storage_block_ref(grp, "lights_cull_buf", &culling_data);
|
||||
DRW_shgroup_storage_block_ref(grp, "lights_zbin_buf", &culling_zbin_buf);
|
||||
DRW_shgroup_storage_block_ref(grp, "lights_tile_buf", &culling_tile_buf);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
|
||||
/* Compute acceleration structure for the given view. If extent is 0, bind no lights. */
|
||||
void LightModule::set_view(const DRWView *view, const int2 extent, bool enable_specular)
|
||||
{
|
||||
const bool no_lights = (extent.x == 0);
|
||||
|
||||
/* Target 1bit per pixel. */
|
||||
uint tile_size = 1u << log2_ceil_u(ceil(sqrtf(culling_data.tile_word_len * 32)));
|
||||
|
||||
int3 tiles_extent;
|
||||
tiles_extent.x = divide_ceil_u(extent.x, tile_size);
|
||||
tiles_extent.y = divide_ceil_u(extent.y, tile_size);
|
||||
tiles_extent.z = batch_len_;
|
||||
|
||||
float far_z = DRW_view_far_distance_get(view);
|
||||
float near_z = DRW_view_near_distance_get(view);
|
||||
|
||||
culling_data.zbin_scale = -CULLING_ZBIN_COUNT / fabsf(far_z - near_z);
|
||||
culling_data.zbin_bias = -near_z * culling_data.zbin_scale;
|
||||
culling_data.tile_size = tile_size;
|
||||
culling_data.tile_x_len = tiles_extent.x;
|
||||
culling_data.tile_y_len = tiles_extent.y;
|
||||
culling_data.tile_to_uv_fac = (!no_lights) ? tile_size / float2(extent) : float2(0.0f);
|
||||
|
||||
culling_data.enable_specular = enable_specular;
|
||||
culling_data.items_count = no_lights ? 0 : light_refs_.size();
|
||||
culling_data.items_no_cull_count = no_lights ? 0 : culling_data.items_no_cull_count;
|
||||
culling_data.visible_count = 0;
|
||||
culling_data.push_update();
|
||||
|
||||
if (no_lights) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint word_count = tiles_extent.x * tiles_extent.y * tiles_extent.z * culling_data.tile_word_len;
|
||||
|
||||
/* TODO(fclem) Only resize once per redraw. */
|
||||
culling_tile_buf.resize(ceil_multiple_u(word_count, 4u));
|
||||
|
||||
culling_tile_dispatch_size_.x = divide_ceil_u(word_count, 1024);
|
||||
culling_tile_dispatch_size_.y = 1;
|
||||
culling_tile_dispatch_size_.z = 1;
|
||||
|
||||
DRW_view_set_active(view);
|
||||
DRW_draw_pass(culling_ps_);
|
||||
}
|
||||
|
||||
void LightModule::debug_draw(GPUFrameBuffer *view_fb, HiZBuffer &hiz)
|
||||
{
|
||||
if (debug_draw_ps_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
input_depth_tx_ = hiz.texture_get();
|
||||
|
||||
GPU_framebuffer_bind(view_fb);
|
||||
DRW_draw_pass(debug_draw_ps_);
|
||||
}
|
||||
|
||||
void LightModule::shgroup_resources(DRWShadingGroup *grp)
|
||||
{
|
||||
DRW_shgroup_storage_block_ref(grp, "lights_buf", &culling_light_buf);
|
||||
DRW_shgroup_storage_block_ref(grp, "lights_cull_buf", &culling_data);
|
||||
DRW_shgroup_storage_block_ref(grp, "lights_zbin_buf", &culling_zbin_buf);
|
||||
DRW_shgroup_storage_block_ref(grp, "lights_tile_buf", &culling_tile_buf);
|
||||
|
||||
DRW_shgroup_uniform_texture(grp, "shadow_atlas_tx", inst_.shadows.atlas_tx_get());
|
||||
DRW_shgroup_uniform_texture(grp, "shadow_tilemaps_tx", inst_.shadows.tilemap_tx_get());
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
140
source/blender/draw/engines/eevee/eevee_light.hh
Normal file
140
source/blender/draw/engines/eevee/eevee_light.hh
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* The light module manages light data buffers and light culling system.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_vector.hh"
|
||||
#include "DNA_light_types.h"
|
||||
|
||||
#include "eevee_camera.hh"
|
||||
#include "eevee_id_map.hh"
|
||||
#include "eevee_sampling.hh"
|
||||
#include "eevee_shader.hh"
|
||||
#include "eevee_shader_shared.hh"
|
||||
#include "eevee_shadow.hh"
|
||||
#include "eevee_wrapper.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Light Object
|
||||
* \{ */
|
||||
|
||||
struct Light : public LightData {
|
||||
public:
|
||||
bool initialized = false;
|
||||
bool used = false;
|
||||
|
||||
public:
|
||||
Light()
|
||||
{
|
||||
shadow_id = LIGHT_NO_SHADOW;
|
||||
}
|
||||
|
||||
void sync(ShadowModule &shadows, const Object *ob, float threshold);
|
||||
|
||||
void shadow_discard_safe(ShadowModule &shadows);
|
||||
|
||||
void debug_draw(void);
|
||||
|
||||
private:
|
||||
float attenuation_radius_get(const ::Light *la, float light_threshold, float light_power);
|
||||
void shape_parameters_set(const ::Light *la, const float scale[3]);
|
||||
float shape_power_get(const ::Light *la);
|
||||
float point_power_get(const ::Light *la);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name LightModule
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* The light module manages light data buffers and light culling system.
|
||||
*/
|
||||
class LightModule {
|
||||
friend ShadowModule;
|
||||
|
||||
public:
|
||||
/** Scene lights data. */
|
||||
LightDataBuf lights_data;
|
||||
/** Shadow data. TODO(fclem): merge with lights_data. */
|
||||
ShadowDataBuf shadows_data;
|
||||
/** Culling infos. */
|
||||
CullingDataBuf culling_data;
|
||||
/** Key buffer containing only visible lights indices. */
|
||||
CullingKeyBuf culling_key_buf;
|
||||
/** LightData buffer used for rendering. Ordered by the culling phase. */
|
||||
CullingLightBuf culling_light_buf;
|
||||
/** Zbins containing min and max light index for each Z bin. */
|
||||
CullingZbinBuf culling_zbin_buf;
|
||||
/** Bitmap of lights touching each tiles. Using one layer for each culling batch. */
|
||||
CullingTileBuf culling_tile_buf;
|
||||
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
/** Map of light objects. This is used to track light deletion. */
|
||||
Map<ObjectKey, Light> lights_;
|
||||
|
||||
Vector<Light *> light_refs_;
|
||||
|
||||
/** Follows the principles of Tiled Culling + Z binning from:
|
||||
* "Improved Culling for Tiled and Clustered Rendering"
|
||||
* by Michal Drobot
|
||||
* http://advances.realtimerendering.com/s2017/2017_Sig_Improved_Culling_final.pdf */
|
||||
DRWPass *culling_ps_ = nullptr;
|
||||
int3 culling_tile_dispatch_size_ = int3(1);
|
||||
/* Number of batches of lights that are separately processed. */
|
||||
int batch_len_ = 1;
|
||||
|
||||
float light_threshold_;
|
||||
|
||||
/** Debug Culling visualization. */
|
||||
DRWPass *debug_draw_ps_ = nullptr;
|
||||
GPUTexture *input_depth_tx_ = nullptr;
|
||||
|
||||
public:
|
||||
LightModule(Instance &inst) : inst_(inst){};
|
||||
~LightModule(){};
|
||||
|
||||
void begin_sync(void);
|
||||
void sync_light(const Object *ob, ObjectHandle &handle);
|
||||
void end_sync(void);
|
||||
|
||||
void set_view(const DRWView *view, const int2 extent, bool enable_specular = true);
|
||||
|
||||
void shgroup_resources(DRWShadingGroup *grp);
|
||||
|
||||
void debug_end_sync(void);
|
||||
void debug_draw(GPUFrameBuffer *view_fb, HiZBuffer &hiz);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
File diff suppressed because it is too large
Load Diff
1199
source/blender/draw/engines/eevee/eevee_lightcache.cc
Normal file
1199
source/blender/draw/engines/eevee/eevee_lightcache.cc
Normal file
File diff suppressed because it is too large
Load Diff
92
source/blender/draw/engines/eevee/eevee_lightcache.hh
Normal file
92
source/blender/draw/engines/eevee/eevee_lightcache.hh
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2018, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_lightprobe_types.h"
|
||||
|
||||
#include "GPU_capabilities.h"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/**
|
||||
* Wrapper to blender lightcache structure.
|
||||
* Used to define methods for the light cache.
|
||||
**/
|
||||
struct LightCache : public ::LightCache {
|
||||
private:
|
||||
constexpr static int min_cube_lod_level = 3;
|
||||
/* Rounded to nearest PowerOfTwo */
|
||||
constexpr static int irradiance_sample_size_x = 4; /* 3 in reality */
|
||||
constexpr static int irradiance_sample_size_y = 2;
|
||||
/* Manually encoded as RGBM. Also encodes visibility. */
|
||||
constexpr static eGPUTextureFormat irradiance_format = GPU_RGBA8;
|
||||
/* OpenGL 3.3 core requirement, can be extended but it's already very big */
|
||||
constexpr static int irradiance_max_pool_layer = 256;
|
||||
constexpr static int irradiance_max_pool_size = 1024;
|
||||
constexpr static int max_irradiance_samples =
|
||||
(irradiance_max_pool_size / irradiance_sample_size_x) *
|
||||
(irradiance_max_pool_size / irradiance_sample_size_y);
|
||||
|
||||
constexpr static eGPUTextureFormat reflection_format = GPU_R11F_G11F_B10F;
|
||||
|
||||
public:
|
||||
LightCache(const int cube_len,
|
||||
const int grid_len,
|
||||
const int cube_size,
|
||||
const int vis_size,
|
||||
const int irr_size[3]);
|
||||
|
||||
~LightCache();
|
||||
|
||||
static void irradiance_cache_size_get(int visibility_size, int total_samples, int r_size[3]);
|
||||
static void update_info(SceneEEVEE *eevee);
|
||||
|
||||
int irradiance_cells_per_row_get(void) const;
|
||||
|
||||
bool load(void);
|
||||
|
||||
bool validate(const int cube_len,
|
||||
const int cube_res,
|
||||
const int grid_len,
|
||||
const int irr_size[3]) const;
|
||||
|
||||
uint memsize_get(void) const;
|
||||
int64_t irradiance_sample_count(void) const;
|
||||
|
||||
void readback_irradiance(void);
|
||||
void readback_reflections(void);
|
||||
|
||||
private:
|
||||
bool version_check(void) const;
|
||||
bool can_be_saved(void) const;
|
||||
bool load_static(void);
|
||||
|
||||
bool create_reflection_texture(void);
|
||||
bool create_irradiance_texture(void);
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("EEVEE:LightCache")
|
||||
};
|
||||
|
||||
} // namespace blender::eevee
|
||||
579
source/blender/draw/engines/eevee/eevee_lightprobe.cc
Normal file
579
source/blender/draw/engines/eevee/eevee_lightprobe.cc
Normal file
@@ -0,0 +1,579 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2018, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_span.hh"
|
||||
#include "DNA_defaults.h"
|
||||
#include "DNA_lightprobe_types.h"
|
||||
|
||||
#include "eevee_camera.hh"
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
void LightProbeModule::init()
|
||||
{
|
||||
SceneEEVEE &sce_eevee = inst_.scene->eevee;
|
||||
|
||||
lightcache_ = static_cast<LightCache *>(sce_eevee.light_cache_data);
|
||||
|
||||
bool use_lookdev = inst_.use_studio_light();
|
||||
if (!use_lookdev && lightcache_ && lightcache_->load()) {
|
||||
MEM_delete(lightcache_lookdev_);
|
||||
}
|
||||
else {
|
||||
if (lightcache_ && (lightcache_->flag & LIGHTCACHE_NOT_USABLE)) {
|
||||
BLI_snprintf(
|
||||
inst_.info, sizeof(inst_.info), "Error: LightCache cannot be loaded on this GPU");
|
||||
}
|
||||
|
||||
if (lightcache_lookdev_ == nullptr) {
|
||||
int cube_len = 1;
|
||||
int grid_len = 1;
|
||||
int irr_samples_len = 1;
|
||||
|
||||
int3 irr_size;
|
||||
LightCache::irradiance_cache_size_get(
|
||||
sce_eevee.gi_visibility_resolution, irr_samples_len, irr_size);
|
||||
|
||||
lightcache_lookdev_ = new LightCache(cube_len,
|
||||
grid_len,
|
||||
sce_eevee.gi_cubemap_resolution,
|
||||
sce_eevee.gi_visibility_resolution,
|
||||
irr_size);
|
||||
}
|
||||
lightcache_ = lightcache_lookdev_;
|
||||
}
|
||||
|
||||
for (DRWView *&view : face_view_) {
|
||||
view = nullptr;
|
||||
}
|
||||
|
||||
if (info_data_.cubes_info.display_size != sce_eevee.gi_cubemap_draw_size ||
|
||||
info_data_.grids_info.display_size != sce_eevee.gi_irradiance_draw_size ||
|
||||
info_data_.grids_info.irradiance_smooth != square_f(sce_eevee.gi_irradiance_smoothing)) {
|
||||
/* TODO(fclem) reset on scene update instead. */
|
||||
inst_.sampling.reset();
|
||||
}
|
||||
|
||||
info_data_.cubes_info.display_size = sce_eevee.gi_cubemap_draw_size;
|
||||
info_data_.grids_info.display_size = sce_eevee.gi_irradiance_draw_size;
|
||||
info_data_.grids_info.irradiance_smooth = square_f(sce_eevee.gi_irradiance_smoothing);
|
||||
info_data_.grids_info.irradiance_cells_per_row = lightcache_->irradiance_cells_per_row_get();
|
||||
info_data_.grids_info.visibility_size = lightcache_->vis_res;
|
||||
info_data_.grids_info.visibility_cells_per_row = lightcache_->grid_tx.tex_size[0] /
|
||||
info_data_.grids_info.visibility_size;
|
||||
info_data_.grids_info.visibility_cells_per_layer =
|
||||
(lightcache_->grid_tx.tex_size[1] / info_data_.grids_info.visibility_size) *
|
||||
info_data_.grids_info.visibility_cells_per_row;
|
||||
|
||||
glossy_clamp_ = sce_eevee.gi_glossy_clamp;
|
||||
filter_quality_ = clamp_f(sce_eevee.gi_filter_quality, 1.0f, 8.0f);
|
||||
}
|
||||
|
||||
void LightProbeModule::begin_sync()
|
||||
{
|
||||
{
|
||||
cube_downsample_ps_ = DRW_pass_create("Downsample.Cube", DRW_STATE_WRITE_COLOR);
|
||||
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_DOWNSAMPLE_CUBE);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, cube_downsample_ps_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "input_tx", &cube_downsample_input_tx_);
|
||||
DRW_shgroup_uniform_block(grp, "filter_buf", filter_data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 6);
|
||||
}
|
||||
{
|
||||
filter_glossy_ps_ = DRW_pass_create("Filter.GlossyMip", DRW_STATE_WRITE_COLOR);
|
||||
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_GLOSSY);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_glossy_ps_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "radiance_tx", &cube_downsample_input_tx_);
|
||||
DRW_shgroup_uniform_block(grp, "filter_buf", filter_data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 6);
|
||||
}
|
||||
{
|
||||
filter_diffuse_ps_ = DRW_pass_create("Filter.Diffuse", DRW_STATE_WRITE_COLOR);
|
||||
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_DIFFUSE);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_diffuse_ps_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "radiance_tx", &cube_downsample_input_tx_);
|
||||
DRW_shgroup_uniform_block(grp, "filter_buf", filter_data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
{
|
||||
filter_visibility_ps_ = DRW_pass_create("Filter.Visibility", DRW_STATE_WRITE_COLOR);
|
||||
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_VISIBILITY);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_visibility_ps_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &cube_downsample_input_tx_);
|
||||
DRW_shgroup_uniform_block(grp, "filter_buf", filter_data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
|
||||
display_ps_ = nullptr;
|
||||
|
||||
if ((inst_.v3d != nullptr) && ((inst_.v3d->flag2 & V3D_HIDE_OVERLAYS) == 0)) {
|
||||
if (inst_.scene->eevee.flag & (SCE_EEVEE_SHOW_CUBEMAPS | SCE_EEVEE_SHOW_IRRADIANCE)) {
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
|
||||
display_ps_ = DRW_pass_create("LightProbe.Display", state);
|
||||
}
|
||||
|
||||
if (inst_.scene->eevee.flag & SCE_EEVEE_SHOW_CUBEMAPS) {
|
||||
if (lightcache_->cube_len > 1) {
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_DISPLAY_CUBEMAP);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, display_ps_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", cube_tx_ref_get());
|
||||
DRW_shgroup_uniform_block(grp, "cubes_buf", cube_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "probes_buf", info_ubo_get());
|
||||
|
||||
uint cubemap_count = 0;
|
||||
/* Skip world. */
|
||||
for (auto cube_id : IndexRange(1, lightcache_->cube_len - 1)) {
|
||||
const LightProbeCache &cube = lightcache_->cube_data[cube_id];
|
||||
/* Note: only works because probes are rendered in sequential order. */
|
||||
if (cube.is_ready) {
|
||||
cubemap_count++;
|
||||
}
|
||||
}
|
||||
if (cubemap_count > 0) {
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, cubemap_count * 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE) {
|
||||
if (lightcache_->grid_len > 1) {
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_DISPLAY_IRRADIANCE);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, display_ps_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", grid_tx_ref_get());
|
||||
DRW_shgroup_uniform_block(grp, "grids_buf", grid_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "probes_buf", info_ubo_get());
|
||||
|
||||
/* Skip world. */
|
||||
for (auto grid_id : IndexRange(1, lightcache_->grid_len - 1)) {
|
||||
const LightGridCache &grid = lightcache_->grid_data[grid_id];
|
||||
if (grid.is_ready) {
|
||||
DRWShadingGroup *grp_sub = DRW_shgroup_create_sub(grp);
|
||||
DRW_shgroup_uniform_int_copy(grp_sub, "grid_id", grid_id);
|
||||
uint sample_count = grid.resolution[0] * grid.resolution[1] * grid.resolution[2];
|
||||
DRW_shgroup_call_procedural_triangles(grp_sub, nullptr, sample_count * 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LightProbeModule::end_sync()
|
||||
{
|
||||
if (lightcache_->flag & LIGHTCACHE_UPDATE_WORLD) {
|
||||
cubemap_prepare(float3(0.0f), 0.01f, 1.0f, true);
|
||||
}
|
||||
}
|
||||
|
||||
void LightProbeModule::cubemap_prepare(float3 position,
|
||||
float near,
|
||||
float far,
|
||||
bool background_only)
|
||||
{
|
||||
SceneEEVEE &sce_eevee = inst_.scene->eevee;
|
||||
int cube_res = sce_eevee.gi_cubemap_resolution;
|
||||
int cube_mip_count = (int)log2_ceil_u(cube_res);
|
||||
|
||||
float4x4 viewmat = float4x4::identity();
|
||||
negate_v3_v3(viewmat[3], position);
|
||||
|
||||
/* TODO(fclem) We might want to have theses as temporary textures. */
|
||||
cube_depth_tx_.ensure_cube(GPU_DEPTH24_STENCIL8, cube_res, nullptr, cube_mip_count);
|
||||
cube_color_tx_.ensure_cube(GPU_RGBA16F, cube_res, nullptr, cube_mip_count);
|
||||
GPU_texture_mipmap_mode(cube_color_tx_, true, true);
|
||||
|
||||
cube_downsample_fb_.ensure(GPU_ATTACHMENT_TEXTURE(cube_depth_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(cube_color_tx_));
|
||||
|
||||
filter_cube_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(lightcache_->cube_tx.tex));
|
||||
filter_grid_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(lightcache_->grid_tx.tex));
|
||||
|
||||
float4x4 winmat;
|
||||
cubeface_winmat_get(winmat, near, far);
|
||||
|
||||
for (auto i : IndexRange(ARRAY_SIZE(probe_views_))) {
|
||||
probe_views_[i].sync(cube_color_tx_, cube_depth_tx_, winmat, viewmat, background_only);
|
||||
}
|
||||
}
|
||||
|
||||
void LightProbeModule::cubemap_render(void)
|
||||
{
|
||||
DRW_stats_group_start("Cubemap Render");
|
||||
for (auto i : IndexRange(ARRAY_SIZE(probe_views_))) {
|
||||
probe_views_[i].render();
|
||||
}
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* Update mipchain. */
|
||||
filter_data_.target_layer = 0;
|
||||
filter_data_.push_update();
|
||||
cube_downsample_input_tx_ = cube_color_tx_;
|
||||
|
||||
DRW_stats_group_start("Cubemap Downsample");
|
||||
GPU_framebuffer_recursive_downsample(cube_downsample_fb_, 7, cube_downsample_cb, this);
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
void LightProbeModule::filter_glossy(int cube_index, float intensity)
|
||||
{
|
||||
DRW_stats_group_start("Filter.Glossy");
|
||||
|
||||
filter_data_.instensity_fac = intensity;
|
||||
filter_data_.target_layer = cube_index * 6;
|
||||
|
||||
int level_max = lightcache_->mips_len;
|
||||
for (int level = 0; level <= level_max; level++) {
|
||||
filter_data_.luma_max = (glossy_clamp_ > 0.0f) ? glossy_clamp_ : 1e16f;
|
||||
/* Disney Roughness. */
|
||||
filter_data_.roughness = square_f(level / (float)level_max);
|
||||
/* Distribute Roughness across lod more evenly. */
|
||||
filter_data_.roughness = square_f(filter_data_.roughness);
|
||||
/* Avoid artifacts. */
|
||||
filter_data_.roughness = clamp_f(filter_data_.roughness, 1e-4f, 0.9999f);
|
||||
/* Variable sample count and bias to make first levels faster. */
|
||||
switch (level) {
|
||||
case 0:
|
||||
filter_data_.sample_count = 1.0f;
|
||||
filter_data_.lod_bias = -1.0f;
|
||||
break;
|
||||
case 1:
|
||||
filter_data_.sample_count = filter_quality_ * 32.0f;
|
||||
filter_data_.lod_bias = 1.0f;
|
||||
break;
|
||||
case 2:
|
||||
filter_data_.sample_count = filter_quality_ * 40.0f;
|
||||
filter_data_.lod_bias = 2.0f;
|
||||
break;
|
||||
case 3:
|
||||
filter_data_.sample_count = filter_quality_ * 64.0f;
|
||||
filter_data_.lod_bias = 2.0f;
|
||||
break;
|
||||
default:
|
||||
filter_data_.sample_count = filter_quality_ * 128.0f;
|
||||
filter_data_.lod_bias = 2.0f;
|
||||
break;
|
||||
}
|
||||
/* Add automatic LOD bias (based on target size). */
|
||||
filter_data_.lod_bias += lod_bias_from_cubemap();
|
||||
|
||||
filter_data_.push_update();
|
||||
|
||||
filter_cube_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE_MIP(lightcache_->cube_tx.tex, level));
|
||||
GPU_framebuffer_bind(filter_cube_fb_);
|
||||
DRW_draw_pass(filter_glossy_ps_);
|
||||
}
|
||||
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
void LightProbeModule::filter_diffuse(int sample_index, float intensity)
|
||||
{
|
||||
filter_data_.instensity_fac = intensity;
|
||||
filter_data_.target_layer = 0;
|
||||
filter_data_.luma_max = 1e16f;
|
||||
filter_data_.sample_count = 1024.0f;
|
||||
filter_data_.lod_bias = lod_bias_from_cubemap();
|
||||
|
||||
filter_data_.push_update();
|
||||
|
||||
int2 extent = int2(3, 2);
|
||||
int2 offset = extent;
|
||||
offset.x *= sample_index % info_data_.grids_info.irradiance_cells_per_row;
|
||||
offset.y *= sample_index / info_data_.grids_info.irradiance_cells_per_row;
|
||||
|
||||
GPU_framebuffer_bind(filter_grid_fb_);
|
||||
GPU_framebuffer_viewport_set(filter_grid_fb_, UNPACK2(offset), UNPACK2(extent));
|
||||
DRW_draw_pass(filter_diffuse_ps_);
|
||||
GPU_framebuffer_viewport_reset(filter_grid_fb_);
|
||||
}
|
||||
|
||||
void LightProbeModule::filter_visibility(int sample_index,
|
||||
float visibility_blur,
|
||||
float visibility_range)
|
||||
{
|
||||
int2 extent = int2(info_data_.grids_info.visibility_size);
|
||||
int2 offset = extent;
|
||||
offset.x *= sample_index % info_data_.grids_info.visibility_cells_per_row;
|
||||
offset.y *= (sample_index / info_data_.grids_info.visibility_cells_per_row) %
|
||||
info_data_.grids_info.visibility_cells_per_layer;
|
||||
|
||||
filter_data_.target_layer = 1 + sample_index / info_data_.grids_info.visibility_cells_per_layer;
|
||||
filter_data_.sample_count = 512.0f; /* TODO refine */
|
||||
filter_data_.visibility_blur = visibility_blur;
|
||||
filter_data_.visibility_range = visibility_range;
|
||||
|
||||
filter_data_.push_update();
|
||||
|
||||
GPU_framebuffer_bind(filter_grid_fb_);
|
||||
GPU_framebuffer_viewport_set(filter_grid_fb_, UNPACK2(offset), UNPACK2(extent));
|
||||
DRW_draw_pass(filter_visibility_ps_);
|
||||
GPU_framebuffer_viewport_reset(filter_grid_fb_);
|
||||
}
|
||||
|
||||
void LightProbeModule::update_world_cache()
|
||||
{
|
||||
DRW_stats_group_start("LightProbe.world");
|
||||
|
||||
const DRWView *view_active = DRW_view_get_active();
|
||||
|
||||
cubemap_render();
|
||||
|
||||
filter_diffuse(0, 1.0f);
|
||||
|
||||
if ((lightcache_->flag & LIGHTCACHE_NO_REFLECTION) == 0) {
|
||||
/* TODO(fclem) Change ray type. */
|
||||
/* OPTI(fclem) Only re-render if there is a light path node in the world material. */
|
||||
// cubemap_render();
|
||||
|
||||
filter_glossy(0, 1.0f);
|
||||
}
|
||||
|
||||
if (view_active != nullptr) {
|
||||
DRW_view_set_active(view_active);
|
||||
}
|
||||
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
/* Ensure a temporary cache the same size at the target lightcache exists. */
|
||||
LightCache *LightProbeModule::baking_cache_get(void)
|
||||
{
|
||||
if (lightcache_baking_ == nullptr) {
|
||||
lightcache_baking_ = new LightCache(lightcache_->cube_len,
|
||||
lightcache_->grid_len,
|
||||
lightcache_->cube_tx.tex_size[0],
|
||||
lightcache_->vis_res,
|
||||
lightcache_->grid_tx.tex_size);
|
||||
|
||||
if (lightcache_baking_->flag != LIGHTCACHE_INVALID) {
|
||||
LightCache &lcache_src = *lightcache_;
|
||||
LightCache &lcache = *lightcache_baking_;
|
||||
/* Copy cache structure. */
|
||||
memcpy(lcache.cube_data, lcache_src.cube_data, lcache.cube_len * sizeof(*lcache.cube_data));
|
||||
memcpy(lcache.grid_data, lcache_src.grid_data, lcache.grid_len * sizeof(*lcache.grid_data));
|
||||
|
||||
/* Make grids renderable. */
|
||||
for (LightGridCache &grid : MutableSpan(lcache.grid_data, lcache.grid_len)) {
|
||||
grid.is_ready = 1;
|
||||
}
|
||||
/* Avoid sampling further than mip 0. Mips > 0 being undefined. */
|
||||
lcache.mips_len = 0;
|
||||
lcache.flag |= LIGHTCACHE_NO_REFLECTION;
|
||||
|
||||
/* Init to black. */
|
||||
uint data_cube = 0;
|
||||
uchar data_grid[4] = {0, 0, 0, 0};
|
||||
GPU_texture_clear(lcache.cube_tx.tex, GPU_DATA_10_11_11_REV, &data_cube);
|
||||
GPU_texture_clear(lcache.grid_tx.tex, GPU_DATA_UBYTE, &data_grid);
|
||||
}
|
||||
}
|
||||
return lightcache_baking_;
|
||||
}
|
||||
|
||||
void LightProbeModule::bake(Depsgraph *depsgraph,
|
||||
int type,
|
||||
int index,
|
||||
int bounce,
|
||||
const float position[3],
|
||||
const LightProbe *probe,
|
||||
float visibility_range)
|
||||
{
|
||||
rcti rect;
|
||||
BLI_rcti_init(&rect, 0, 0, 1, 1);
|
||||
|
||||
/* Disable screenspace effects. */
|
||||
SceneEEVEE &sce_eevee = DEG_get_evaluated_scene(depsgraph)->eevee;
|
||||
sce_eevee.flag &= ~(SCE_EEVEE_GTAO_ENABLED | SCE_EEVEE_RAYTRACING_ENABLED);
|
||||
|
||||
inst_.init(int2(1), &rect, nullptr, depsgraph, probe);
|
||||
inst_.sampling.reset();
|
||||
inst_.render_sync();
|
||||
inst_.sampling.step();
|
||||
|
||||
float near = (probe) ? probe->clipsta : 0.1f;
|
||||
float far = (probe) ? probe->clipend : 1.0f;
|
||||
float intensity = (probe) ? probe->intensity : 1.0f;
|
||||
|
||||
bool background_only = (probe == nullptr);
|
||||
cubemap_prepare(position, near, far, background_only);
|
||||
|
||||
if (type == LIGHTPROBE_TYPE_CUBE && probe != nullptr) {
|
||||
/* Reflections cubemaps are rendered after all irradiance bounces.
|
||||
* Swap to get the final irradiance in lightcache_baking_. */
|
||||
swap_irradiance_cache();
|
||||
}
|
||||
|
||||
/* Render using the previous bounce to light the scene. */
|
||||
lightcache_ = baking_cache_get();
|
||||
|
||||
cubemap_render();
|
||||
|
||||
/* Filter on the original cache. */
|
||||
lightcache_ = reinterpret_cast<LightCache *>(sce_eevee.light_cache_data);
|
||||
|
||||
if (type == LIGHTPROBE_TYPE_CUBE) {
|
||||
filter_glossy(index, intensity);
|
||||
/* Swap back final irradiance to lightcache_. */
|
||||
if (probe != nullptr) {
|
||||
swap_irradiance_cache();
|
||||
}
|
||||
}
|
||||
else {
|
||||
filter_diffuse(index, intensity);
|
||||
if (probe && bounce < 2) {
|
||||
/* No need to filter visibility after 2nd bounce since both lightcache_ and
|
||||
* lightcache_baking_ will have correct visibility grid. */
|
||||
filter_visibility(index, probe->vis_blur, visibility_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Push world probe to first grid and cubemap slots. */
|
||||
void LightProbeModule::sync_world(const DRWView *view)
|
||||
{
|
||||
BoundSphere view_bounds = DRW_view_frustum_bsphere_get(view);
|
||||
/* Playing safe. The fake grid needs to be bigger than the frustum. */
|
||||
view_bounds.radius = clamp_f(view_bounds.radius * 2.0, 0.0f, FLT_MAX);
|
||||
|
||||
CubemapData &cube = cube_data_[0];
|
||||
GridData &grid = grid_data_[0];
|
||||
|
||||
scale_m4_fl(grid.local_mat.ptr(), view_bounds.radius);
|
||||
negate_v3_v3(grid.local_mat[3], view_bounds.center);
|
||||
cube.parallax_mat = cube.influence_mat = grid.local_mat;
|
||||
|
||||
grid.resolution = int3(1);
|
||||
grid.offset = 0;
|
||||
grid.level_skip = 1;
|
||||
grid.attenuation_bias = 0.001f;
|
||||
grid.attenuation_scale = 1.0f;
|
||||
grid.visibility_range = 1.0f;
|
||||
grid.visibility_bleed = 0.001f;
|
||||
grid.visibility_bias = 0.0f;
|
||||
grid.increment_x = float3(0.0f);
|
||||
grid.increment_y = float3(0.0f);
|
||||
grid.increment_z = float3(0.0f);
|
||||
grid.corner = float3(0.0f);
|
||||
|
||||
cube._parallax_type = CUBEMAP_SHAPE_SPHERE;
|
||||
cube._layer = 0.0;
|
||||
}
|
||||
|
||||
void LightProbeModule::sync_grid(const DRWView *UNUSED(view),
|
||||
const LightGridCache &grid_cache,
|
||||
int grid_index)
|
||||
{
|
||||
/* Skip the world probe. */
|
||||
if (grid_index == 0 || grid_cache.is_ready != 1) {
|
||||
return;
|
||||
}
|
||||
GridData &grid = grid_data_[info_data_.grids_info.grid_count];
|
||||
copy_m4_m4(grid.local_mat.ptr(), grid_cache.mat);
|
||||
grid.resolution = int3(grid_cache.resolution);
|
||||
grid.offset = grid_cache.offset;
|
||||
grid.level_skip = grid_cache.level_bias;
|
||||
grid.attenuation_bias = grid_cache.attenuation_bias;
|
||||
grid.attenuation_scale = grid_cache.attenuation_scale;
|
||||
grid.visibility_range = grid_cache.visibility_range;
|
||||
grid.visibility_bleed = grid_cache.visibility_bleed;
|
||||
grid.visibility_bias = grid_cache.visibility_bias;
|
||||
grid.increment_x = float3(grid_cache.increment_x);
|
||||
grid.increment_y = float3(grid_cache.increment_y);
|
||||
grid.increment_z = float3(grid_cache.increment_z);
|
||||
grid.corner = float3(grid_cache.corner);
|
||||
|
||||
info_data_.grids_info.grid_count++;
|
||||
}
|
||||
|
||||
void LightProbeModule::sync_cubemap(const DRWView *UNUSED(view),
|
||||
const LightProbeCache &cube_cache,
|
||||
int cube_index)
|
||||
{
|
||||
/* Skip the world probe. */
|
||||
if (cube_index == 0 || cube_cache.is_ready != 1) {
|
||||
return;
|
||||
}
|
||||
CubemapData &cube = cube_data_[info_data_.cubes_info.cube_count];
|
||||
copy_m4_m4(cube.parallax_mat.ptr(), cube_cache.parallaxmat);
|
||||
copy_m4_m4(cube.influence_mat.ptr(), cube_cache.attenuationmat);
|
||||
cube._attenuation_factor = cube_cache.attenuation_fac;
|
||||
cube._attenuation_type = cube_cache.attenuation_type;
|
||||
cube._parallax_type = cube_cache.parallax_type;
|
||||
cube._layer = cube_index;
|
||||
cube._world_position_x = cube_cache.position[0];
|
||||
cube._world_position_y = cube_cache.position[1];
|
||||
cube._world_position_z = cube_cache.position[2];
|
||||
|
||||
info_data_.cubes_info.cube_count++;
|
||||
}
|
||||
|
||||
/* Only enables world light probe if extent is invalid (no culling possible). */
|
||||
void LightProbeModule::set_view(const DRWView *view, const int2 extent)
|
||||
{
|
||||
if (lightcache_->flag & LIGHTCACHE_UPDATE_WORLD) {
|
||||
/* Set before update to avoid infinite recursion. */
|
||||
lightcache_->flag &= ~LIGHTCACHE_UPDATE_WORLD;
|
||||
update_world_cache();
|
||||
}
|
||||
|
||||
/* Only sync when setting the view. This way we can cull probes not in frustum. */
|
||||
/* TODO(fclem) implement culling. But needs to fix display when not all probes are present. */
|
||||
info_data_.grids_info.grid_count = 1;
|
||||
info_data_.cubes_info.cube_count = 1;
|
||||
|
||||
sync_world(view);
|
||||
/* Only world if extent is 0. */
|
||||
if (extent.x > 0) {
|
||||
for (auto i : IndexRange(lightcache_->grid_len)) {
|
||||
sync_grid(view, lightcache_->grid_data[i], i);
|
||||
}
|
||||
for (auto i : IndexRange(lightcache_->cube_len)) {
|
||||
sync_cubemap(view, lightcache_->cube_data[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
info_data_.cubes_info.roughness_max_lod = lightcache_->mips_len;
|
||||
inst_.lookdev.rotation_get(info_data_.cubes_info.lookdev_rotation);
|
||||
inst_.lookdev.rotation_get(info_data_.grids_info.lookdev_rotation);
|
||||
|
||||
active_grid_tx_ = lightcache_->grid_tx.tex;
|
||||
active_cube_tx_ = lightcache_->cube_tx.tex;
|
||||
|
||||
info_data_.push_update();
|
||||
grid_data_.push_update();
|
||||
cube_data_.push_update();
|
||||
}
|
||||
|
||||
void LightProbeModule::draw_cache_display(void)
|
||||
{
|
||||
/* Only draws something if enabled. */
|
||||
DRW_draw_pass(display_ps_);
|
||||
}
|
||||
|
||||
} // namespace blender::eevee
|
||||
175
source/blender/draw/engines/eevee/eevee_lightprobe.hh
Normal file
175
source/blender/draw/engines/eevee/eevee_lightprobe.hh
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2018, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "eevee_lightcache.hh"
|
||||
#include "eevee_view.hh"
|
||||
|
||||
#include "eevee_wrapper.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
class LightProbeModule {
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
LightProbeFilterDataBuf filter_data_;
|
||||
LightProbeInfoDataBuf info_data_;
|
||||
GridDataBuf grid_data_;
|
||||
CubemapDataBuf cube_data_;
|
||||
|
||||
/* Either scene lightcache or lookdev lightcache */
|
||||
LightCache *lightcache_ = nullptr;
|
||||
/* Own lightcache used for lookdev lighting or as fallback. */
|
||||
LightCache *lightcache_lookdev_ = nullptr;
|
||||
/* Temporary cache used for baking. */
|
||||
LightCache *lightcache_baking_ = nullptr;
|
||||
|
||||
/* Used for rendering probes. */
|
||||
/* OPTI(fclem) Share for the whole scene? Only allocate temporary? */
|
||||
Texture cube_depth_tx_ = {"CubemapDepth"};
|
||||
Texture cube_color_tx_ = {"CubemapColor"};
|
||||
LightProbeView probe_views_[6];
|
||||
|
||||
Framebuffer cube_downsample_fb_ = {"cube_downsample"};
|
||||
Framebuffer filter_cube_fb_ = {"filter_cube"};
|
||||
Framebuffer filter_grid_fb_ = {"filter_grid"};
|
||||
|
||||
std::array<DRWView *, 6> face_view_ = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
|
||||
|
||||
DRWPass *cube_downsample_ps_ = nullptr;
|
||||
DRWPass *filter_glossy_ps_ = nullptr;
|
||||
DRWPass *filter_diffuse_ps_ = nullptr;
|
||||
DRWPass *filter_visibility_ps_ = nullptr;
|
||||
|
||||
DRWPass *display_ps_ = nullptr;
|
||||
|
||||
/** Input texture to downsample cube pass. */
|
||||
GPUTexture *cube_downsample_input_tx_ = nullptr;
|
||||
/** Copy of actual textures from the lightcache_. */
|
||||
GPUTexture *active_grid_tx_ = nullptr;
|
||||
GPUTexture *active_cube_tx_ = nullptr;
|
||||
/** Constant values during baking. */
|
||||
float glossy_clamp_ = 0.0;
|
||||
float filter_quality_ = 0.0;
|
||||
|
||||
public:
|
||||
LightProbeModule(Instance &inst)
|
||||
: inst_(inst),
|
||||
probe_views_{{inst, "posX_view", cubeface_mat[0], 0},
|
||||
{inst, "negX_view", cubeface_mat[1], 1},
|
||||
{inst, "posY_view", cubeface_mat[2], 2},
|
||||
{inst, "negY_view", cubeface_mat[3], 3},
|
||||
{inst, "posZ_view", cubeface_mat[4], 4},
|
||||
{inst, "negZ_view", cubeface_mat[5], 5}}
|
||||
{
|
||||
}
|
||||
|
||||
~LightProbeModule()
|
||||
{
|
||||
MEM_delete(lightcache_lookdev_);
|
||||
MEM_delete(lightcache_baking_);
|
||||
}
|
||||
|
||||
void init();
|
||||
|
||||
void begin_sync();
|
||||
void end_sync();
|
||||
|
||||
void set_view(const DRWView *view, const int2 extent);
|
||||
|
||||
void set_world_dirty(void)
|
||||
{
|
||||
lightcache_->flag |= LIGHTCACHE_UPDATE_WORLD;
|
||||
}
|
||||
|
||||
void swap_irradiance_cache(void)
|
||||
{
|
||||
if (lightcache_baking_ && lightcache_) {
|
||||
SWAP(GPUTexture *, lightcache_baking_->grid_tx.tex, lightcache_->grid_tx.tex);
|
||||
}
|
||||
}
|
||||
|
||||
const GPUUniformBuf *grid_ubo_get() const
|
||||
{
|
||||
return grid_data_;
|
||||
}
|
||||
const GPUUniformBuf *cube_ubo_get() const
|
||||
{
|
||||
return cube_data_;
|
||||
}
|
||||
const GPUUniformBuf *info_ubo_get() const
|
||||
{
|
||||
return info_data_;
|
||||
}
|
||||
GPUTexture **grid_tx_ref_get()
|
||||
{
|
||||
return &active_grid_tx_;
|
||||
}
|
||||
GPUTexture **cube_tx_ref_get()
|
||||
{
|
||||
return &active_cube_tx_;
|
||||
}
|
||||
|
||||
void bake(Depsgraph *depsgraph,
|
||||
int type,
|
||||
int index,
|
||||
int bounce,
|
||||
const float position[3],
|
||||
const LightProbe *probe = nullptr,
|
||||
float visibility_range = 0.0f);
|
||||
|
||||
void draw_cache_display(void);
|
||||
|
||||
private:
|
||||
void update_world_cache();
|
||||
|
||||
void sync_world(const DRWView *view);
|
||||
void sync_grid(const DRWView *view, const struct LightGridCache &grid_cache, int grid_index);
|
||||
void sync_cubemap(const DRWView *view, const struct LightProbeCache &cube_cache, int cube_index);
|
||||
|
||||
LightCache *baking_cache_get(void);
|
||||
|
||||
void cubemap_prepare(float3 position, float near, float far, bool background_only);
|
||||
|
||||
void filter_glossy(int cube_index, float intensity);
|
||||
void filter_diffuse(int sample_index, float intensity);
|
||||
void filter_visibility(int sample_index, float visibility_blur, float visibility_range);
|
||||
|
||||
float lod_bias_from_cubemap(void)
|
||||
{
|
||||
float target_size_sq = square_f(GPU_texture_width(cube_color_tx_));
|
||||
return 0.5f * logf(target_size_sq / filter_data_.sample_count) / logf(2);
|
||||
}
|
||||
|
||||
static void cube_downsample_cb(void *thunk, int UNUSED(level))
|
||||
{
|
||||
DRW_draw_pass(reinterpret_cast<LightProbeModule *>(thunk)->cube_downsample_ps_);
|
||||
}
|
||||
|
||||
void cubemap_render(void);
|
||||
};
|
||||
|
||||
} // namespace blender::eevee
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,268 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup DNA
|
||||
*/
|
||||
|
||||
#include "BLI_sys_types.h" /* bool */
|
||||
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "eevee_private.h"
|
||||
|
||||
void eevee_light_matrix_get(const EEVEE_Light *evli, float r_mat[4][4])
|
||||
{
|
||||
copy_v3_v3(r_mat[0], evli->rightvec);
|
||||
copy_v3_v3(r_mat[1], evli->upvec);
|
||||
negate_v3_v3(r_mat[2], evli->forwardvec);
|
||||
copy_v3_v3(r_mat[3], evli->position);
|
||||
r_mat[0][3] = 0.0f;
|
||||
r_mat[1][3] = 0.0f;
|
||||
r_mat[2][3] = 0.0f;
|
||||
r_mat[3][3] = 1.0f;
|
||||
}
|
||||
|
||||
static float light_attenuation_radius_get(const Light *la,
|
||||
float light_threshold,
|
||||
float light_power)
|
||||
{
|
||||
if (la->mode & LA_CUSTOM_ATTENUATION) {
|
||||
return la->att_dist;
|
||||
}
|
||||
/* Compute the distance (using the inverse square law)
|
||||
* at which the light power reaches the light_threshold. */
|
||||
return sqrtf(max_ff(1e-16, light_power / max_ff(1e-16, light_threshold)));
|
||||
}
|
||||
|
||||
static void light_shape_parameters_set(EEVEE_Light *evli, const Light *la, const float scale[3])
|
||||
{
|
||||
if (la->type == LA_SPOT) {
|
||||
/* Spot size & blend */
|
||||
evli->sizex = scale[0] / scale[2];
|
||||
evli->sizey = scale[1] / scale[2];
|
||||
evli->spotsize = cosf(la->spotsize * 0.5f);
|
||||
evli->spotblend = (1.0f - evli->spotsize) * la->spotblend;
|
||||
evli->radius = max_ff(0.001f, la->area_size);
|
||||
}
|
||||
else if (la->type == LA_AREA) {
|
||||
evli->sizex = max_ff(0.003f, la->area_size * scale[0] * 0.5f);
|
||||
if (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE)) {
|
||||
evli->sizey = max_ff(0.003f, la->area_sizey * scale[1] * 0.5f);
|
||||
}
|
||||
else {
|
||||
evli->sizey = max_ff(0.003f, la->area_size * scale[1] * 0.5f);
|
||||
}
|
||||
/* For volume point lighting. */
|
||||
evli->radius = max_ff(0.001f, hypotf(evli->sizex, evli->sizey) * 0.5f);
|
||||
}
|
||||
else if (la->type == LA_SUN) {
|
||||
evli->radius = max_ff(0.001f, tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f));
|
||||
}
|
||||
else {
|
||||
evli->radius = max_ff(0.001f, la->area_size);
|
||||
}
|
||||
}
|
||||
|
||||
static float light_shape_power_get(const Light *la, const EEVEE_Light *evli)
|
||||
{
|
||||
float power;
|
||||
/* Make illumination power constant */
|
||||
if (la->type == LA_AREA) {
|
||||
power = 1.0f / (evli->sizex * evli->sizey * 4.0f * M_PI) * /* 1/(w*h*Pi) */
|
||||
0.8f; /* XXX: Empirical, Fit cycles power. */
|
||||
if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
|
||||
/* Scale power to account for the lower area of the ellipse compared to the surrounding
|
||||
* rectangle. */
|
||||
power *= 4.0f / M_PI;
|
||||
}
|
||||
}
|
||||
else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
|
||||
power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI); /* `1/(4*(r^2)*(Pi^2))` */
|
||||
|
||||
/* for point lights (a.k.a radius == 0.0) */
|
||||
// power = M_PI * M_PI * 0.78; /* XXX: Empirical, Fit cycles power. */
|
||||
}
|
||||
else { /* LA_SUN */
|
||||
power = 1.0f / (evli->radius * evli->radius * M_PI);
|
||||
/* Make illumination power closer to cycles for bigger radii. Cycles uses a cos^3 term that we
|
||||
* cannot reproduce so we account for that by scaling the light power. This function is the
|
||||
* result of a rough manual fitting. */
|
||||
power += 1.0f / (2.0f * M_PI); /* `power *= 1 + (r^2)/2` */
|
||||
}
|
||||
return power;
|
||||
}
|
||||
|
||||
static float light_shape_power_volume_get(const Light *la,
|
||||
const EEVEE_Light *evli,
|
||||
float area_power)
|
||||
{
|
||||
/* Volume light is evaluated as point lights. Remove the shape power. */
|
||||
float power = 1.0f / area_power;
|
||||
|
||||
if (la->type == LA_AREA) {
|
||||
/* Match cycles. Empirical fit... must correspond to some constant. */
|
||||
power *= 0.0792f * M_PI;
|
||||
/* This corrects for area light most representative point trick. The fit was found by
|
||||
* reducing the average error compared to cycles. */
|
||||
float area = evli->sizex * evli->sizey;
|
||||
float tmp = M_PI_2 / (M_PI_2 + sqrtf(area));
|
||||
/* Lerp between 1.0 and the limit (1 / pi). */
|
||||
power *= tmp + (1.0f - tmp) * M_1_PI;
|
||||
}
|
||||
else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
|
||||
/* Match cycles. Empirical fit... must correspond to some constant. */
|
||||
power *= 0.0792f;
|
||||
}
|
||||
else { /* LA_SUN */
|
||||
/* Nothing to do. */
|
||||
}
|
||||
return power;
|
||||
}
|
||||
|
||||
/* Update buffer with light data */
|
||||
static void eevee_light_setup(Object *ob, EEVEE_Light *evli)
|
||||
{
|
||||
const Light *la = (Light *)ob->data;
|
||||
float mat[4][4], scale[3];
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const float light_threshold = draw_ctx->scene->eevee.light_threshold;
|
||||
|
||||
/* Position */
|
||||
copy_v3_v3(evli->position, ob->obmat[3]);
|
||||
|
||||
/* Color */
|
||||
copy_v3_v3(evli->color, &la->r);
|
||||
|
||||
evli->diff = la->diff_fac;
|
||||
evli->spec = la->spec_fac;
|
||||
evli->volume = la->volume_fac;
|
||||
|
||||
float max_power = max_fff(la->r, la->g, la->b) * fabsf(la->energy / 100.0f);
|
||||
float surface_max_power = max_ff(evli->diff, evli->spec) * max_power;
|
||||
float volume_max_power = evli->volume * max_power;
|
||||
|
||||
/* Influence Radii. */
|
||||
float att_radius = light_attenuation_radius_get(la, light_threshold, surface_max_power);
|
||||
float att_radius_volume = light_attenuation_radius_get(la, light_threshold, volume_max_power);
|
||||
/* Take the inverse square of this distance. */
|
||||
evli->invsqrdist = 1.0f / max_ff(1e-4f, square_f(att_radius));
|
||||
evli->invsqrdist_volume = 1.0f / max_ff(1e-4f, square_f(att_radius_volume));
|
||||
|
||||
/* Vectors */
|
||||
normalize_m4_m4_ex(mat, ob->obmat, scale);
|
||||
copy_v3_v3(evli->forwardvec, mat[2]);
|
||||
normalize_v3(evli->forwardvec);
|
||||
negate_v3(evli->forwardvec);
|
||||
|
||||
copy_v3_v3(evli->rightvec, mat[0]);
|
||||
normalize_v3(evli->rightvec);
|
||||
|
||||
copy_v3_v3(evli->upvec, mat[1]);
|
||||
normalize_v3(evli->upvec);
|
||||
|
||||
/* Make sure we have a consistent Right Hand coord frame.
|
||||
* (in case of negatively scaled Z axis) */
|
||||
float cross[3];
|
||||
cross_v3_v3v3(cross, evli->rightvec, evli->forwardvec);
|
||||
if (dot_v3v3(cross, evli->upvec) < 0.0) {
|
||||
negate_v3(evli->upvec);
|
||||
}
|
||||
|
||||
light_shape_parameters_set(evli, la, scale);
|
||||
|
||||
/* Light Type */
|
||||
evli->light_type = (float)la->type;
|
||||
if ((la->type == LA_AREA) && ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
|
||||
evli->light_type = LAMPTYPE_AREA_ELLIPSE;
|
||||
}
|
||||
|
||||
float shape_power = light_shape_power_get(la, evli);
|
||||
mul_v3_fl(evli->color, shape_power * la->energy);
|
||||
|
||||
evli->volume *= light_shape_power_volume_get(la, evli, shape_power);
|
||||
|
||||
/* No shadow by default */
|
||||
evli->shadow_id = -1.0f;
|
||||
}
|
||||
|
||||
void EEVEE_lights_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_LightsInfo *linfo = sldata->lights;
|
||||
linfo->num_light = 0;
|
||||
|
||||
EEVEE_shadows_cache_init(sldata, vedata);
|
||||
}
|
||||
|
||||
void EEVEE_lights_cache_add(EEVEE_ViewLayerData *sldata, Object *ob)
|
||||
{
|
||||
EEVEE_LightsInfo *linfo = sldata->lights;
|
||||
const Light *la = (Light *)ob->data;
|
||||
|
||||
if (linfo->num_light >= MAX_LIGHT) {
|
||||
printf("Too many lights in the scene !!!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Early out if light has no power. */
|
||||
if (la->energy == 0.0f || is_zero_v3(&la->r)) {
|
||||
return;
|
||||
}
|
||||
|
||||
EEVEE_Light *evli = linfo->light_data + linfo->num_light;
|
||||
eevee_light_setup(ob, evli);
|
||||
|
||||
if (la->mode & LA_SHADOW) {
|
||||
if (la->type == LA_SUN) {
|
||||
EEVEE_shadows_cascade_add(linfo, evli, ob);
|
||||
}
|
||||
else if (ELEM(la->type, LA_SPOT, LA_LOCAL, LA_AREA)) {
|
||||
EEVEE_shadows_cube_add(linfo, evli, ob);
|
||||
}
|
||||
}
|
||||
|
||||
linfo->num_light++;
|
||||
}
|
||||
|
||||
void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_LightsInfo *linfo = sldata->lights;
|
||||
|
||||
sldata->common_data.la_num_light = linfo->num_light;
|
||||
|
||||
/* Clamp volume lights power. */
|
||||
float upper_bound = vedata->stl->effects->volume_light_clamp;
|
||||
for (int i = 0; i < linfo->num_light; i++) {
|
||||
EEVEE_Light *evli = linfo->light_data + i;
|
||||
|
||||
float power = max_fff(UNPACK3(evli->color)) * evli->volume;
|
||||
if (power > 0.0f && evli->light_type != LA_SUN) {
|
||||
/* The limit of the power attenuation function when the distance to the light goes to 0 is
|
||||
* `2 / r^2` where r is the light radius. We need to find the right radius that emits at most
|
||||
* the volume light upper bound. Inverting the function we get: */
|
||||
float min_radius = 1.0f / sqrtf(0.5f * upper_bound / power);
|
||||
/* Square it here to avoid a multiplication inside the shader. */
|
||||
evli->volume_radius = square_f(max_ff(min_radius, evli->radius));
|
||||
}
|
||||
}
|
||||
|
||||
GPU_uniformbuf_update(sldata->light_ubo, &linfo->light_data);
|
||||
}
|
||||
@@ -1,383 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BKE_camera.h"
|
||||
#include "BKE_studiolight.h"
|
||||
|
||||
#include "BLI_rand.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "ED_screen.h"
|
||||
|
||||
#include "GPU_material.h"
|
||||
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "eevee_lightcache.h"
|
||||
#include "eevee_private.h"
|
||||
|
||||
#include "draw_common.h"
|
||||
|
||||
static void eevee_lookdev_lightcache_delete(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
|
||||
MEM_SAFE_FREE(stl->lookdev_lightcache);
|
||||
MEM_SAFE_FREE(stl->lookdev_grid_data);
|
||||
MEM_SAFE_FREE(stl->lookdev_cube_data);
|
||||
DRW_TEXTURE_FREE_SAFE(txl->lookdev_grid_tx);
|
||||
DRW_TEXTURE_FREE_SAFE(txl->lookdev_cube_tx);
|
||||
g_data->studiolight_index = -1;
|
||||
g_data->studiolight_rot_z = 0.0f;
|
||||
}
|
||||
|
||||
static void eevee_lookdev_hdri_preview_init(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
Scene *scene = draw_ctx->scene;
|
||||
DRWShadingGroup *grp;
|
||||
|
||||
const EEVEE_EffectsInfo *effects = vedata->stl->effects;
|
||||
struct GPUBatch *sphere = DRW_cache_sphere_get(effects->sphere_lod);
|
||||
int mat_options = VAR_MAT_MESH | VAR_MAT_LOOKDEV;
|
||||
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS |
|
||||
DRW_STATE_CULL_BACK;
|
||||
|
||||
{
|
||||
Material *ma = EEVEE_material_default_diffuse_get();
|
||||
GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options);
|
||||
struct GPUShader *sh = GPU_material_get_shader(gpumat);
|
||||
|
||||
DRW_PASS_CREATE(psl->lookdev_diffuse_pass, state);
|
||||
grp = DRW_shgroup_create(sh, psl->lookdev_diffuse_pass);
|
||||
EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false);
|
||||
DRW_shgroup_add_material_resources(grp, gpumat);
|
||||
DRW_shgroup_call(grp, sphere, NULL);
|
||||
}
|
||||
{
|
||||
Material *ma = EEVEE_material_default_glossy_get();
|
||||
GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options);
|
||||
struct GPUShader *sh = GPU_material_get_shader(gpumat);
|
||||
|
||||
DRW_PASS_CREATE(psl->lookdev_glossy_pass, state);
|
||||
grp = DRW_shgroup_create(sh, psl->lookdev_glossy_pass);
|
||||
EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false);
|
||||
DRW_shgroup_add_material_resources(grp, gpumat);
|
||||
DRW_shgroup_call(grp, sphere, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_lookdev_init(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
/* The view will be NULL when rendering previews. */
|
||||
const View3D *v3d = draw_ctx->v3d;
|
||||
|
||||
if (eevee_hdri_preview_overlay_enabled(v3d)) {
|
||||
/* Viewport / Spheres size. */
|
||||
const rcti *rect;
|
||||
rcti fallback_rect;
|
||||
if (DRW_state_is_opengl_render()) {
|
||||
const float *vp_size = DRW_viewport_size_get();
|
||||
fallback_rect.xmax = vp_size[0];
|
||||
fallback_rect.ymax = vp_size[1];
|
||||
fallback_rect.xmin = fallback_rect.ymin = 0;
|
||||
rect = &fallback_rect;
|
||||
}
|
||||
else {
|
||||
rect = ED_region_visible_rect(draw_ctx->region);
|
||||
}
|
||||
|
||||
/* Make the viewport width scale the lookdev spheres a bit.
|
||||
* Scale between 1000px and 2000px. */
|
||||
const float viewport_scale = clamp_f(
|
||||
BLI_rcti_size_x(rect) / (2000.0f * U.dpi_fac), 0.5f, 1.0f);
|
||||
const int sphere_size = U.lookdev_sphere_size * U.dpi_fac * viewport_scale;
|
||||
|
||||
if (sphere_size != effects->sphere_size || rect->xmax != effects->anchor[0] ||
|
||||
rect->ymin != effects->anchor[1]) {
|
||||
/* Make sphere resolution adaptive to viewport_scale, dpi and lookdev_sphere_size */
|
||||
float res_scale = clamp_f(
|
||||
(U.lookdev_sphere_size / 400.0f) * viewport_scale * U.dpi_fac, 0.1f, 1.0f);
|
||||
|
||||
if (res_scale > 0.7f) {
|
||||
effects->sphere_lod = DRW_LOD_HIGH;
|
||||
}
|
||||
else if (res_scale > 0.25f) {
|
||||
effects->sphere_lod = DRW_LOD_MEDIUM;
|
||||
}
|
||||
else {
|
||||
effects->sphere_lod = DRW_LOD_LOW;
|
||||
}
|
||||
/* If sphere size or anchor point moves, reset TAA to avoid ghosting issue.
|
||||
* This needs to happen early because we are changing taa_current_sample. */
|
||||
effects->sphere_size = sphere_size;
|
||||
effects->anchor[0] = rect->xmax;
|
||||
effects->anchor[1] = rect->ymin;
|
||||
stl->g_data->valid_double_buffer = false;
|
||||
EEVEE_temporal_sampling_reset(vedata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_lookdev_cache_init(EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata,
|
||||
DRWPass *pass,
|
||||
EEVEE_LightProbesInfo *pinfo,
|
||||
DRWShadingGroup **r_shgrp)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
/* The view will be NULL when rendering previews. */
|
||||
const View3D *v3d = draw_ctx->v3d;
|
||||
const Scene *scene = draw_ctx->scene;
|
||||
|
||||
const bool probe_render = pinfo != NULL;
|
||||
|
||||
effects->lookdev_view = NULL;
|
||||
|
||||
if (eevee_hdri_preview_overlay_enabled(v3d)) {
|
||||
eevee_lookdev_hdri_preview_init(vedata, sldata);
|
||||
}
|
||||
|
||||
if (LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d)) {
|
||||
const View3DShading *shading = &v3d->shading;
|
||||
StudioLight *sl = BKE_studiolight_find(shading->lookdev_light,
|
||||
STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE);
|
||||
if (sl == NULL || (sl->flag & STUDIOLIGHT_TYPE_WORLD) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GPUShader *shader = probe_render ? EEVEE_shaders_studiolight_probe_sh_get() :
|
||||
EEVEE_shaders_studiolight_background_sh_get();
|
||||
|
||||
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
|
||||
int cube_res = scene_eval->eevee.gi_cubemap_resolution;
|
||||
|
||||
/* If one of the component is missing we start from scratch. */
|
||||
if ((stl->lookdev_grid_data == NULL) || (stl->lookdev_cube_data == NULL) ||
|
||||
(txl->lookdev_grid_tx == NULL) || (txl->lookdev_cube_tx == NULL) ||
|
||||
(g_data->light_cache && g_data->light_cache->ref_res != cube_res)) {
|
||||
eevee_lookdev_lightcache_delete(vedata);
|
||||
}
|
||||
|
||||
if (stl->lookdev_lightcache == NULL) {
|
||||
#if defined(IRRADIANCE_SH_L2)
|
||||
int grid_res = 4;
|
||||
#elif defined(IRRADIANCE_HL2)
|
||||
int grid_res = 4;
|
||||
#endif
|
||||
|
||||
stl->lookdev_lightcache = EEVEE_lightcache_create(
|
||||
1, 1, cube_res, 8, (int[3]){grid_res, grid_res, 1});
|
||||
|
||||
/* XXX: Fix memleak. TODO: find out why. */
|
||||
MEM_SAFE_FREE(stl->lookdev_cube_mips);
|
||||
|
||||
/* We do this to use a special light cache for lookdev.
|
||||
* This light-cache needs to be per viewport. But we need to
|
||||
* have correct freeing when the viewport is closed. So we
|
||||
* need to reference all textures to the txl and the memblocks
|
||||
* to the stl. */
|
||||
stl->lookdev_grid_data = stl->lookdev_lightcache->grid_data;
|
||||
stl->lookdev_cube_data = stl->lookdev_lightcache->cube_data;
|
||||
stl->lookdev_cube_mips = stl->lookdev_lightcache->cube_mips;
|
||||
txl->lookdev_grid_tx = stl->lookdev_lightcache->grid_tx.tex;
|
||||
txl->lookdev_cube_tx = stl->lookdev_lightcache->cube_tx.tex;
|
||||
}
|
||||
|
||||
g_data->light_cache = stl->lookdev_lightcache;
|
||||
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(shader, pass);
|
||||
axis_angle_to_mat3_single(g_data->studiolight_matrix, 'Z', shading->studiolight_rot_z);
|
||||
|
||||
float studiolight_matrix[3][3] = {{0.0f}};
|
||||
if (shading->flag & V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION) {
|
||||
float view_matrix[4][4];
|
||||
float view_rot_matrix[3][3];
|
||||
float x_rot_matrix[3][3];
|
||||
DRW_view_viewmat_get(NULL, view_matrix, false);
|
||||
copy_m3_m4(view_rot_matrix, view_matrix);
|
||||
axis_angle_to_mat3_single(x_rot_matrix, 'X', M_PI / 2.0f);
|
||||
mul_m3_m3m3(view_rot_matrix, x_rot_matrix, view_rot_matrix);
|
||||
mul_m3_m3m3(view_rot_matrix, g_data->studiolight_matrix, view_rot_matrix);
|
||||
copy_m3_m3(studiolight_matrix, view_rot_matrix);
|
||||
}
|
||||
|
||||
DRW_shgroup_uniform_mat3(grp, "StudioLightMatrix", g_data->studiolight_matrix);
|
||||
|
||||
if (probe_render) {
|
||||
/* Avoid artifact with equirectangular mapping. */
|
||||
eGPUSamplerState state = (GPU_SAMPLER_FILTER | GPU_SAMPLER_REPEAT_S);
|
||||
DRW_shgroup_uniform_float_copy(grp, "studioLightIntensity", shading->studiolight_intensity);
|
||||
BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "studioLight", sl->equirect_radiance_gputexture, state);
|
||||
/* Do not fade-out when doing probe rendering, only when drawing the background. */
|
||||
DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f);
|
||||
}
|
||||
else {
|
||||
float background_alpha = g_data->background_alpha * shading->studiolight_background;
|
||||
float studiolight_blur = powf(shading->studiolight_blur, 2.5f);
|
||||
DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", background_alpha);
|
||||
DRW_shgroup_uniform_float_copy(grp, "studioLightBlur", studiolight_blur);
|
||||
DRW_shgroup_uniform_texture(grp, "probeCubes", txl->lookdev_cube_tx);
|
||||
}
|
||||
|
||||
/* Common UBOs are setup latter. */
|
||||
*r_shgrp = grp;
|
||||
|
||||
/* Do we need to recalc the lightprobes? */
|
||||
if (g_data->studiolight_index != sl->index ||
|
||||
(shading->flag & V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION &&
|
||||
!equals_m3m3(g_data->studiolight_matrix, studiolight_matrix)) ||
|
||||
g_data->studiolight_rot_z != shading->studiolight_rot_z ||
|
||||
g_data->studiolight_intensity != shading->studiolight_intensity ||
|
||||
g_data->studiolight_cubemap_res != scene->eevee.gi_cubemap_resolution ||
|
||||
g_data->studiolight_glossy_clamp != scene->eevee.gi_glossy_clamp ||
|
||||
g_data->studiolight_filter_quality != scene->eevee.gi_filter_quality) {
|
||||
stl->lookdev_lightcache->flag |= LIGHTCACHE_UPDATE_WORLD;
|
||||
g_data->studiolight_index = sl->index;
|
||||
copy_m3_m3(g_data->studiolight_matrix, studiolight_matrix);
|
||||
g_data->studiolight_rot_z = shading->studiolight_rot_z;
|
||||
g_data->studiolight_intensity = shading->studiolight_intensity;
|
||||
g_data->studiolight_cubemap_res = scene->eevee.gi_cubemap_resolution;
|
||||
g_data->studiolight_glossy_clamp = scene->eevee.gi_glossy_clamp;
|
||||
g_data->studiolight_filter_quality = scene->eevee.gi_filter_quality;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_lookdev_apply_taa(const EEVEE_EffectsInfo *effects,
|
||||
int sphere_size,
|
||||
float winmat[4][4])
|
||||
{
|
||||
if (DRW_state_is_image_render() || ((effects->enabled_effects & EFFECT_TAA) != 0)) {
|
||||
double ht_point[2];
|
||||
double ht_offset[2] = {0.0, 0.0};
|
||||
const uint ht_primes[2] = {2, 3};
|
||||
float ofs[2];
|
||||
|
||||
BLI_halton_2d(ht_primes, ht_offset, effects->taa_current_sample, ht_point);
|
||||
EEVEE_temporal_sampling_offset_calc(ht_point, 1.5f, ofs);
|
||||
winmat[3][0] += ofs[0] / sphere_size;
|
||||
winmat[3][1] += ofs[1] / sphere_size;
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_lookdev_draw(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
|
||||
if (psl->lookdev_diffuse_pass && eevee_hdri_preview_overlay_enabled(draw_ctx->v3d)) {
|
||||
/* Config renderer. */
|
||||
EEVEE_CommonUniformBuffer *common = &sldata->common_data;
|
||||
common->la_num_light = 0;
|
||||
common->prb_num_planar = 0;
|
||||
common->prb_num_render_cube = 1;
|
||||
common->prb_num_render_grid = 1;
|
||||
common->ao_dist = 0.0f;
|
||||
common->ao_factor = 0.0f;
|
||||
common->ao_settings = 0.0f;
|
||||
GPU_uniformbuf_update(sldata->common_ubo, common);
|
||||
|
||||
/* override matrices */
|
||||
float winmat[4][4], viewmat[4][4];
|
||||
unit_m4(winmat);
|
||||
/* Look through the negative Z. */
|
||||
negate_v3(winmat[2]);
|
||||
|
||||
eevee_lookdev_apply_taa(effects, effects->sphere_size, winmat);
|
||||
|
||||
/* "Remove" view matrix location. Leaving only rotation. */
|
||||
DRW_view_viewmat_get(NULL, viewmat, false);
|
||||
zero_v3(viewmat[3]);
|
||||
|
||||
if (effects->lookdev_view) {
|
||||
/* When rendering just update the view. This avoids recomputing the culling. */
|
||||
DRW_view_update_sub(effects->lookdev_view, viewmat, winmat);
|
||||
}
|
||||
else {
|
||||
/* Using default view bypasses the culling. */
|
||||
const DRWView *default_view = DRW_view_default_get();
|
||||
effects->lookdev_view = DRW_view_create_sub(default_view, viewmat, winmat);
|
||||
}
|
||||
|
||||
DRW_view_set_active(effects->lookdev_view);
|
||||
|
||||
/* Find the right frame-buffers to render to. */
|
||||
GPUFrameBuffer *fb = (effects->target_buffer == fbl->effect_color_fb) ? fbl->main_fb :
|
||||
fbl->effect_fb;
|
||||
|
||||
DRW_stats_group_start("Look Dev");
|
||||
|
||||
GPU_framebuffer_bind(fb);
|
||||
|
||||
const int sphere_margin = effects->sphere_size / 6.0f;
|
||||
float offset[2] = {0.0f, sphere_margin};
|
||||
|
||||
offset[0] = effects->sphere_size + sphere_margin;
|
||||
GPU_framebuffer_viewport_set(fb,
|
||||
effects->anchor[0] - offset[0],
|
||||
effects->anchor[1] + offset[1],
|
||||
effects->sphere_size,
|
||||
effects->sphere_size);
|
||||
|
||||
DRW_draw_pass(psl->lookdev_diffuse_pass);
|
||||
|
||||
offset[0] = (effects->sphere_size + sphere_margin) +
|
||||
(sphere_margin + effects->sphere_size + sphere_margin);
|
||||
GPU_framebuffer_viewport_set(fb,
|
||||
effects->anchor[0] - offset[0],
|
||||
effects->anchor[1] + offset[1],
|
||||
effects->sphere_size,
|
||||
effects->sphere_size);
|
||||
|
||||
DRW_draw_pass(psl->lookdev_glossy_pass);
|
||||
|
||||
GPU_framebuffer_viewport_reset(fb);
|
||||
|
||||
DRW_stats_group_end();
|
||||
|
||||
DRW_view_set_active(NULL);
|
||||
}
|
||||
}
|
||||
359
source/blender/draw/engines/eevee/eevee_lookdev.cc
Normal file
359
source/blender/draw/engines/eevee/eevee_lookdev.cc
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2018, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_node.h"
|
||||
#include "BKE_studiolight.h"
|
||||
#include "BKE_world.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_rect.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "ED_screen.h"
|
||||
#include "NOD_shader.h"
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Lookdev Nodetree
|
||||
*
|
||||
* \{ */
|
||||
|
||||
LookDevWorldNodeTree::LookDevWorldNodeTree()
|
||||
{
|
||||
bNodeTree *ntree = ntreeAddTree(NULL, "Lookdev Nodetree", ntreeType_Shader->idname);
|
||||
bNode *background = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND);
|
||||
bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD);
|
||||
bNodeSocket *background_out = nodeFindSocket(background, SOCK_OUT, "Background");
|
||||
bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface");
|
||||
nodeAddLink(ntree, background, background_out, output, output_in);
|
||||
nodeSetActive(ntree, output);
|
||||
|
||||
/* Note that we do not populate the environment texture input.
|
||||
* We plug the GPUTexture directly using the sampler binding name ("samp1"). */
|
||||
bNode *environment = nodeAddStaticNode(NULL, ntree, SH_NODE_TEX_ENVIRONMENT);
|
||||
bNodeSocket *background_in = nodeFindSocket(background, SOCK_IN, "Color");
|
||||
bNodeSocket *environment_out = nodeFindSocket(environment, SOCK_OUT, "Color");
|
||||
nodeAddLink(ntree, environment, environment_out, background, background_in);
|
||||
|
||||
strength_socket_ =
|
||||
(bNodeSocketValueFloat *)nodeFindSocket(background, SOCK_IN, "Strength")->default_value;
|
||||
|
||||
ntree_ = ntree;
|
||||
}
|
||||
|
||||
LookDevWorldNodeTree::~LookDevWorldNodeTree()
|
||||
{
|
||||
ntreeFreeEmbeddedTree(ntree_);
|
||||
MEM_SAFE_FREE(ntree_);
|
||||
}
|
||||
|
||||
/* Configure a default nodetree with the given parameters. */
|
||||
bNodeTree *LookDevWorldNodeTree::nodetree_get(float strength)
|
||||
{
|
||||
/* WARNING: This function is not threadsafe. Which is not a problem for the moment. */
|
||||
strength_socket_->value = strength;
|
||||
return ntree_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name LookDev Studiolight
|
||||
*
|
||||
* Light the scene using the studiolight hdri. Overrides the lightcache (if any) and
|
||||
* use custom shader to draw the background.
|
||||
* \{ */
|
||||
|
||||
void LookDev::init(const int2 &output_res, const rcti *render_border)
|
||||
{
|
||||
StudioLight *studiolight = nullptr;
|
||||
if (inst_.v3d) {
|
||||
studiolight = BKE_studiolight_find(inst_.v3d->shading.lookdev_light,
|
||||
STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE);
|
||||
}
|
||||
|
||||
if (inst_.use_studio_light() && studiolight && (studiolight->flag & STUDIOLIGHT_TYPE_WORLD)) {
|
||||
const View3DShading &shading = inst_.v3d->shading;
|
||||
studiolight_ = studiolight;
|
||||
|
||||
/* Detect update. */
|
||||
if ((opacity_ != shading.studiolight_background) || (rotation_ != shading.studiolight_rot_z) ||
|
||||
(instensity_ != shading.studiolight_intensity) || (blur_ != shading.studiolight_blur) ||
|
||||
(view_rotation_ != ((shading.flag & V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION) != 0)) ||
|
||||
(studiolight_index_ != studiolight_->index)) {
|
||||
opacity_ = shading.studiolight_background;
|
||||
instensity_ = shading.studiolight_intensity;
|
||||
blur_ = shading.studiolight_blur;
|
||||
rotation_ = shading.studiolight_rot_z;
|
||||
studiolight_index_ = studiolight_->index;
|
||||
view_rotation_ = (shading.flag & V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION) != 0;
|
||||
|
||||
inst_.sampling.reset();
|
||||
inst_.lightprobes.set_world_dirty();
|
||||
|
||||
/* Update the material. */
|
||||
GPU_material_free(&material);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (studiolight_ != nullptr) {
|
||||
inst_.sampling.reset();
|
||||
inst_.lightprobes.set_world_dirty();
|
||||
}
|
||||
studiolight_ = nullptr;
|
||||
studiolight_index_ = -1;
|
||||
|
||||
GPU_material_free(&material);
|
||||
}
|
||||
|
||||
if (do_overlay(output_res, render_border)) {
|
||||
rcti rect;
|
||||
if (DRW_state_is_opengl_render()) {
|
||||
BLI_rcti_init(&rect, 0, output_res.x, 0, output_res.y);
|
||||
}
|
||||
else {
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
rect = *ED_region_visible_rect(draw_ctx->region);
|
||||
}
|
||||
|
||||
/* Make the viewport width scale the lookdev spheres a bit.
|
||||
* Scale between 1000px and 2000px. */
|
||||
float viewport_scale = clamp_f(BLI_rcti_size_x(&rect) / (2000.0f * U.dpi_fac), 0.5f, 1.0f);
|
||||
int sphere_size = U.lookdev_sphere_size * U.dpi_fac * viewport_scale;
|
||||
int2 anchor = int2(rect.xmax, rect.ymin);
|
||||
|
||||
if (sphere_size != sphere_size_ || anchor != anchor_) {
|
||||
/* Make sphere resolution adaptive to viewport_scale, dpi and lookdev_sphere_size */
|
||||
float res_scale = (U.lookdev_sphere_size / 400.0f) * viewport_scale * U.dpi_fac;
|
||||
if (res_scale > 0.7f) {
|
||||
sphere_lod_ = DRW_LOD_HIGH;
|
||||
}
|
||||
else if (res_scale > 0.25f) {
|
||||
sphere_lod_ = DRW_LOD_MEDIUM;
|
||||
}
|
||||
else {
|
||||
sphere_lod_ = DRW_LOD_LOW;
|
||||
}
|
||||
sphere_size_ = sphere_size;
|
||||
anchor_ = anchor;
|
||||
inst_.sampling.reset();
|
||||
}
|
||||
}
|
||||
else if (sphere_size_ != 0) {
|
||||
sphere_size_ = 0;
|
||||
inst_.sampling.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool LookDev::do_overlay(const int2 &output_res, const rcti *render_border)
|
||||
{
|
||||
const View3D *v3d = inst_.v3d;
|
||||
/* Only show the HDRI Preview in Shading Preview in the Viewport. */
|
||||
if (v3d == nullptr || v3d->shading.type != OB_MATERIAL) {
|
||||
return false;
|
||||
}
|
||||
/* Only show the HDRI Preview when viewing the Combined render pass */
|
||||
if (v3d->shading.render_pass != SCE_PASS_COMBINED) {
|
||||
return false;
|
||||
}
|
||||
if (v3d->flag2 & V3D_HIDE_OVERLAYS) {
|
||||
return false;
|
||||
}
|
||||
if ((v3d->overlay.flag & V3D_OVERLAY_LOOK_DEV) == 0) {
|
||||
return false;
|
||||
}
|
||||
if (inst_.camera.is_panoramic()) {
|
||||
return false;
|
||||
}
|
||||
if (output_res != int2(BLI_rcti_size_x(render_border), BLI_rcti_size_y(render_border))) {
|
||||
/* TODO(fclem) support this case. */
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LookDev::sync_world(void)
|
||||
{
|
||||
if (studiolight_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
/* World light probes render. */
|
||||
bNodeTree *nodetree = world_tree.nodetree_get(instensity_);
|
||||
GPUMaterial *gpumat = inst_.shaders.material_shader_get(
|
||||
"LookDev", material, nodetree, MAT_PIPE_FORWARD, MAT_GEOM_WORLD, true);
|
||||
|
||||
BKE_studiolight_ensure_flag(studiolight_, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE);
|
||||
GPUTexture *gputex = studiolight_->equirect_radiance_gputexture;
|
||||
|
||||
if (gputex == nullptr) {
|
||||
return false;
|
||||
}
|
||||
inst_.shading_passes.background.sync(gpumat, gputex);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LookDev::rotation_get(float4x4 &r_mat)
|
||||
{
|
||||
if (studiolight_ == nullptr) {
|
||||
r_mat = float4x4::identity();
|
||||
}
|
||||
else {
|
||||
axis_angle_to_mat4_single(r_mat.ptr(), 'Z', rotation_);
|
||||
}
|
||||
|
||||
if (view_rotation_) {
|
||||
float4x4 x_rot_matrix;
|
||||
const CameraData &cam = inst_.camera.data_get();
|
||||
axis_angle_to_mat4_single(x_rot_matrix.ptr(), 'X', M_PI / 2.0f);
|
||||
r_mat = r_mat * (x_rot_matrix * cam.viewmat);
|
||||
}
|
||||
}
|
||||
|
||||
void LookDev::sync_background(void)
|
||||
{
|
||||
if (studiolight_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
/* Viewport display. */
|
||||
background_ps_ = DRW_pass_create("LookDev.Background", DRW_STATE_WRITE_COLOR);
|
||||
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(LOOKDEV_BACKGROUND);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, background_ps_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", inst_.lightprobes.cube_tx_ref_get());
|
||||
DRW_shgroup_uniform_block(grp, "probes_buf", inst_.lightprobes.info_ubo_get());
|
||||
DRW_shgroup_uniform_float_copy(grp, "blur", clamp_f(blur_, 0.0f, 0.99999f));
|
||||
DRW_shgroup_uniform_float_copy(grp, "opacity", opacity_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
|
||||
/* Renders background using lightcache. */
|
||||
bool LookDev::render_background(void)
|
||||
{
|
||||
if (studiolight_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
DRW_draw_pass(background_ps_);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name LookDev Reference spheres
|
||||
*
|
||||
* Render reference spheres into a separate framebuffer to not distrub the main rendering.
|
||||
* The final texture is composited onto the render.
|
||||
* \{ */
|
||||
|
||||
void LookDev::sync_overlay(void)
|
||||
{
|
||||
if (sphere_size_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS |
|
||||
DRW_STATE_CULL_BACK;
|
||||
overlay_ps_ = DRW_pass_create("LookDev.Overlay", state);
|
||||
|
||||
GPUBatch *sphere = DRW_cache_sphere_get(sphere_lod_);
|
||||
|
||||
const CameraData &cam = inst_.camera.data_get();
|
||||
LightModule &lights = inst_.lights;
|
||||
LightProbeModule &lightprobes = inst_.lightprobes;
|
||||
|
||||
/* Jitter for AA. */
|
||||
float2 jitter = -0.5f + float2(inst_.sampling.rng_get(SAMPLING_FILTER_U),
|
||||
inst_.sampling.rng_get(SAMPLING_FILTER_V));
|
||||
|
||||
/* Matrix used to position the spheres in viewport space. */
|
||||
float4x4 sphere_mat = cam.viewmat;
|
||||
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
const int sphere_margin = sphere_size_ / 6;
|
||||
float2 offset = float2(0, sphere_margin);
|
||||
|
||||
std::array<::Material *, 2> materials = {inst_.materials.diffuse_mat_,
|
||||
inst_.materials.glossy_mat_};
|
||||
for (::Material *mat : materials) {
|
||||
GPUMaterial *gpumat = inst_.shaders.material_shader_get(
|
||||
mat, mat->nodetree, MAT_PIPE_FORWARD, MAT_GEOM_LOOKDEV, false);
|
||||
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, overlay_ps_);
|
||||
lights.shgroup_resources(grp);
|
||||
DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "probes_buf", lightprobes.info_ubo_get());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
|
||||
|
||||
offset.x -= sphere_size_ + sphere_margin;
|
||||
|
||||
/* Pass 2D scale and bias factor in the last column. */
|
||||
float2 scale = sphere_size_ / float2(viewport_size);
|
||||
float2 bias = -1.0f + scale +
|
||||
2.0f * (float2(anchor_) + offset + jitter) / float2(viewport_size);
|
||||
copy_v4_fl4(sphere_mat[3], UNPACK2(scale), UNPACK2(bias));
|
||||
DRW_shgroup_call_obmat(grp, sphere, sphere_mat.ptr());
|
||||
|
||||
offset.x -= sphere_margin;
|
||||
}
|
||||
|
||||
view_ = nullptr;
|
||||
}
|
||||
|
||||
/* Renders the reference spheres. */
|
||||
void LookDev::render_overlay(GPUFrameBuffer *fb)
|
||||
{
|
||||
if (sphere_size_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const DRWView *active_view = DRW_view_get_active();
|
||||
|
||||
inst_.lightprobes.set_view(active_view, int2(0));
|
||||
inst_.lights.set_view(active_view, int2(0));
|
||||
|
||||
/* Create subview for correct shading. Sub because we don not care about culling. */
|
||||
const CameraData &cam = inst_.camera.data_get();
|
||||
float4x4 winmat;
|
||||
orthographic_m4(winmat.ptr(), -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
|
||||
if (view_) {
|
||||
DRW_view_update_sub(view_, cam.viewmat.ptr(), winmat.ptr());
|
||||
}
|
||||
else {
|
||||
view_ = DRW_view_create_sub(active_view, cam.viewmat.ptr(), winmat.ptr());
|
||||
}
|
||||
|
||||
DRW_view_set_active(view_);
|
||||
|
||||
GPU_framebuffer_bind(fb);
|
||||
DRW_draw_pass(overlay_ps_);
|
||||
|
||||
DRW_view_set_active(active_view);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
112
source/blender/draw/engines/eevee/eevee_lookdev.hh
Normal file
112
source/blender/draw/engines/eevee/eevee_lookdev.hh
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2018, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_studiolight.h"
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Lookdev World Nodetree
|
||||
*
|
||||
* \{ */
|
||||
|
||||
class LookDevWorldNodeTree {
|
||||
private:
|
||||
bNodeTree *ntree_;
|
||||
bNodeSocketValueFloat *strength_socket_;
|
||||
|
||||
public:
|
||||
LookDevWorldNodeTree();
|
||||
~LookDevWorldNodeTree();
|
||||
|
||||
bNodeTree *nodetree_get(float strength);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Look Dev
|
||||
*
|
||||
* \{ */
|
||||
|
||||
class LookDev {
|
||||
private:
|
||||
Instance &inst_;
|
||||
/** Nodetree used to render the world reflection cubemap and irradiance. */
|
||||
LookDevWorldNodeTree world_tree;
|
||||
/** Compiled gpu material for the nodetree. Owned. */
|
||||
ListBase material = {nullptr, nullptr};
|
||||
/** Choosen studio light. */
|
||||
StudioLight *studiolight_ = nullptr;
|
||||
int studiolight_index_ = -1;
|
||||
/** Draw pass to draw the viewport background. */
|
||||
DRWPass *background_ps_ = nullptr;
|
||||
/** Parameters. */
|
||||
float instensity_ = -1.0f;
|
||||
float blur_ = -1.0f;
|
||||
float opacity_ = -1.0f;
|
||||
float rotation_ = -9999.0f;
|
||||
bool view_rotation_ = false;
|
||||
|
||||
/** Overlay (reference spheres). */
|
||||
DRWPass *overlay_ps_ = nullptr;
|
||||
/** View based on main view with orthographic projection. Without this, shading is incorrect. */
|
||||
DRWView *view_ = nullptr;
|
||||
/** Selected LOD of the sphere mesh. */
|
||||
eDRWLevelOfDetail sphere_lod_;
|
||||
/** Screen space radius in pixels. */
|
||||
int sphere_size_ = 0;
|
||||
/** Lower right corner of the area where we can start drawing. */
|
||||
int2 anchor_;
|
||||
|
||||
public:
|
||||
LookDev(Instance &inst) : inst_(inst){};
|
||||
~LookDev()
|
||||
{
|
||||
GPU_material_free(&material);
|
||||
};
|
||||
|
||||
void init(const int2 &output_res, const rcti *render_border);
|
||||
|
||||
void sync_background(void);
|
||||
bool sync_world(void);
|
||||
void sync_overlay(void);
|
||||
|
||||
bool render_background(void);
|
||||
void render_overlay(GPUFrameBuffer *view_fb);
|
||||
|
||||
void rotation_get(float4x4 &r_mat);
|
||||
|
||||
private:
|
||||
bool do_overlay(const int2 &output_res, const rcti *render_border);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
@@ -23,9 +23,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const float ltc_mat_ggx[64 * 64 * 4];
|
||||
extern const float ltc_mag_ggx[64 * 64 * 2];
|
||||
extern const float bsdf_split_sum_ggx[64 * 64 * 2];
|
||||
extern const float ltc_disk_integral[64 * 64];
|
||||
extern const float btdf_split_sum_ggx[16][64 * 64 * 2];
|
||||
extern const float blue_noise[64 * 64][4];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2020, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*
|
||||
* EEVEE LUT generation:
|
||||
*
|
||||
* Routine to generate the LUT used by eevee stored in eevee_lut.h
|
||||
* These functions are not to be used in the final executable.
|
||||
*/
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_rand.h"
|
||||
#include "BLI_string_utils.h"
|
||||
|
||||
#include "eevee_private.h"
|
||||
|
||||
#define DO_FILE_OUTPUT 0
|
||||
|
||||
float *EEVEE_lut_update_ggx_brdf(int lut_size)
|
||||
{
|
||||
DRWPass *pass = DRW_pass_create(__func__, DRW_STATE_WRITE_COLOR);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_ggx_lut_sh_get(), pass);
|
||||
DRW_shgroup_uniform_float_copy(grp, "sampleCount", 64.0f); /* Actual sample count is squared. */
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
GPUTexture *tex = DRW_texture_create_2d(lut_size, lut_size, GPU_RG16F, 0, NULL);
|
||||
GPUFrameBuffer *fb = NULL;
|
||||
GPU_framebuffer_ensure_config(&fb,
|
||||
{
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(tex),
|
||||
});
|
||||
GPU_framebuffer_bind(fb);
|
||||
DRW_draw_pass(pass);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fb);
|
||||
|
||||
float *data = GPU_texture_read(tex, GPU_DATA_FLOAT, 0);
|
||||
GPU_texture_free(tex);
|
||||
#if DO_FILE_OUTPUT
|
||||
/* Content is to be put inside eevee_lut.c */
|
||||
FILE *f = BLI_fopen("bsdf_split_sum_ggx.h", "w");
|
||||
fprintf(f, "const float bsdf_split_sum_ggx[%d * %d * 2] = {", lut_size, lut_size);
|
||||
for (int i = 0; i < lut_size * lut_size * 2;) {
|
||||
fprintf(f, "\n ");
|
||||
for (int j = 0; j < 4; j++, i += 2) {
|
||||
fprintf(f, "%ff, %ff, ", data[i], data[i + 1]);
|
||||
}
|
||||
}
|
||||
fprintf(f, "\n};\n");
|
||||
fclose(f);
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
float *EEVEE_lut_update_ggx_btdf(int lut_size, int lut_depth)
|
||||
{
|
||||
float roughness;
|
||||
DRWPass *pass = DRW_pass_create(__func__, DRW_STATE_WRITE_COLOR);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_ggx_refraction_lut_sh_get(), pass);
|
||||
DRW_shgroup_uniform_float_copy(grp, "sampleCount", 64.0f); /* Actual sample count is squared. */
|
||||
DRW_shgroup_uniform_float(grp, "z", &roughness, 1);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
GPUTexture *tex = DRW_texture_create_2d_array(lut_size, lut_size, lut_depth, GPU_RG16F, 0, NULL);
|
||||
GPUFrameBuffer *fb = NULL;
|
||||
for (int i = 0; i < lut_depth; i++) {
|
||||
GPU_framebuffer_ensure_config(&fb,
|
||||
{
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE_LAYER(tex, i),
|
||||
});
|
||||
GPU_framebuffer_bind(fb);
|
||||
roughness = i / (lut_depth - 1.0f);
|
||||
DRW_draw_pass(pass);
|
||||
}
|
||||
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fb);
|
||||
|
||||
float *data = GPU_texture_read(tex, GPU_DATA_FLOAT, 0);
|
||||
GPU_texture_free(tex);
|
||||
|
||||
#if DO_FILE_OUTPUT
|
||||
/* Content is to be put inside eevee_lut.c. Don't forget to format the output. */
|
||||
FILE *f = BLI_fopen("btdf_split_sum_ggx.h", "w");
|
||||
fprintf(f, "const float btdf_split_sum_ggx[%d][%d * %d * 2] = {", lut_depth, lut_size, lut_size);
|
||||
fprintf(f, "\n ");
|
||||
int ofs = 0;
|
||||
for (int d = 0; d < lut_depth; d++) {
|
||||
fprintf(f, "{\n");
|
||||
for (int i = 0; i < lut_size * lut_size * 2;) {
|
||||
for (int j = 0; j < 4; j++, i += 2, ofs += 2) {
|
||||
fprintf(f, "%ff, %ff, ", data[ofs], data[ofs + 1]);
|
||||
}
|
||||
fprintf(f, "\n ");
|
||||
}
|
||||
fprintf(f, "},\n");
|
||||
}
|
||||
fprintf(f, "};\n");
|
||||
fclose(f);
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
335
source/blender/draw/engines/eevee/eevee_material.cc
Normal file
335
source/blender/draw/engines/eevee/eevee_material.cc
Normal file
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_node.h"
|
||||
#include "NOD_shader.h"
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
#include "eevee_material.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Default Material
|
||||
*
|
||||
* \{ */
|
||||
|
||||
DefaultSurfaceNodeTree::DefaultSurfaceNodeTree()
|
||||
{
|
||||
bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
|
||||
bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_PRINCIPLED);
|
||||
bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL);
|
||||
bNodeSocket *bsdf_out = nodeFindSocket(bsdf, SOCK_OUT, "BSDF");
|
||||
bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface");
|
||||
nodeAddLink(ntree, bsdf, bsdf_out, output, output_in);
|
||||
nodeSetActive(ntree, output);
|
||||
|
||||
color_socket_ =
|
||||
(bNodeSocketValueRGBA *)nodeFindSocket(bsdf, SOCK_IN, "Base Color")->default_value;
|
||||
metallic_socket_ =
|
||||
(bNodeSocketValueFloat *)nodeFindSocket(bsdf, SOCK_IN, "Metallic")->default_value;
|
||||
roughness_socket_ =
|
||||
(bNodeSocketValueFloat *)nodeFindSocket(bsdf, SOCK_IN, "Roughness")->default_value;
|
||||
specular_socket_ =
|
||||
(bNodeSocketValueFloat *)nodeFindSocket(bsdf, SOCK_IN, "Specular")->default_value;
|
||||
ntree_ = ntree;
|
||||
}
|
||||
|
||||
DefaultSurfaceNodeTree::~DefaultSurfaceNodeTree()
|
||||
{
|
||||
ntreeFreeEmbeddedTree(ntree_);
|
||||
MEM_SAFE_FREE(ntree_);
|
||||
}
|
||||
|
||||
/* Configure a default nodetree with the given material. */
|
||||
bNodeTree *DefaultSurfaceNodeTree::nodetree_get(::Material *ma)
|
||||
{
|
||||
/* WARNING: This function is not threadsafe. Which is not a problem for the moment. */
|
||||
copy_v3_fl3(color_socket_->value, ma->r, ma->g, ma->b);
|
||||
metallic_socket_->value = ma->metallic;
|
||||
roughness_socket_->value = ma->roughness;
|
||||
specular_socket_->value = ma->spec;
|
||||
|
||||
return ntree_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Material
|
||||
*
|
||||
* \{ */
|
||||
|
||||
MaterialModule::MaterialModule(Instance &inst) : inst_(inst)
|
||||
{
|
||||
{
|
||||
bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
|
||||
|
||||
diffuse_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default diffuse");
|
||||
diffuse_mat_->nodetree = ntree;
|
||||
diffuse_mat_->use_nodes = true;
|
||||
/* To use the forward pipeline. */
|
||||
diffuse_mat_->blend_method = MA_BM_BLEND;
|
||||
|
||||
bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_DIFFUSE);
|
||||
bNodeSocket *base_color = nodeFindSocket(bsdf, SOCK_IN, "Color");
|
||||
copy_v3_fl(((bNodeSocketValueRGBA *)base_color->default_value)->value, 0.8f);
|
||||
|
||||
bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL);
|
||||
|
||||
nodeAddLink(ntree,
|
||||
bsdf,
|
||||
nodeFindSocket(bsdf, SOCK_OUT, "BSDF"),
|
||||
output,
|
||||
nodeFindSocket(output, SOCK_IN, "Surface"));
|
||||
|
||||
nodeSetActive(ntree, output);
|
||||
}
|
||||
{
|
||||
bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
|
||||
|
||||
glossy_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default metal");
|
||||
glossy_mat_->nodetree = ntree;
|
||||
glossy_mat_->use_nodes = true;
|
||||
/* To use the forward pipeline. */
|
||||
glossy_mat_->blend_method = MA_BM_BLEND;
|
||||
|
||||
bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_GLOSSY);
|
||||
bNodeSocket *base_color = nodeFindSocket(bsdf, SOCK_IN, "Color");
|
||||
copy_v3_fl(((bNodeSocketValueRGBA *)base_color->default_value)->value, 1.0f);
|
||||
bNodeSocket *roughness = nodeFindSocket(bsdf, SOCK_IN, "Roughness");
|
||||
((bNodeSocketValueFloat *)roughness->default_value)->value = 0.0f;
|
||||
|
||||
bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL);
|
||||
|
||||
nodeAddLink(ntree,
|
||||
bsdf,
|
||||
nodeFindSocket(bsdf, SOCK_OUT, "BSDF"),
|
||||
output,
|
||||
nodeFindSocket(output, SOCK_IN, "Surface"));
|
||||
|
||||
nodeSetActive(ntree, output);
|
||||
}
|
||||
{
|
||||
bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
|
||||
|
||||
error_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default error");
|
||||
error_mat_->nodetree = ntree;
|
||||
error_mat_->use_nodes = true;
|
||||
|
||||
/* Use emission and output material to be compatible with both World and Material. */
|
||||
bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION);
|
||||
bNodeSocket *color = nodeFindSocket(bsdf, SOCK_IN, "Color");
|
||||
copy_v3_fl3(((bNodeSocketValueRGBA *)color->default_value)->value, 1.0f, 0.0f, 1.0f);
|
||||
|
||||
bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL);
|
||||
|
||||
nodeAddLink(ntree,
|
||||
bsdf,
|
||||
nodeFindSocket(bsdf, SOCK_OUT, "Emission"),
|
||||
output,
|
||||
nodeFindSocket(output, SOCK_IN, "Surface"));
|
||||
|
||||
nodeSetActive(ntree, output);
|
||||
}
|
||||
}
|
||||
|
||||
MaterialModule::~MaterialModule()
|
||||
{
|
||||
for (Material *mat : material_map_.values()) {
|
||||
delete mat;
|
||||
mat = nullptr;
|
||||
}
|
||||
for (DRWShadingGroup **shgroup : shader_map_.values()) {
|
||||
delete shgroup;
|
||||
shgroup = nullptr;
|
||||
}
|
||||
BKE_id_free(NULL, glossy_mat_);
|
||||
BKE_id_free(NULL, diffuse_mat_);
|
||||
BKE_id_free(NULL, error_mat_);
|
||||
}
|
||||
|
||||
void MaterialModule::begin_sync(void)
|
||||
{
|
||||
queued_shaders_count_ = 0;
|
||||
|
||||
for (Material *mat : material_map_.values()) {
|
||||
mat->init = false;
|
||||
}
|
||||
for (DRWShadingGroup **shgroup : shader_map_.values()) {
|
||||
*shgroup = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
MaterialPass MaterialModule::material_pass_get(::Material *blender_mat,
|
||||
eMaterialPipeline pipeline_type,
|
||||
eMaterialGeometry geometry_type)
|
||||
{
|
||||
bNodeTree *ntree = (blender_mat->use_nodes && blender_mat->nodetree != nullptr) ?
|
||||
blender_mat->nodetree :
|
||||
default_surface_ntree_.nodetree_get(blender_mat);
|
||||
|
||||
MaterialPass matpass;
|
||||
matpass.gpumat = inst_.shaders.material_shader_get(
|
||||
blender_mat, ntree, pipeline_type, geometry_type, true);
|
||||
|
||||
switch (GPU_material_status(matpass.gpumat)) {
|
||||
case GPU_MAT_SUCCESS:
|
||||
break;
|
||||
case GPU_MAT_QUEUED:
|
||||
queued_shaders_count_++;
|
||||
blender_mat = (geometry_type == MAT_GEOM_VOLUME) ? BKE_material_default_volume() :
|
||||
BKE_material_default_surface();
|
||||
matpass.gpumat = inst_.shaders.material_shader_get(
|
||||
blender_mat, blender_mat->nodetree, pipeline_type, geometry_type, false);
|
||||
break;
|
||||
case GPU_MAT_FAILED:
|
||||
default:
|
||||
matpass.gpumat = inst_.shaders.material_shader_get(
|
||||
error_mat_, error_mat_->nodetree, pipeline_type, geometry_type, false);
|
||||
break;
|
||||
}
|
||||
/* Returned material should be ready to be drawn. */
|
||||
BLI_assert(GPU_material_status(matpass.gpumat) == GPU_MAT_SUCCESS);
|
||||
|
||||
if (GPU_material_recalc_flag_get(matpass.gpumat)) {
|
||||
inst_.sampling.reset();
|
||||
}
|
||||
|
||||
if ((pipeline_type == MAT_PIPE_DEFERRED) &&
|
||||
GPU_material_flag_get(matpass.gpumat, GPU_MATFLAG_SHADER_TO_RGBA)) {
|
||||
pipeline_type = MAT_PIPE_FORWARD;
|
||||
}
|
||||
|
||||
if ((pipeline_type == MAT_PIPE_FORWARD) &&
|
||||
GPU_material_flag_get(matpass.gpumat, GPU_MATFLAG_TRANSPARENT)) {
|
||||
/* Transparent needs to use one shgroup per object to support reordering. */
|
||||
matpass.shgrp = inst_.shading_passes.material_add(blender_mat, matpass.gpumat, pipeline_type);
|
||||
}
|
||||
else {
|
||||
ShaderKey shader_key(matpass.gpumat, geometry_type, pipeline_type);
|
||||
|
||||
/* TODO(fclem) allocate in blocks to avoid memory fragmentation. */
|
||||
auto add_cb = [&]() { return new DRWShadingGroup *(); };
|
||||
DRWShadingGroup *&grp = *shader_map_.lookup_or_add_cb(shader_key, add_cb);
|
||||
|
||||
if (grp == nullptr) {
|
||||
/* First time encountering this shader. Create a shading group. */
|
||||
grp = inst_.shading_passes.material_add(blender_mat, matpass.gpumat, pipeline_type);
|
||||
}
|
||||
|
||||
if (grp != nullptr) {
|
||||
/* Shading group for this shader already exists. Create a sub one for this material. */
|
||||
/* IMPORTANT: We always create a subgroup so that all subgroups are inserted after the
|
||||
* first "empty" shgroup. This avoids messing the order of subgroups when there is more
|
||||
* nested subgroup (i.e: hair drawing). */
|
||||
/* TODO(fclem) Remove material resource binding from the first group creation. */
|
||||
matpass.shgrp = DRW_shgroup_create_sub(grp);
|
||||
DRW_shgroup_add_material_resources(matpass.shgrp, matpass.gpumat);
|
||||
}
|
||||
}
|
||||
|
||||
return matpass;
|
||||
}
|
||||
|
||||
Material &MaterialModule::material_sync(::Material *blender_mat, eMaterialGeometry geometry_type)
|
||||
{
|
||||
eMaterialPipeline surface_pipe = (blender_mat->blend_method == MA_BM_BLEND) ? MAT_PIPE_FORWARD :
|
||||
MAT_PIPE_DEFERRED;
|
||||
eMaterialPipeline prepass_pipe = (blender_mat->blend_method == MA_BM_BLEND) ?
|
||||
MAT_PIPE_FORWARD_PREPASS :
|
||||
MAT_PIPE_DEFERRED_PREPASS;
|
||||
|
||||
MaterialKey material_key(blender_mat, geometry_type, surface_pipe);
|
||||
|
||||
/* TODO allocate in blocks to avoid memory fragmentation. */
|
||||
auto add_cb = [&]() { return new Material(); };
|
||||
Material &mat = *material_map_.lookup_or_add_cb(material_key, add_cb);
|
||||
|
||||
/* Forward pipeline needs to use one shgroup per object. */
|
||||
if (mat.init == false || (surface_pipe == MAT_PIPE_FORWARD)) {
|
||||
mat.init = true;
|
||||
/* Order is important for transparent. */
|
||||
mat.prepass = material_pass_get(blender_mat, prepass_pipe, geometry_type);
|
||||
mat.shading = material_pass_get(blender_mat, surface_pipe, geometry_type);
|
||||
mat.shadow = material_pass_get(blender_mat, MAT_PIPE_SHADOW, geometry_type);
|
||||
|
||||
mat.is_alpha_blend_transparent = (blender_mat->blend_method == MA_BM_BLEND) &&
|
||||
GPU_material_flag_get(mat.prepass.gpumat,
|
||||
GPU_MATFLAG_TRANSPARENT);
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
/* Return correct material or empty default material if slot is empty. */
|
||||
::Material *MaterialModule::material_from_slot(Object *ob, int slot)
|
||||
{
|
||||
if (ob->base_flag & BASE_HOLDOUT) {
|
||||
return BKE_material_default_holdout();
|
||||
}
|
||||
::Material *ma = BKE_object_material_get(ob, slot + 1);
|
||||
if (ma == nullptr) {
|
||||
if (ob->type == OB_VOLUME) {
|
||||
return BKE_material_default_volume();
|
||||
}
|
||||
else {
|
||||
return BKE_material_default_surface();
|
||||
}
|
||||
}
|
||||
return ma;
|
||||
}
|
||||
|
||||
/* Return Material references are valid until the next call to this function or
|
||||
* material_get(). */
|
||||
MaterialArray &MaterialModule::material_array_get(Object *ob)
|
||||
{
|
||||
material_array_.materials.clear();
|
||||
material_array_.gpu_materials.clear();
|
||||
|
||||
const int materials_len = DRW_cache_object_material_count_get(ob);
|
||||
|
||||
for (auto i : IndexRange(materials_len)) {
|
||||
::Material *blender_mat = material_from_slot(ob, i);
|
||||
Material &mat = material_sync(blender_mat, to_material_geometry(ob));
|
||||
material_array_.materials.append(&mat);
|
||||
material_array_.gpu_materials.append(mat.shading.gpumat);
|
||||
}
|
||||
return material_array_;
|
||||
}
|
||||
|
||||
/* Return Material references are valid until the next call to this function or
|
||||
* material_array_get(). */
|
||||
Material &MaterialModule::material_get(Object *ob, int mat_nr, eMaterialGeometry geometry_type)
|
||||
{
|
||||
::Material *blender_mat = material_from_slot(ob, mat_nr);
|
||||
Material &mat = material_sync(blender_mat, geometry_type);
|
||||
return mat;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
123
source/blender/draw/engines/eevee/eevee_material.hh
Normal file
123
source/blender/draw/engines/eevee/eevee_material.hh
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_vector.hh"
|
||||
#include "GPU_material.h"
|
||||
|
||||
#include "eevee_id_map.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Default Material Nodetree
|
||||
*
|
||||
* In order to support materials without nodetree we reuse and configure a standalone nodetree that
|
||||
* we pass for shader generation. The GPUMaterial is still stored inside the Material even if
|
||||
* it does not use a nodetree.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
class DefaultSurfaceNodeTree {
|
||||
private:
|
||||
bNodeTree *ntree_;
|
||||
bNodeSocketValueRGBA *color_socket_;
|
||||
bNodeSocketValueFloat *metallic_socket_;
|
||||
bNodeSocketValueFloat *roughness_socket_;
|
||||
bNodeSocketValueFloat *specular_socket_;
|
||||
|
||||
public:
|
||||
DefaultSurfaceNodeTree();
|
||||
~DefaultSurfaceNodeTree();
|
||||
|
||||
bNodeTree *nodetree_get(::Material *ma);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Material
|
||||
*
|
||||
* \{ */
|
||||
|
||||
struct MaterialPass {
|
||||
GPUMaterial *gpumat = nullptr;
|
||||
DRWShadingGroup *shgrp = nullptr;
|
||||
};
|
||||
|
||||
struct Material {
|
||||
bool init = false;
|
||||
bool is_alpha_blend_transparent;
|
||||
MaterialPass shadow, shading, prepass;
|
||||
};
|
||||
|
||||
struct MaterialArray {
|
||||
Vector<Material *> materials;
|
||||
Vector<GPUMaterial *> gpu_materials;
|
||||
};
|
||||
|
||||
class MaterialModule {
|
||||
public:
|
||||
::Material *diffuse_mat_;
|
||||
::Material *glossy_mat_;
|
||||
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
Map<MaterialKey, Material *> material_map_;
|
||||
Map<ShaderKey, DRWShadingGroup **> shader_map_;
|
||||
|
||||
MaterialArray material_array_;
|
||||
|
||||
DefaultSurfaceNodeTree default_surface_ntree_;
|
||||
|
||||
::Material *error_mat_;
|
||||
|
||||
int64_t queued_shaders_count_ = 0;
|
||||
|
||||
public:
|
||||
MaterialModule(Instance &inst);
|
||||
~MaterialModule();
|
||||
|
||||
void begin_sync(void);
|
||||
|
||||
MaterialArray &material_array_get(Object *ob);
|
||||
Material &material_get(Object *ob, int mat_nr, eMaterialGeometry geometry_type);
|
||||
|
||||
private:
|
||||
Material &material_sync(::Material *blender_mat, eMaterialGeometry geometry_type);
|
||||
|
||||
::Material *material_from_slot(Object *ob, int slot);
|
||||
MaterialPass material_pass_get(::Material *blender_mat,
|
||||
eMaterialPipeline pipeline_type,
|
||||
eMaterialGeometry geometry_type);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
File diff suppressed because it is too large
Load Diff
58
source/blender/draw/engines/eevee/eevee_mesh.cc
Normal file
58
source/blender/draw/engines/eevee/eevee_mesh.cc
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
void Instance::mesh_sync(Object *ob, ObjectHandle &ob_handle)
|
||||
{
|
||||
MaterialArray &material_array = materials.material_array_get(ob);
|
||||
|
||||
GPUBatch **mat_geom = DRW_cache_object_surface_material_get(
|
||||
ob, material_array.gpu_materials.data(), material_array.gpu_materials.size());
|
||||
|
||||
if (mat_geom == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_shadow_caster = false;
|
||||
bool is_alpha_blend = false;
|
||||
for (auto i : material_array.gpu_materials.index_range()) {
|
||||
GPUBatch *geom = mat_geom[i];
|
||||
if (geom == nullptr) {
|
||||
continue;
|
||||
}
|
||||
Material *material = material_array.materials[i];
|
||||
shgroup_geometry_call(material->shading.shgrp, ob, geom);
|
||||
shgroup_geometry_call(material->prepass.shgrp, ob, geom);
|
||||
shgroup_geometry_call(material->shadow.shgrp, ob, geom);
|
||||
|
||||
is_shadow_caster = is_shadow_caster || material->shadow.shgrp != nullptr;
|
||||
is_alpha_blend = is_alpha_blend || material->is_alpha_blend_transparent;
|
||||
}
|
||||
shading_passes.velocity.mesh_add(ob, ob_handle);
|
||||
|
||||
shadows.sync_object(ob, ob_handle, is_shadow_caster, is_alpha_blend);
|
||||
}
|
||||
|
||||
} // namespace blender::eevee
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*
|
||||
* Implementation of Blender Mist pass.
|
||||
* IMPORTANT: This is a "post process" of the Z depth so it will lack any transparent objects.
|
||||
*/
|
||||
|
||||
#include "DRW_engine.h"
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "BLI_string_utils.h"
|
||||
|
||||
#include "eevee_private.h"
|
||||
|
||||
void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
Scene *scene = draw_ctx->scene;
|
||||
|
||||
/* Create FrameBuffer. */
|
||||
/* Should be enough precision for many samples. */
|
||||
DRW_texture_ensure_fullscreen_2d(&txl->mist_accum, GPU_R32F, 0);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->mist_accum_fb,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->mist_accum)});
|
||||
|
||||
/* Mist settings. */
|
||||
if (scene && scene->world) {
|
||||
g_data->mist_start = scene->world->miststa;
|
||||
g_data->mist_inv_dist = (scene->world->mistdist > 0.0f) ? 1.0f / scene->world->mistdist : 0.0f;
|
||||
|
||||
switch (scene->world->mistype) {
|
||||
case WO_MIST_QUADRATIC:
|
||||
g_data->mist_falloff = 2.0f;
|
||||
break;
|
||||
case WO_MIST_LINEAR:
|
||||
g_data->mist_falloff = 1.0f;
|
||||
break;
|
||||
case WO_MIST_INVERSE_QUADRATIC:
|
||||
g_data->mist_falloff = 0.5f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
float near = DRW_view_near_distance_get(NULL);
|
||||
float far = DRW_view_far_distance_get(NULL);
|
||||
/* Fallback */
|
||||
g_data->mist_start = near;
|
||||
g_data->mist_inv_dist = 1.0f / fabsf(far - near);
|
||||
g_data->mist_falloff = 1.0f;
|
||||
}
|
||||
|
||||
/* XXX ??!! WHY? If not it does not match cycles. */
|
||||
g_data->mist_falloff *= 0.5f;
|
||||
|
||||
/* Create Pass and shgroup. */
|
||||
DRW_PASS_CREATE(psl->mist_accum_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_effect_mist_sh_get(),
|
||||
psl->mist_accum_ps);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
|
||||
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
|
||||
DRW_shgroup_uniform_vec3(grp, "mistSettings", &g_data->mist_start, 1);
|
||||
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
|
||||
}
|
||||
|
||||
void EEVEE_mist_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_EffectsInfo *effects = vedata->stl->effects;
|
||||
|
||||
if (fbl->mist_accum_fb != NULL) {
|
||||
GPU_framebuffer_bind(fbl->mist_accum_fb);
|
||||
|
||||
/* Clear texture. */
|
||||
if (effects->taa_current_sample == 1) {
|
||||
const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
GPU_framebuffer_clear_color(fbl->mist_accum_fb, clear);
|
||||
}
|
||||
|
||||
DRW_draw_pass(psl->mist_accum_ps);
|
||||
|
||||
/* Restore */
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
}
|
||||
}
|
||||
@@ -1,613 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*
|
||||
* Gather all screen space effects technique such as Bloom, Motion Blur, DoF, SSAO, SSR, ...
|
||||
*/
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BLI_rand.h"
|
||||
#include "BLI_string_utils.h"
|
||||
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_camera.h"
|
||||
#include "BKE_duplilist.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_screen.h"
|
||||
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_particle_types.h"
|
||||
#include "DNA_rigidbody_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
|
||||
#include "ED_screen.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
#include "GPU_texture.h"
|
||||
#include "eevee_private.h"
|
||||
|
||||
int EEVEE_motion_blur_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
Scene *scene = draw_ctx->scene;
|
||||
|
||||
/* Viewport not supported for now. */
|
||||
if (!DRW_state_is_scene_render()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
effects->motion_blur_max = max_ii(0, scene->eevee.motion_blur_max);
|
||||
|
||||
if ((effects->motion_blur_max > 0) && (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED)) {
|
||||
if (DRW_state_is_scene_render()) {
|
||||
int mb_step = effects->motion_blur_step;
|
||||
DRW_view_viewmat_get(NULL, effects->motion_blur.camera[mb_step].viewmat, false);
|
||||
DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persmat, false);
|
||||
DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persinv, true);
|
||||
}
|
||||
|
||||
const float *fs_size = DRW_viewport_size_get();
|
||||
const int tx_size[2] = {
|
||||
1 + ((int)fs_size[0] / EEVEE_VELOCITY_TILE_SIZE),
|
||||
1 + ((int)fs_size[1] / EEVEE_VELOCITY_TILE_SIZE),
|
||||
};
|
||||
|
||||
effects->velocity_tiles_x_tx = DRW_texture_pool_query_2d(
|
||||
tx_size[0], fs_size[1], GPU_RGBA16, &draw_engine_eevee_type);
|
||||
GPU_framebuffer_ensure_config(&fbl->velocity_tiles_fb[0],
|
||||
{
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(effects->velocity_tiles_x_tx),
|
||||
});
|
||||
|
||||
effects->velocity_tiles_tx = DRW_texture_pool_query_2d(
|
||||
tx_size[0], tx_size[1], GPU_RGBA16, &draw_engine_eevee_type);
|
||||
GPU_framebuffer_ensure_config(&fbl->velocity_tiles_fb[1],
|
||||
{
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(effects->velocity_tiles_tx),
|
||||
});
|
||||
|
||||
return EFFECT_MOTION_BLUR | EFFECT_POST_BUFFER | EFFECT_VELOCITY_BUFFER;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EEVEE_motion_blur_step_set(EEVEE_Data *vedata, int step)
|
||||
{
|
||||
BLI_assert(step < 3);
|
||||
vedata->stl->effects->motion_blur_step = step;
|
||||
}
|
||||
|
||||
static void eevee_motion_blur_sync_camera(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_EffectsInfo *effects = vedata->stl->effects;
|
||||
if (DRW_state_is_scene_render()) {
|
||||
int mb_step = effects->motion_blur_step;
|
||||
DRW_view_viewmat_get(NULL, effects->motion_blur.camera[mb_step].viewmat, false);
|
||||
DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persmat, false);
|
||||
DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persinv, true);
|
||||
}
|
||||
|
||||
effects->motion_blur_near_far[0] = fabsf(DRW_view_near_distance_get(NULL));
|
||||
effects->motion_blur_near_far[1] = fabsf(DRW_view_far_distance_get(NULL));
|
||||
}
|
||||
|
||||
void EEVEE_motion_blur_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
EEVEE_MotionBlurData *mb_data = &effects->motion_blur;
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
Scene *scene = draw_ctx->scene;
|
||||
|
||||
if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) {
|
||||
const float *fs_size = DRW_viewport_size_get();
|
||||
const int tx_size[2] = {
|
||||
GPU_texture_width(effects->velocity_tiles_tx),
|
||||
GPU_texture_height(effects->velocity_tiles_tx),
|
||||
};
|
||||
|
||||
eevee_motion_blur_sync_camera(vedata);
|
||||
|
||||
DRWShadingGroup *grp;
|
||||
{
|
||||
DRW_PASS_CREATE(psl->velocity_tiles_x, DRW_STATE_WRITE_COLOR);
|
||||
DRW_PASS_CREATE(psl->velocity_tiles, DRW_STATE_WRITE_COLOR);
|
||||
|
||||
/* Create max velocity tiles in 2 passes. One for X and one for Y */
|
||||
GPUShader *sh = EEVEE_shaders_effect_motion_blur_velocity_tiles_sh_get();
|
||||
grp = DRW_shgroup_create(sh, psl->velocity_tiles_x);
|
||||
DRW_shgroup_uniform_texture(grp, "velocityBuffer", effects->velocity_tx);
|
||||
DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", (int[2]){fs_size[0], fs_size[1]});
|
||||
DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1);
|
||||
DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1);
|
||||
DRW_shgroup_uniform_ivec2_copy(grp, "gatherStep", (int[2]){1, 0});
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
grp = DRW_shgroup_create(sh, psl->velocity_tiles);
|
||||
DRW_shgroup_uniform_texture(grp, "velocityBuffer", effects->velocity_tiles_x_tx);
|
||||
DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", (int[2]){tx_size[0], fs_size[1]});
|
||||
DRW_shgroup_uniform_ivec2_copy(grp, "gatherStep", (int[2]){0, 1});
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
/* Expand max tiles by keeping the max tile in each tile neighborhood. */
|
||||
DRW_PASS_CREATE(psl->velocity_tiles_expand[0], DRW_STATE_WRITE_COLOR);
|
||||
DRW_PASS_CREATE(psl->velocity_tiles_expand[1], DRW_STATE_WRITE_COLOR);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
GPUTexture *tile_tx = (i == 0) ? effects->velocity_tiles_tx : effects->velocity_tiles_x_tx;
|
||||
GPUShader *sh_expand = EEVEE_shaders_effect_motion_blur_velocity_tiles_expand_sh_get();
|
||||
grp = DRW_shgroup_create(sh_expand, psl->velocity_tiles_expand[i]);
|
||||
DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", tx_size);
|
||||
DRW_shgroup_uniform_texture(grp, "velocityBuffer", tile_tx);
|
||||
DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1);
|
||||
DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
}
|
||||
}
|
||||
{
|
||||
DRW_PASS_CREATE(psl->motion_blur, DRW_STATE_WRITE_COLOR);
|
||||
eGPUSamplerState state = 0;
|
||||
int expand_steps = 1 + (max_ii(0, effects->motion_blur_max - 1) / EEVEE_VELOCITY_TILE_SIZE);
|
||||
GPUTexture *tile_tx = (expand_steps & 1) ? effects->velocity_tiles_x_tx :
|
||||
effects->velocity_tiles_tx;
|
||||
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_sh_get(), psl->motion_blur);
|
||||
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &effects->source_buffer, state);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &dtxl->depth, state);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "velocityBuffer", &effects->velocity_tx, state);
|
||||
DRW_shgroup_uniform_texture(grp, "tileMaxBuffer", tile_tx);
|
||||
DRW_shgroup_uniform_float_copy(grp, "depthScale", scene->eevee.motion_blur_depth_scale);
|
||||
DRW_shgroup_uniform_vec2(grp, "nearFar", effects->motion_blur_near_far, 1);
|
||||
DRW_shgroup_uniform_bool_copy(grp, "isPerspective", DRW_view_is_persp_get(NULL));
|
||||
DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1);
|
||||
DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1);
|
||||
DRW_shgroup_uniform_ivec2_copy(grp, "tileBufferSize", tx_size);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
}
|
||||
{
|
||||
DRW_PASS_CREATE(psl->velocity_object, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
|
||||
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_object_sh_get(),
|
||||
psl->velocity_object);
|
||||
DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat);
|
||||
DRW_shgroup_uniform_mat4(grp, "currViewProjMatrix", mb_data->camera[MB_CURR].persmat);
|
||||
DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat);
|
||||
|
||||
DRW_PASS_CREATE(psl->velocity_hair, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
|
||||
|
||||
mb_data->hair_grp = grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_hair_sh_get(),
|
||||
psl->velocity_hair);
|
||||
DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat);
|
||||
DRW_shgroup_uniform_mat4(grp, "currViewProjMatrix", mb_data->camera[MB_CURR].persmat);
|
||||
DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat);
|
||||
|
||||
DRW_pass_link(psl->velocity_object, psl->velocity_hair);
|
||||
}
|
||||
|
||||
EEVEE_motion_blur_data_init(mb_data);
|
||||
}
|
||||
else {
|
||||
psl->motion_blur = NULL;
|
||||
psl->velocity_object = NULL;
|
||||
psl->velocity_hair = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
|
||||
EEVEE_Data *vedata,
|
||||
Object *ob,
|
||||
ParticleSystem *psys,
|
||||
ModifierData *md)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
DRWShadingGroup *grp = NULL;
|
||||
|
||||
if (!DRW_state_is_scene_render() || psl->velocity_hair == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* For now we assume hair objects are always moving. */
|
||||
EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(
|
||||
&effects->motion_blur, ob, true);
|
||||
|
||||
if (mb_data) {
|
||||
int mb_step = effects->motion_blur_step;
|
||||
/* Store transform. */
|
||||
DRW_hair_duplimat_get(ob, psys, md, mb_data->obmat[mb_step]);
|
||||
|
||||
EEVEE_HairMotionData *mb_hair = EEVEE_motion_blur_hair_data_get(&effects->motion_blur, ob);
|
||||
int psys_id = (md != NULL) ? BLI_findindex(&ob->modifiers, md) : 0;
|
||||
|
||||
if (psys_id >= mb_hair->psys_len) {
|
||||
/* This should never happen. It means the modifier list was changed by frame evaluation. */
|
||||
BLI_assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mb_step == MB_CURR) {
|
||||
/* Fill missing matrices if the object was hidden in previous or next frame. */
|
||||
if (is_zero_m4(mb_data->obmat[MB_PREV])) {
|
||||
copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]);
|
||||
}
|
||||
if (is_zero_m4(mb_data->obmat[MB_NEXT])) {
|
||||
copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]);
|
||||
}
|
||||
|
||||
GPUTexture *tex_prev = mb_hair->psys[psys_id].hair_pos_tx[MB_PREV];
|
||||
GPUTexture *tex_next = mb_hair->psys[psys_id].hair_pos_tx[MB_NEXT];
|
||||
|
||||
grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp, NULL);
|
||||
DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]);
|
||||
DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]);
|
||||
DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]);
|
||||
DRW_shgroup_uniform_texture(grp, "prvBuffer", tex_prev);
|
||||
DRW_shgroup_uniform_texture(grp, "nxtBuffer", tex_next);
|
||||
DRW_shgroup_uniform_bool(grp, "useDeform", &mb_hair->use_deform, 1);
|
||||
}
|
||||
else {
|
||||
/* Store vertex position buffer. */
|
||||
mb_hair->psys[psys_id].hair_pos[mb_step] = DRW_hair_pos_buffer_get(ob, psys, md);
|
||||
mb_hair->use_deform = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
|
||||
EEVEE_Data *vedata,
|
||||
Object *ob)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
DRWShadingGroup *grp = NULL;
|
||||
|
||||
if (!DRW_state_is_scene_render() || psl->velocity_object == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
RigidBodyOb *rbo = ob->rigidbody_object;
|
||||
|
||||
/* active rigidbody objects only, as only those are affected by sim. */
|
||||
const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE));
|
||||
#if 0
|
||||
/* For now we assume dupli objects are moving. */
|
||||
const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0;
|
||||
const bool object_moves = is_dupli || has_rigidbody || BKE_object_moves_in_time(ob, true);
|
||||
#else
|
||||
/* BKE_object_moves_in_time does not work in some cases.
|
||||
* Better detect non moving object after evaluation. */
|
||||
const bool object_moves = true;
|
||||
#endif
|
||||
const bool is_deform = BKE_object_is_deform_modified(DRW_context_state_get()->scene, ob) ||
|
||||
(has_rigidbody && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0);
|
||||
|
||||
if (!(object_moves || is_deform)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const DupliObject *dup = DRW_object_get_dupli(ob);
|
||||
if (dup != NULL && dup->ob->data != dup->ob_data) {
|
||||
/* Geometry instances do not support motion blur correctly yet. The #key used in
|
||||
* #motion_blur_deform_data_get has to take ids of instances (#DupliObject.persistent_id) into
|
||||
* account. Otherwise it can't find matching geometry instances at different points in time. */
|
||||
return;
|
||||
}
|
||||
|
||||
EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(
|
||||
&effects->motion_blur, ob, false);
|
||||
|
||||
if (mb_data) {
|
||||
int mb_step = effects->motion_blur_step;
|
||||
/* Store transform. */
|
||||
copy_m4_m4(mb_data->obmat[mb_step], ob->obmat);
|
||||
|
||||
EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(&effects->motion_blur,
|
||||
ob);
|
||||
|
||||
if (mb_step == MB_CURR) {
|
||||
GPUBatch *batch = DRW_cache_object_surface_get(ob);
|
||||
if (batch == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fill missing matrices if the object was hidden in previous or next frame. */
|
||||
if (is_zero_m4(mb_data->obmat[MB_PREV])) {
|
||||
copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]);
|
||||
}
|
||||
if (is_zero_m4(mb_data->obmat[MB_NEXT])) {
|
||||
copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]);
|
||||
}
|
||||
|
||||
if (mb_geom->use_deform) {
|
||||
/* Keep to modify later (after init). */
|
||||
mb_geom->batch = batch;
|
||||
}
|
||||
|
||||
/* Avoid drawing object that has no motions since object_moves is always true. */
|
||||
if (!mb_geom->use_deform && /* Object deformation can happen without transform. */
|
||||
equals_m4m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]) &&
|
||||
equals_m4m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR])) {
|
||||
return;
|
||||
}
|
||||
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_object_sh_get(),
|
||||
psl->velocity_object);
|
||||
DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]);
|
||||
DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]);
|
||||
DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]);
|
||||
DRW_shgroup_uniform_bool(grp, "useDeform", &mb_geom->use_deform, 1);
|
||||
|
||||
DRW_shgroup_call(grp, batch, ob);
|
||||
}
|
||||
else if (is_deform) {
|
||||
/* Store vertex position buffer. */
|
||||
mb_geom->vbo[mb_step] = DRW_cache_object_pos_vertbuf_get(ob);
|
||||
mb_geom->use_deform = (mb_geom->vbo[mb_step] != NULL);
|
||||
}
|
||||
else {
|
||||
mb_geom->vbo[mb_step] = NULL;
|
||||
mb_geom->use_deform = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void motion_blur_remove_vbo_reference_from_batch(GPUBatch *batch,
|
||||
GPUVertBuf *vbo1,
|
||||
GPUVertBuf *vbo2)
|
||||
{
|
||||
|
||||
for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN; i++) {
|
||||
if (ELEM(batch->verts[i], vbo1, vbo2)) {
|
||||
/* Avoid double reference of the VBOs. */
|
||||
batch->verts[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
GHashIterator ghi;
|
||||
|
||||
if ((effects->enabled_effects & EFFECT_MOTION_BLUR) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int mb_step = effects->motion_blur_step;
|
||||
|
||||
if (mb_step != MB_CURR) {
|
||||
/* Push instances attributes to the GPU. */
|
||||
DRW_render_instance_buffer_finish();
|
||||
|
||||
/* Need to be called after #DRW_render_instance_buffer_finish() */
|
||||
/* Also we weed to have a correct FBO bound for #DRW_hair_update. */
|
||||
GPU_framebuffer_bind(vedata->fbl->main_fb);
|
||||
DRW_hair_update();
|
||||
|
||||
DRW_cache_restart();
|
||||
}
|
||||
|
||||
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom);
|
||||
BLI_ghashIterator_done(&ghi) == false;
|
||||
BLI_ghashIterator_step(&ghi)) {
|
||||
EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi);
|
||||
EEVEE_HairMotionData *mb_hair = (EEVEE_HairMotionData *)mb_geom;
|
||||
|
||||
if (!mb_geom->use_deform) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (mb_geom->type) {
|
||||
case EEVEE_MOTION_DATA_HAIR:
|
||||
if (mb_step == MB_CURR) {
|
||||
/* TODO(fclem): Check if vertex count mismatch. */
|
||||
mb_hair->use_deform = true;
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < mb_hair->psys_len; i++) {
|
||||
if (mb_hair->psys[i].hair_pos[mb_step] == NULL) {
|
||||
continue;
|
||||
}
|
||||
mb_hair->psys[i].hair_pos[mb_step] = GPU_vertbuf_duplicate(
|
||||
mb_hair->psys[i].hair_pos[mb_step]);
|
||||
|
||||
/* Create vbo immediately to bind to texture buffer. */
|
||||
GPU_vertbuf_use(mb_hair->psys[i].hair_pos[mb_step]);
|
||||
|
||||
mb_hair->psys[i].hair_pos_tx[mb_step] = GPU_texture_create_from_vertbuf(
|
||||
"hair_pos_motion_blur", mb_hair->psys[i].hair_pos[mb_step]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EEVEE_MOTION_DATA_MESH:
|
||||
if (mb_step == MB_CURR) {
|
||||
/* Modify batch to have data from adjacent frames. */
|
||||
GPUBatch *batch = mb_geom->batch;
|
||||
for (int i = 0; i < MB_CURR; i++) {
|
||||
GPUVertBuf *vbo = mb_geom->vbo[i];
|
||||
if (vbo && batch) {
|
||||
if (GPU_vertbuf_get_vertex_len(vbo) != GPU_vertbuf_get_vertex_len(batch->verts[0])) {
|
||||
/* Vertex count mismatch, disable deform motion blur. */
|
||||
mb_geom->use_deform = false;
|
||||
}
|
||||
|
||||
if (mb_geom->use_deform == false) {
|
||||
motion_blur_remove_vbo_reference_from_batch(
|
||||
batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]);
|
||||
|
||||
GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
|
||||
GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_NEXT]);
|
||||
break;
|
||||
}
|
||||
|
||||
GPU_batch_vertbuf_add_ex(batch, vbo, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
GPUVertBuf *vbo = mb_geom->vbo[mb_step];
|
||||
if (vbo) {
|
||||
/* Use the vbo to perform the copy on the GPU. */
|
||||
GPU_vertbuf_use(vbo);
|
||||
/* Perform a copy to avoid losing it after RE_engine_frame_set(). */
|
||||
mb_geom->vbo[mb_step] = vbo = GPU_vertbuf_duplicate(vbo);
|
||||
/* Find and replace "pos" attrib name. */
|
||||
GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo);
|
||||
int attrib_id = GPU_vertformat_attr_id_get(format, "pos");
|
||||
GPU_vertformat_attr_rename(format, attrib_id, (mb_step == MB_PREV) ? "prv" : "nxt");
|
||||
}
|
||||
else {
|
||||
/* This might happen if the object visibility has been animated. */
|
||||
mb_geom->use_deform = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
BLI_assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
GHashIterator ghi;
|
||||
|
||||
BLI_assert((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0);
|
||||
|
||||
/* Camera Data. */
|
||||
effects->motion_blur.camera[MB_PREV] = effects->motion_blur.camera[MB_NEXT];
|
||||
|
||||
/* Object Data. */
|
||||
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object);
|
||||
BLI_ghashIterator_done(&ghi) == false;
|
||||
BLI_ghashIterator_step(&ghi)) {
|
||||
EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi);
|
||||
|
||||
copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]);
|
||||
}
|
||||
|
||||
/* Deformation Data. */
|
||||
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom);
|
||||
BLI_ghashIterator_done(&ghi) == false;
|
||||
BLI_ghashIterator_step(&ghi)) {
|
||||
EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi);
|
||||
EEVEE_HairMotionData *mb_hair = (EEVEE_HairMotionData *)mb_geom;
|
||||
|
||||
switch (mb_geom->type) {
|
||||
case EEVEE_MOTION_DATA_HAIR:
|
||||
for (int i = 0; i < mb_hair->psys_len; i++) {
|
||||
GPU_VERTBUF_DISCARD_SAFE(mb_hair->psys[i].hair_pos[MB_PREV]);
|
||||
DRW_TEXTURE_FREE_SAFE(mb_hair->psys[i].hair_pos_tx[MB_PREV]);
|
||||
mb_hair->psys[i].hair_pos[MB_PREV] = mb_hair->psys[i].hair_pos[MB_NEXT];
|
||||
mb_hair->psys[i].hair_pos_tx[MB_PREV] = mb_hair->psys[i].hair_pos_tx[MB_NEXT];
|
||||
mb_hair->psys[i].hair_pos[MB_NEXT] = NULL;
|
||||
mb_hair->psys[i].hair_pos_tx[MB_NEXT] = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case EEVEE_MOTION_DATA_MESH:
|
||||
if (mb_geom->batch != NULL) {
|
||||
motion_blur_remove_vbo_reference_from_batch(
|
||||
mb_geom->batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]);
|
||||
}
|
||||
GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
|
||||
mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT];
|
||||
mb_geom->vbo[MB_NEXT] = NULL;
|
||||
|
||||
if (mb_geom->vbo[MB_PREV]) {
|
||||
GPUVertBuf *vbo = mb_geom->vbo[MB_PREV];
|
||||
GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo);
|
||||
int attrib_id = GPU_vertformat_attr_id_get(format, "nxt");
|
||||
GPU_vertformat_attr_rename(format, attrib_id, "prv");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
BLI_assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_motion_blur_draw(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
/* Motion Blur */
|
||||
if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) {
|
||||
/* Create velocity max tiles in 2 passes. One for each dimension. */
|
||||
GPU_framebuffer_bind(fbl->velocity_tiles_fb[0]);
|
||||
DRW_draw_pass(psl->velocity_tiles_x);
|
||||
|
||||
GPU_framebuffer_bind(fbl->velocity_tiles_fb[1]);
|
||||
DRW_draw_pass(psl->velocity_tiles);
|
||||
|
||||
/* Expand the tiles by reading the neighborhood. Do as many passes as required. */
|
||||
int buf = 0;
|
||||
for (int i = effects->motion_blur_max; i > 0; i -= EEVEE_VELOCITY_TILE_SIZE) {
|
||||
GPU_framebuffer_bind(fbl->velocity_tiles_fb[buf]);
|
||||
|
||||
/* Change viewport to avoid invoking more pixel shaders than necessary since in one of the
|
||||
* buffer the texture is way bigger in height. This avoid creating another texture and
|
||||
* reduce VRAM usage. */
|
||||
int w = GPU_texture_width(effects->velocity_tiles_tx);
|
||||
int h = GPU_texture_height(effects->velocity_tiles_tx);
|
||||
GPU_framebuffer_viewport_set(fbl->velocity_tiles_fb[buf], 0, 0, w, h);
|
||||
|
||||
DRW_draw_pass(psl->velocity_tiles_expand[buf]);
|
||||
|
||||
GPU_framebuffer_viewport_reset(fbl->velocity_tiles_fb[buf]);
|
||||
|
||||
buf = buf ? 0 : 1;
|
||||
}
|
||||
|
||||
GPU_framebuffer_bind(effects->target_buffer);
|
||||
DRW_draw_pass(psl->motion_blur);
|
||||
SWAP_BUFFERS();
|
||||
}
|
||||
}
|
||||
248
source/blender/draw/engines/eevee/eevee_motion_blur.cc
Normal file
248
source/blender/draw/engines/eevee/eevee_motion_blur.cc
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
#include "eevee_sampling.hh"
|
||||
#include "eevee_shader_shared.hh"
|
||||
#include "eevee_velocity.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name MotionBlurModule
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void MotionBlurModule::init(void)
|
||||
{
|
||||
const Scene *scene = inst_.scene;
|
||||
|
||||
enabled_ = (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0;
|
||||
|
||||
/* Viewport not supported for now. */
|
||||
if (!DRW_state_is_scene_render()) {
|
||||
enabled_ = false;
|
||||
}
|
||||
if (!enabled_) {
|
||||
motion_blur_fx_enabled_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Take into account the steps needed for fx motion blur. */
|
||||
int steps_count = max_ii(1, scene->eevee.motion_blur_steps) * 2 + 1;
|
||||
|
||||
time_steps_.resize(steps_count);
|
||||
|
||||
initial_frame_ = CFRA;
|
||||
initial_subframe_ = SUBFRA;
|
||||
frame_time_ = initial_frame_ + initial_subframe_;
|
||||
motion_blur_position_ = scene->eevee.motion_blur_position;
|
||||
motion_blur_shutter_ = scene->eevee.motion_blur_shutter;
|
||||
|
||||
/* Without this there is the possibility of the curve table not being allocated. */
|
||||
BKE_curvemapping_changed((struct CurveMapping *)&scene->r.mblur_shutter_curve, false);
|
||||
|
||||
Vector<float> cdf(CM_TABLE);
|
||||
Sampling::cdf_from_curvemapping(scene->r.mblur_shutter_curve, cdf);
|
||||
Sampling::cdf_invert(cdf, time_steps_);
|
||||
|
||||
for (float &time : time_steps_) {
|
||||
time = this->shutter_time_to_scene_time(time);
|
||||
}
|
||||
|
||||
motion_blur_fx_enabled_ = scene->eevee.motion_blur_max > 0.5f;
|
||||
step_id_ = 1;
|
||||
|
||||
if (motion_blur_fx_enabled_) {
|
||||
/* A bit weird but we have to sync the first 2 steps here because the step()
|
||||
* function is only called after rendering a sample. */
|
||||
inst_.velocity.step_sync(VelocityModule::STEP_PREVIOUS, time_steps_[0]);
|
||||
inst_.velocity.step_sync(VelocityModule::STEP_NEXT, time_steps_[2]);
|
||||
}
|
||||
inst_.set_time(time_steps_[1]);
|
||||
}
|
||||
|
||||
/* Runs after rendering a sample. */
|
||||
void MotionBlurModule::step(void)
|
||||
{
|
||||
if (!enabled_) {
|
||||
return;
|
||||
}
|
||||
else if (inst_.sampling.finished()) {
|
||||
/* Restore original frame number. This is because the render pipeline expects it. */
|
||||
RE_engine_frame_set(inst_.render, initial_frame_, initial_subframe_);
|
||||
}
|
||||
else if (inst_.sampling.do_render_sync()) {
|
||||
/* Time to change motion step. */
|
||||
BLI_assert(time_steps_.size() > step_id_ + 2);
|
||||
step_id_ += 2;
|
||||
|
||||
if (motion_blur_fx_enabled_) {
|
||||
inst_.velocity.step_swap();
|
||||
inst_.velocity.step_sync(VelocityModule::STEP_NEXT, time_steps_[step_id_ + 1]);
|
||||
}
|
||||
inst_.set_time(time_steps_[step_id_]);
|
||||
}
|
||||
}
|
||||
|
||||
float MotionBlurModule::shutter_time_to_scene_time(float time)
|
||||
{
|
||||
switch (motion_blur_position_) {
|
||||
case SCE_EEVEE_MB_START:
|
||||
/* No offset. */
|
||||
break;
|
||||
case SCE_EEVEE_MB_CENTER:
|
||||
time -= 0.5f;
|
||||
break;
|
||||
case SCE_EEVEE_MB_END:
|
||||
time -= 1.0;
|
||||
break;
|
||||
default:
|
||||
BLI_assert(!"Invalid motion blur position enum!");
|
||||
break;
|
||||
}
|
||||
time *= motion_blur_shutter_;
|
||||
time += frame_time_;
|
||||
return time;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name MotionBlur
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void MotionBlur::init()
|
||||
{
|
||||
const Scene *scene = inst_.scene;
|
||||
data_.blur_max = scene->eevee.motion_blur_max;
|
||||
data_.depth_scale = scene->eevee.motion_blur_depth_scale;
|
||||
enabled_ = ((scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0) && (data_.blur_max > 0.5f);
|
||||
}
|
||||
|
||||
void MotionBlur::sync(int extent[2])
|
||||
{
|
||||
if (!enabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
|
||||
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
|
||||
uint res[2] = {divide_ceil_u(extent[0], MB_TILE_DIVISOR),
|
||||
divide_ceil_u(extent[1], MB_TILE_DIVISOR)};
|
||||
|
||||
{
|
||||
/* Create max velocity tiles in 2 passes. One for X and one for Y */
|
||||
DRW_PASS_CREATE(tiles_flatten_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_TILE_FLATTEN);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_flatten_ps_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "velocity_tx", &input_velocity_tx_, no_filter);
|
||||
DRW_shgroup_uniform_block(grp, "motion_blur_buf", data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
tiles_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
|
||||
|
||||
tiles_flatten_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(tiles_tx_));
|
||||
}
|
||||
{
|
||||
/* Expand max tiles by keeping the max tile in each tile neighborhood. */
|
||||
DRW_PASS_CREATE(tiles_dilate_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_TILE_DILATE);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_dilate_ps_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_tx", &tiles_tx_, no_filter);
|
||||
DRW_shgroup_uniform_block(grp, "motion_blur_buf", data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
tiles_dilated_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
|
||||
|
||||
tiles_dilate_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(tiles_dilated_tx_));
|
||||
}
|
||||
{
|
||||
data_.target_size_inv[0] = 1.0f / extent[0];
|
||||
data_.target_size_inv[1] = 1.0f / extent[1];
|
||||
|
||||
/* Do the motion blur gather algorithm. */
|
||||
DRW_PASS_CREATE(gather_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_GATHER);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_ps_);
|
||||
DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "motion_blur_buf", data_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "color_tx", &input_color_tx_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "velocity_tx", &input_velocity_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_tx", &tiles_dilated_tx_, no_filter);
|
||||
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
}
|
||||
|
||||
data_.is_viewport = !DRW_state_is_image_render();
|
||||
data_.push_update();
|
||||
}
|
||||
|
||||
void MotionBlur::render(GPUTexture *depth_tx,
|
||||
GPUTexture *velocity_tx,
|
||||
GPUTexture **input_tx,
|
||||
GPUTexture **output_tx)
|
||||
{
|
||||
if (!enabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
input_color_tx_ = *input_tx;
|
||||
input_depth_tx_ = depth_tx;
|
||||
input_velocity_tx_ = velocity_tx;
|
||||
|
||||
DRW_stats_group_start("Motion Blur");
|
||||
|
||||
GPU_framebuffer_bind(tiles_flatten_fb_);
|
||||
DRW_draw_pass(tiles_flatten_ps_);
|
||||
|
||||
for (int max_blur = data_.blur_max; max_blur > 0; max_blur -= MB_TILE_DIVISOR) {
|
||||
GPU_framebuffer_bind(tiles_dilate_fb_);
|
||||
DRW_draw_pass(tiles_dilate_ps_);
|
||||
SWAP(GPUTexture *, tiles_tx_, tiles_dilated_tx_);
|
||||
Framebuffer::swap(tiles_flatten_fb_, tiles_dilate_fb_);
|
||||
}
|
||||
/* Swap again so result is in tiles_dilated_tx_. */
|
||||
SWAP(GPUTexture *, tiles_tx_, tiles_dilated_tx_);
|
||||
Framebuffer::swap(tiles_flatten_fb_, tiles_dilate_fb_);
|
||||
|
||||
gather_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(*output_tx));
|
||||
|
||||
GPU_framebuffer_bind(gather_fb_);
|
||||
DRW_draw_pass(gather_ps_);
|
||||
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* Swap buffers so that next effect has the right input. */
|
||||
*input_tx = *output_tx;
|
||||
*output_tx = input_color_tx_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
159
source/blender/draw/engines/eevee/eevee_motion_blur.hh
Normal file
159
source/blender/draw/engines/eevee/eevee_motion_blur.hh
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Motion blur is done by accumulating scene samples over shutter time.
|
||||
* Since the number of step is discrete, quite low, and not per pixel randomized,
|
||||
* we couple this with a post processing motion blur.
|
||||
*
|
||||
* The post-fx motion blur is done in two directions, from the previous step and to the next.
|
||||
*
|
||||
* For a scene with 3 motion steps, a flat shutter curve and shutter time of 2 frame
|
||||
* centered on frame we have:
|
||||
*
|
||||
* |--------------------|--------------------|
|
||||
* -1 0 1 Frames
|
||||
*
|
||||
* |-------------|-------------|-------------|
|
||||
* 1 2 3 Motion steps
|
||||
*
|
||||
* |------|------|------|------|------|------|
|
||||
* 0 1 2 4 5 6 7 Time Steps
|
||||
*
|
||||
* |-------------| One motion step blurs this range.
|
||||
* -1 | +1 Objects and geometry steps are recorded here.
|
||||
* 0 Scene is rendered here.
|
||||
*
|
||||
* Since motion step N and N+1 share one time step we reuse it to avoid an extra scene evaluation.
|
||||
*
|
||||
* Note that we have to evaluate -1 and +1 time steps before rendering so eval order is -1, +1, 0.
|
||||
* This is because all GPUBatches from the DRWCache are being free when changing a frame.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "eevee_sampling.hh"
|
||||
#include "eevee_shader_shared.hh"
|
||||
#include "eevee_velocity.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name MotionBlur
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Manages timesteps evaluations and accumulation Motion blur.
|
||||
* Post process motion blur is handled by the MotionBlur class.
|
||||
*/
|
||||
class MotionBlurModule {
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
/**
|
||||
* Array containing all steps (in scene time) we need to evaluate (not render).
|
||||
* Only odd steps are rendered. The even ones are evaluated for fx motion blur.
|
||||
*/
|
||||
Vector<float> time_steps_;
|
||||
|
||||
/** Copy of input frame an subframe to restore after render. */
|
||||
int initial_frame_;
|
||||
float initial_subframe_;
|
||||
/** Time of the frame we are rendering. */
|
||||
float frame_time_;
|
||||
/** Copy of scene settings. */
|
||||
int motion_blur_position_;
|
||||
float motion_blur_shutter_;
|
||||
|
||||
bool enabled_ = false;
|
||||
float motion_blur_fx_enabled_ = false;
|
||||
|
||||
int step_id_ = 0;
|
||||
|
||||
public:
|
||||
MotionBlurModule(Instance &inst) : inst_(inst){};
|
||||
~MotionBlurModule(){};
|
||||
|
||||
void init(void);
|
||||
|
||||
void step(void);
|
||||
|
||||
private:
|
||||
float shutter_time_to_scene_time(float time);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name MotionBlur
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Per view fx module. Perform a motion blur using the result of the velocity pass.
|
||||
*/
|
||||
class MotionBlur {
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
StringRefNull view_name_;
|
||||
|
||||
/** Textures from pool. Not owned. */
|
||||
GPUTexture *tiles_tx_ = nullptr;
|
||||
GPUTexture *tiles_dilated_tx_ = nullptr;
|
||||
/** Input texture. Not owned. */
|
||||
GPUTexture *input_velocity_tx_ = nullptr;
|
||||
GPUTexture *input_color_tx_ = nullptr;
|
||||
GPUTexture *input_depth_tx_ = nullptr;
|
||||
/** Passes. Not owned. */
|
||||
DRWPass *tiles_flatten_ps_ = nullptr;
|
||||
DRWPass *tiles_dilate_ps_ = nullptr;
|
||||
DRWPass *gather_ps_ = nullptr;
|
||||
/** Framebuffers. Owned. */
|
||||
Framebuffer tiles_flatten_fb_;
|
||||
Framebuffer tiles_dilate_fb_;
|
||||
Framebuffer gather_fb_;
|
||||
|
||||
draw::UniformBuffer<MotionBlurData> data_;
|
||||
|
||||
bool enabled_;
|
||||
|
||||
public:
|
||||
MotionBlur(Instance &inst, StringRefNull view_name) : inst_(inst), view_name_(view_name){};
|
||||
|
||||
~MotionBlur(){};
|
||||
|
||||
void init(void);
|
||||
|
||||
void sync(int extent[2]);
|
||||
|
||||
void render(GPUTexture *depth_tx,
|
||||
GPUTexture *velocity_tx,
|
||||
GPUTexture **input_tx,
|
||||
GPUTexture **output_tx);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
@@ -1,281 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*
|
||||
* Implementation of the screen space Ground Truth Ambient Occlusion.
|
||||
*/
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BLI_string_utils.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "BKE_global.h" /* for G.debug_value */
|
||||
|
||||
#include "eevee_private.h"
|
||||
|
||||
#include "GPU_capabilities.h"
|
||||
#include "GPU_platform.h"
|
||||
#include "GPU_state.h"
|
||||
|
||||
static struct {
|
||||
struct GPUTexture *dummy_horizon_tx;
|
||||
} e_data = {NULL}; /* Engine data */
|
||||
|
||||
int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
|
||||
|
||||
if (!e_data.dummy_horizon_tx) {
|
||||
const float pixel[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
e_data.dummy_horizon_tx = DRW_texture_create_2d(1, 1, GPU_RGBA8, DRW_TEX_WRAP, pixel);
|
||||
}
|
||||
|
||||
if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED ||
|
||||
stl->g_data->render_passes & EEVEE_RENDER_PASS_AO) {
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
|
||||
|
||||
common_data->ao_dist = scene_eval->eevee.gtao_distance;
|
||||
common_data->ao_factor = max_ff(1e-4f, scene_eval->eevee.gtao_factor);
|
||||
common_data->ao_quality = scene_eval->eevee.gtao_quality;
|
||||
|
||||
if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) {
|
||||
common_data->ao_settings = 1.0f; /* USE_AO */
|
||||
}
|
||||
if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BENT_NORMALS) {
|
||||
common_data->ao_settings += 2.0f; /* USE_BENT_NORMAL */
|
||||
}
|
||||
if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BOUNCE) {
|
||||
common_data->ao_settings += 4.0f; /* USE_DENOISE */
|
||||
}
|
||||
|
||||
common_data->ao_bounce_fac = (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BOUNCE) ? 1.0f : 0.0f;
|
||||
|
||||
effects->gtao_horizons_renderpass = DRW_texture_pool_query_2d(
|
||||
UNPACK2(effects->hiz_size), GPU_RGBA8, &draw_engine_eevee_type);
|
||||
GPU_framebuffer_ensure_config(
|
||||
&fbl->gtao_fb,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons_renderpass)});
|
||||
|
||||
if (G.debug_value == 6) {
|
||||
effects->gtao_horizons_debug = DRW_texture_pool_query_2d(
|
||||
UNPACK2(fs_size), GPU_RGBA8, &draw_engine_eevee_type);
|
||||
GPU_framebuffer_ensure_config(
|
||||
&fbl->gtao_debug_fb,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons_debug)});
|
||||
}
|
||||
else {
|
||||
effects->gtao_horizons_debug = NULL;
|
||||
}
|
||||
|
||||
effects->gtao_horizons = (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) ?
|
||||
effects->gtao_horizons_renderpass :
|
||||
e_data.dummy_horizon_tx;
|
||||
|
||||
return EFFECT_GTAO | EFFECT_NORMAL_BUFFER;
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
effects->gtao_horizons_renderpass = e_data.dummy_horizon_tx;
|
||||
effects->gtao_horizons = e_data.dummy_horizon_tx;
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fbl->gtao_fb);
|
||||
common_data->ao_settings = 0.0f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_R32F : GPU_R16F;
|
||||
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
|
||||
/* Should be enough precision for many samples. */
|
||||
DRW_texture_ensure_fullscreen_2d(&txl->ao_accum, texture_format, 0);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->ao_accum_fb,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ao_accum)});
|
||||
|
||||
/* Accumulation pass */
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD;
|
||||
DRW_PASS_CREATE(psl->ao_accum_ps, state);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_effect_ambient_occlusion_debug_sh_get(),
|
||||
psl->ao_accum_ps);
|
||||
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons_renderpass);
|
||||
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
|
||||
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
|
||||
}
|
||||
|
||||
void EEVEE_occlusion_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
|
||||
if ((effects->enabled_effects & EFFECT_GTAO) != 0) {
|
||||
/**
|
||||
* Occlusion Algorithm Overview:
|
||||
*
|
||||
* We separate the computation into 2 steps.
|
||||
*
|
||||
* - First we scan the neighborhood pixels to find the maximum horizon angle.
|
||||
* We save this angle in a RG8 array texture.
|
||||
*
|
||||
* - Then we use this angle to compute occlusion with the shading normal at
|
||||
* the shading stage. This let us do correct shadowing for each diffuse / specular
|
||||
* lobe present in the shader using the correct normal.
|
||||
*/
|
||||
DRW_PASS_CREATE(psl->ao_horizon_search, DRW_STATE_WRITE_COLOR);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_effect_ambient_occlusion_sh_get(),
|
||||
psl->ao_horizon_search);
|
||||
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
|
||||
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
if (G.debug_value == 6) {
|
||||
DRW_PASS_CREATE(psl->ao_horizon_debug, DRW_STATE_WRITE_COLOR);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_ambient_occlusion_debug_sh_get(),
|
||||
psl->ao_horizon_debug);
|
||||
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons_renderpass);
|
||||
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_occlusion_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
|
||||
|
||||
if ((effects->enabled_effects & EFFECT_GTAO) != 0) {
|
||||
DRW_stats_group_start("GTAO Horizon Scan");
|
||||
|
||||
GPU_framebuffer_bind(fbl->gtao_fb);
|
||||
|
||||
/** NOTE(fclem): Kind of fragile. We need this to make sure everything lines up
|
||||
* nicely during planar reflection. */
|
||||
if (common_data->ray_type != EEVEE_RAY_GLOSSY) {
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
GPU_framebuffer_viewport_set(fbl->gtao_fb, 0, 0, UNPACK2(viewport_size));
|
||||
}
|
||||
|
||||
DRW_draw_pass(psl->ao_horizon_search);
|
||||
|
||||
if (common_data->ray_type != EEVEE_RAY_GLOSSY) {
|
||||
GPU_framebuffer_viewport_reset(fbl->gtao_fb);
|
||||
}
|
||||
|
||||
if (GPU_mip_render_workaround() ||
|
||||
GPU_type_matches(GPU_DEVICE_INTEL_UHD, GPU_OS_WIN, GPU_DRIVER_ANY)) {
|
||||
/* Fix dot corruption on intel HD5XX/HD6XX series. */
|
||||
GPU_flush();
|
||||
}
|
||||
|
||||
/* Restore */
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_occlusion_draw_debug(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
if (((effects->enabled_effects & EFFECT_GTAO) != 0) && (G.debug_value == 6)) {
|
||||
DRW_stats_group_start("GTAO Debug");
|
||||
|
||||
GPU_framebuffer_bind(fbl->gtao_debug_fb);
|
||||
DRW_draw_pass(psl->ao_horizon_debug);
|
||||
|
||||
/* Restore */
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_occlusion_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_EffectsInfo *effects = vedata->stl->effects;
|
||||
|
||||
if (fbl->ao_accum_fb != NULL) {
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
|
||||
/* Update the min_max/horizon buffers so the refraction materials appear in it. */
|
||||
EEVEE_create_minmax_buffer(vedata, dtxl->depth, -1);
|
||||
EEVEE_occlusion_compute(sldata, vedata);
|
||||
|
||||
GPU_framebuffer_bind(fbl->ao_accum_fb);
|
||||
|
||||
/* Clear texture. */
|
||||
if (effects->taa_current_sample == 1) {
|
||||
const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear);
|
||||
}
|
||||
|
||||
DRW_draw_pass(psl->ao_accum_ps);
|
||||
|
||||
/* Restore */
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_occlusion_free(void)
|
||||
{
|
||||
DRW_TEXTURE_FREE_SAFE(e_data.dummy_horizon_tx);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
303
source/blender/draw/engines/eevee/eevee_raytracing.cc
Normal file
303
source/blender/draw/engines/eevee/eevee_raytracing.cc
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Module containing passes and parameters used for raytracing.
|
||||
* NOTE: For now only screen space raytracing is supported.
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
#include "eevee_raytracing.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Raytracing
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void RaytracingModule::sync(void)
|
||||
{
|
||||
SceneEEVEE &sce_eevee = inst_.scene->eevee;
|
||||
|
||||
reflection_data_.thickness = sce_eevee.ssr_thickness;
|
||||
reflection_data_.brightness_clamp = (sce_eevee.ssr_firefly_fac < 1e-8f) ?
|
||||
FLT_MAX :
|
||||
sce_eevee.ssr_firefly_fac;
|
||||
reflection_data_.max_roughness = sce_eevee.ssr_max_roughness + 0.01f;
|
||||
reflection_data_.quality = 1.0f - 0.95f * sce_eevee.ssr_quality;
|
||||
reflection_data_.bias = 0.8f + sce_eevee.ssr_quality * 0.15f;
|
||||
reflection_data_.pool_offset = inst_.sampling.sample_get() / 5;
|
||||
|
||||
refraction_data_ = static_cast<RaytraceData>(reflection_data_);
|
||||
// refraction_data_.thickness = 1e16;
|
||||
/* TODO(fclem): Clamp option for refraction. */
|
||||
/* TODO(fclem): bias option for refraction. */
|
||||
/* TODO(fclem): bias option for refraction. */
|
||||
|
||||
diffuse_data_ = static_cast<RaytraceData>(reflection_data_);
|
||||
diffuse_data_.max_roughness = 1.01f;
|
||||
|
||||
reflection_data_.push_update();
|
||||
refraction_data_.push_update();
|
||||
diffuse_data_.push_update();
|
||||
|
||||
// enabled_ = (sce_eevee.flag & SCE_EEVEE_RAYTRACING_ENABLED) != 0;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Raytracing Buffers
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void RaytraceBuffer::sync(int2 extent)
|
||||
{
|
||||
extent_ = extent;
|
||||
dispatch_size_.x = divide_ceil_u(extent.x, 8);
|
||||
dispatch_size_.y = divide_ceil_u(extent.y, 8);
|
||||
dispatch_size_.z = 1;
|
||||
|
||||
/* Make sure the history matrix is up to date. */
|
||||
data_.push_update();
|
||||
|
||||
LightProbeModule &lightprobes = inst_.lightprobes;
|
||||
eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
|
||||
|
||||
/* The raytracing buffer contains the draw passes since it is stored per view and we need to
|
||||
* dispatch compute shaders with the right workgroup size. */
|
||||
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL;
|
||||
std::array<DRWShadingGroup *, 3> grps;
|
||||
bool do_rt = inst_.raytracing.enabled();
|
||||
{
|
||||
trace_reflection_ps_ = DRW_pass_create("TraceReflection", state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(do_rt ? RAYTRACE_REFLECTION :
|
||||
RAYTRACE_REFLECTION_FALLBACK);
|
||||
grps[0] = DRW_shgroup_create(sh, trace_reflection_ps_);
|
||||
DRW_shgroup_uniform_block(grps[0], "raytrace_buf", inst_.raytracing.reflection_ubo_get());
|
||||
DRW_shgroup_stencil_set(grps[0], 0x0, 0x0, CLOSURE_REFLECTION);
|
||||
}
|
||||
{
|
||||
trace_refraction_ps_ = DRW_pass_create("TraceRefraction", state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(do_rt ? RAYTRACE_REFRACTION :
|
||||
RAYTRACE_REFRACTION_FALLBACK);
|
||||
grps[1] = DRW_shgroup_create(sh, trace_refraction_ps_);
|
||||
DRW_shgroup_uniform_block(grps[1], "raytrace_buf", inst_.raytracing.refraction_ubo_get());
|
||||
DRW_shgroup_stencil_set(grps[1], 0x0, 0x0, CLOSURE_REFRACTION);
|
||||
}
|
||||
{
|
||||
trace_diffuse_ps_ = DRW_pass_create("TraceDiffuse", state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(do_rt ? RAYTRACE_DIFFUSE :
|
||||
RAYTRACE_DIFFUSE_FALLBACK);
|
||||
grps[2] = DRW_shgroup_create(sh, trace_diffuse_ps_);
|
||||
DRW_shgroup_uniform_block(grps[2], "raytrace_buf", inst_.raytracing.diffuse_ubo_get());
|
||||
DRW_shgroup_stencil_set(grps[2], 0x0, 0x0, CLOSURE_DIFFUSE);
|
||||
}
|
||||
|
||||
for (DRWShadingGroup *grp : grps) {
|
||||
DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "probes_buf", lightprobes.info_ubo_get());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_tx_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "hiz_front_tx", &input_hiz_front_tx_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_radiance_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "combined_tx", &input_combined_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_color_tx", &input_cl_color_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_normal_tx", &input_cl_normal_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_data_tx", &input_cl_data_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/* Compute stage. No state needed. */
|
||||
DRWState state = (DRWState)0;
|
||||
std::array<DRWShadingGroup *, 3> grps;
|
||||
{
|
||||
denoise_reflection_ps_ = DRW_pass_create("DenoiseReflection", state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_DENOISE_REFLECTION);
|
||||
grps[0] = DRW_shgroup_create(sh, denoise_reflection_ps_);
|
||||
}
|
||||
{
|
||||
denoise_refraction_ps_ = DRW_pass_create("DenoiseRefraction", state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_DENOISE_REFRACTION);
|
||||
grps[1] = DRW_shgroup_create(sh, denoise_refraction_ps_);
|
||||
}
|
||||
{
|
||||
denoise_diffuse_ps_ = DRW_pass_create("DenoiseDiffuse", state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_DENOISE_DIFFUSE);
|
||||
grps[2] = DRW_shgroup_create(sh, denoise_diffuse_ps_);
|
||||
}
|
||||
|
||||
for (DRWShadingGroup *grp : grps) {
|
||||
/* Does not matter which raytrace_block we use. */
|
||||
DRW_shgroup_uniform_block(grp, "raytrace_buf", inst_.raytracing.diffuse_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "rtbuf_buf", data_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "ray_data_tx", &input_ray_data_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "ray_radiance_tx", &input_ray_color_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "hiz_tx", &input_hiz_front_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_color_tx", &input_cl_color_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_normal_tx", &input_cl_normal_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_data_tx", &input_cl_data_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "ray_history_tx", &input_history_tx_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "ray_variance_tx", &input_variance_tx_);
|
||||
DRW_shgroup_uniform_image_ref(grp, "out_history_img", &output_history_tx_);
|
||||
DRW_shgroup_uniform_image_ref(grp, "out_variance_img", &output_variance_tx_);
|
||||
DRW_shgroup_call_compute_ref(grp, dispatch_size_);
|
||||
DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_ADD_FULL;
|
||||
std::array<DRWShadingGroup *, 3> grps;
|
||||
{
|
||||
resolve_reflection_ps_ = DRW_pass_create("ResolveReflection", state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_RESOLVE_REFLECTION);
|
||||
grps[0] = DRW_shgroup_create(sh, resolve_reflection_ps_);
|
||||
DRW_shgroup_stencil_set(grps[0], 0x0, 0x0, CLOSURE_REFLECTION);
|
||||
}
|
||||
{
|
||||
resolve_refraction_ps_ = DRW_pass_create("ResolveRefraction", state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_RESOLVE_REFRACTION);
|
||||
grps[1] = DRW_shgroup_create(sh, resolve_refraction_ps_);
|
||||
DRW_shgroup_stencil_set(grps[1], 0x0, 0x0, CLOSURE_REFRACTION);
|
||||
}
|
||||
{
|
||||
resolve_diffuse_ps_ = DRW_pass_create("ResolveDiffuse", state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_RESOLVE_DIFFUSE);
|
||||
grps[2] = DRW_shgroup_create(sh, resolve_diffuse_ps_);
|
||||
DRW_shgroup_stencil_set(grps[2], 0x0, 0x0, CLOSURE_DIFFUSE);
|
||||
}
|
||||
|
||||
for (DRWShadingGroup *grp : grps) {
|
||||
DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get());
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "ray_radiance_tx", &output_history_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "ray_variance_tx", &output_variance_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_color_tx", &input_cl_color_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_normal_tx", &input_cl_normal_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_data_tx", &input_cl_data_tx_, no_interp);
|
||||
// DRW_shgroup_call_compute_ref(grp, dispatch_size_);
|
||||
// DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RaytraceBuffer::trace(eClosureBits closure_type,
|
||||
GBuffer &gbuffer,
|
||||
HiZBuffer &hiz,
|
||||
HiZBuffer &hiz_front)
|
||||
{
|
||||
gbuffer.bind_tracing();
|
||||
|
||||
input_hiz_tx_ = hiz.texture_get();
|
||||
input_hiz_front_tx_ = hiz_front.texture_get();
|
||||
if (closure_type == CLOSURE_REFLECTION) {
|
||||
input_cl_color_tx_ = gbuffer.reflect_color_tx;
|
||||
input_cl_normal_tx_ = gbuffer.reflect_normal_tx;
|
||||
input_cl_data_tx_ = gbuffer.reflect_normal_tx;
|
||||
}
|
||||
else {
|
||||
input_cl_color_tx_ = gbuffer.transmit_color_tx;
|
||||
input_cl_normal_tx_ = gbuffer.transmit_normal_tx;
|
||||
input_cl_data_tx_ = gbuffer.transmit_data_tx;
|
||||
}
|
||||
|
||||
switch (closure_type) {
|
||||
default:
|
||||
case CLOSURE_REFLECTION:
|
||||
input_radiance_tx_ = gbuffer.combined_tx;
|
||||
DRW_draw_pass(trace_reflection_ps_);
|
||||
break;
|
||||
case CLOSURE_REFRACTION:
|
||||
input_radiance_tx_ = gbuffer.combined_tx;
|
||||
DRW_draw_pass(trace_refraction_ps_);
|
||||
break;
|
||||
case CLOSURE_DIFFUSE:
|
||||
input_radiance_tx_ = gbuffer.diffuse_tx;
|
||||
input_combined_tx_ = gbuffer.combined_tx;
|
||||
DRW_draw_pass(trace_diffuse_ps_);
|
||||
break;
|
||||
}
|
||||
|
||||
input_ray_data_tx_ = gbuffer.ray_data_tx;
|
||||
input_ray_color_tx_ = gbuffer.ray_radiance_tx;
|
||||
}
|
||||
|
||||
void RaytraceBuffer::denoise(eClosureBits closure_type)
|
||||
{
|
||||
switch (closure_type) {
|
||||
default:
|
||||
case CLOSURE_REFLECTION:
|
||||
input_history_tx_ = reflection_radiance_history_get();
|
||||
input_variance_tx_ = reflection_variance_history_get();
|
||||
output_history_tx_ = reflection_radiance_get();
|
||||
output_variance_tx_ = reflection_variance_get();
|
||||
DRW_draw_pass(denoise_reflection_ps_);
|
||||
break;
|
||||
case CLOSURE_REFRACTION:
|
||||
input_history_tx_ = refraction_radiance_history_get();
|
||||
input_variance_tx_ = refraction_variance_history_get();
|
||||
output_history_tx_ = refraction_radiance_get();
|
||||
output_variance_tx_ = refraction_variance_get();
|
||||
DRW_draw_pass(denoise_refraction_ps_);
|
||||
break;
|
||||
case CLOSURE_DIFFUSE:
|
||||
input_history_tx_ = diffuse_radiance_history_get();
|
||||
input_variance_tx_ = diffuse_variance_history_get();
|
||||
output_history_tx_ = diffuse_radiance_get();
|
||||
output_variance_tx_ = diffuse_variance_get();
|
||||
DRW_draw_pass(denoise_diffuse_ps_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RaytraceBuffer::resolve(eClosureBits closure_type, GBuffer &gbuffer)
|
||||
{
|
||||
gbuffer.bind_radiance();
|
||||
switch (closure_type) {
|
||||
default:
|
||||
case CLOSURE_REFLECTION:
|
||||
DRW_draw_pass(resolve_reflection_ps_);
|
||||
break;
|
||||
case CLOSURE_REFRACTION:
|
||||
DRW_draw_pass(resolve_refraction_ps_);
|
||||
break;
|
||||
case CLOSURE_DIFFUSE:
|
||||
DRW_draw_pass(resolve_diffuse_ps_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
229
source/blender/draw/engines/eevee/eevee_raytracing.hh
Normal file
229
source/blender/draw/engines/eevee/eevee_raytracing.hh
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "eevee_gbuffer.hh"
|
||||
#include "eevee_shader_shared.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Raytracing
|
||||
* \{ */
|
||||
|
||||
class RaytracingModule {
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
RaytraceDataBuf reflection_data_;
|
||||
RaytraceDataBuf refraction_data_;
|
||||
RaytraceDataBuf diffuse_data_;
|
||||
|
||||
bool enabled_ = false;
|
||||
|
||||
public:
|
||||
RaytracingModule(Instance &inst) : inst_(inst){};
|
||||
|
||||
void sync(void);
|
||||
|
||||
const GPUUniformBuf *reflection_ubo_get(void) const
|
||||
{
|
||||
return reflection_data_;
|
||||
}
|
||||
const GPUUniformBuf *refraction_ubo_get(void) const
|
||||
{
|
||||
return refraction_data_;
|
||||
}
|
||||
const GPUUniformBuf *diffuse_ubo_get(void) const
|
||||
{
|
||||
return diffuse_data_;
|
||||
}
|
||||
|
||||
bool enabled(void) const
|
||||
{
|
||||
return enabled_;
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Raytracing Buffers
|
||||
*
|
||||
* Contain persistent data used for temporal denoising. Similar to \class GBuffer but only contains
|
||||
* persistent data.
|
||||
* \{ */
|
||||
|
||||
struct RaytraceBuffer {
|
||||
public:
|
||||
DRWPass *denoise_diffuse_ps_ = nullptr;
|
||||
DRWPass *denoise_reflection_ps_ = nullptr;
|
||||
DRWPass *denoise_refraction_ps_ = nullptr;
|
||||
DRWPass *resolve_diffuse_ps_ = nullptr;
|
||||
DRWPass *resolve_reflection_ps_ = nullptr;
|
||||
DRWPass *resolve_refraction_ps_ = nullptr;
|
||||
DRWPass *trace_diffuse_ps_ = nullptr;
|
||||
DRWPass *trace_reflection_ps_ = nullptr;
|
||||
DRWPass *trace_refraction_ps_ = nullptr;
|
||||
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
/* Only allocated if used. */
|
||||
Texture diffuse_radiance_tx_ = {"DiffuseHistory_A"};
|
||||
Texture diffuse_radiance_history_tx_ = {"DiffuseHistory_B"};
|
||||
Texture diffuse_variance_tx_ = {"DiffuseVariance_A"};
|
||||
Texture diffuse_variance_history_tx_ = {"DiffuseVariance_B"};
|
||||
Texture reflection_radiance_tx_ = {"ReflectionHistory_A"};
|
||||
Texture reflection_radiance_history_tx_ = {"ReflectionHistory_B"};
|
||||
Texture reflection_variance_tx_ = {"ReflectionVariance_A"};
|
||||
Texture reflection_variance_history_tx_ = {"ReflectionVariance_B"};
|
||||
Texture refraction_radiance_tx_ = {"RefractionHistory_A"};
|
||||
Texture refraction_radiance_history_tx_ = {"RefractionHistory_B"};
|
||||
Texture refraction_variance_tx_ = {"RefractionVariance_A"};
|
||||
Texture refraction_variance_history_tx_ = {"RefractionVariance_B"};
|
||||
|
||||
/* Reference only. */
|
||||
GPUTexture *input_radiance_tx_;
|
||||
GPUTexture *input_combined_tx_;
|
||||
GPUTexture *input_ray_data_tx_;
|
||||
GPUTexture *input_ray_color_tx_;
|
||||
GPUTexture *input_hiz_tx_;
|
||||
GPUTexture *input_hiz_front_tx_;
|
||||
GPUTexture *input_cl_color_tx_;
|
||||
GPUTexture *input_cl_normal_tx_;
|
||||
GPUTexture *input_cl_data_tx_;
|
||||
GPUTexture *input_history_tx_;
|
||||
GPUTexture *input_variance_tx_;
|
||||
GPUTexture *output_history_tx_;
|
||||
GPUTexture *output_variance_tx_;
|
||||
|
||||
RaytraceBufferDataBuf data_;
|
||||
|
||||
int2 extent_ = int2(0);
|
||||
int3 dispatch_size_ = int3(1);
|
||||
|
||||
public:
|
||||
RaytraceBuffer(Instance &inst) : inst_(inst){};
|
||||
~RaytraceBuffer(){};
|
||||
|
||||
void sync(int2 extent);
|
||||
|
||||
void trace(eClosureBits closure_type, GBuffer &gbuffer, HiZBuffer &hiz, HiZBuffer &hiz_front);
|
||||
void denoise(eClosureBits closure_type);
|
||||
void resolve(eClosureBits closure_type, GBuffer &gbuffer);
|
||||
|
||||
GPUTexture *diffuse_radiance_history_get(void)
|
||||
{
|
||||
ensure_buffer(diffuse_radiance_history_tx_, data_.valid_history_diffuse, GPU_RGBA16F);
|
||||
return diffuse_radiance_history_tx_;
|
||||
}
|
||||
GPUTexture *reflection_radiance_history_get(void)
|
||||
{
|
||||
ensure_buffer(reflection_radiance_history_tx_, data_.valid_history_reflection, GPU_RGBA16F);
|
||||
return reflection_radiance_history_tx_;
|
||||
}
|
||||
GPUTexture *refraction_radiance_history_get(void)
|
||||
{
|
||||
ensure_buffer(refraction_radiance_history_tx_, data_.valid_history_refraction, GPU_RGBA16F);
|
||||
return refraction_radiance_history_tx_;
|
||||
}
|
||||
|
||||
GPUTexture *diffuse_variance_history_get(void)
|
||||
{
|
||||
ensure_buffer(diffuse_variance_history_tx_, data_.valid_history_diffuse, GPU_R8);
|
||||
return diffuse_variance_history_tx_;
|
||||
}
|
||||
GPUTexture *reflection_variance_history_get(void)
|
||||
{
|
||||
ensure_buffer(reflection_variance_history_tx_, data_.valid_history_reflection, GPU_R8);
|
||||
return reflection_variance_history_tx_;
|
||||
}
|
||||
GPUTexture *refraction_variance_history_get(void)
|
||||
{
|
||||
ensure_buffer(refraction_variance_history_tx_, data_.valid_history_refraction, GPU_R8);
|
||||
return refraction_variance_history_tx_;
|
||||
}
|
||||
|
||||
GPUTexture *diffuse_radiance_get(void)
|
||||
{
|
||||
ensure_buffer(diffuse_radiance_tx_, data_.valid_history_diffuse, GPU_RGBA16F);
|
||||
return diffuse_radiance_tx_;
|
||||
}
|
||||
GPUTexture *reflection_radiance_get(void)
|
||||
{
|
||||
ensure_buffer(reflection_radiance_tx_, data_.valid_history_reflection, GPU_RGBA16F);
|
||||
return reflection_radiance_tx_;
|
||||
}
|
||||
GPUTexture *refraction_radiance_get(void)
|
||||
{
|
||||
ensure_buffer(refraction_radiance_tx_, data_.valid_history_refraction, GPU_RGBA16F);
|
||||
return refraction_radiance_tx_;
|
||||
}
|
||||
|
||||
GPUTexture *diffuse_variance_get(void)
|
||||
{
|
||||
ensure_buffer(diffuse_variance_tx_, data_.valid_history_diffuse, GPU_R8);
|
||||
return diffuse_variance_tx_;
|
||||
}
|
||||
GPUTexture *reflection_variance_get(void)
|
||||
{
|
||||
ensure_buffer(reflection_variance_tx_, data_.valid_history_reflection, GPU_R8);
|
||||
return reflection_variance_tx_;
|
||||
}
|
||||
GPUTexture *refraction_variance_get(void)
|
||||
{
|
||||
ensure_buffer(refraction_variance_tx_, data_.valid_history_refraction, GPU_R8);
|
||||
return refraction_variance_tx_;
|
||||
}
|
||||
|
||||
void render_end(const DRWView *view)
|
||||
{
|
||||
using draw::Texture;
|
||||
DRW_view_persmat_get(view, data_.history_persmat.ptr(), false);
|
||||
Texture::swap(diffuse_radiance_tx_, diffuse_radiance_history_tx_);
|
||||
Texture::swap(diffuse_variance_tx_, diffuse_variance_history_tx_);
|
||||
Texture::swap(reflection_radiance_tx_, reflection_radiance_history_tx_);
|
||||
Texture::swap(reflection_variance_tx_, reflection_variance_history_tx_);
|
||||
Texture::swap(refraction_radiance_tx_, refraction_radiance_history_tx_);
|
||||
Texture::swap(refraction_variance_tx_, refraction_variance_history_tx_);
|
||||
}
|
||||
|
||||
private:
|
||||
void ensure_buffer(Texture &texture, int &valid_history, eGPUTextureFormat format)
|
||||
{
|
||||
bool was_allocated = texture.ensure_2d(format, extent_);
|
||||
if (was_allocated && valid_history) {
|
||||
valid_history = false;
|
||||
data_.push_update();
|
||||
}
|
||||
else if (!was_allocated && !valid_history) {
|
||||
valid_history = true;
|
||||
data_.push_update();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
@@ -1,742 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
/**
|
||||
* Render functions for final render outputs.
|
||||
*/
|
||||
|
||||
#include "DRW_engine.h"
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "BLI_rand.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "GPU_capabilities.h"
|
||||
#include "GPU_framebuffer.h"
|
||||
#include "GPU_state.h"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "eevee_private.h"
|
||||
|
||||
bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *depsgraph)
|
||||
{
|
||||
EEVEE_Data *vedata = (EEVEE_Data *)ved;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
Scene *scene = DEG_get_evaluated_scene(depsgraph);
|
||||
const float *size_orig = DRW_viewport_size_get();
|
||||
float size_final[2];
|
||||
|
||||
/* Init default FB and render targets:
|
||||
* In render mode the default framebuffer is not generated
|
||||
* because there is no viewport. So we need to manually create it or
|
||||
* not use it. For code clarity we just allocate it make use of it. */
|
||||
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
|
||||
/* Alloc transient data. */
|
||||
if (!stl->g_data) {
|
||||
stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__);
|
||||
}
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
g_data->background_alpha = DRW_state_draw_background() ? 1.0f : 0.0f;
|
||||
g_data->valid_double_buffer = 0;
|
||||
copy_v2_v2(g_data->size_orig, size_orig);
|
||||
|
||||
float *camtexcofac = g_data->camtexcofac;
|
||||
if (scene->eevee.flag & SCE_EEVEE_OVERSCAN) {
|
||||
g_data->overscan = scene->eevee.overscan / 100.0f;
|
||||
g_data->overscan_pixels = roundf(max_ff(size_orig[0], size_orig[1]) * g_data->overscan);
|
||||
|
||||
madd_v2_v2v2fl(size_final, size_orig, (float[2]){2.0f, 2.0f}, g_data->overscan_pixels);
|
||||
|
||||
camtexcofac[0] = size_final[0] / size_orig[0];
|
||||
camtexcofac[1] = size_final[1] / size_orig[1];
|
||||
|
||||
camtexcofac[2] = -camtexcofac[0] * g_data->overscan_pixels / size_final[0];
|
||||
camtexcofac[3] = -camtexcofac[1] * g_data->overscan_pixels / size_final[1];
|
||||
}
|
||||
else {
|
||||
copy_v2_v2(size_final, size_orig);
|
||||
g_data->overscan = 0.0f;
|
||||
g_data->overscan_pixels = 0.0f;
|
||||
copy_v4_fl4(camtexcofac, 1.0f, 1.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
const int final_res[2] = {
|
||||
size_orig[0] + g_data->overscan_pixels * 2.0f,
|
||||
size_orig[1] + g_data->overscan_pixels * 2.0f,
|
||||
};
|
||||
|
||||
int max_dim = max_ii(final_res[0], final_res[1]);
|
||||
if (max_dim > GPU_max_texture_size()) {
|
||||
char error_msg[128];
|
||||
BLI_snprintf(error_msg,
|
||||
sizeof(error_msg),
|
||||
"Error: Reported texture size limit (%dpx) is lower than output size (%dpx).",
|
||||
GPU_max_texture_size(),
|
||||
max_dim);
|
||||
RE_engine_set_error_message(engine, error_msg);
|
||||
G.is_break = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* XXX overriding viewport size. Simplify things but is not really 100% safe. */
|
||||
DRW_render_viewport_size_set(final_res);
|
||||
|
||||
/* TODO: 32 bit depth. */
|
||||
DRW_texture_ensure_fullscreen_2d(&dtxl->depth, GPU_DEPTH24_STENCIL8, 0);
|
||||
DRW_texture_ensure_fullscreen_2d(&txl->color, GPU_RGBA32F, DRW_TEX_FILTER);
|
||||
|
||||
GPU_framebuffer_ensure_config(
|
||||
&dfbl->default_fb,
|
||||
{GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(txl->color)});
|
||||
GPU_framebuffer_ensure_config(
|
||||
&fbl->main_fb, {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(txl->color)});
|
||||
GPU_framebuffer_ensure_config(&fbl->main_color_fb,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->color)});
|
||||
|
||||
/* Camera could change because of Motion blur. */
|
||||
g_data->cam_original_ob = RE_GetCamera(engine->re);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EEVEE_render_modules_init(EEVEE_Data *vedata,
|
||||
RenderEngine *engine,
|
||||
struct Depsgraph *depsgraph)
|
||||
{
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = vedata->stl->g_data;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
/* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */
|
||||
struct Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, g_data->cam_original_ob);
|
||||
EEVEE_render_view_sync(vedata, engine, depsgraph);
|
||||
|
||||
/* `EEVEE_renderpasses_init` will set the active render passes used by `EEVEE_effects_init`.
|
||||
* `EEVEE_effects_init` needs to go second for TAA. */
|
||||
EEVEE_renderpasses_init(vedata);
|
||||
EEVEE_effects_init(sldata, vedata, ob_camera_eval, false);
|
||||
EEVEE_materials_init(sldata, vedata, stl, fbl);
|
||||
EEVEE_shadows_init(sldata);
|
||||
EEVEE_lightprobes_init(sldata, vedata);
|
||||
}
|
||||
|
||||
void EEVEE_render_view_sync(EEVEE_Data *vedata, RenderEngine *engine, struct Depsgraph *depsgraph)
|
||||
{
|
||||
EEVEE_PrivateData *g_data = vedata->stl->g_data;
|
||||
|
||||
/* Set the perspective & view matrix. */
|
||||
float winmat[4][4], viewmat[4][4], viewinv[4][4];
|
||||
/* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */
|
||||
struct Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, g_data->cam_original_ob);
|
||||
|
||||
RE_GetCameraWindow(engine->re, ob_camera_eval, winmat);
|
||||
RE_GetCameraWindowWithOverscan(engine->re, g_data->overscan, winmat);
|
||||
RE_GetCameraModelMatrix(engine->re, ob_camera_eval, viewinv);
|
||||
|
||||
invert_m4_m4(viewmat, viewinv);
|
||||
|
||||
DRWView *view = DRW_view_create(viewmat, winmat, NULL, NULL, NULL);
|
||||
DRW_view_reset();
|
||||
DRW_view_default_set(view);
|
||||
DRW_view_set_active(view);
|
||||
|
||||
DRW_view_camtexco_set(view, g_data->camtexcofac);
|
||||
}
|
||||
|
||||
void EEVEE_render_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_view_layer_data_ensure();
|
||||
EEVEE_bloom_cache_init(sldata, vedata);
|
||||
EEVEE_depth_of_field_cache_init(sldata, vedata);
|
||||
EEVEE_effects_cache_init(sldata, vedata);
|
||||
EEVEE_lightprobes_cache_init(sldata, vedata);
|
||||
EEVEE_lights_cache_init(sldata, vedata);
|
||||
EEVEE_materials_cache_init(sldata, vedata);
|
||||
EEVEE_motion_blur_cache_init(sldata, vedata);
|
||||
EEVEE_occlusion_cache_init(sldata, vedata);
|
||||
EEVEE_screen_raytrace_cache_init(sldata, vedata);
|
||||
EEVEE_subsurface_cache_init(sldata, vedata);
|
||||
EEVEE_temporal_sampling_cache_init(sldata, vedata);
|
||||
EEVEE_volumes_cache_init(sldata, vedata);
|
||||
EEVEE_cryptomatte_cache_init(sldata, vedata);
|
||||
}
|
||||
|
||||
void EEVEE_render_cache(void *vedata,
|
||||
struct Object *ob,
|
||||
struct RenderEngine *engine,
|
||||
struct Depsgraph *depsgraph)
|
||||
{
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
EEVEE_Data *data = vedata;
|
||||
EEVEE_StorageList *stl = data->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
EEVEE_LightProbesInfo *pinfo = sldata->probes;
|
||||
bool cast_shadow = false;
|
||||
|
||||
const bool do_cryptomatte = (engine != NULL) &&
|
||||
((g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0);
|
||||
|
||||
eevee_id_update(vedata, &ob->id);
|
||||
|
||||
if (pinfo->vis_data.collection) {
|
||||
/* Used for rendering probe with visibility groups. */
|
||||
bool ob_vis = BKE_collection_has_object_recursive(pinfo->vis_data.collection, ob);
|
||||
ob_vis = (pinfo->vis_data.invert) ? !ob_vis : ob_vis;
|
||||
|
||||
if (!ob_vis) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't print dupli objects as this can be very verbose and
|
||||
* increase the render time on Windows because of slow windows term.
|
||||
* (see T59649) */
|
||||
if (engine && (ob->base_flag & BASE_FROM_DUPLI) == 0) {
|
||||
char info[42];
|
||||
BLI_snprintf(info, sizeof(info), "Syncing %s", ob->id.name + 2);
|
||||
RE_engine_update_stats(engine, NULL, info);
|
||||
}
|
||||
|
||||
const int ob_visibility = DRW_object_visibility_in_active_context(ob);
|
||||
if (ob_visibility & OB_VISIBLE_PARTICLES) {
|
||||
EEVEE_particle_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
|
||||
if (do_cryptomatte) {
|
||||
EEVEE_cryptomatte_particle_hair_cache_populate(data, sldata, ob);
|
||||
}
|
||||
}
|
||||
|
||||
if (ob_visibility & OB_VISIBLE_SELF) {
|
||||
if (ELEM(ob->type, OB_MESH, OB_SURF, OB_MBALL)) {
|
||||
EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
|
||||
if (do_cryptomatte) {
|
||||
EEVEE_cryptomatte_cache_populate(data, sldata, ob);
|
||||
}
|
||||
}
|
||||
else if (ob->type == OB_HAIR) {
|
||||
EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
|
||||
if (do_cryptomatte) {
|
||||
EEVEE_cryptomatte_object_hair_cache_populate(data, sldata, ob);
|
||||
}
|
||||
}
|
||||
else if (ob->type == OB_VOLUME) {
|
||||
Scene *scene = DEG_get_evaluated_scene(depsgraph);
|
||||
EEVEE_volumes_cache_object_add(sldata, vedata, scene, ob);
|
||||
}
|
||||
else if (ob->type == OB_LIGHTPROBE) {
|
||||
EEVEE_lightprobes_cache_add(sldata, vedata, ob);
|
||||
}
|
||||
else if (ob->type == OB_LAMP) {
|
||||
EEVEE_lights_cache_add(sldata, ob);
|
||||
}
|
||||
}
|
||||
|
||||
if (cast_shadow) {
|
||||
EEVEE_shadows_caster_register(sldata, ob);
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_render_color_result(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
const char *render_pass_name,
|
||||
int num_channels,
|
||||
GPUFrameBuffer *framebuffer,
|
||||
EEVEE_Data *vedata)
|
||||
{
|
||||
RenderPass *rp = RE_pass_find_by_name(rl, render_pass_name, viewname);
|
||||
if (rp == NULL) {
|
||||
return;
|
||||
}
|
||||
GPU_framebuffer_bind(framebuffer);
|
||||
GPU_framebuffer_read_color(framebuffer,
|
||||
vedata->stl->g_data->overscan_pixels + rect->xmin,
|
||||
vedata->stl->g_data->overscan_pixels + rect->ymin,
|
||||
BLI_rcti_size_x(rect),
|
||||
BLI_rcti_size_y(rect),
|
||||
num_channels,
|
||||
0,
|
||||
GPU_DATA_FLOAT,
|
||||
rp->rect);
|
||||
}
|
||||
|
||||
static void eevee_render_result_combined(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *UNUSED(sldata))
|
||||
{
|
||||
eevee_render_color_result(
|
||||
rl, viewname, rect, RE_PASSNAME_COMBINED, 4, vedata->stl->effects->final_fb, vedata);
|
||||
}
|
||||
|
||||
static void eevee_render_result_normal(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
const int current_sample = vedata->stl->effects->taa_current_sample;
|
||||
|
||||
/* Only read the center texel. */
|
||||
if (current_sample > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_NORMAL) != 0) {
|
||||
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_NORMAL, 0);
|
||||
eevee_render_color_result(
|
||||
rl, viewname, rect, RE_PASSNAME_NORMAL, 3, vedata->fbl->renderpass_fb, vedata);
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_render_result_z(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
const int current_sample = vedata->stl->effects->taa_current_sample;
|
||||
|
||||
/* Only read the center texel. */
|
||||
if (current_sample > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_Z) != 0) {
|
||||
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_Z, 0);
|
||||
eevee_render_color_result(
|
||||
rl, viewname, rect, RE_PASSNAME_Z, 1, vedata->fbl->renderpass_fb, vedata);
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_render_result_mist(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_MIST) != 0) {
|
||||
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_MIST, 0);
|
||||
eevee_render_color_result(
|
||||
rl, viewname, rect, RE_PASSNAME_MIST, 1, vedata->fbl->renderpass_fb, vedata);
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_render_result_shadow(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_SHADOW) != 0) {
|
||||
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_SHADOW, 0);
|
||||
eevee_render_color_result(
|
||||
rl, viewname, rect, RE_PASSNAME_SHADOW, 3, vedata->fbl->renderpass_fb, vedata);
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_render_result_occlusion(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_AO) != 0) {
|
||||
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AO, 0);
|
||||
eevee_render_color_result(
|
||||
rl, viewname, rect, RE_PASSNAME_AO, 3, vedata->fbl->renderpass_fb, vedata);
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_render_result_bloom(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
if ((vedata->stl->effects->enabled_effects & EFFECT_BLOOM) == 0) {
|
||||
/* Bloom is not enabled. */
|
||||
return;
|
||||
}
|
||||
|
||||
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_BLOOM) != 0) {
|
||||
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_BLOOM, 0);
|
||||
eevee_render_color_result(
|
||||
rl, viewname, rect, RE_PASSNAME_BLOOM, 3, vedata->fbl->renderpass_fb, vedata);
|
||||
}
|
||||
}
|
||||
|
||||
#define EEVEE_RENDER_RESULT_MATERIAL_PASS(pass_name, eevee_pass_type) \
|
||||
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_##eevee_pass_type) != 0) { \
|
||||
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_##eevee_pass_type, 0); \
|
||||
eevee_render_color_result( \
|
||||
rl, viewname, rect, RE_PASSNAME_##pass_name, 3, vedata->fbl->renderpass_fb, vedata); \
|
||||
}
|
||||
|
||||
static void eevee_render_result_diffuse_color(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
EEVEE_RENDER_RESULT_MATERIAL_PASS(DIFFUSE_COLOR, DIFFUSE_COLOR)
|
||||
}
|
||||
|
||||
static void eevee_render_result_diffuse_direct(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
EEVEE_RENDER_RESULT_MATERIAL_PASS(DIFFUSE_DIRECT, DIFFUSE_LIGHT)
|
||||
}
|
||||
|
||||
static void eevee_render_result_specular_color(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
EEVEE_RENDER_RESULT_MATERIAL_PASS(GLOSSY_COLOR, SPECULAR_COLOR)
|
||||
}
|
||||
|
||||
static void eevee_render_result_specular_direct(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
EEVEE_RENDER_RESULT_MATERIAL_PASS(GLOSSY_DIRECT, SPECULAR_LIGHT)
|
||||
}
|
||||
|
||||
static void eevee_render_result_emission(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
EEVEE_RENDER_RESULT_MATERIAL_PASS(EMIT, EMIT)
|
||||
}
|
||||
|
||||
static void eevee_render_result_environment(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
EEVEE_RENDER_RESULT_MATERIAL_PASS(ENVIRONMENT, ENVIRONMENT)
|
||||
}
|
||||
|
||||
static void eevee_render_result_volume_light(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
EEVEE_RENDER_RESULT_MATERIAL_PASS(VOLUME_LIGHT, VOLUME_LIGHT)
|
||||
}
|
||||
|
||||
static void eevee_render_result_aovs(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_AOV) != 0) {
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
int aov_index = 0;
|
||||
LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
|
||||
if ((aov->flag & AOV_CONFLICT) != 0) {
|
||||
continue;
|
||||
}
|
||||
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AOV, aov_index);
|
||||
switch (aov->type) {
|
||||
case AOV_TYPE_COLOR:
|
||||
eevee_render_color_result(
|
||||
rl, viewname, rect, aov->name, 4, vedata->fbl->renderpass_fb, vedata);
|
||||
break;
|
||||
case AOV_TYPE_VALUE:
|
||||
eevee_render_color_result(
|
||||
rl, viewname, rect, aov->name, 1, vedata->fbl->renderpass_fb, vedata);
|
||||
}
|
||||
aov_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef EEVEE_RENDER_RESULT_MATERIAL_PASS
|
||||
|
||||
static void eevee_render_result_cryptomatte(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) {
|
||||
EEVEE_cryptomatte_render_result(rl, viewname, rect, vedata, sldata);
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_render_draw_background(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
|
||||
/* Prevent background to write to data buffers.
|
||||
* NOTE: This also make sure the textures are bound to the right double buffer. */
|
||||
GPU_framebuffer_ensure_config(&fbl->main_fb,
|
||||
{GPU_ATTACHMENT_LEAVE,
|
||||
GPU_ATTACHMENT_LEAVE,
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_NONE});
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
|
||||
DRW_draw_pass(psl->background_ps);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->main_fb,
|
||||
{GPU_ATTACHMENT_LEAVE,
|
||||
GPU_ATTACHMENT_LEAVE,
|
||||
GPU_ATTACHMENT_TEXTURE(stl->effects->ssr_normal_input),
|
||||
GPU_ATTACHMENT_TEXTURE(stl->effects->ssr_specrough_input),
|
||||
GPU_ATTACHMENT_TEXTURE(stl->effects->sss_irradiance),
|
||||
GPU_ATTACHMENT_TEXTURE(stl->effects->sss_radius),
|
||||
GPU_ATTACHMENT_TEXTURE(stl->effects->sss_albedo)});
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
}
|
||||
|
||||
void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl, const rcti *rect)
|
||||
{
|
||||
const char *viewname = RE_GetActiveRenderView(engine->re);
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
|
||||
/* Push instances attributes to the GPU. */
|
||||
DRW_render_instance_buffer_finish();
|
||||
|
||||
/* Need to be called after DRW_render_instance_buffer_finish() */
|
||||
/* Also we weed to have a correct FBO bound for DRW_hair_update */
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
DRW_hair_update();
|
||||
|
||||
/* Sort transparents before the loop. */
|
||||
DRW_pass_sort_shgroup_z(psl->transparent_pass);
|
||||
|
||||
uint tot_sample = stl->g_data->render_sample_count_per_timestep;
|
||||
uint render_samples = 0;
|
||||
|
||||
/* SSR needs one iteration to start properly. */
|
||||
if ((stl->effects->enabled_effects & EFFECT_SSR) && !stl->effects->ssr_was_valid_double_buffer) {
|
||||
tot_sample += 1;
|
||||
}
|
||||
|
||||
while (render_samples < tot_sample && !RE_engine_test_break(engine)) {
|
||||
const float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
float clear_depth = 1.0f;
|
||||
uint clear_stencil = 0x00;
|
||||
const uint primes[3] = {2, 3, 7};
|
||||
double offset[3] = {0.0, 0.0, 0.0};
|
||||
double r[3];
|
||||
|
||||
if ((stl->effects->enabled_effects & EFFECT_SSR) && (render_samples == 1) &&
|
||||
!stl->effects->ssr_was_valid_double_buffer) {
|
||||
/* SSR needs one iteration to start properly.
|
||||
* This iteration was done, reset to the original target sample count. */
|
||||
render_samples--;
|
||||
tot_sample--;
|
||||
/* Reset sampling (and accumulation) after the first sample to avoid
|
||||
* washed out first bounce for SSR. */
|
||||
EEVEE_temporal_sampling_reset(vedata);
|
||||
stl->effects->ssr_was_valid_double_buffer = stl->g_data->valid_double_buffer;
|
||||
}
|
||||
/* Don't print every samples as it can lead to bad performance. (see T59649) */
|
||||
else if ((render_samples % 25) == 0 || (render_samples + 1) == tot_sample) {
|
||||
char info[42];
|
||||
BLI_snprintf(
|
||||
info, sizeof(info), "Rendering %u / %u samples", render_samples + 1, tot_sample);
|
||||
RE_engine_update_stats(engine, NULL, info);
|
||||
}
|
||||
|
||||
/* Copy previous persmat to UBO data */
|
||||
copy_m4_m4(sldata->common_data.prev_persmat, stl->effects->prev_persmat);
|
||||
|
||||
BLI_halton_3d(primes, offset, stl->effects->taa_current_sample, r);
|
||||
EEVEE_update_noise(psl, fbl, r);
|
||||
EEVEE_temporal_sampling_matrices_calc(stl->effects, r);
|
||||
EEVEE_volumes_set_jitter(sldata, stl->effects->taa_current_sample - 1);
|
||||
EEVEE_materials_init(sldata, vedata, stl, fbl);
|
||||
|
||||
/* Refresh Probes
|
||||
* Shadows needs to be updated for correct probes */
|
||||
EEVEE_shadows_update(sldata, vedata);
|
||||
EEVEE_lightprobes_refresh(sldata, vedata);
|
||||
EEVEE_lightprobes_refresh_planar(sldata, vedata);
|
||||
|
||||
/* Refresh Shadows */
|
||||
EEVEE_shadows_draw(sldata, vedata, stl->effects->taa_view);
|
||||
|
||||
/* Set matrices. */
|
||||
DRW_view_set_active(stl->effects->taa_view);
|
||||
|
||||
/* Set ray type. */
|
||||
sldata->common_data.ray_type = EEVEE_RAY_CAMERA;
|
||||
sldata->common_data.ray_depth = 0.0f;
|
||||
GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
|
||||
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
GPU_framebuffer_clear_color_depth_stencil(fbl->main_fb, clear_col, clear_depth, clear_stencil);
|
||||
/* Depth prepass */
|
||||
DRW_draw_pass(psl->depth_ps);
|
||||
/* Create minmax texture */
|
||||
EEVEE_create_minmax_buffer(vedata, dtxl->depth, -1);
|
||||
EEVEE_occlusion_compute(sldata, vedata);
|
||||
EEVEE_volumes_compute(sldata, vedata);
|
||||
/* Shading pass */
|
||||
eevee_render_draw_background(vedata);
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
DRW_draw_pass(psl->material_ps);
|
||||
EEVEE_subsurface_data_render(sldata, vedata);
|
||||
/* Effects pre-transparency */
|
||||
EEVEE_subsurface_compute(sldata, vedata);
|
||||
EEVEE_reflection_compute(sldata, vedata);
|
||||
EEVEE_refraction_compute(sldata, vedata);
|
||||
/* Opaque refraction */
|
||||
DRW_draw_pass(psl->depth_refract_ps);
|
||||
DRW_draw_pass(psl->material_refract_ps);
|
||||
/* Result NORMAL */
|
||||
eevee_render_result_normal(rl, viewname, rect, vedata, sldata);
|
||||
/* Volumetrics Resolve Opaque */
|
||||
EEVEE_volumes_resolve(sldata, vedata);
|
||||
/* Subsurface output, Occlusion output, Mist output */
|
||||
EEVEE_renderpasses_output_accumulate(sldata, vedata, false);
|
||||
/* Transparent */
|
||||
GPU_framebuffer_texture_attach(fbl->main_color_fb, dtxl->depth, 0, 0);
|
||||
GPU_framebuffer_bind(fbl->main_color_fb);
|
||||
DRW_draw_pass(psl->transparent_pass);
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
GPU_framebuffer_texture_detach(fbl->main_color_fb, dtxl->depth);
|
||||
/* Result Z */
|
||||
eevee_render_result_z(rl, viewname, rect, vedata, sldata);
|
||||
/* Post Process */
|
||||
EEVEE_draw_effects(sldata, vedata);
|
||||
|
||||
/* XXX Seems to fix TDR issue with NVidia drivers on linux. */
|
||||
GPU_finish();
|
||||
|
||||
RE_engine_update_progress(engine, (float)(render_samples++) / (float)tot_sample);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_render_read_result(EEVEE_Data *vedata,
|
||||
RenderEngine *engine,
|
||||
RenderLayer *rl,
|
||||
const rcti *rect)
|
||||
{
|
||||
const char *viewname = RE_GetActiveRenderView(engine->re);
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
|
||||
eevee_render_result_combined(rl, viewname, rect, vedata, sldata);
|
||||
eevee_render_result_mist(rl, viewname, rect, vedata, sldata);
|
||||
eevee_render_result_occlusion(rl, viewname, rect, vedata, sldata);
|
||||
eevee_render_result_shadow(rl, viewname, rect, vedata, sldata);
|
||||
eevee_render_result_diffuse_color(rl, viewname, rect, vedata, sldata);
|
||||
eevee_render_result_diffuse_direct(rl, viewname, rect, vedata, sldata);
|
||||
eevee_render_result_specular_color(rl, viewname, rect, vedata, sldata);
|
||||
eevee_render_result_specular_direct(rl, viewname, rect, vedata, sldata);
|
||||
eevee_render_result_emission(rl, viewname, rect, vedata, sldata);
|
||||
eevee_render_result_environment(rl, viewname, rect, vedata, sldata);
|
||||
eevee_render_result_bloom(rl, viewname, rect, vedata, sldata);
|
||||
eevee_render_result_volume_light(rl, viewname, rect, vedata, sldata);
|
||||
eevee_render_result_aovs(rl, viewname, rect, vedata, sldata);
|
||||
eevee_render_result_cryptomatte(rl, viewname, rect, vedata, sldata);
|
||||
}
|
||||
|
||||
void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
|
||||
{
|
||||
RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
|
||||
|
||||
#define CHECK_PASS_LEGACY(name, type, channels, chanid) \
|
||||
if (view_layer->passflag & (SCE_PASS_##name)) { \
|
||||
RE_engine_register_pass( \
|
||||
engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
|
||||
} \
|
||||
((void)0)
|
||||
#define CHECK_PASS_EEVEE(name, type, channels, chanid) \
|
||||
if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \
|
||||
RE_engine_register_pass( \
|
||||
engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z");
|
||||
CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z");
|
||||
CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ");
|
||||
CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_EEVEE(BLOOM, SOCK_RGBA, 3, "RGB");
|
||||
|
||||
LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
|
||||
if ((aov->flag & AOV_CONFLICT) != 0) {
|
||||
continue;
|
||||
}
|
||||
switch (aov->type) {
|
||||
case AOV_TYPE_COLOR:
|
||||
RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA);
|
||||
break;
|
||||
case AOV_TYPE_VALUE:
|
||||
RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
EEVEE_cryptomatte_update_passes(engine, scene, view_layer);
|
||||
|
||||
#undef CHECK_PASS_LEGACY
|
||||
#undef CHECK_PASS_EEVEE
|
||||
}
|
||||
@@ -1,518 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2019, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#include "DRW_engine.h"
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "draw_color_management.h" /* TODO: remove dependency. */
|
||||
|
||||
#include "BKE_global.h" /* for G.debug_value */
|
||||
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_string_utils.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "eevee_private.h"
|
||||
|
||||
typedef enum eRenderPassPostProcessType {
|
||||
PASS_POST_UNDEFINED = 0,
|
||||
PASS_POST_ACCUMULATED_COLOR = 1,
|
||||
PASS_POST_ACCUMULATED_COLOR_ALPHA = 2,
|
||||
PASS_POST_ACCUMULATED_LIGHT = 3,
|
||||
PASS_POST_ACCUMULATED_VALUE = 4,
|
||||
PASS_POST_DEPTH = 5,
|
||||
PASS_POST_AO = 6,
|
||||
PASS_POST_NORMAL = 7,
|
||||
PASS_POST_TWO_LIGHT_BUFFERS = 8,
|
||||
PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR = 9,
|
||||
} eRenderPassPostProcessType;
|
||||
|
||||
/* bitmask containing all renderpasses that need post-processing */
|
||||
#define EEVEE_RENDERPASSES_WITH_POST_PROCESSING \
|
||||
(EEVEE_RENDER_PASS_Z | EEVEE_RENDER_PASS_MIST | EEVEE_RENDER_PASS_NORMAL | \
|
||||
EEVEE_RENDER_PASS_AO | EEVEE_RENDER_PASS_BLOOM | EEVEE_RENDER_PASS_VOLUME_LIGHT | \
|
||||
EEVEE_RENDER_PASS_SHADOW | EEVEE_RENDERPASSES_MATERIAL)
|
||||
|
||||
#define EEVEE_RENDERPASSES_ALL \
|
||||
(EEVEE_RENDERPASSES_WITH_POST_PROCESSING | EEVEE_RENDER_PASS_COMBINED)
|
||||
|
||||
#define EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE \
|
||||
(EEVEE_RENDER_PASS_Z | EEVEE_RENDER_PASS_NORMAL)
|
||||
|
||||
#define EEVEE_RENDERPASSES_COLOR_PASS \
|
||||
(EEVEE_RENDER_PASS_DIFFUSE_COLOR | EEVEE_RENDER_PASS_SPECULAR_COLOR | EEVEE_RENDER_PASS_EMIT | \
|
||||
EEVEE_RENDER_PASS_BLOOM)
|
||||
#define EEVEE_RENDERPASSES_LIGHT_PASS \
|
||||
(EEVEE_RENDER_PASS_DIFFUSE_LIGHT | EEVEE_RENDER_PASS_SPECULAR_LIGHT)
|
||||
/* Render passes that uses volume transmittance when available */
|
||||
#define EEVEE_RENDERPASSES_USES_TRANSMITTANCE \
|
||||
(EEVEE_RENDER_PASS_DIFFUSE_COLOR | EEVEE_RENDER_PASS_SPECULAR_COLOR | EEVEE_RENDER_PASS_EMIT | \
|
||||
EEVEE_RENDER_PASS_ENVIRONMENT)
|
||||
bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
return (g_data->render_passes & ~EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE) == 0;
|
||||
}
|
||||
|
||||
int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov)
|
||||
{
|
||||
int hash = BLI_hash_string(aov->name) << 1;
|
||||
SET_FLAG_FROM_TEST(hash, aov->type == AOV_TYPE_COLOR, EEVEE_AOV_HASH_COLOR_TYPE_MASK);
|
||||
return hash;
|
||||
}
|
||||
|
||||
void EEVEE_renderpasses_init(EEVEE_Data *vedata)
|
||||
{
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
View3D *v3d = draw_ctx->v3d;
|
||||
|
||||
if (v3d) {
|
||||
const Scene *scene = draw_ctx->scene;
|
||||
eViewLayerEEVEEPassType render_pass = v3d->shading.render_pass;
|
||||
g_data->aov_hash = 0;
|
||||
|
||||
if (render_pass == EEVEE_RENDER_PASS_BLOOM &&
|
||||
((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) {
|
||||
render_pass = EEVEE_RENDER_PASS_COMBINED;
|
||||
}
|
||||
if (render_pass == EEVEE_RENDER_PASS_AOV) {
|
||||
ViewLayerAOV *aov = BLI_findstring(
|
||||
&view_layer->aovs, v3d->shading.aov_name, offsetof(ViewLayerAOV, name));
|
||||
if (aov != NULL) {
|
||||
g_data->aov_hash = EEVEE_renderpasses_aov_hash(aov);
|
||||
}
|
||||
else {
|
||||
/* AOV not found in view layer. */
|
||||
render_pass = EEVEE_RENDER_PASS_COMBINED;
|
||||
}
|
||||
}
|
||||
|
||||
g_data->render_passes = render_pass;
|
||||
}
|
||||
else {
|
||||
eViewLayerEEVEEPassType enabled_render_passes = view_layer->eevee.render_passes;
|
||||
|
||||
#define ENABLE_FROM_LEGACY(name_legacy, name_eevee) \
|
||||
SET_FLAG_FROM_TEST(enabled_render_passes, \
|
||||
(view_layer->passflag & SCE_PASS_##name_legacy) != 0, \
|
||||
EEVEE_RENDER_PASS_##name_eevee);
|
||||
|
||||
ENABLE_FROM_LEGACY(Z, Z)
|
||||
ENABLE_FROM_LEGACY(MIST, MIST)
|
||||
ENABLE_FROM_LEGACY(NORMAL, NORMAL)
|
||||
ENABLE_FROM_LEGACY(SHADOW, SHADOW)
|
||||
ENABLE_FROM_LEGACY(AO, AO)
|
||||
ENABLE_FROM_LEGACY(EMIT, EMIT)
|
||||
ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT)
|
||||
ENABLE_FROM_LEGACY(DIFFUSE_COLOR, DIFFUSE_COLOR)
|
||||
ENABLE_FROM_LEGACY(GLOSSY_COLOR, SPECULAR_COLOR)
|
||||
ENABLE_FROM_LEGACY(DIFFUSE_DIRECT, DIFFUSE_LIGHT)
|
||||
ENABLE_FROM_LEGACY(GLOSSY_DIRECT, SPECULAR_LIGHT)
|
||||
|
||||
ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT)
|
||||
|
||||
#undef ENABLE_FROM_LEGACY
|
||||
if (DRW_state_is_image_render() && !BLI_listbase_is_empty(&view_layer->aovs)) {
|
||||
enabled_render_passes |= EEVEE_RENDER_PASS_AOV;
|
||||
g_data->aov_hash = EEVEE_AOV_HASH_ALL;
|
||||
}
|
||||
|
||||
g_data->render_passes = (enabled_render_passes & EEVEE_RENDERPASSES_ALL) |
|
||||
EEVEE_RENDER_PASS_COMBINED;
|
||||
}
|
||||
EEVEE_material_renderpasses_init(vedata);
|
||||
EEVEE_cryptomatte_renderpasses_init(vedata);
|
||||
}
|
||||
|
||||
BLI_INLINE bool eevee_renderpasses_volumetric_active(const EEVEE_EffectsInfo *effects,
|
||||
const EEVEE_PrivateData *g_data)
|
||||
{
|
||||
if (effects->enabled_effects & EFFECT_VOLUMETRIC) {
|
||||
if (g_data->render_passes &
|
||||
(EEVEE_RENDER_PASS_VOLUME_LIGHT | EEVEE_RENDERPASSES_USES_TRANSMITTANCE)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata,
|
||||
EEVEE_Data *vedata,
|
||||
uint tot_samples)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
|
||||
const bool needs_post_processing = (g_data->render_passes &
|
||||
EEVEE_RENDERPASSES_WITH_POST_PROCESSING) > 0;
|
||||
if (needs_post_processing) {
|
||||
/* Create FrameBuffer. */
|
||||
|
||||
/* Should be enough to store the data needs for a single pass.
|
||||
* Some passes will use less, but it is only relevant for final renderings and
|
||||
* when renderpasses other than `EEVEE_RENDER_PASS_COMBINED` are requested */
|
||||
DRW_texture_ensure_fullscreen_2d(&txl->renderpass, GPU_RGBA16F, 0);
|
||||
GPU_framebuffer_ensure_config(&fbl->renderpass_fb,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->renderpass)});
|
||||
|
||||
if ((g_data->render_passes & EEVEE_RENDERPASSES_MATERIAL) != 0) {
|
||||
EEVEE_material_output_init(sldata, vedata, tot_samples);
|
||||
}
|
||||
|
||||
if ((g_data->render_passes & EEVEE_RENDER_PASS_MIST) != 0) {
|
||||
EEVEE_mist_output_init(sldata, vedata);
|
||||
}
|
||||
if ((g_data->render_passes & EEVEE_RENDER_PASS_SHADOW) != 0) {
|
||||
EEVEE_shadow_output_init(sldata, vedata, tot_samples);
|
||||
}
|
||||
|
||||
if ((g_data->render_passes & EEVEE_RENDER_PASS_AO) != 0) {
|
||||
EEVEE_occlusion_output_init(sldata, vedata, tot_samples);
|
||||
}
|
||||
|
||||
if ((g_data->render_passes & EEVEE_RENDER_PASS_BLOOM) != 0 &&
|
||||
(effects->enabled_effects & EFFECT_BLOOM) != 0) {
|
||||
EEVEE_bloom_output_init(sldata, vedata, tot_samples);
|
||||
}
|
||||
|
||||
if (eevee_renderpasses_volumetric_active(effects, g_data)) {
|
||||
EEVEE_volumes_output_init(sldata, vedata, tot_samples);
|
||||
}
|
||||
|
||||
/* We set a default texture as not all post processes uses the inputBuffer. */
|
||||
g_data->renderpass_input = txl->color;
|
||||
g_data->renderpass_col_input = txl->color;
|
||||
g_data->renderpass_light_input = txl->color;
|
||||
g_data->renderpass_transmittance_input = txl->color;
|
||||
}
|
||||
else {
|
||||
/* Free unneeded memory */
|
||||
DRW_TEXTURE_FREE_SAFE(txl->renderpass);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fbl->renderpass_fb);
|
||||
}
|
||||
|
||||
/* Cryptomatte doesn't use the GPU shader for post processing */
|
||||
if ((g_data->render_passes & (EEVEE_RENDER_PASS_CRYPTOMATTE)) != 0) {
|
||||
EEVEE_cryptomatte_output_init(sldata, vedata, tot_samples);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_PrivateData *g_data = vedata->stl->g_data;
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
|
||||
const bool needs_post_processing = (g_data->render_passes &
|
||||
EEVEE_RENDERPASSES_WITH_POST_PROCESSING) > 0;
|
||||
if (needs_post_processing) {
|
||||
DRW_PASS_CREATE(psl->renderpass_pass, DRW_STATE_WRITE_COLOR);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_renderpasses_post_process_sh_get(),
|
||||
psl->renderpass_pass);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "inputBuffer", &g_data->renderpass_input);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "inputColorBuffer", &g_data->renderpass_col_input);
|
||||
DRW_shgroup_uniform_texture_ref(
|
||||
grp, "inputSecondLightBuffer", &g_data->renderpass_light_input);
|
||||
DRW_shgroup_uniform_texture_ref(
|
||||
grp, "inputTransmittanceBuffer", &g_data->renderpass_transmittance_input);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
|
||||
DRW_shgroup_uniform_block_ref(grp, "common_block", &sldata->common_ubo);
|
||||
DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &sldata->renderpass_ubo.combined);
|
||||
DRW_shgroup_uniform_int(grp, "currentSample", &g_data->renderpass_current_sample, 1);
|
||||
DRW_shgroup_uniform_int(grp, "renderpassType", &g_data->renderpass_type, 1);
|
||||
DRW_shgroup_uniform_int(grp, "postProcessType", &g_data->renderpass_postprocess, 1);
|
||||
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
|
||||
}
|
||||
else {
|
||||
psl->renderpass_pass = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *UNUSED(sldata),
|
||||
EEVEE_Data *vedata,
|
||||
eViewLayerEEVEEPassType renderpass_type,
|
||||
int aov_index)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
/* Compensate for taa_current_sample being incremented after last drawing in
|
||||
* EEVEE_temporal_sampling_draw when DRW_state_is_image_render(). */
|
||||
const int current_sample = DRW_state_is_image_render() ? effects->taa_current_sample - 1 :
|
||||
effects->taa_current_sample;
|
||||
g_data->renderpass_current_sample = current_sample;
|
||||
g_data->renderpass_type = renderpass_type;
|
||||
g_data->renderpass_postprocess = PASS_POST_UNDEFINED;
|
||||
const bool volumetric_active = eevee_renderpasses_volumetric_active(effects, g_data);
|
||||
eRenderPassPostProcessType default_color_pass_type =
|
||||
volumetric_active ? PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR : PASS_POST_ACCUMULATED_COLOR;
|
||||
g_data->renderpass_transmittance_input = volumetric_active ? txl->volume_transmittance_accum :
|
||||
txl->color;
|
||||
|
||||
if (!volumetric_active && renderpass_type == EEVEE_RENDER_PASS_VOLUME_LIGHT) {
|
||||
/* Early exit: Volumetric effect is off, but the volume light pass was requested. */
|
||||
static float clear_col[4] = {0.0f};
|
||||
GPU_framebuffer_bind(fbl->renderpass_fb);
|
||||
GPU_framebuffer_clear_color(fbl->renderpass_fb, clear_col);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (renderpass_type) {
|
||||
case EEVEE_RENDER_PASS_Z: {
|
||||
g_data->renderpass_postprocess = PASS_POST_DEPTH;
|
||||
break;
|
||||
}
|
||||
case EEVEE_RENDER_PASS_AO: {
|
||||
g_data->renderpass_postprocess = PASS_POST_AO;
|
||||
g_data->renderpass_input = txl->ao_accum;
|
||||
break;
|
||||
}
|
||||
case EEVEE_RENDER_PASS_NORMAL: {
|
||||
g_data->renderpass_postprocess = PASS_POST_NORMAL;
|
||||
g_data->renderpass_input = effects->ssr_normal_input;
|
||||
break;
|
||||
}
|
||||
case EEVEE_RENDER_PASS_MIST: {
|
||||
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_VALUE;
|
||||
g_data->renderpass_input = txl->mist_accum;
|
||||
break;
|
||||
}
|
||||
case EEVEE_RENDER_PASS_VOLUME_LIGHT: {
|
||||
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR;
|
||||
g_data->renderpass_input = txl->volume_scatter_accum;
|
||||
break;
|
||||
}
|
||||
case EEVEE_RENDER_PASS_SHADOW: {
|
||||
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_VALUE;
|
||||
g_data->renderpass_input = txl->shadow_accum;
|
||||
break;
|
||||
}
|
||||
case EEVEE_RENDER_PASS_DIFFUSE_COLOR: {
|
||||
g_data->renderpass_postprocess = default_color_pass_type;
|
||||
g_data->renderpass_input = txl->diff_color_accum;
|
||||
break;
|
||||
}
|
||||
case EEVEE_RENDER_PASS_SPECULAR_COLOR: {
|
||||
g_data->renderpass_postprocess = default_color_pass_type;
|
||||
g_data->renderpass_input = txl->spec_color_accum;
|
||||
break;
|
||||
}
|
||||
case EEVEE_RENDER_PASS_ENVIRONMENT: {
|
||||
g_data->renderpass_postprocess = default_color_pass_type;
|
||||
g_data->renderpass_input = txl->env_accum;
|
||||
break;
|
||||
}
|
||||
case EEVEE_RENDER_PASS_EMIT: {
|
||||
g_data->renderpass_postprocess = default_color_pass_type;
|
||||
g_data->renderpass_input = txl->emit_accum;
|
||||
break;
|
||||
}
|
||||
case EEVEE_RENDER_PASS_SPECULAR_LIGHT: {
|
||||
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_LIGHT;
|
||||
g_data->renderpass_input = txl->spec_light_accum;
|
||||
g_data->renderpass_col_input = txl->spec_color_accum;
|
||||
if ((stl->effects->enabled_effects & EFFECT_SSR) != 0) {
|
||||
g_data->renderpass_postprocess = PASS_POST_TWO_LIGHT_BUFFERS;
|
||||
g_data->renderpass_light_input = txl->ssr_accum;
|
||||
}
|
||||
else {
|
||||
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_LIGHT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EEVEE_RENDER_PASS_DIFFUSE_LIGHT: {
|
||||
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_LIGHT;
|
||||
g_data->renderpass_input = txl->diff_light_accum;
|
||||
g_data->renderpass_col_input = txl->diff_color_accum;
|
||||
if ((stl->effects->enabled_effects & EFFECT_SSS) != 0) {
|
||||
g_data->renderpass_postprocess = PASS_POST_TWO_LIGHT_BUFFERS;
|
||||
g_data->renderpass_light_input = txl->sss_accum;
|
||||
}
|
||||
else {
|
||||
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_LIGHT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EEVEE_RENDER_PASS_AOV: {
|
||||
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR_ALPHA;
|
||||
g_data->renderpass_input = txl->aov_surface_accum[aov_index];
|
||||
break;
|
||||
}
|
||||
case EEVEE_RENDER_PASS_BLOOM: {
|
||||
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR;
|
||||
g_data->renderpass_input = txl->bloom_accum;
|
||||
g_data->renderpass_current_sample = 1;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
GPU_framebuffer_bind(fbl->renderpass_fb);
|
||||
DRW_draw_pass(psl->renderpass_pass);
|
||||
}
|
||||
|
||||
void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata,
|
||||
EEVEE_Data *vedata,
|
||||
bool post_effect)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
eViewLayerEEVEEPassType render_pass = g_data->render_passes;
|
||||
|
||||
if (!post_effect) {
|
||||
if ((render_pass & EEVEE_RENDER_PASS_MIST) != 0) {
|
||||
EEVEE_mist_output_accumulate(sldata, vedata);
|
||||
}
|
||||
if ((render_pass & EEVEE_RENDER_PASS_AO) != 0) {
|
||||
EEVEE_occlusion_output_accumulate(sldata, vedata);
|
||||
}
|
||||
if ((render_pass & EEVEE_RENDER_PASS_SHADOW) != 0) {
|
||||
EEVEE_shadow_output_accumulate(sldata, vedata);
|
||||
}
|
||||
if ((render_pass & EEVEE_RENDERPASSES_MATERIAL) != 0) {
|
||||
EEVEE_material_output_accumulate(sldata, vedata);
|
||||
}
|
||||
if (eevee_renderpasses_volumetric_active(effects, g_data)) {
|
||||
EEVEE_volumes_output_accumulate(sldata, vedata);
|
||||
}
|
||||
if ((render_pass & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) {
|
||||
EEVEE_cryptomatte_output_accumulate(sldata, vedata);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((render_pass & EEVEE_RENDER_PASS_BLOOM) != 0 &&
|
||||
(effects->enabled_effects & EFFECT_BLOOM) != 0) {
|
||||
EEVEE_bloom_output_accumulate(sldata, vedata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_renderpasses_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
|
||||
|
||||
/* We can only draw a single render-pass. Light-passes also select their color pass
|
||||
* (a second pass). We mask the light pass when a light pass is selected. */
|
||||
const eViewLayerEEVEEPassType render_pass =
|
||||
((stl->g_data->render_passes & EEVEE_RENDERPASSES_LIGHT_PASS) != 0) ?
|
||||
(stl->g_data->render_passes & EEVEE_RENDERPASSES_LIGHT_PASS) :
|
||||
stl->g_data->render_passes;
|
||||
|
||||
bool is_valid = (render_pass & EEVEE_RENDERPASSES_ALL) != 0;
|
||||
bool needs_color_transfer = (render_pass & EEVEE_RENDERPASSES_COLOR_PASS) != 0 &&
|
||||
DRW_state_is_opengl_render();
|
||||
UNUSED_VARS(needs_color_transfer);
|
||||
|
||||
if ((render_pass & EEVEE_RENDER_PASS_BLOOM) != 0 &&
|
||||
(effects->enabled_effects & EFFECT_BLOOM) == 0) {
|
||||
is_valid = false;
|
||||
}
|
||||
|
||||
const int current_sample = stl->effects->taa_current_sample;
|
||||
const int total_samples = stl->effects->taa_total_sample;
|
||||
if ((render_pass & EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE) &&
|
||||
(current_sample > 1 && total_samples != 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_valid) {
|
||||
EEVEE_renderpasses_postprocess(sldata, vedata, render_pass, 0);
|
||||
GPU_framebuffer_bind(dfbl->default_fb);
|
||||
DRW_transform_none(txl->renderpass);
|
||||
}
|
||||
else {
|
||||
/* Draw state is not valid for this pass, clear the buffer */
|
||||
static float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
GPU_framebuffer_bind(dfbl->default_fb);
|
||||
GPU_framebuffer_clear_color(dfbl->default_fb, clear_color);
|
||||
}
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
}
|
||||
|
||||
void EEVEE_renderpasses_draw_debug(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
GPUTexture *tx = NULL;
|
||||
/* Debug : Output buffer to view. */
|
||||
switch (G.debug_value) {
|
||||
case 1:
|
||||
tx = txl->maxzbuffer;
|
||||
break;
|
||||
case 2:
|
||||
/* UNUSED */
|
||||
break;
|
||||
case 3:
|
||||
tx = effects->ssr_normal_input;
|
||||
break;
|
||||
case 4:
|
||||
tx = effects->ssr_specrough_input;
|
||||
break;
|
||||
case 5:
|
||||
tx = txl->color_double_buffer;
|
||||
break;
|
||||
case 6:
|
||||
tx = effects->gtao_horizons_renderpass;
|
||||
break;
|
||||
case 7:
|
||||
tx = effects->gtao_horizons_renderpass;
|
||||
break;
|
||||
case 8:
|
||||
tx = effects->sss_irradiance;
|
||||
break;
|
||||
case 9:
|
||||
tx = effects->sss_radius;
|
||||
break;
|
||||
case 10:
|
||||
tx = effects->sss_albedo;
|
||||
break;
|
||||
case 11:
|
||||
tx = effects->velocity_tx;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (tx) {
|
||||
DRW_transform_none(tx);
|
||||
}
|
||||
}
|
||||
92
source/blender/draw/engines/eevee/eevee_renderpasses.cc
Normal file
92
source/blender/draw/engines/eevee/eevee_renderpasses.cc
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
#include "BLI_hash_tables.hh"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
#include "eevee_renderpasses.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name RenderPasses
|
||||
* \{ */
|
||||
|
||||
void RenderPasses::init(const int extent[2], const rcti *output_rect)
|
||||
{
|
||||
const Scene *scene = inst_.scene;
|
||||
|
||||
eRenderPassBit enabled_passes;
|
||||
if (inst_.render_layer) {
|
||||
enabled_passes = to_render_passes_bits(inst_.render_layer->passflag);
|
||||
/* Cannot output motion vectors when using motion blur. */
|
||||
if (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) {
|
||||
enabled_passes &= ~RENDERPASS_VECTOR;
|
||||
}
|
||||
}
|
||||
else if (inst_.v3d) {
|
||||
enabled_passes = to_render_passes_bits(inst_.v3d->shading.render_pass);
|
||||
/* We need the depth pass for compositing overlays or GPencil. */
|
||||
if (!DRW_state_is_scene_render()) {
|
||||
enabled_passes |= RENDERPASS_DEPTH;
|
||||
}
|
||||
}
|
||||
else {
|
||||
enabled_passes = RENDERPASS_COMBINED;
|
||||
}
|
||||
|
||||
const bool use_log_encoding = scene->eevee.flag & SCE_EEVEE_FILM_LOG_ENCODING;
|
||||
|
||||
rcti fallback_rect;
|
||||
if (BLI_rcti_is_empty(output_rect)) {
|
||||
BLI_rcti_init(&fallback_rect, 0, extent[0], 0, extent[1]);
|
||||
output_rect = &fallback_rect;
|
||||
}
|
||||
|
||||
/* HACK to iterate over all passes. */
|
||||
enabled_passes_ = RENDERPASS_ALL;
|
||||
for (RenderPassItem rpi : *this) {
|
||||
bool enable = (enabled_passes & rpi.pass_bit) != 0;
|
||||
if (enable && rpi.film == nullptr) {
|
||||
rpi.film = new Film(inst_,
|
||||
to_render_passes_data_type(rpi.pass_bit, use_log_encoding),
|
||||
to_render_passes_name(rpi.pass_bit));
|
||||
}
|
||||
else if (!enable && rpi.film != nullptr) {
|
||||
/* Delete unused passes. */
|
||||
delete rpi.film;
|
||||
rpi.film = nullptr;
|
||||
}
|
||||
|
||||
if (rpi.film) {
|
||||
rpi.film->init(extent, output_rect);
|
||||
}
|
||||
}
|
||||
|
||||
enabled_passes_ = enabled_passes;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
249
source/blender/draw/engines/eevee/eevee_renderpasses.hh
Normal file
249
source/blender/draw/engines/eevee/eevee_renderpasses.hh
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BLI_hash_tables.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "eevee_film.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name eRenderPassBit
|
||||
*
|
||||
* This enum might seems redundant but there is an opportunity to use it for internal debug passes.
|
||||
* \{ */
|
||||
|
||||
enum eRenderPassBit {
|
||||
RENDERPASS_NONE = 0,
|
||||
RENDERPASS_COMBINED = (1 << 0),
|
||||
RENDERPASS_DEPTH = (1 << 1),
|
||||
RENDERPASS_NORMAL = (1 << 2),
|
||||
RENDERPASS_VECTOR = (1 << 3),
|
||||
/** Used for iterator. */
|
||||
RENDERPASS_MAX,
|
||||
RENDERPASS_ALL = ((RENDERPASS_MAX - 1) << 1) - 1,
|
||||
};
|
||||
|
||||
ENUM_OPERATORS(eRenderPassBit, RENDERPASS_NORMAL)
|
||||
|
||||
static inline eRenderPassBit to_render_passes_bits(int i_rpasses)
|
||||
{
|
||||
eRenderPassBit rpasses = RENDERPASS_NONE;
|
||||
SET_FLAG_FROM_TEST(rpasses, i_rpasses & SCE_PASS_COMBINED, RENDERPASS_COMBINED);
|
||||
SET_FLAG_FROM_TEST(rpasses, i_rpasses & SCE_PASS_Z, RENDERPASS_DEPTH);
|
||||
SET_FLAG_FROM_TEST(rpasses, i_rpasses & SCE_PASS_NORMAL, RENDERPASS_NORMAL);
|
||||
SET_FLAG_FROM_TEST(rpasses, i_rpasses & SCE_PASS_VECTOR, RENDERPASS_VECTOR);
|
||||
return rpasses;
|
||||
}
|
||||
|
||||
static inline const char *to_render_passes_name(eRenderPassBit rpass)
|
||||
{
|
||||
switch (rpass) {
|
||||
case RENDERPASS_COMBINED:
|
||||
return RE_PASSNAME_COMBINED;
|
||||
case RENDERPASS_DEPTH:
|
||||
return RE_PASSNAME_Z;
|
||||
case RENDERPASS_NORMAL:
|
||||
return RE_PASSNAME_NORMAL;
|
||||
case RENDERPASS_VECTOR:
|
||||
return RE_PASSNAME_VECTOR;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static inline eFilmDataType to_render_passes_data_type(eRenderPassBit rpass,
|
||||
const bool use_log_encoding)
|
||||
{
|
||||
switch (rpass) {
|
||||
case RENDERPASS_COMBINED:
|
||||
return (use_log_encoding) ? FILM_DATA_COLOR_LOG : FILM_DATA_COLOR;
|
||||
case RENDERPASS_DEPTH:
|
||||
return FILM_DATA_DEPTH;
|
||||
case RENDERPASS_NORMAL:
|
||||
return FILM_DATA_NORMAL;
|
||||
case RENDERPASS_VECTOR:
|
||||
return FILM_DATA_MOTION;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
return FILM_DATA_COLOR;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name RenderPasses
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Contains and manages each \c Film output for each render pass output.
|
||||
*/
|
||||
class RenderPasses {
|
||||
public:
|
||||
/** Film for each render pass. A nullptr means the pass is not needed. */
|
||||
Film *combined = nullptr;
|
||||
Film *depth = nullptr;
|
||||
Film *normal = nullptr;
|
||||
Film *vector = nullptr;
|
||||
Vector<Film *> aovs;
|
||||
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
eRenderPassBit enabled_passes_ = RENDERPASS_NONE;
|
||||
|
||||
public:
|
||||
RenderPasses(Instance &inst) : inst_(inst){};
|
||||
|
||||
~RenderPasses()
|
||||
{
|
||||
delete combined;
|
||||
delete depth;
|
||||
delete normal;
|
||||
delete vector;
|
||||
}
|
||||
|
||||
void init(const int extent[2], const rcti *output_rect);
|
||||
|
||||
void sync(void)
|
||||
{
|
||||
for (RenderPassItem rpi : *this) {
|
||||
rpi.film->sync();
|
||||
}
|
||||
}
|
||||
|
||||
void end_sync(void)
|
||||
{
|
||||
for (RenderPassItem rpi : *this) {
|
||||
rpi.film->end_sync();
|
||||
}
|
||||
}
|
||||
|
||||
void resolve_viewport(DefaultFramebufferList *dfbl)
|
||||
{
|
||||
for (RenderPassItem rpi : *this) {
|
||||
if (rpi.pass_bit == RENDERPASS_DEPTH) {
|
||||
rpi.film->resolve_viewport(dfbl->depth_only_fb);
|
||||
}
|
||||
else {
|
||||
/* Ensures only one color render pass is enabled. */
|
||||
BLI_assert((enabled_passes_ & ~RENDERPASS_DEPTH) == rpi.pass_bit);
|
||||
rpi.film->resolve_viewport(dfbl->color_only_fb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void read_result(RenderLayer *render_layer, const char *view_name)
|
||||
{
|
||||
for (RenderPassItem rpi : *this) {
|
||||
const char *pass_name = to_render_passes_name(rpi.pass_bit);
|
||||
RenderPass *rp = RE_pass_find_by_name(render_layer, pass_name, view_name);
|
||||
if (rp) {
|
||||
rpi.film->read_result(rp->rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr Film *&render_pass_bit_to_film_p(eRenderPassBit rpass)
|
||||
{
|
||||
switch (rpass) {
|
||||
case RENDERPASS_COMBINED:
|
||||
return combined;
|
||||
case RENDERPASS_DEPTH:
|
||||
return depth;
|
||||
case RENDERPASS_NORMAL:
|
||||
return normal;
|
||||
case RENDERPASS_VECTOR:
|
||||
return vector;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
return combined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator
|
||||
**/
|
||||
|
||||
struct RenderPassItem {
|
||||
Film *&film;
|
||||
eRenderPassBit pass_bit;
|
||||
|
||||
constexpr explicit RenderPassItem(Film *&film_, eRenderPassBit pass_bit_)
|
||||
: film(film_), pass_bit(pass_bit_){};
|
||||
};
|
||||
|
||||
class Iterator {
|
||||
private:
|
||||
RenderPasses &render_passes_;
|
||||
int64_t current_;
|
||||
|
||||
public:
|
||||
constexpr explicit Iterator(RenderPasses &rpasses, int64_t current)
|
||||
: render_passes_(rpasses), current_(current){};
|
||||
|
||||
constexpr Iterator &operator++()
|
||||
{
|
||||
while (current_ < RENDERPASS_MAX) {
|
||||
current_ <<= 1;
|
||||
if (current_ & render_passes_.enabled_passes_) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr friend bool operator!=(const Iterator &a, const Iterator &b)
|
||||
{
|
||||
return a.current_ != b.current_;
|
||||
}
|
||||
|
||||
constexpr RenderPassItem operator*()
|
||||
{
|
||||
eRenderPassBit pass_bit = static_cast<eRenderPassBit>(current_);
|
||||
return RenderPassItem(render_passes_.render_pass_bit_to_film_p(pass_bit), pass_bit);
|
||||
}
|
||||
};
|
||||
|
||||
/* Iterator over all enabled passes. */
|
||||
constexpr Iterator begin()
|
||||
{
|
||||
return Iterator(*this, 1);
|
||||
}
|
||||
|
||||
constexpr Iterator end()
|
||||
{
|
||||
return Iterator(*this, power_of_2_max_constexpr(RENDERPASS_MAX));
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2019, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup EEVEE
|
||||
*/
|
||||
|
||||
#include "eevee_private.h"
|
||||
|
||||
#include "BLI_rand.h"
|
||||
|
||||
void EEVEE_sample_ball(int sample_ofs, float radius, float rsample[3])
|
||||
{
|
||||
double ht_point[3];
|
||||
double ht_offset[3] = {0.0, 0.0, 0.0};
|
||||
const uint ht_primes[3] = {2, 3, 7};
|
||||
|
||||
BLI_halton_3d(ht_primes, ht_offset, sample_ofs, ht_point);
|
||||
|
||||
/* De-correlate AA and shadow samples. (see T68594) */
|
||||
ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0);
|
||||
ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0);
|
||||
ht_point[2] = fmod(ht_point[2] * 1151.0, 1.0);
|
||||
|
||||
float omega = ht_point[1] * 2.0f * M_PI;
|
||||
|
||||
rsample[2] = ht_point[0] * 2.0f - 1.0f; /* cos theta */
|
||||
|
||||
float r = sqrtf(fmaxf(0.0f, 1.0f - rsample[2] * rsample[2])); /* sin theta */
|
||||
|
||||
rsample[0] = r * cosf(omega);
|
||||
rsample[1] = r * sinf(omega);
|
||||
|
||||
radius *= sqrt(sqrt(ht_point[2]));
|
||||
mul_v3_fl(rsample, radius);
|
||||
}
|
||||
|
||||
void EEVEE_sample_rectangle(int sample_ofs,
|
||||
const float x_axis[3],
|
||||
const float y_axis[3],
|
||||
float size_x,
|
||||
float size_y,
|
||||
float rsample[3])
|
||||
{
|
||||
double ht_point[2];
|
||||
double ht_offset[2] = {0.0, 0.0};
|
||||
const uint ht_primes[2] = {2, 3};
|
||||
|
||||
BLI_halton_2d(ht_primes, ht_offset, sample_ofs, ht_point);
|
||||
|
||||
/* De-correlate AA and shadow samples. (see T68594) */
|
||||
ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0);
|
||||
ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0);
|
||||
|
||||
/* Change distribution center to be 0,0 */
|
||||
ht_point[0] = (ht_point[0] > 0.5f) ? ht_point[0] - 1.0f : ht_point[0];
|
||||
ht_point[1] = (ht_point[1] > 0.5f) ? ht_point[1] - 1.0f : ht_point[1];
|
||||
|
||||
zero_v3(rsample);
|
||||
madd_v3_v3fl(rsample, x_axis, (ht_point[0] * 2.0f) * size_x);
|
||||
madd_v3_v3fl(rsample, y_axis, (ht_point[1] * 2.0f) * size_y);
|
||||
}
|
||||
|
||||
void EEVEE_sample_ellipse(int sample_ofs,
|
||||
const float x_axis[3],
|
||||
const float y_axis[3],
|
||||
float size_x,
|
||||
float size_y,
|
||||
float rsample[3])
|
||||
{
|
||||
double ht_point[2];
|
||||
double ht_offset[2] = {0.0, 0.0};
|
||||
const uint ht_primes[2] = {2, 3};
|
||||
|
||||
BLI_halton_2d(ht_primes, ht_offset, sample_ofs, ht_point);
|
||||
|
||||
/* Decorelate AA and shadow samples. (see T68594) */
|
||||
ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0);
|
||||
ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0);
|
||||
|
||||
/* Uniform disc sampling. */
|
||||
float omega = ht_point[1] * 2.0f * M_PI;
|
||||
float r = sqrtf(ht_point[0]);
|
||||
ht_point[0] = r * cosf(omega) * size_x;
|
||||
ht_point[1] = r * sinf(omega) * size_y;
|
||||
|
||||
zero_v3(rsample);
|
||||
madd_v3_v3fl(rsample, x_axis, ht_point[0]);
|
||||
madd_v3_v3fl(rsample, y_axis, ht_point[1]);
|
||||
}
|
||||
|
||||
void EEVEE_random_rotation_m4(int sample_ofs, float scale, float r_mat[4][4])
|
||||
{
|
||||
double ht_point[3];
|
||||
double ht_offset[3] = {0.0, 0.0, 0.0};
|
||||
const uint ht_primes[3] = {2, 3, 5};
|
||||
|
||||
BLI_halton_3d(ht_primes, ht_offset, sample_ofs, ht_point);
|
||||
|
||||
/* Decorelate AA and shadow samples. (see T68594) */
|
||||
ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0);
|
||||
ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0);
|
||||
ht_point[2] = fmod(ht_point[2] * 1151.0, 1.0);
|
||||
|
||||
rotate_m4(r_mat, 'X', ht_point[0] * scale);
|
||||
rotate_m4(r_mat, 'Y', ht_point[1] * scale);
|
||||
rotate_m4(r_mat, 'Z', ht_point[2] * scale);
|
||||
}
|
||||
358
source/blender/draw/engines/eevee/eevee_sampling.hh
Normal file
358
source/blender/draw/engines/eevee/eevee_sampling.hh
Normal file
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Random Number Generator
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_colortools.h"
|
||||
#include "BLI_rand.h"
|
||||
#include "BLI_vector.hh"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DRW_render.h"
|
||||
#include "GPU_framebuffer.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "eevee_shader_shared.hh"
|
||||
#include "eevee_wrapper.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/**
|
||||
* Random number generator, contains persistent state and sample count logic.
|
||||
*/
|
||||
class Sampling {
|
||||
private:
|
||||
/* Number of samples in the first ring of jittered depth of field. */
|
||||
constexpr static uint64_t dof_web_density_ = 6;
|
||||
/* High number of sample for viewport infinite rendering. */
|
||||
constexpr static uint64_t infinite_sample_count_ = 0xFFFFFFu;
|
||||
|
||||
/** 1 based current sample. */
|
||||
uint64_t sample_ = 1;
|
||||
/** Target sample count. */
|
||||
uint64_t sample_count_ = 64;
|
||||
/** Number of ring in the web pattern of the jittered Depth of Field. */
|
||||
uint64_t dof_ring_count_ = 0;
|
||||
/** Number of samples in the web pattern of the jittered Depth of Field. */
|
||||
uint64_t dof_sample_count_ = 1;
|
||||
/** Motion blur steps. */
|
||||
uint64_t motion_blur_steps_ = 1;
|
||||
/** Used for viewport smooth transition. */
|
||||
int64_t sample_viewport_ = 1;
|
||||
int64_t viewport_smoothing_start = 0;
|
||||
int64_t viewport_smoothing_duration = 0;
|
||||
/** Tag to reset sampling for the next sample. */
|
||||
bool reset_ = false;
|
||||
|
||||
draw::UniformBuffer<SamplingData> data_;
|
||||
|
||||
public:
|
||||
Sampling(){};
|
||||
~Sampling(){};
|
||||
|
||||
void init(const Scene *scene)
|
||||
{
|
||||
sample_count_ = DRW_state_is_image_render() ? scene->eevee.taa_render_samples :
|
||||
scene->eevee.taa_samples;
|
||||
|
||||
if (sample_count_ == 0) {
|
||||
BLI_assert(!DRW_state_is_image_render());
|
||||
sample_count_ = infinite_sample_count_;
|
||||
}
|
||||
|
||||
motion_blur_steps_ = DRW_state_is_image_render() ? scene->eevee.motion_blur_steps : 1;
|
||||
sample_count_ = divide_ceil_u(sample_count_, motion_blur_steps_);
|
||||
|
||||
if (scene->eevee.flag & SCE_EEVEE_DOF_JITTER) {
|
||||
if (sample_count_ == infinite_sample_count_) {
|
||||
/* Special case for viewport continuous rendering. We clamp to a max sample
|
||||
* to avoid the jittered dof never converging. */
|
||||
dof_ring_count_ = 6;
|
||||
}
|
||||
else {
|
||||
dof_ring_count_ = web_ring_count_get(dof_web_density_, sample_count_);
|
||||
}
|
||||
dof_sample_count_ = web_sample_count_get(dof_web_density_, dof_ring_count_);
|
||||
/* Change total sample count to fill the web pattern entirely. */
|
||||
sample_count_ = divide_ceil_u(sample_count_, dof_sample_count_) * dof_sample_count_;
|
||||
}
|
||||
else {
|
||||
dof_ring_count_ = 0;
|
||||
dof_sample_count_ = 1;
|
||||
}
|
||||
|
||||
/* Only multiply after to have full the full DoF web pattern for each time steps. */
|
||||
sample_count_ *= motion_blur_steps_;
|
||||
|
||||
if (!DRW_state_is_image_render()) {
|
||||
/* TODO(fclem) UI. */
|
||||
viewport_smoothing_start = 16;
|
||||
viewport_smoothing_duration = 32;
|
||||
/* At minima start when rendering has finished. */
|
||||
viewport_smoothing_start = min_ii(viewport_smoothing_start, sample_count_);
|
||||
/* Basically counts the number of redraw. */
|
||||
sample_viewport_ += 1;
|
||||
}
|
||||
else {
|
||||
viewport_smoothing_start = 0;
|
||||
viewport_smoothing_duration = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void end_sync(void)
|
||||
{
|
||||
if (reset_) {
|
||||
sample_ = 1;
|
||||
sample_viewport_ = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void step(void)
|
||||
{
|
||||
{
|
||||
/* TODO(fclem) we could use some persistent states to speedup the computation. */
|
||||
double r[2], offset[2] = {0, 0};
|
||||
/* Using 2,3 primes as per UE4 Temporal AA presentation.
|
||||
* advances.realtimerendering.com/s2014/epic/TemporalAA.pptx (slide 14) */
|
||||
uint32_t primes[2] = {2, 3};
|
||||
BLI_halton_2d(primes, offset, sample_, r);
|
||||
data_.dimensions[SAMPLING_FILTER_U][0] = r[0];
|
||||
data_.dimensions[SAMPLING_FILTER_V][0] = r[1];
|
||||
/* TODO decorelate. */
|
||||
data_.dimensions[SAMPLING_TIME][0] = r[0];
|
||||
data_.dimensions[SAMPLING_CLOSURE][0] = r[1];
|
||||
data_.dimensions[SAMPLING_RAYTRACE_X][0] = r[0];
|
||||
}
|
||||
{
|
||||
double r[2], offset[2] = {0, 0};
|
||||
uint32_t primes[2] = {5, 7};
|
||||
BLI_halton_2d(primes, offset, sample_, r);
|
||||
data_.dimensions[SAMPLING_LENS_U][0] = r[0];
|
||||
data_.dimensions[SAMPLING_LENS_V][0] = r[1];
|
||||
/* TODO decorelate. */
|
||||
data_.dimensions[SAMPLING_LIGHTPROBE][0] = r[0];
|
||||
data_.dimensions[SAMPLING_TRANSPARENCY][0] = r[1];
|
||||
}
|
||||
{
|
||||
/* Using leaped halton sequence so we can reused the same primes as lens. */
|
||||
double r[3], offset[3] = {0, 0, 0};
|
||||
uint64_t leap = 11;
|
||||
uint32_t primes[3] = {5, 4, 7};
|
||||
BLI_halton_3d(primes, offset, (sample_ - 1) * leap, r);
|
||||
data_.dimensions[SAMPLING_SHADOW_U][0] = r[0];
|
||||
data_.dimensions[SAMPLING_SHADOW_V][0] = r[1];
|
||||
data_.dimensions[SAMPLING_SHADOW_W][0] = r[2];
|
||||
/* TODO decorelate. */
|
||||
data_.dimensions[SAMPLING_RAYTRACE_U][0] = r[0];
|
||||
data_.dimensions[SAMPLING_RAYTRACE_V][0] = r[1];
|
||||
data_.dimensions[SAMPLING_RAYTRACE_W][0] = r[2];
|
||||
}
|
||||
{
|
||||
/* Using leaped halton sequence so we can reused the same primes. */
|
||||
double r[2], offset[2] = {0, 0};
|
||||
uint64_t leap = 5;
|
||||
uint32_t primes[2] = {2, 3};
|
||||
BLI_halton_2d(primes, offset, (sample_ - 1) * leap, r);
|
||||
data_.dimensions[SAMPLING_SHADOW_X][0] = r[0];
|
||||
data_.dimensions[SAMPLING_SHADOW_Y][0] = r[1];
|
||||
/* TODO decorelate. */
|
||||
data_.dimensions[SAMPLING_SSS_U][0] = r[0];
|
||||
data_.dimensions[SAMPLING_SSS_V][0] = r[1];
|
||||
}
|
||||
|
||||
data_.push_update();
|
||||
sample_++;
|
||||
|
||||
reset_ = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getters
|
||||
**/
|
||||
/* Returns current, 1 based, scene sample index. */
|
||||
uint64_t sample_get(void) const
|
||||
{
|
||||
return sample_;
|
||||
}
|
||||
/* Returns blend factor to apply to film to have a smooth transition instead of flickering
|
||||
* for the first samples of random shadows. */
|
||||
float viewport_smoothing_opacity_factor_get(void) const
|
||||
{
|
||||
return (sample_ == 1 || viewport_smoothing_duration == 0) ?
|
||||
1.0f :
|
||||
square_f(clamp_f((sample_viewport_ - viewport_smoothing_start) /
|
||||
float(viewport_smoothing_duration),
|
||||
0.0f,
|
||||
1.0f));
|
||||
}
|
||||
/* Returns sample count inside the jittered depth of field web pattern. */
|
||||
uint64_t dof_ring_count_get(void) const
|
||||
{
|
||||
return dof_ring_count_;
|
||||
}
|
||||
/* Returns sample count inside the jittered depth of field web pattern. */
|
||||
uint64_t dof_sample_count_get(void) const
|
||||
{
|
||||
return dof_sample_count_;
|
||||
}
|
||||
const GPUUniformBuf *ubo_get(void) const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
/* Returns a pseudo random number in [0..1] range. Each dimension are uncorrelated. */
|
||||
float rng_get(eSamplingDimension dimension) const
|
||||
{
|
||||
return data_.dimensions[dimension][0];
|
||||
}
|
||||
/* Returns true if rendering has finished. */
|
||||
bool finished(void) const
|
||||
{
|
||||
return (sample_ > sample_count_);
|
||||
}
|
||||
/* Returns true if viewport smoothing and sampling has finished. */
|
||||
bool finished_viewport(void) const
|
||||
{
|
||||
return finished() &&
|
||||
(sample_viewport_ > (viewport_smoothing_start + viewport_smoothing_duration));
|
||||
}
|
||||
/* Viewport Only: Function to call to notify something in the scene changed.
|
||||
* This will reset accumulation. Do not call after end_sync() or during sample rendering. */
|
||||
void reset(void)
|
||||
{
|
||||
reset_ = true;
|
||||
}
|
||||
/* Viewport Only: true if an update happened in the scene and accumulation needs reset. */
|
||||
bool is_reset(void) const
|
||||
{
|
||||
return reset_;
|
||||
}
|
||||
/* Return true if we are starting a new motion blur step. We need to run sync agains since
|
||||
* depsgraph was updated by MotionBlur::step(). */
|
||||
bool do_render_sync(void) const
|
||||
{
|
||||
return DRW_state_is_image_render() &&
|
||||
(((sample_ - 1) % (sample_count_ / motion_blur_steps_)) == 0);
|
||||
}
|
||||
|
||||
void dof_disk_sample_get(float *r_radius, float *r_theta)
|
||||
{
|
||||
if (dof_ring_count_ == 0) {
|
||||
*r_radius = *r_theta = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
int s = sample_ - 1;
|
||||
int ring = 0;
|
||||
int ring_sample_count = 1;
|
||||
int ring_sample = 1;
|
||||
|
||||
s = s * (dof_web_density_ - 1);
|
||||
s = s % dof_sample_count_;
|
||||
|
||||
/* Choosing sample to we get faster convergence.
|
||||
* The issue here is that we cannot map a low descripency sequence to this sampling pattern
|
||||
* because the same sample could be choosen twice in relatively short intervals. */
|
||||
/* For now just use an ascending sequence with an offset. This gives us relatively quick
|
||||
* initial coverage and relatively high distance between samples. */
|
||||
/* TODO(fclem) We can try to order samples based on a LDS into a table to avoid duplicates.
|
||||
* The drawback would be some memory consumption and init time. */
|
||||
int samples_passed = 1;
|
||||
while (s >= samples_passed) {
|
||||
ring++;
|
||||
ring_sample_count = ring * dof_web_density_;
|
||||
ring_sample = s - samples_passed;
|
||||
ring_sample = (ring_sample + 1) % ring_sample_count;
|
||||
samples_passed += ring_sample_count;
|
||||
}
|
||||
|
||||
*r_radius = ring / (float)dof_ring_count_;
|
||||
*r_theta = 2.0f * M_PI * ring_sample / (float)ring_sample_count;
|
||||
}
|
||||
|
||||
/* Creates a discrete cumulative distribution function table from a given curvemapping.
|
||||
* Output cdf vector is expected to already be sized according to the wanted resolution. */
|
||||
static void cdf_from_curvemapping(const CurveMapping &curve, Vector<float> &cdf)
|
||||
{
|
||||
BLI_assert(cdf.size() > 1);
|
||||
cdf[0] = 0.0f;
|
||||
/* Actual CDF evaluation. */
|
||||
for (int u : cdf.index_range()) {
|
||||
float x = (float)(u + 1) / (float)(cdf.size() - 1);
|
||||
cdf[u + 1] = cdf[u] + BKE_curvemapping_evaluateF(&curve, 0, x);
|
||||
}
|
||||
/* Normalize the CDF. */
|
||||
for (int u : cdf.index_range()) {
|
||||
cdf[u] /= cdf.last();
|
||||
}
|
||||
/* Just to make sure. */
|
||||
cdf.last() = 1.0f;
|
||||
}
|
||||
|
||||
/* Inverts a cumulative distribution function.
|
||||
* Output vector is expected to already be sized according to the wanted resolution. */
|
||||
static void cdf_invert(Vector<float> &cdf, Vector<float> &inverted_cdf)
|
||||
{
|
||||
for (int u : inverted_cdf.index_range()) {
|
||||
float x = (float)u / (float)(inverted_cdf.size() - 1);
|
||||
for (int i : cdf.index_range()) {
|
||||
if (i == cdf.size() - 1) {
|
||||
inverted_cdf[u] = 1.0f;
|
||||
}
|
||||
else if (cdf[i] >= x) {
|
||||
float t = (x - cdf[i]) / (cdf[i + 1] - cdf[i]);
|
||||
inverted_cdf[u] = ((float)i + t) / (float)(cdf.size() - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Special ball distribution:
|
||||
* Point are distributed in a way that when they are orthogonally
|
||||
* projected into any plane, the resulting distribution is (close to)
|
||||
* a uniform disc distribution.
|
||||
*/
|
||||
float3 sample_ball(const float rand[3])
|
||||
{
|
||||
float3 sample;
|
||||
sample.z = rand[0] * 2.0f - 1.0f; /* cos theta */
|
||||
|
||||
float r = sqrtf(fmaxf(0.0f, 1.0f - square_f(sample.z))); /* sin theta */
|
||||
|
||||
float omega = rand[1] * 2.0f * M_PI;
|
||||
sample.x = r * cosf(omega);
|
||||
sample.y = r * sinf(omega);
|
||||
|
||||
sample *= sqrtf(sqrtf(rand[2]));
|
||||
return sample;
|
||||
}
|
||||
|
||||
float2 sample_disk(const float rand[2])
|
||||
{
|
||||
float omega = rand[1] * 2.0f * M_PI;
|
||||
return sqrtf(rand[0]) * float2(cosf(omega), sinf(omega));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::eevee
|
||||
@@ -1,264 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*
|
||||
* Screen space reflections and refractions techniques.
|
||||
*/
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BLI_dynstr.h"
|
||||
#include "BLI_string_utils.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "GPU_texture.h"
|
||||
#include "eevee_private.h"
|
||||
|
||||
int EEVEE_screen_raytrace_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
|
||||
|
||||
if (scene_eval->eevee.flag & SCE_EEVEE_SSR_ENABLED) {
|
||||
const bool use_refraction = (scene_eval->eevee.flag & SCE_EEVEE_SSR_REFRACTION) != 0;
|
||||
|
||||
const bool is_persp = DRW_view_is_persp_get(NULL);
|
||||
if (effects->ssr_was_persp != is_persp) {
|
||||
effects->ssr_was_persp = is_persp;
|
||||
DRW_viewport_request_redraw();
|
||||
EEVEE_temporal_sampling_reset(vedata);
|
||||
stl->g_data->valid_double_buffer = false;
|
||||
}
|
||||
|
||||
if (!effects->ssr_was_valid_double_buffer) {
|
||||
DRW_viewport_request_redraw();
|
||||
EEVEE_temporal_sampling_reset(vedata);
|
||||
}
|
||||
effects->ssr_was_valid_double_buffer = stl->g_data->valid_double_buffer;
|
||||
|
||||
effects->reflection_trace_full = (scene_eval->eevee.flag & SCE_EEVEE_SSR_HALF_RESOLUTION) == 0;
|
||||
common_data->ssr_thickness = scene_eval->eevee.ssr_thickness;
|
||||
common_data->ssr_border_fac = scene_eval->eevee.ssr_border_fade;
|
||||
common_data->ssr_firefly_fac = scene_eval->eevee.ssr_firefly_fac;
|
||||
common_data->ssr_max_roughness = scene_eval->eevee.ssr_max_roughness;
|
||||
common_data->ssr_quality = 1.0f - 0.95f * scene_eval->eevee.ssr_quality;
|
||||
common_data->ssr_brdf_bias = 0.1f + common_data->ssr_quality * 0.6f; /* Range [0.1, 0.7]. */
|
||||
|
||||
if (common_data->ssr_firefly_fac < 1e-8f) {
|
||||
common_data->ssr_firefly_fac = FLT_MAX;
|
||||
}
|
||||
|
||||
void *owner = (void *)EEVEE_screen_raytrace_init;
|
||||
const int divisor = (effects->reflection_trace_full) ? 1 : 2;
|
||||
int tracing_res[2] = {(int)viewport_size[0] / divisor, (int)viewport_size[1] / divisor};
|
||||
const int size_fs[2] = {(int)viewport_size[0], (int)viewport_size[1]};
|
||||
const bool high_qual_input = true; /* TODO: dither low quality input. */
|
||||
const eGPUTextureFormat format = (high_qual_input) ? GPU_RGBA16F : GPU_RGBA8;
|
||||
|
||||
tracing_res[0] = max_ii(1, tracing_res[0]);
|
||||
tracing_res[1] = max_ii(1, tracing_res[1]);
|
||||
|
||||
common_data->ssr_uv_scale[0] = size_fs[0] / ((float)tracing_res[0] * divisor);
|
||||
common_data->ssr_uv_scale[1] = size_fs[1] / ((float)tracing_res[1] * divisor);
|
||||
|
||||
/* MRT for the shading pass in order to output needed data for the SSR pass. */
|
||||
effects->ssr_specrough_input = DRW_texture_pool_query_2d(UNPACK2(size_fs), format, owner);
|
||||
|
||||
GPU_framebuffer_texture_attach(fbl->main_fb, effects->ssr_specrough_input, 2, 0);
|
||||
|
||||
/* Ray-tracing output. */
|
||||
effects->ssr_hit_output = DRW_texture_pool_query_2d(UNPACK2(tracing_res), GPU_RGBA16F, owner);
|
||||
effects->ssr_hit_depth = DRW_texture_pool_query_2d(UNPACK2(tracing_res), GPU_R16F, owner);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->screen_tracing_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(effects->ssr_hit_output),
|
||||
GPU_ATTACHMENT_TEXTURE(effects->ssr_hit_depth),
|
||||
});
|
||||
|
||||
return EFFECT_SSR | EFFECT_NORMAL_BUFFER | EFFECT_RADIANCE_BUFFER | EFFECT_DOUBLE_BUFFER |
|
||||
((use_refraction) ? EFFECT_REFRACT : 0);
|
||||
}
|
||||
|
||||
/* Cleanup to release memory */
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fbl->screen_tracing_fb);
|
||||
effects->ssr_specrough_input = NULL;
|
||||
effects->ssr_hit_output = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
LightCache *lcache = stl->g_data->light_cache;
|
||||
|
||||
if ((effects->enabled_effects & EFFECT_SSR) != 0) {
|
||||
struct GPUShader *trace_shader = EEVEE_shaders_effect_reflection_trace_sh_get();
|
||||
struct GPUShader *resolve_shader = EEVEE_shaders_effect_reflection_resolve_sh_get();
|
||||
|
||||
int hitbuf_size[3];
|
||||
GPU_texture_get_mipmap_size(effects->ssr_hit_output, 0, hitbuf_size);
|
||||
|
||||
/** Screen space ray-tracing overview
|
||||
*
|
||||
* Following Frostbite stochastic SSR.
|
||||
*
|
||||
* - First pass Trace rays across the depth buffer. The hit position and PDF are
|
||||
* recorded in a RGBA16F render target for each ray (sample).
|
||||
*
|
||||
* - We down-sample the previous frame color buffer.
|
||||
*
|
||||
* - For each final pixel, we gather neighbors rays and choose a color buffer
|
||||
* mipmap for each ray using its PDF. (filtered importance sampling)
|
||||
* We then evaluate the lighting from the probes and mix the results together.
|
||||
*/
|
||||
DRW_PASS_CREATE(psl->ssr_raytrace, DRW_STATE_WRITE_COLOR);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(trace_shader, psl->ssr_raytrace);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &vedata->txl->planar_depth);
|
||||
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
|
||||
DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
|
||||
DRW_shgroup_uniform_vec2_copy(grp, "targetSize", (float[2]){hitbuf_size[0], hitbuf_size[1]});
|
||||
DRW_shgroup_uniform_float_copy(
|
||||
grp, "randomScale", effects->reflection_trace_full ? 0.0f : 0.5f);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
|
||||
DRW_PASS_CREATE(psl->ssr_resolve, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD);
|
||||
grp = DRW_shgroup_create(resolve_shader, psl->ssr_resolve);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &vedata->txl->planar_pool);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &vedata->txl->planar_depth);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "hitBuffer", &effects->ssr_hit_output, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "hitDepth", &effects->ssr_hit_depth, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &txl->filtered_radiance);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool);
|
||||
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
|
||||
DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
|
||||
DRW_shgroup_uniform_int(grp, "samplePoolOffset", &effects->taa_current_sample, 1);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_refraction_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
if ((effects->enabled_effects & EFFECT_REFRACT) != 0) {
|
||||
EEVEE_effects_downsample_radiance_buffer(vedata, txl->color);
|
||||
|
||||
/* Restore */
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_reflection_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
if (((effects->enabled_effects & EFFECT_SSR) != 0) && stl->g_data->valid_double_buffer) {
|
||||
DRW_stats_group_start("SSR");
|
||||
|
||||
/* Raytrace. */
|
||||
GPU_framebuffer_bind(fbl->screen_tracing_fb);
|
||||
DRW_draw_pass(psl->ssr_raytrace);
|
||||
|
||||
EEVEE_effects_downsample_radiance_buffer(vedata, txl->color_double_buffer);
|
||||
|
||||
GPU_framebuffer_bind(fbl->main_color_fb);
|
||||
DRW_draw_pass(psl->ssr_resolve);
|
||||
|
||||
/* Restore */
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_reflection_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
|
||||
EEVEE_Data *vedata,
|
||||
uint tot_samples)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
|
||||
/* Create FrameBuffer. */
|
||||
const eGPUTextureFormat texture_format = (tot_samples > 256) ? GPU_RGBA32F : GPU_RGBA16F;
|
||||
DRW_texture_ensure_fullscreen_2d(&txl->ssr_accum, texture_format, 0);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->ssr_accum_fb,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ssr_accum)});
|
||||
}
|
||||
|
||||
void EEVEE_reflection_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = vedata->stl->effects;
|
||||
|
||||
if (stl->g_data->valid_double_buffer) {
|
||||
GPU_framebuffer_bind(fbl->ssr_accum_fb);
|
||||
|
||||
/* Clear texture. */
|
||||
if (effects->taa_current_sample == 1) {
|
||||
const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
GPU_framebuffer_clear_color(fbl->ssr_accum_fb, clear);
|
||||
}
|
||||
|
||||
DRW_draw_pass(psl->ssr_resolve);
|
||||
}
|
||||
}
|
||||
538
source/blender/draw/engines/eevee/eevee_shader.cc
Normal file
538
source/blender/draw/engines/eevee/eevee_shader.cc
Normal file
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Shader module that manage shader libraries, deferred compilation,
|
||||
* and static shader usage.
|
||||
*/
|
||||
|
||||
#include "eevee_shader.hh"
|
||||
#include "eevee_material.hh"
|
||||
|
||||
#include "../../gpu/intern/gpu_shader_create_info.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module
|
||||
*
|
||||
* \{ */
|
||||
|
||||
ShaderModule *ShaderModule::g_shader_module = nullptr;
|
||||
|
||||
ShaderModule *ShaderModule::module_get()
|
||||
{
|
||||
if (g_shader_module == nullptr) {
|
||||
/* TODO(fclem) threadsafety. */
|
||||
g_shader_module = new ShaderModule();
|
||||
}
|
||||
return g_shader_module;
|
||||
}
|
||||
|
||||
void ShaderModule::module_free()
|
||||
{
|
||||
if (g_shader_module != nullptr) {
|
||||
/* TODO(fclem) threadsafety. */
|
||||
delete g_shader_module;
|
||||
g_shader_module = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ShaderModule::ShaderModule()
|
||||
{
|
||||
for (GPUShader *&shader : shaders_) {
|
||||
shader = nullptr;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/* Ensure all shader are described. */
|
||||
for (auto i : IndexRange(MAX_SHADER_TYPE)) {
|
||||
const char *name = static_shader_create_info_name_get(eShaderType(i));
|
||||
if (name == nullptr) {
|
||||
std::cerr << "EEVEE: Missing case for eShaderType(" << i
|
||||
<< ") in static_shader_create_info_name_get().";
|
||||
BLI_assert(0);
|
||||
}
|
||||
const GPUShaderCreateInfo *create_info = GPU_shader_create_info_get(name);
|
||||
BLI_assert_msg(create_info != nullptr, "EEVEE: Missing create info for static shader.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ShaderModule::~ShaderModule()
|
||||
{
|
||||
for (GPUShader *&shader : shaders_) {
|
||||
DRW_SHADER_FREE_SAFE(shader);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Static shaders
|
||||
*
|
||||
* \{ */
|
||||
|
||||
const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_type)
|
||||
{
|
||||
switch (shader_type) {
|
||||
case CULLING_DEBUG:
|
||||
return "eevee_culling_debug";
|
||||
case CULLING_SELECT:
|
||||
return "eevee_culling_select";
|
||||
case CULLING_SORT:
|
||||
return "eevee_culling_sort";
|
||||
case CULLING_TILE:
|
||||
return "eevee_culling_tile";
|
||||
|
||||
case FILM_FILTER:
|
||||
return "eevee_film_filter";
|
||||
case FILM_RESOLVE:
|
||||
return "eevee_film_resolve";
|
||||
case FILM_RESOLVE_DEPTH:
|
||||
return "eevee_film_resolve_depth";
|
||||
|
||||
case DEFERRED_EVAL_DIRECT:
|
||||
return "eevee_deferred_direct";
|
||||
case DEFERRED_EVAL_HOLDOUT:
|
||||
return "eevee_deferred_holdout";
|
||||
case DEFERRED_EVAL_TRANSPARENT:
|
||||
return "eevee_deferred_transparent";
|
||||
case DEFERRED_EVAL_VOLUME:
|
||||
return "eevee_deferred_volume";
|
||||
|
||||
case DEFERRED_VOLUME:
|
||||
return "eevee_volume_deferred";
|
||||
|
||||
case HIZ_COPY:
|
||||
return "eevee_hiz_copy";
|
||||
case HIZ_DOWNSAMPLE:
|
||||
return "eevee_hiz_downsample";
|
||||
|
||||
case DOF_BOKEH_LUT:
|
||||
return "eevee_depth_of_field_bokeh_lut";
|
||||
case DOF_FILTER:
|
||||
return "eevee_depth_of_field_filter";
|
||||
case DOF_GATHER_BACKGROUND_LUT:
|
||||
return "eevee_depth_of_field_gather_background_lut";
|
||||
case DOF_GATHER_BACKGROUND:
|
||||
return "eevee_depth_of_field_gather_background";
|
||||
case DOF_GATHER_FOREGROUND_LUT:
|
||||
return "eevee_depth_of_field_gather_foreground_lut";
|
||||
case DOF_GATHER_FOREGROUND:
|
||||
return "eevee_depth_of_field_gather_foreground";
|
||||
case DOF_GATHER_HOLEFILL:
|
||||
return "eevee_depth_of_field_gather_holefill";
|
||||
case DOF_REDUCE_COPY:
|
||||
return "eevee_depth_of_field_reduce_copy";
|
||||
case DOF_REDUCE_DOWNSAMPLE:
|
||||
return "eevee_depth_of_field_reduce_downsample";
|
||||
case DOF_REDUCE_RECURSIVE:
|
||||
return "eevee_depth_of_field_reduce_recursive";
|
||||
case DOF_RESOLVE_LUT:
|
||||
return "eevee_depth_of_field_resolve_lq_lut";
|
||||
case DOF_RESOLVE_LUT_HQ:
|
||||
return "eevee_depth_of_field_resolve_hq_lut";
|
||||
case DOF_RESOLVE:
|
||||
return "eevee_depth_of_field_resolve_lq";
|
||||
case DOF_RESOLVE_HQ:
|
||||
return "eevee_depth_of_field_resolve_hq";
|
||||
case DOF_SCATTER_BACKGROUND_LUT:
|
||||
return "eevee_depth_of_field_scatter_background_lut";
|
||||
case DOF_SCATTER_BACKGROUND:
|
||||
return "eevee_depth_of_field_scatter_background";
|
||||
case DOF_SCATTER_FOREGROUND_LUT:
|
||||
return "eevee_depth_of_field_scatter_foreground_lut";
|
||||
case DOF_SCATTER_FOREGROUND:
|
||||
return "eevee_depth_of_field_scatter_foreground";
|
||||
case DOF_SETUP:
|
||||
return "eevee_depth_of_field_setup";
|
||||
case DOF_TILES_DILATE_MINABS:
|
||||
return "eevee_depth_of_field_tiles_dilate_minabs";
|
||||
case DOF_TILES_DILATE_MINMAX:
|
||||
return "eevee_depth_of_field_tiles_dilate_minmax";
|
||||
case DOF_TILES_FLATTEN:
|
||||
return "eevee_depth_of_field_tiles_flatten";
|
||||
|
||||
case LIGHTPROBE_DISPLAY_CUBEMAP:
|
||||
return "eevee_lightprobe_display_cubemap";
|
||||
case LIGHTPROBE_DISPLAY_IRRADIANCE:
|
||||
return "eevee_lightprobe_display_grid";
|
||||
case LIGHTPROBE_FILTER_DOWNSAMPLE_CUBE:
|
||||
return "eevee_lightprobe_filter_downsample";
|
||||
case LIGHTPROBE_FILTER_GLOSSY:
|
||||
return "eevee_lightprobe_filter_glossy";
|
||||
case LIGHTPROBE_FILTER_DIFFUSE:
|
||||
return "eevee_lightprobe_filter_diffuse";
|
||||
case LIGHTPROBE_FILTER_VISIBILITY:
|
||||
return "eevee_lightprobe_filter_visibility";
|
||||
|
||||
case LOOKDEV_BACKGROUND:
|
||||
return "eevee_background_lookdev";
|
||||
|
||||
case MOTION_BLUR_GATHER:
|
||||
return "eevee_motion_blur_gather";
|
||||
case MOTION_BLUR_TILE_DILATE:
|
||||
return "eevee_motion_blur_tiles_dilate";
|
||||
case MOTION_BLUR_TILE_FLATTEN:
|
||||
return "eevee_motion_blur_tiles_flatten";
|
||||
|
||||
case RAYTRACE_DIFFUSE:
|
||||
return "eevee_raytrace_raygen_diffuse";
|
||||
case RAYTRACE_REFLECTION:
|
||||
return "eevee_raytrace_raygen_reflection";
|
||||
case RAYTRACE_REFRACTION:
|
||||
return "eevee_raytrace_raygen_refraction";
|
||||
case RAYTRACE_DIFFUSE_FALLBACK:
|
||||
return "eevee_raytrace_raygen_fallback_diffuse";
|
||||
case RAYTRACE_REFLECTION_FALLBACK:
|
||||
return "eevee_raytrace_raygen_fallback_reflection";
|
||||
case RAYTRACE_REFRACTION_FALLBACK:
|
||||
return "eevee_raytrace_raygen_fallback_refraction";
|
||||
case RAYTRACE_DENOISE_DIFFUSE:
|
||||
return "eevee_raytrace_denoise_diffuse";
|
||||
case RAYTRACE_DENOISE_REFLECTION:
|
||||
return "eevee_raytrace_denoise_reflection";
|
||||
case RAYTRACE_DENOISE_REFRACTION:
|
||||
return "eevee_raytrace_denoise_refraction";
|
||||
case RAYTRACE_RESOLVE_DIFFUSE:
|
||||
return "eevee_raytrace_resolve_diffuse";
|
||||
case RAYTRACE_RESOLVE_REFLECTION:
|
||||
return "eevee_raytrace_resolve_reflection";
|
||||
case RAYTRACE_RESOLVE_REFRACTION:
|
||||
return "eevee_raytrace_resolve_refraction";
|
||||
|
||||
case SHADOW_DEBUG:
|
||||
return "eevee_shadow_debug";
|
||||
case SHADOW_PAGE_ALLOC:
|
||||
return "eevee_shadow_page_alloc";
|
||||
case SHADOW_PAGE_COPY:
|
||||
return "eevee_shadow_page_copy";
|
||||
case SHADOW_PAGE_DEBUG:
|
||||
return "eevee_shadow_page_debug";
|
||||
case SHADOW_PAGE_DEFRAG:
|
||||
return "eevee_shadow_page_defrag";
|
||||
case SHADOW_PAGE_FREE:
|
||||
return "eevee_shadow_page_free";
|
||||
case SHADOW_PAGE_INIT:
|
||||
return "eevee_shadow_page_init";
|
||||
case SHADOW_PAGE_MARK:
|
||||
return "eevee_shadow_page_mark";
|
||||
case SHADOW_TILE_DEPTH_SCAN:
|
||||
return "eevee_shadow_tilemap_depth_scan";
|
||||
case SHADOW_TILE_LOD_MASK:
|
||||
return "eevee_shadow_tilemap_lod_mask";
|
||||
case SHADOW_TILE_SETUP:
|
||||
return "eevee_shadow_tilemap_setup";
|
||||
case SHADOW_TILE_TAG_UPDATE:
|
||||
return "eevee_shadow_tilemap_tag_update";
|
||||
case SHADOW_TILE_TAG_USAGE:
|
||||
return "eevee_shadow_tilemap_tag_usage";
|
||||
case SHADOW_TILE_TAG_VISIBILITY:
|
||||
return "eevee_shadow_tilemap_visibility";
|
||||
|
||||
case SUBSURFACE_EVAL:
|
||||
return "eevee_subsurface_eval";
|
||||
|
||||
case VELOCITY_MESH:
|
||||
return "eevee_velocity_surface_mesh";
|
||||
case VELOCITY_CAMERA:
|
||||
return "eevee_velocity_camera";
|
||||
/* To avoid compiler warning about missing case. */
|
||||
case MAX_SHADER_TYPE:
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
GPUShader *ShaderModule::static_shader_get(eShaderType shader_type)
|
||||
{
|
||||
if (shaders_[shader_type] == nullptr) {
|
||||
const char *shader_name = static_shader_create_info_name_get(shader_type);
|
||||
|
||||
shaders_[shader_type] = GPU_shader_create_from_info_name(shader_name);
|
||||
|
||||
if (shaders_[shader_type] == nullptr) {
|
||||
fprintf(stderr, "EEVEE: error: Could not compile static shader \"%s\"\n", shader_name);
|
||||
}
|
||||
BLI_assert(shaders_[shader_type] != nullptr);
|
||||
}
|
||||
return shaders_[shader_type];
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPU Materials
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOutput *codegen_)
|
||||
{
|
||||
using namespace blender::gpu::shader;
|
||||
|
||||
uint64_t shader_uuid = GPU_material_uuid_get(gpumat);
|
||||
|
||||
eMaterialPipeline pipeline_type;
|
||||
eMaterialGeometry geometry_type;
|
||||
material_type_from_shader_uuid(shader_uuid, pipeline_type, geometry_type);
|
||||
|
||||
GPUCodegenOutput &codegen = *codegen_;
|
||||
ShaderCreateInfo &info = *reinterpret_cast<ShaderCreateInfo *>(codegen.create_info);
|
||||
|
||||
info.auto_resource_location(true);
|
||||
|
||||
std::stringstream global_vars;
|
||||
switch (geometry_type) {
|
||||
case MAT_GEOM_MESH:
|
||||
/** Noop. */
|
||||
break;
|
||||
case MAT_GEOM_HAIR:
|
||||
/** Hair attributes comme from sampler buffer. Transfer attributes to sampler. */
|
||||
for (auto &input : info.vertex_inputs_) {
|
||||
info.sampler(0, ImageType::FLOAT_BUFFER, input.name, Frequency::BATCH);
|
||||
}
|
||||
info.vertex_inputs_.clear();
|
||||
break;
|
||||
case MAT_GEOM_WORLD:
|
||||
/**
|
||||
* Only orco layer is supported by world and it is procedurally generated. These are here to
|
||||
* make the attribs_load function calls valids.
|
||||
*/
|
||||
ATTR_FALLTHROUGH;
|
||||
case MAT_GEOM_GPENCIL:
|
||||
/**
|
||||
* Only one uv and one color attribute layer are supported by gpencil objects and they are
|
||||
* already declared in another createInfo. These are here to make the attribs_load
|
||||
* function calls valids.
|
||||
*/
|
||||
for (auto &input : info.vertex_inputs_) {
|
||||
global_vars << input.type << " " << input.name << ";\n";
|
||||
}
|
||||
info.vertex_inputs_.clear();
|
||||
break;
|
||||
case MAT_GEOM_VOLUME:
|
||||
case MAT_GEOM_LOOKDEV:
|
||||
/** No attributes supported. */
|
||||
info.vertex_inputs_.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
const bool do_fragment_attrib_load = (geometry_type == MAT_GEOM_WORLD);
|
||||
|
||||
if (do_fragment_attrib_load && !info.vertex_out_interfaces_.is_empty()) {
|
||||
/* Codegen outputs only one interface. */
|
||||
const StageInterfaceInfo &iface = *info.vertex_out_interfaces_.first();
|
||||
/* Globals the attrib_load() can write to when it is in the fragment shader. */
|
||||
global_vars << "struct " << iface.name << " {\n";
|
||||
for (auto &inout : iface.inouts) {
|
||||
global_vars << " " << inout.type << " " << inout.name << ";\n";
|
||||
}
|
||||
global_vars << "};\n";
|
||||
global_vars << iface.name << " " << iface.instance_name << ";\n";
|
||||
|
||||
info.vertex_out_interfaces_.clear();
|
||||
}
|
||||
|
||||
std::stringstream attr_load;
|
||||
attr_load << "void attrib_load()\n";
|
||||
attr_load << "{\n";
|
||||
attr_load << ((codegen.attr_load) ? codegen.attr_load : "");
|
||||
attr_load << "}\n\n";
|
||||
|
||||
std::stringstream vert_gen, frag_gen;
|
||||
|
||||
if (do_fragment_attrib_load) {
|
||||
frag_gen << global_vars.str() << attr_load.str();
|
||||
}
|
||||
else {
|
||||
vert_gen << global_vars.str() << attr_load.str();
|
||||
}
|
||||
|
||||
{
|
||||
/* Only mesh and hair support displacement for now. */
|
||||
if (ELEM(geometry_type, MAT_GEOM_MESH, MAT_GEOM_HAIR)) {
|
||||
vert_gen << "vec3 nodetree_displacement()\n";
|
||||
vert_gen << "{\n";
|
||||
vert_gen << ((codegen.displacement) ? codegen.displacement : "return vec3(0);\n");
|
||||
vert_gen << "}\n\n";
|
||||
}
|
||||
|
||||
info.vertex_source_generated = vert_gen.str();
|
||||
}
|
||||
|
||||
{
|
||||
frag_gen << "Closure nodetree_surface()\n";
|
||||
frag_gen << "{\n";
|
||||
frag_gen << ((codegen.surface) ? codegen.surface : "return CLOSURE_DEFAULT;\n");
|
||||
frag_gen << "}\n\n";
|
||||
|
||||
frag_gen << "Closure nodetree_volume()\n";
|
||||
frag_gen << "{\n";
|
||||
frag_gen << ((codegen.volume) ? codegen.volume : "return CLOSURE_DEFAULT;\n");
|
||||
frag_gen << "}\n\n";
|
||||
|
||||
frag_gen << "float nodetree_thickness()\n";
|
||||
frag_gen << "{\n";
|
||||
/* TODO(fclem): Better default. */
|
||||
frag_gen << ((codegen.thickness) ? codegen.thickness : "return 0.1;\n");
|
||||
frag_gen << "}\n\n";
|
||||
|
||||
info.fragment_source_generated = frag_gen.str();
|
||||
}
|
||||
|
||||
/* Geometry Info. */
|
||||
switch (geometry_type) {
|
||||
case MAT_GEOM_WORLD:
|
||||
info.additional_info("eevee_surface_world");
|
||||
break;
|
||||
case MAT_GEOM_VOLUME:
|
||||
info.additional_info("eevee_volume");
|
||||
break;
|
||||
case MAT_GEOM_GPENCIL:
|
||||
info.additional_info("eevee_surface_gpencil");
|
||||
break;
|
||||
case MAT_GEOM_LOOKDEV:
|
||||
info.additional_info("eevee_surface_lookdev");
|
||||
break;
|
||||
case MAT_GEOM_HAIR:
|
||||
info.additional_info("eevee_surface_hair");
|
||||
break;
|
||||
case MAT_GEOM_MESH:
|
||||
default:
|
||||
info.additional_info("eevee_surface_mesh");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Pipeline Info. */
|
||||
switch (geometry_type) {
|
||||
case MAT_GEOM_WORLD:
|
||||
info.additional_info("eevee_surface_background");
|
||||
break;
|
||||
case MAT_GEOM_VOLUME:
|
||||
switch (pipeline_type) {
|
||||
case MAT_PIPE_DEFERRED:
|
||||
info.additional_info("eevee_volume_deferred");
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
switch (pipeline_type) {
|
||||
case MAT_PIPE_FORWARD_PREPASS:
|
||||
info.additional_info("eevee_surface_depth_simple");
|
||||
break;
|
||||
case MAT_PIPE_DEFERRED_PREPASS:
|
||||
case MAT_PIPE_SHADOW:
|
||||
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
|
||||
info.additional_info("eevee_surface_depth");
|
||||
}
|
||||
else {
|
||||
info.additional_info("eevee_surface_depth_simple");
|
||||
info.fragment_source_generated = "";
|
||||
}
|
||||
break;
|
||||
case MAT_PIPE_DEFERRED:
|
||||
info.additional_info("eevee_surface_deferred");
|
||||
break;
|
||||
case MAT_PIPE_FORWARD:
|
||||
info.additional_info("eevee_surface_forward");
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* WATCH: This can be called from another thread! Needs to not touch the shader module in any
|
||||
* thread unsafe manner. */
|
||||
static void codegen_callback(void *thunk, GPUMaterial *mat, GPUCodegenOutput *codegen)
|
||||
{
|
||||
reinterpret_cast<ShaderModule *>(thunk)->material_create_info_ammend(mat, codegen);
|
||||
}
|
||||
|
||||
GPUMaterial *ShaderModule::material_shader_get(::Material *blender_mat,
|
||||
struct bNodeTree *nodetree,
|
||||
eMaterialPipeline pipeline_type,
|
||||
eMaterialGeometry geometry_type,
|
||||
bool deferred_compilation)
|
||||
{
|
||||
uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
|
||||
|
||||
bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
|
||||
|
||||
return DRW_shader_from_material(
|
||||
blender_mat, nodetree, shader_uuid, is_volume, deferred_compilation, codegen_callback, this);
|
||||
}
|
||||
|
||||
GPUMaterial *ShaderModule::world_shader_get(::World *blender_world, struct bNodeTree *nodetree)
|
||||
{
|
||||
eMaterialPipeline pipeline_type = MAT_PIPE_DEFERRED; /* Unused. */
|
||||
eMaterialGeometry geometry_type = MAT_GEOM_WORLD;
|
||||
|
||||
uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
|
||||
|
||||
bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
|
||||
bool deferred_compilation = false;
|
||||
|
||||
return DRW_shader_from_world(blender_world,
|
||||
nodetree,
|
||||
shader_uuid,
|
||||
is_volume,
|
||||
deferred_compilation,
|
||||
codegen_callback,
|
||||
this);
|
||||
}
|
||||
|
||||
/* Variation to compile a material only with a nodetree. Caller needs to maintain the list of
|
||||
* materials and call GPU_material_free on it to update the material. */
|
||||
GPUMaterial *ShaderModule::material_shader_get(const char *name,
|
||||
ListBase &materials,
|
||||
struct bNodeTree *nodetree,
|
||||
eMaterialPipeline pipeline_type,
|
||||
eMaterialGeometry geometry_type,
|
||||
bool is_lookdev)
|
||||
{
|
||||
uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
|
||||
|
||||
bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
|
||||
|
||||
GPUMaterial *gpumat = GPU_material_from_nodetree(nullptr,
|
||||
nullptr,
|
||||
nodetree,
|
||||
&materials,
|
||||
name,
|
||||
shader_uuid,
|
||||
is_volume,
|
||||
is_lookdev,
|
||||
codegen_callback,
|
||||
this);
|
||||
GPU_material_status_set(gpumat, GPU_MAT_QUEUED);
|
||||
GPU_material_compile(gpumat);
|
||||
return gpumat;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
172
source/blender/draw/engines/eevee/eevee_shader.hh
Normal file
172
source/blender/draw/engines/eevee/eevee_shader.hh
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Shader module that manage shader libraries, deferred compilation,
|
||||
* and static shader usage.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "DRW_render.h"
|
||||
#include "GPU_material.h"
|
||||
#include "GPU_shader.h"
|
||||
|
||||
#include "eevee_id_map.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* Keep alphabetical order and clean prefix. */
|
||||
enum eShaderType {
|
||||
CULLING_DEBUG = 0,
|
||||
CULLING_SELECT,
|
||||
CULLING_SORT,
|
||||
CULLING_TILE,
|
||||
|
||||
DEFERRED_EVAL_DIRECT,
|
||||
DEFERRED_EVAL_HOLDOUT,
|
||||
DEFERRED_EVAL_TRANSPARENT,
|
||||
DEFERRED_EVAL_VOLUME,
|
||||
|
||||
DEFERRED_VOLUME,
|
||||
|
||||
DOF_BOKEH_LUT,
|
||||
DOF_GATHER_BACKGROUND_LUT,
|
||||
DOF_GATHER_BACKGROUND,
|
||||
DOF_FILTER,
|
||||
DOF_GATHER_FOREGROUND_LUT,
|
||||
DOF_GATHER_FOREGROUND,
|
||||
DOF_GATHER_HOLEFILL,
|
||||
DOF_REDUCE_COPY,
|
||||
DOF_REDUCE_DOWNSAMPLE,
|
||||
DOF_REDUCE_RECURSIVE,
|
||||
DOF_RESOLVE,
|
||||
DOF_RESOLVE_HQ,
|
||||
DOF_RESOLVE_LUT,
|
||||
DOF_RESOLVE_LUT_HQ,
|
||||
DOF_SCATTER_BACKGROUND_LUT,
|
||||
DOF_SCATTER_BACKGROUND,
|
||||
DOF_SCATTER_FOREGROUND_LUT,
|
||||
DOF_SCATTER_FOREGROUND,
|
||||
DOF_SETUP,
|
||||
DOF_TILES_DILATE_MINABS,
|
||||
DOF_TILES_DILATE_MINMAX,
|
||||
DOF_TILES_FLATTEN,
|
||||
|
||||
FILM_FILTER,
|
||||
FILM_RESOLVE,
|
||||
FILM_RESOLVE_DEPTH,
|
||||
|
||||
HIZ_COPY,
|
||||
HIZ_DOWNSAMPLE,
|
||||
|
||||
LIGHTPROBE_DISPLAY_CUBEMAP,
|
||||
LIGHTPROBE_DISPLAY_IRRADIANCE,
|
||||
|
||||
LIGHTPROBE_FILTER_DOWNSAMPLE_CUBE,
|
||||
LIGHTPROBE_FILTER_GLOSSY,
|
||||
LIGHTPROBE_FILTER_DIFFUSE,
|
||||
LIGHTPROBE_FILTER_VISIBILITY,
|
||||
|
||||
LOOKDEV_BACKGROUND,
|
||||
|
||||
MOTION_BLUR_GATHER,
|
||||
MOTION_BLUR_TILE_DILATE,
|
||||
MOTION_BLUR_TILE_FLATTEN,
|
||||
|
||||
RAYTRACE_DIFFUSE,
|
||||
RAYTRACE_DIFFUSE_FALLBACK,
|
||||
RAYTRACE_REFLECTION,
|
||||
RAYTRACE_REFLECTION_FALLBACK,
|
||||
RAYTRACE_REFRACTION,
|
||||
RAYTRACE_REFRACTION_FALLBACK,
|
||||
RAYTRACE_DENOISE_DIFFUSE,
|
||||
RAYTRACE_DENOISE_REFLECTION,
|
||||
RAYTRACE_DENOISE_REFRACTION,
|
||||
RAYTRACE_RESOLVE_DIFFUSE,
|
||||
RAYTRACE_RESOLVE_REFLECTION,
|
||||
RAYTRACE_RESOLVE_REFRACTION,
|
||||
|
||||
SHADOW_DEBUG,
|
||||
SHADOW_PAGE_ALLOC,
|
||||
SHADOW_PAGE_COPY,
|
||||
SHADOW_PAGE_DEBUG,
|
||||
SHADOW_PAGE_DEFRAG,
|
||||
SHADOW_PAGE_FREE,
|
||||
SHADOW_PAGE_INIT,
|
||||
SHADOW_PAGE_MARK,
|
||||
SHADOW_TILE_DEPTH_SCAN,
|
||||
SHADOW_TILE_LOD_MASK,
|
||||
SHADOW_TILE_SETUP,
|
||||
SHADOW_TILE_TAG_UPDATE,
|
||||
SHADOW_TILE_TAG_USAGE,
|
||||
SHADOW_TILE_TAG_VISIBILITY,
|
||||
|
||||
SUBSURFACE_EVAL,
|
||||
|
||||
VELOCITY_CAMERA,
|
||||
VELOCITY_MESH,
|
||||
|
||||
MAX_SHADER_TYPE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Shader module. shared between instances.
|
||||
*/
|
||||
class ShaderModule {
|
||||
private:
|
||||
std::array<GPUShader *, MAX_SHADER_TYPE> shaders_;
|
||||
|
||||
/** Shared shader module accross all engine instances. */
|
||||
static ShaderModule *g_shader_module;
|
||||
|
||||
public:
|
||||
ShaderModule();
|
||||
~ShaderModule();
|
||||
|
||||
GPUShader *static_shader_get(eShaderType shader_type);
|
||||
GPUMaterial *material_shader_get(::Material *blender_mat,
|
||||
struct bNodeTree *nodetree,
|
||||
eMaterialPipeline pipeline_type,
|
||||
eMaterialGeometry geometry_type,
|
||||
bool deferred_compilation);
|
||||
GPUMaterial *world_shader_get(::World *blender_world, struct bNodeTree *nodetree);
|
||||
GPUMaterial *material_shader_get(const char *name,
|
||||
ListBase &materials,
|
||||
struct bNodeTree *nodetree,
|
||||
eMaterialPipeline pipeline_type,
|
||||
eMaterialGeometry geometry_type,
|
||||
bool is_lookdev);
|
||||
|
||||
void material_create_info_ammend(GPUMaterial *mat, GPUCodegenOutput *codegen);
|
||||
|
||||
/** Only to be used by Instance constructor. */
|
||||
static ShaderModule *module_get();
|
||||
static void module_free();
|
||||
|
||||
private:
|
||||
const char *static_shader_create_info_name_get(eShaderType shader_type);
|
||||
};
|
||||
|
||||
} // namespace blender::eevee
|
||||
856
source/blender/draw/engines/eevee/eevee_shader_shared.hh
Normal file
856
source/blender/draw/engines/eevee/eevee_shader_shared.hh
Normal file
@@ -0,0 +1,856 @@
|
||||
|
||||
/**
|
||||
* Shared structures, enums & defines between C++ and GLSL.
|
||||
* Can also include some math functions but they need to be simple enough to be valid in both
|
||||
* language.
|
||||
*/
|
||||
|
||||
#ifndef USE_GPU_SHADER_CREATE_INFO
|
||||
# pragma once
|
||||
|
||||
# include "eevee_defines.hh"
|
||||
# include "eevee_wrapper.hh"
|
||||
|
||||
# include "GPU_shader_shared.h"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
#endif
|
||||
|
||||
#define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Sampling
|
||||
* \{ */
|
||||
|
||||
enum eSamplingDimension : uint32_t {
|
||||
SAMPLING_FILTER_U = 0u,
|
||||
SAMPLING_FILTER_V = 1u,
|
||||
SAMPLING_LENS_U = 2u,
|
||||
SAMPLING_LENS_V = 3u,
|
||||
SAMPLING_TIME = 4u,
|
||||
SAMPLING_SHADOW_U = 5u,
|
||||
SAMPLING_SHADOW_V = 6u,
|
||||
SAMPLING_SHADOW_W = 7u,
|
||||
SAMPLING_SHADOW_X = 8u,
|
||||
SAMPLING_SHADOW_Y = 9u,
|
||||
SAMPLING_CLOSURE = 10u,
|
||||
SAMPLING_LIGHTPROBE = 11u,
|
||||
SAMPLING_TRANSPARENCY = 12u,
|
||||
SAMPLING_SSS_U = 13u,
|
||||
SAMPLING_SSS_V = 14u,
|
||||
SAMPLING_RAYTRACE_U = 15u,
|
||||
SAMPLING_RAYTRACE_V = 16u,
|
||||
SAMPLING_RAYTRACE_W = 17u,
|
||||
SAMPLING_RAYTRACE_X = 18u
|
||||
};
|
||||
|
||||
/** IMPORTANT: Make sure the array can contain all sampling dimensions. */
|
||||
#define SAMPLING_DIMENSION_COUNT 19
|
||||
|
||||
struct SamplingData {
|
||||
/** Array containing random values from Low Discrepency Sequence in [0..1) range. */
|
||||
/** HACK: float arrays are padded to float4 in GLSL. Using float4 for now to get the same
|
||||
* alignment but this is wasteful. */
|
||||
float4 dimensions[SAMPLING_DIMENSION_COUNT];
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(SamplingData, 16)
|
||||
|
||||
/* Returns total sample count in a web pattern of the given size. */
|
||||
static inline int web_sample_count_get(int web_density, int ring_count)
|
||||
{
|
||||
return ((ring_count * ring_count + ring_count) / 2) * web_density + 1;
|
||||
}
|
||||
|
||||
/* Returns lowest possible ring count that contains at least sample_count samples. */
|
||||
static inline int web_ring_count_get(int web_density, int sample_count)
|
||||
{
|
||||
/* Inversion of web_sample_count_get(). */
|
||||
float x = 2.0f * (float(sample_count) - 1.0f) / float(web_density);
|
||||
/* Solving polynomial. We only search positive solution. */
|
||||
float discriminant = 1.0f + 4.0f * x;
|
||||
return int(ceilf(0.5f * (sqrtf(discriminant) - 1.0f)));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Camera
|
||||
* \{ */
|
||||
|
||||
enum eCameraType : uint32_t {
|
||||
CAMERA_PERSP = 0u,
|
||||
CAMERA_ORTHO = 1u,
|
||||
CAMERA_PANO_EQUIRECT = 2u,
|
||||
CAMERA_PANO_EQUISOLID = 3u,
|
||||
CAMERA_PANO_EQUIDISTANT = 4u,
|
||||
CAMERA_PANO_MIRROR = 5u
|
||||
};
|
||||
|
||||
static inline bool is_panoramic(eCameraType type)
|
||||
{
|
||||
return type > CAMERA_ORTHO;
|
||||
}
|
||||
|
||||
struct CameraData {
|
||||
/* View Matrices of the camera, not from any view! */
|
||||
float4x4 persmat;
|
||||
float4x4 persinv;
|
||||
float4x4 viewmat;
|
||||
float4x4 viewinv;
|
||||
float4x4 winmat;
|
||||
float4x4 wininv;
|
||||
/** Camera UV scale and bias. Also known as viewcamtexcofac. */
|
||||
float2 uv_scale;
|
||||
float2 uv_bias;
|
||||
/** Panorama parameters. */
|
||||
float2 equirect_scale;
|
||||
float2 equirect_scale_inv;
|
||||
float2 equirect_bias;
|
||||
float fisheye_fov;
|
||||
float fisheye_lens;
|
||||
/** Clipping distances. */
|
||||
float clip_near;
|
||||
float clip_far;
|
||||
/** Film pixel filter radius. */
|
||||
float filter_size;
|
||||
eCameraType type;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(CameraData, 16)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Film
|
||||
* \{ */
|
||||
|
||||
enum eDebugMode : uint32_t {
|
||||
/* TODO(fclem) Rename shadow cases. */
|
||||
SHADOW_DEBUG_NONE = 0u,
|
||||
/**
|
||||
* Gradient showing light evaluation hotspots.
|
||||
*/
|
||||
DEBUG_LIGHT_CULLING = 4u,
|
||||
/**
|
||||
* Tilemaps to screen. Is also present in other modes.
|
||||
* - Black pixels, no pages allocated.
|
||||
* - Green pixels, pages cached.
|
||||
* - Red pixels, pages allocated.
|
||||
*/
|
||||
SHADOW_DEBUG_TILEMAPS = 5u,
|
||||
/**
|
||||
* Random color per pages. Validates page density allocation and sampling.
|
||||
*/
|
||||
SHADOW_DEBUG_PAGES = 6u,
|
||||
/**
|
||||
* Outputs random color per tilemap (or tilemap level). Validates tilemaps coverage.
|
||||
* Black means not covered by any tilemaps LOD of the shadow.
|
||||
*/
|
||||
SHADOW_DEBUG_LOD = 7u,
|
||||
/**
|
||||
* Outputs white pixels for pages allocated and black pixels for unused pages.
|
||||
* This needs SHADOW_DEBUG_PAGE_ALLOCATION_ENABLED defined in order to work.
|
||||
*/
|
||||
SHADOW_DEBUG_PAGE_ALLOCATION = 8u,
|
||||
/**
|
||||
* Outputs the tilemap atlas. Default tilemap is too big for the usual screen resolution.
|
||||
* Try lowering SHADOW_TILEMAP_PER_ROW and SHADOW_MAX_TILEMAP before using this option.
|
||||
*/
|
||||
SHADOW_DEBUG_TILE_ALLOCATION = 9u,
|
||||
/**
|
||||
* Visualize linear depth stored in the atlas regions of the active light.
|
||||
* This way, one can check if the rendering, the copying and the shadow sampling functions works.
|
||||
*/
|
||||
SHADOW_DEBUG_SHADOW_DEPTH = 10u
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Film
|
||||
* \{ */
|
||||
|
||||
enum eFilmDataType : uint32_t {
|
||||
/** Color is accumulated using the pixel filter. No negative values. */
|
||||
FILM_DATA_COLOR = 0u,
|
||||
/** Variant where we accumulate using pre-exposed values and log space. */
|
||||
FILM_DATA_COLOR_LOG = 1u,
|
||||
/** Non-Color will be accumulated using nearest filter. All values are allowed. */
|
||||
FILM_DATA_FLOAT = 2u,
|
||||
FILM_DATA_VEC2 = 3u,
|
||||
/** No VEC3 because GPU_RGB16F is not a renderable format. */
|
||||
FILM_DATA_VEC4 = 4u,
|
||||
FILM_DATA_NORMAL = 5u,
|
||||
FILM_DATA_DEPTH = 6u,
|
||||
FILM_DATA_MOTION = 7u
|
||||
};
|
||||
|
||||
struct FilmData {
|
||||
/** Size of the render target in pixels. */
|
||||
int2 extent;
|
||||
/** Offset of the render target in the full-res frame, in pixels. */
|
||||
int2 offset;
|
||||
/** Scale and bias to filter only a region of the render (aka. render_border). */
|
||||
float2 uv_bias;
|
||||
float2 uv_scale;
|
||||
float2 uv_scale_inv;
|
||||
/** Data type stored by this film. */
|
||||
eFilmDataType data_type;
|
||||
/** Is true if history is valid and can be sampled. Bypassing history to resets accumulation. */
|
||||
bool1 use_history;
|
||||
/** Used for fade-in effect. */
|
||||
float opacity;
|
||||
/** Padding to sizeof(float4). */
|
||||
int _pad0, _pad1, _pad2;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(FilmData, 16)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Depth of field
|
||||
* \{ */
|
||||
|
||||
/* 5% error threshold. */
|
||||
#define DOF_FAST_GATHER_COC_ERROR 0.05
|
||||
#define DOF_GATHER_RING_COUNT 5
|
||||
#define DOF_DILATE_RING_COUNT 3
|
||||
#define DOF_TILE_DIVISOR 16
|
||||
#define DOF_BOKEH_LUT_SIZE 32
|
||||
|
||||
struct DepthOfFieldData {
|
||||
/** Size of the render targets for gather & scatter passes. */
|
||||
int2 extent;
|
||||
/** Size of a pixel in uv space (1.0 / extent). */
|
||||
float2 texel_size;
|
||||
/** Bokeh Scale factor. */
|
||||
float2 bokeh_anisotropic_scale;
|
||||
float2 bokeh_anisotropic_scale_inv;
|
||||
/* Correction factor to align main target pixels with the filtered mipmap chain texture. */
|
||||
float2 gather_uv_fac;
|
||||
/** Scatter parameters. */
|
||||
float scatter_coc_threshold;
|
||||
float scatter_color_threshold;
|
||||
float scatter_neighbor_max_color;
|
||||
int scatter_sprite_per_row;
|
||||
/** Downsampling paramters. */
|
||||
float denoise_factor;
|
||||
/** Bokeh Shape parameters. */
|
||||
float bokeh_blades;
|
||||
float bokeh_rotation;
|
||||
float bokeh_aperture_ratio;
|
||||
/** Circle of confusion (CoC) parameters. */
|
||||
float coc_mul;
|
||||
float coc_bias;
|
||||
float coc_abs_max;
|
||||
/** Copy of camera type. */
|
||||
eCameraType camera_type;
|
||||
int _pad0, _pad1;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(DepthOfFieldData, 16)
|
||||
|
||||
/** WORKAROUND(@fclem): This is because this file is included before common_math_lib.glsl. */
|
||||
#ifndef M_PI
|
||||
# define EEVEE_PI
|
||||
# define M_PI 3.14159265358979323846 /* pi */
|
||||
#endif
|
||||
|
||||
static inline float coc_radius_from_camera_depth(DepthOfFieldData dof, float depth)
|
||||
{
|
||||
depth = (dof.camera_type != CAMERA_ORTHO) ? 1.0f / depth : depth;
|
||||
return dof.coc_mul * depth + dof.coc_bias;
|
||||
}
|
||||
|
||||
static inline float regular_polygon_side_length(float sides_count)
|
||||
{
|
||||
return 2.0f * sinf(M_PI / sides_count);
|
||||
}
|
||||
|
||||
/* Returns intersection ratio between the radius edge at theta and the regular polygon edge.
|
||||
* Start first corners at theta == 0. */
|
||||
static inline float circle_to_polygon_radius(float sides_count, float theta)
|
||||
{
|
||||
/* From Graphics Gems from CryENGINE 3 (Siggraph 2013) by Tiago Sousa (slide
|
||||
* 36). */
|
||||
float side_angle = (2.0f * M_PI) / sides_count;
|
||||
return cosf(side_angle * 0.5f) /
|
||||
cosf(theta - side_angle * floorf((sides_count * theta + M_PI) / (2.0f * M_PI)));
|
||||
}
|
||||
|
||||
/* Remap input angle to have homogenous spacing of points along a polygon edge.
|
||||
* Expects theta to be in [0..2pi] range. */
|
||||
static inline float circle_to_polygon_angle(float sides_count, float theta)
|
||||
{
|
||||
float side_angle = (2.0f * M_PI) / sides_count;
|
||||
float halfside_angle = side_angle * 0.5f;
|
||||
float side = floorf(theta / side_angle);
|
||||
/* Length of segment from center to the middle of polygon side. */
|
||||
float adjacent = circle_to_polygon_radius(sides_count, 0.0f);
|
||||
|
||||
/* This is the relative position of the sample on the polygon half side. */
|
||||
float local_theta = theta - side * side_angle;
|
||||
float ratio = (local_theta - halfside_angle) / halfside_angle;
|
||||
|
||||
float halfside_len = regular_polygon_side_length(sides_count) * 0.5f;
|
||||
float opposite = ratio * halfside_len;
|
||||
|
||||
/* NOTE: atan(y_over_x) has output range [-M_PI_2..M_PI_2]. */
|
||||
float final_local_theta = atanf(opposite / adjacent);
|
||||
|
||||
return side * side_angle + final_local_theta;
|
||||
}
|
||||
|
||||
#ifdef EEVEE_PI
|
||||
# undef M_PI
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name VelocityModule
|
||||
* \{ */
|
||||
|
||||
struct VelocityObjectData {
|
||||
float4x4 next_object_mat;
|
||||
float4x4 prev_object_mat;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(VelocityObjectData, 16)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Motion Blur
|
||||
* \{ */
|
||||
|
||||
#define MB_TILE_DIVISOR 32
|
||||
|
||||
struct MotionBlurData {
|
||||
/** Motion vector lengths are clamped to this maximum. A value of 0 means effect is bypassed. */
|
||||
float blur_max;
|
||||
/** Depth scaling factor. Avoid bluring background behind moving objects. */
|
||||
float depth_scale;
|
||||
/** As the name suggests. Used to avoid a division in the sampling. */
|
||||
float2 target_size_inv;
|
||||
/** Viewport motion blur only blurs using previous frame vectors. */
|
||||
bool1 is_viewport;
|
||||
int _pad0, _pad1, _pad2;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(MotionBlurData, 16)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Cullings
|
||||
* \{ */
|
||||
|
||||
/* TODO(fclem) Rename this. Only used by probes now. */
|
||||
#define CULLING_ITEM_BATCH 128
|
||||
/* Number of items we can cull. Limited by how we store CullingZBin. */
|
||||
#define CULLING_MAX_ITEM 65536
|
||||
/* Maximum number of 32 bit uint stored per tile. */
|
||||
#define CULLING_MAX_WORD (CULLING_BATCH_SIZE / 32)
|
||||
/* Fine grained subdivision in the Z direction (Must be multiple of CULLING_BATCH_SIZE). */
|
||||
#define CULLING_ZBIN_COUNT 4096
|
||||
|
||||
struct CullingData {
|
||||
/** Scale applied to tile pixel coordinates to get target UV coordinate. */
|
||||
float2 tile_to_uv_fac;
|
||||
/** Scale and bias applied to linear Z to get zbin. */
|
||||
float zbin_scale;
|
||||
float zbin_bias;
|
||||
/** Valid item count in the source data array. */
|
||||
uint items_count;
|
||||
/** Items to skip that are not processed by the 2.5D culling. */
|
||||
uint items_no_cull_count;
|
||||
/** Number of items that passes the first culling test. */
|
||||
uint visible_count;
|
||||
/** Will disable specular during light data copy.. */
|
||||
bool1 enable_specular;
|
||||
/** Extent of one square tile in pixels. */
|
||||
float tile_size;
|
||||
/** Number of tiles on the X/Y axis. */
|
||||
uint tile_x_len;
|
||||
uint tile_y_len;
|
||||
/** Number of word per tile. Depends on the maximum number of lights. */
|
||||
uint tile_word_len;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(CullingData, 16)
|
||||
|
||||
#define CullingZBin uint
|
||||
#define CullingWord uint
|
||||
|
||||
static inline int culling_z_to_zbin(CullingData data, float z)
|
||||
{
|
||||
return int(z * data.zbin_scale + data.zbin_bias);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shadows
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Shadow data for either a directional shadow or a punctual shadow.
|
||||
*
|
||||
* A punctual shadow is composed of 1, 5 or 6 shadow regions.
|
||||
* Regions are sorted in this order -Z, +X, -X, +Y, -Y, +Z.
|
||||
* Face index is computed from light's object space coordinates.
|
||||
*
|
||||
* A directional light shadow is composed of multiple clipmaps with each level
|
||||
* covering twice as much area as the previous one.
|
||||
*/
|
||||
struct ShadowData {
|
||||
/**
|
||||
* Point : Unused.
|
||||
* Directional : Rotation matrix to local light coordinate.
|
||||
* The scale is uniform for the Z axis.
|
||||
* For the X & Y axes, it is scaled to be the size of a tile.
|
||||
* Origin is the one of the largest clipmap.
|
||||
* So after transformation, you are in the tilemap space [0..SHADOW_TILEMAP_RES]
|
||||
* of the largest clipmap.
|
||||
*/
|
||||
float4x4 mat;
|
||||
/** NOTE: It is ok to use float3 here. A float is declared right after it.
|
||||
* float3 is also aligned to 16 bytes. */
|
||||
/** Shadow offset caused by jittering projection origin (for soft shadows). */
|
||||
float3 offset;
|
||||
/** Shadow bias in world space. */
|
||||
float bias;
|
||||
/** Near and far clipping distance to convert shadowmap to world space distances. */
|
||||
float clip_near;
|
||||
float clip_far;
|
||||
/** Index of the first tilemap. */
|
||||
int tilemap_index;
|
||||
/** Index of the last tilemap. */
|
||||
int tilemap_last;
|
||||
/** Directional : Clipmap lod range to avoid sampling outside of valid range. */
|
||||
int clipmap_lod_min, clipmap_lod_max;
|
||||
/** Directional : Offset of the lod min in base units. */
|
||||
int2 base_offset;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowData, 16)
|
||||
|
||||
#define SHADOW_DEBUG_PAGE_ALLOCATION_ENABLED
|
||||
#define SHADOW_DEBUG_TILE_ALLOCATION_ENABLED
|
||||
/** Debug shadow tile allocation. */
|
||||
// #define SHADOW_DEBUG_NO_CACHING
|
||||
/* Debug: Comment to only use BBox tagging instead of depth scanning. */
|
||||
// #define SHADOW_DEBUG_NO_DEPTH_SCAN
|
||||
/* Debug: Will freeze the camera used for shadow tagging if G.debug_value is >= 4. */
|
||||
// #define SHADOW_DEBUG_FREEZE_CAMERA
|
||||
|
||||
#if defined(SHADOW_DEBUG_FREEZE_CAMERA) && defined(SHADOW_DEBUG_NO_DEPTH_SCAN)
|
||||
# error Freeze camera debug option is incompatible with depth scanning.
|
||||
#endif
|
||||
|
||||
/* Given an input tile coordinate [0..SHADOW_TILEMAP_RES] returns the coordinate in NDC [-1..1]. */
|
||||
static inline float2 shadow_tile_coord_to_ndc(int2 tile)
|
||||
{
|
||||
float2 co = float2(tile.x, tile.y) / float(SHADOW_TILEMAP_RES);
|
||||
return co * 2.0f - 1.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Small descriptor used for the tile update phase.
|
||||
*/
|
||||
struct ShadowTileMapData {
|
||||
/** View Projection matrix used to tag tiles (World > UV Tile [0..SHADOW_TILEMAP_RES]). */
|
||||
float4x4 tilemat;
|
||||
/** Corners of the frustum. */
|
||||
float4 corners[4];
|
||||
/** NDC depths to clip usage bbox. */
|
||||
#define _max_usage_depth corners[0].w
|
||||
#define _min_usage_depth corners[1].w
|
||||
#define _punctual_distance corners[2].w
|
||||
/** Shift to apply to the tile grid in the setup phase. */
|
||||
int2 grid_shift;
|
||||
/** True for punctual lights. */
|
||||
bool1 is_cubeface;
|
||||
/** Index inside the tilemap allocator. */
|
||||
int index;
|
||||
/** Cone direction for punctual shadows. */
|
||||
float3 cone_direction;
|
||||
/** Cosine of the max angle. Offset to take into acount the max tile angle. */
|
||||
float cone_angle_cos;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowTileMapData, 16)
|
||||
|
||||
struct ShadowPagesInfoData {
|
||||
/** Index of the next free pages in the free page heap. */
|
||||
int page_free_next;
|
||||
/** Last number of free pages at the begining of the redraw. */
|
||||
int page_free_next_prev;
|
||||
/** Number of pages that needs to be rendered. */
|
||||
int page_updated_count;
|
||||
int _pad0;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowPagesInfoData, 16)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Lights
|
||||
* \{ */
|
||||
|
||||
#define LIGHT_NO_SHADOW -1
|
||||
|
||||
enum eLightType : uint32_t {
|
||||
LIGHT_SUN = 0u,
|
||||
LIGHT_POINT = 1u,
|
||||
LIGHT_SPOT = 2u,
|
||||
LIGHT_RECT = 3u,
|
||||
LIGHT_ELLIPSE = 4u
|
||||
};
|
||||
|
||||
static inline bool is_area_light(eLightType type)
|
||||
{
|
||||
return type >= LIGHT_RECT;
|
||||
}
|
||||
|
||||
struct LightData {
|
||||
/** Normalized obmat. Last column contains data accessible using the following macros. */
|
||||
float4x4 object_mat;
|
||||
/** Packed data in the last column of the object_mat. */
|
||||
#define _area_size_x object_mat[0][3]
|
||||
#define _area_size_y object_mat[1][3]
|
||||
#define _radius _area_size_x
|
||||
#define _spot_mul object_mat[2][3]
|
||||
#define _spot_bias object_mat[3][3]
|
||||
/** Aliases for axes. */
|
||||
#ifdef __cplusplus
|
||||
# define _right object_mat[0]
|
||||
# define _up object_mat[1]
|
||||
# define _back object_mat[2]
|
||||
# define _position object_mat[3]
|
||||
#else
|
||||
# define _right object_mat[0].xyz
|
||||
# define _up object_mat[1].xyz
|
||||
# define _back object_mat[2].xyz
|
||||
# define _position object_mat[3].xyz
|
||||
#endif
|
||||
/** Influence radius (inversed and squared) adjusted for Surface / Volume power. */
|
||||
float influence_radius_invsqr_surface;
|
||||
float influence_radius_invsqr_volume;
|
||||
/** Maximum influence radius. Used for culling. */
|
||||
float influence_radius_max;
|
||||
/** Index of the shadow struct on CPU. -1 means no shadow. */
|
||||
int shadow_id;
|
||||
/** NOTE: It is ok to use float3 here. A float is declared right after it.
|
||||
* float3 is also aligned to 16 bytes. */
|
||||
float3 color;
|
||||
/** Power depending on shader type. */
|
||||
float diffuse_power;
|
||||
float specular_power;
|
||||
float volume_power;
|
||||
float transmit_power;
|
||||
/** Special radius factor for point lighting. */
|
||||
float radius_squared;
|
||||
/** Light Type. */
|
||||
eLightType type;
|
||||
/** Padding to sizeof(float2). */
|
||||
float _pad1;
|
||||
/** Spot size. Aligned to size of float2. */
|
||||
float2 spot_size_inv;
|
||||
/** Associated shadow data. Only valid if shadow_id is not LIGHT_NO_SHADOW. */
|
||||
ShadowData shadow_data;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(LightData, 16)
|
||||
|
||||
/**
|
||||
* Shadow data for debugging the active light shadow.
|
||||
*/
|
||||
struct ShadowDebugData {
|
||||
LightData light;
|
||||
ShadowData shadow;
|
||||
float3 camera_position;
|
||||
eDebugMode type;
|
||||
int tilemap_data_index;
|
||||
int _pad1;
|
||||
int _pad2;
|
||||
int _pad3;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowDebugData, 16)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Light Probes
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Data used when filtering the cubemap.
|
||||
* NOTE(fclem): We might want to promote some of theses to push constants as they are changed
|
||||
* very frequently (Vulkan).
|
||||
*/
|
||||
struct LightProbeFilterData {
|
||||
/** For glossy filter. */
|
||||
float roughness;
|
||||
/** Higher bias lowers the noise but increases blur and reduces quality. */
|
||||
float lod_bias;
|
||||
/** Final intensity multiplicator. */
|
||||
float instensity_fac;
|
||||
/** Luma maximum value. */
|
||||
float luma_max;
|
||||
/** Sample count to take from the input cubemap. */
|
||||
float sample_count;
|
||||
/** Visibility blur ratio [0..1]. Converted to angle in [0..PI/2] range. */
|
||||
float visibility_blur;
|
||||
/** Depth range to encode in the resulting visibility map. */
|
||||
float visibility_range;
|
||||
/** Target layer to render the fullscreen triangle to. */
|
||||
int target_layer;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(LightProbeFilterData, 16)
|
||||
|
||||
/**
|
||||
* Common data to all irradiance grid.
|
||||
*/
|
||||
struct GridInfoData {
|
||||
float4x4 lookdev_rotation;
|
||||
/** Total of visibility cells per row and layer. */
|
||||
int visibility_cells_per_row;
|
||||
int visibility_cells_per_layer;
|
||||
/** Size of visibility cell. */
|
||||
int visibility_size;
|
||||
/** Number of irradiance cells per row. */
|
||||
int irradiance_cells_per_row;
|
||||
/** Smooth irradiance sample interpolation but increases light leaks. */
|
||||
float irradiance_smooth;
|
||||
/** Total number of active irradiance grid including world. */
|
||||
int grid_count;
|
||||
/** Display size of sample spheres. */
|
||||
float display_size;
|
||||
float _pad0;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(GridInfoData, 16)
|
||||
|
||||
/**
|
||||
* Data for a single irradiance grid.
|
||||
*/
|
||||
struct GridData {
|
||||
/** Object matrix inverse (World -> Local). */
|
||||
float4x4 local_mat;
|
||||
/** Resolution of the light grid. */
|
||||
int3 resolution;
|
||||
/** Offset of the first cell of this grid in the grid texture. */
|
||||
int offset;
|
||||
/** World space vector between 2 adjacent cells. */
|
||||
float3 increment_x;
|
||||
/** Attenuation Bias. */
|
||||
float attenuation_bias;
|
||||
/** World space vector between 2 adjacent cells. */
|
||||
float3 increment_y;
|
||||
/** Attenuation scaling. */
|
||||
float attenuation_scale;
|
||||
/** World space vector between 2 adjacent cells. */
|
||||
float3 increment_z;
|
||||
/** Number of grid levels not ready for display during baking. */
|
||||
int level_skip;
|
||||
/** World space corner position. */
|
||||
float3 corner;
|
||||
/** Visibility test parameters. */
|
||||
float visibility_range;
|
||||
float visibility_bleed;
|
||||
float visibility_bias;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(GridData, 16)
|
||||
|
||||
static inline int3 grid_cell_index_to_coordinate(int cell_id, int3 resolution)
|
||||
{
|
||||
int3 cell_coord;
|
||||
cell_coord.z = cell_id % resolution.z;
|
||||
cell_coord.y = (cell_id / resolution.z) % resolution.y;
|
||||
cell_coord.x = cell_id / (resolution.z * resolution.y);
|
||||
return cell_coord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common data to all cubemaps.
|
||||
*/
|
||||
struct CubemapInfoData {
|
||||
float4x4 lookdev_rotation;
|
||||
/** LOD containing data for roughness of 1. */
|
||||
float roughness_max_lod;
|
||||
/** Total number of active cubemaps including world. */
|
||||
int cube_count;
|
||||
/** Display size of sample spheres. */
|
||||
float display_size;
|
||||
float _pad2;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(CubemapInfoData, 16)
|
||||
|
||||
#define CUBEMAP_SHAPE_SPHERE 0.0
|
||||
#define CUBEMAP_SHAPE_BOX 1.0
|
||||
|
||||
/**
|
||||
* Data for a single reflection cubemap probe.
|
||||
*/
|
||||
struct CubemapData {
|
||||
/** Influence shape matrix (World -> Local). */
|
||||
float4x4 influence_mat;
|
||||
/** Packed data in the last column of the influence_mat. */
|
||||
#define _attenuation_factor influence_mat[0][3]
|
||||
#define _attenuation_type influence_mat[1][3]
|
||||
#define _parallax_type influence_mat[2][3]
|
||||
/** Layer of the cube array to sample. */
|
||||
#define _layer influence_mat[3][3]
|
||||
/** Parallax shape matrix (World -> Local). */
|
||||
float4x4 parallax_mat;
|
||||
/** Packed data in the last column of the parallax_mat. */
|
||||
#define _world_position_x parallax_mat[0][3]
|
||||
#define _world_position_y parallax_mat[1][3]
|
||||
#define _world_position_z parallax_mat[2][3]
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(CubemapData, 16)
|
||||
|
||||
struct LightProbeInfoData {
|
||||
GridInfoData grids_info;
|
||||
CubemapInfoData cubes_info;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(LightProbeInfoData, 16)
|
||||
|
||||
#define GRID_MAX 64
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Hierarchical-Z Buffer
|
||||
* \{ */
|
||||
|
||||
struct HiZData {
|
||||
/** Scale factor to remove HiZBuffer padding. */
|
||||
float2 uv_scale;
|
||||
/** Scale factor to convert from pixel space to Normalized Device Coordinates [-1..1]. */
|
||||
float2 pixel_to_ndc;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(HiZData, 16)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Raytracing
|
||||
* \{ */
|
||||
|
||||
struct RaytraceData {
|
||||
/** View space thickness the objects. */
|
||||
float thickness;
|
||||
/** Determine how fast the sample steps are getting bigger. */
|
||||
float quality;
|
||||
/** Importance sample bias. Lower values will make the render less noisy. */
|
||||
float bias;
|
||||
/** Maximum brightness during lighting evaluation. */
|
||||
float brightness_clamp;
|
||||
/** Maximum roughness for which we will trace a ray. */
|
||||
float max_roughness;
|
||||
/** Resolve sample pool offset, based on scene current sample. */
|
||||
int pool_offset;
|
||||
int _pad0;
|
||||
int _pad1;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(RaytraceData, 16)
|
||||
|
||||
struct RaytraceBufferData {
|
||||
/** ViewProjection matrix used to render the previous frame. */
|
||||
float4x4 history_persmat;
|
||||
/** False if the history buffer was just allocated and contains uninitialized data. */
|
||||
bool1 valid_history_diffuse;
|
||||
bool1 valid_history_reflection;
|
||||
bool1 valid_history_refraction;
|
||||
int _pad0;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(RaytraceData, 16)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Subsurface
|
||||
* \{ */
|
||||
|
||||
#define SSS_SAMPLE_MAX 64
|
||||
#define SSS_BURLEY_TRUNCATE 16.0
|
||||
#define SSS_BURLEY_TRUNCATE_CDF 0.9963790093708328
|
||||
#define SSS_TRANSMIT_LUT_SIZE 64.0
|
||||
#define SSS_TRANSMIT_LUT_RADIUS 1.218
|
||||
#define SSS_TRANSMIT_LUT_SCALE ((SSS_TRANSMIT_LUT_SIZE - 1.0) / float(SSS_TRANSMIT_LUT_SIZE))
|
||||
#define SSS_TRANSMIT_LUT_BIAS (0.5 / float(SSS_TRANSMIT_LUT_SIZE))
|
||||
#define SSS_TRANSMIT_LUT_STEP_RES 64.0
|
||||
|
||||
struct SubsurfaceData {
|
||||
/** xy: 2D sample position [-1..1], zw: sample_bounds. */
|
||||
/* NOTE(fclem) Using float4 for alignment. */
|
||||
float4 samples[SSS_SAMPLE_MAX];
|
||||
/** Sample index after which samples are not randomly rotated anymore. */
|
||||
int jitter_threshold;
|
||||
/** Number of samples precomputed in the set. */
|
||||
int sample_len;
|
||||
int _pad0;
|
||||
int _pad1;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(SubsurfaceData, 16)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utility Texture
|
||||
* \{ */
|
||||
|
||||
#define UTIL_TEX_SIZE 64
|
||||
#define UTIL_BTDF_LAYER_COUNT 16
|
||||
/* Scale and bias to avoid interpolation of the border pixel.
|
||||
* Remap UVs to the border pixels centers. */
|
||||
#define UTIL_TEX_UV_SCALE ((UTIL_TEX_SIZE - 1.0f) / UTIL_TEX_SIZE)
|
||||
#define UTIL_TEX_UV_BIAS (0.5f / UTIL_TEX_SIZE)
|
||||
|
||||
#define UTIL_BLUE_NOISE_LAYER 0
|
||||
#define UTIL_LTC_MAT_LAYER 1
|
||||
#define UTIL_LTC_MAG_LAYER 2
|
||||
#define UTIL_BSDF_LAYER 2
|
||||
#define UTIL_BTDF_LAYER 3
|
||||
#define UTIL_DISK_INTEGRAL_LAYER 3
|
||||
#define UTIL_DISK_INTEGRAL_COMP 2
|
||||
|
||||
#ifndef __cplusplus
|
||||
/* Fetch texel. Wrapping if above range. */
|
||||
float4 utility_tx_fetch(sampler2DArray util_tx, float2 texel, float layer)
|
||||
{
|
||||
return texelFetch(util_tx, int3(int2(texel) % UTIL_TEX_SIZE, layer), 0);
|
||||
}
|
||||
|
||||
/* Sample at uv position. Filtered & Wrapping enabled. */
|
||||
float4 utility_tx_sample(sampler2DArray util_tx, float2 uv, float layer)
|
||||
{
|
||||
return textureLod(util_tx, float3(uv, layer), 0.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
using CameraDataBuf = draw::UniformBuffer<CameraData>;
|
||||
using CubemapDataBuf = draw::UniformArrayBuffer<CubemapData, CULLING_ITEM_BATCH>;
|
||||
using CullingDataBuf = draw::StorageBuffer<CullingData>;
|
||||
using CullingKeyBuf = draw::StorageArrayBuffer<uint, CULLING_BATCH_SIZE, true>;
|
||||
using CullingLightBuf = draw::StorageArrayBuffer<LightData, CULLING_BATCH_SIZE, true>;
|
||||
using CullingTileBuf = draw::StorageArrayBuffer<uint, 16 * 16 * CULLING_MAX_WORD, true>;
|
||||
using CullingZbinBuf = draw::StorageArrayBuffer<uint, CULLING_ZBIN_COUNT, true>;
|
||||
using DepthOfFieldDataBuf = draw::UniformBuffer<DepthOfFieldData>;
|
||||
using GridDataBuf = draw::UniformArrayBuffer<GridData, GRID_MAX>;
|
||||
using HiZDataBuf = draw::UniformBuffer<HiZData>;
|
||||
using LightDataBuf = draw::StorageArrayBuffer<LightData, CULLING_BATCH_SIZE>;
|
||||
using LightProbeFilterDataBuf = draw::UniformBuffer<LightProbeFilterData>;
|
||||
using LightProbeInfoDataBuf = draw::UniformBuffer<LightProbeInfoData>;
|
||||
using RaytraceBufferDataBuf = draw::UniformBuffer<RaytraceBufferData>;
|
||||
using RaytraceDataBuf = draw::UniformBuffer<RaytraceData>;
|
||||
using ShadowDataBuf = draw::StorageArrayBuffer<ShadowData, CULLING_BATCH_SIZE>;
|
||||
using ShadowDebugDataBuf = draw::UniformBuffer<ShadowDebugData>;
|
||||
using ShadowPagesInfoDataBuf = draw::StorageBuffer<ShadowPagesInfoData, true>;
|
||||
using ShadowPageHeapBuf = draw::StorageArrayBuffer<uint, SHADOW_MAX_PAGE, true>;
|
||||
using ShadowTileMapDataBuf = draw::StorageArrayBuffer<ShadowTileMapData, SHADOW_MAX_TILEMAP>;
|
||||
using SubsurfaceDataBuf = draw::UniformBuffer<SubsurfaceData>;
|
||||
using VelocityObjectBuf = draw::UniformBuffer<VelocityObjectData>;
|
||||
|
||||
} // namespace blender::eevee
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
599
source/blender/draw/engines/eevee/eevee_shading.cc
Normal file
599
source/blender/draw/engines/eevee/eevee_shading.cc
Normal file
@@ -0,0 +1,599 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Shading passes contain drawcalls specific to shading pipelines.
|
||||
* They are to be shared across views.
|
||||
* This file is only for shading passes. Other passes are declared in their own module.
|
||||
*/
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
#include "eevee_shading.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Background Pass
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void BackgroundPass::sync(GPUMaterial *gpumat, GPUTexture *lookdev_tx)
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR;
|
||||
background_ps_ = DRW_pass_create("Background", state);
|
||||
|
||||
/* Push a matrix at the same location as the camera. */
|
||||
float4x4 camera_mat = float4x4::identity();
|
||||
copy_v3_v3(camera_mat[3], inst_.camera.data_get().viewinv[3]);
|
||||
|
||||
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, background_ps_);
|
||||
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
|
||||
if (lookdev_tx != nullptr) {
|
||||
/* HACK(fclem) This particular texture has been left without resource to be set here. */
|
||||
DRW_shgroup_uniform_texture(grp, "samp0", lookdev_tx);
|
||||
}
|
||||
DRW_shgroup_call_obmat(grp, DRW_cache_fullscreen_quad_get(), camera_mat.ptr());
|
||||
}
|
||||
|
||||
void BackgroundPass::render(void)
|
||||
{
|
||||
DRW_draw_pass(background_ps_);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Forward Pass
|
||||
*
|
||||
* NPR materials (using Closure to RGBA) or material using ALPHA_BLEND.
|
||||
* \{ */
|
||||
|
||||
void ForwardPass::sync(void)
|
||||
{
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
|
||||
prepass_ps_ = DRW_pass_create("Forward.Opaque.Prepass", state);
|
||||
|
||||
state |= DRW_STATE_CULL_BACK;
|
||||
prepass_culled_ps_ = DRW_pass_create("Forward.Opaque.Prepass.Culled", state);
|
||||
|
||||
DRW_pass_link(prepass_ps_, prepass_culled_ps_);
|
||||
}
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL;
|
||||
opaque_ps_ = DRW_pass_create("Forward.Opaque", state);
|
||||
|
||||
state |= DRW_STATE_CULL_BACK;
|
||||
opaque_culled_ps_ = DRW_pass_create("Forward.Opaque.Culled", state);
|
||||
|
||||
DRW_pass_link(opaque_ps_, opaque_culled_ps_);
|
||||
}
|
||||
{
|
||||
DRWState state = DRW_STATE_DEPTH_LESS_EQUAL;
|
||||
transparent_ps_ = DRW_pass_create("Forward.Transparent", state);
|
||||
}
|
||||
}
|
||||
|
||||
DRWShadingGroup *ForwardPass::material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat)
|
||||
{
|
||||
DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? opaque_culled_ps_ : opaque_ps_;
|
||||
LightModule &lights = inst_.lights;
|
||||
LightProbeModule &lightprobes = inst_.lightprobes;
|
||||
RaytracingModule &raytracing = inst_.raytracing;
|
||||
eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
|
||||
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
|
||||
lights.shgroup_resources(grp);
|
||||
DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "probes_buf", lightprobes.info_ubo_get());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
|
||||
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
|
||||
/* TODO(fclem): Make this only needed if material uses it ... somehow. */
|
||||
if (true) {
|
||||
DRW_shgroup_uniform_texture_ref(
|
||||
grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get());
|
||||
}
|
||||
if (raytracing.enabled()) {
|
||||
DRW_shgroup_uniform_block(grp, "rt_diffuse_buf", raytracing.diffuse_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "rt_reflection_buf", raytracing.reflection_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "rt_refraction_buf", raytracing.refraction_ubo_get());
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_radiance_tx_, no_interp);
|
||||
}
|
||||
if (raytracing.enabled()) {
|
||||
DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_tx_);
|
||||
}
|
||||
return grp;
|
||||
}
|
||||
|
||||
DRWShadingGroup *ForwardPass::prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat)
|
||||
{
|
||||
DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? prepass_culled_ps_ :
|
||||
prepass_ps_;
|
||||
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
|
||||
return grp;
|
||||
}
|
||||
|
||||
DRWShadingGroup *ForwardPass::material_transparent_add(::Material *blender_mat,
|
||||
GPUMaterial *gpumat)
|
||||
{
|
||||
LightModule &lights = inst_.lights;
|
||||
LightProbeModule &lightprobes = inst_.lightprobes;
|
||||
RaytracingModule &raytracing = inst_.raytracing;
|
||||
eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
|
||||
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_);
|
||||
lights.shgroup_resources(grp);
|
||||
DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "probes_buf", lightprobes.info_ubo_get());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
|
||||
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
|
||||
/* TODO(fclem): Make this only needed if material uses it ... somehow. */
|
||||
if (true) {
|
||||
DRW_shgroup_uniform_texture_ref(
|
||||
grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get());
|
||||
}
|
||||
if (raytracing.enabled()) {
|
||||
DRW_shgroup_uniform_block(grp, "rt_diffuse_buf", raytracing.diffuse_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "rt_reflection_buf", raytracing.reflection_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "rt_refraction_buf", raytracing.refraction_ubo_get());
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_radiance_tx_, no_interp);
|
||||
}
|
||||
if (raytracing.enabled()) {
|
||||
DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_tx_);
|
||||
}
|
||||
|
||||
DRWState state_disable = DRW_STATE_WRITE_DEPTH;
|
||||
DRWState state_enable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM;
|
||||
if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) {
|
||||
state_enable |= DRW_STATE_CULL_BACK;
|
||||
}
|
||||
DRW_shgroup_state_disable(grp, state_disable);
|
||||
DRW_shgroup_state_enable(grp, state_enable);
|
||||
return grp;
|
||||
}
|
||||
|
||||
DRWShadingGroup *ForwardPass::prepass_transparent_add(::Material *blender_mat, GPUMaterial *gpumat)
|
||||
{
|
||||
if ((blender_mat->blend_flag & MA_BL_HIDE_BACKFACE) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_);
|
||||
|
||||
DRWState state_disable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM;
|
||||
DRWState state_enable = DRW_STATE_WRITE_DEPTH;
|
||||
if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) {
|
||||
state_enable |= DRW_STATE_CULL_BACK;
|
||||
}
|
||||
DRW_shgroup_state_disable(grp, state_disable);
|
||||
DRW_shgroup_state_enable(grp, state_enable);
|
||||
return grp;
|
||||
}
|
||||
|
||||
void ForwardPass::render(const DRWView *view,
|
||||
GBuffer &gbuffer,
|
||||
HiZBuffer &hiz,
|
||||
GPUFrameBuffer *view_fb)
|
||||
{
|
||||
if (inst_.raytracing.enabled()) {
|
||||
int2 extent = {GPU_texture_width(gbuffer.depth_tx), GPU_texture_height(gbuffer.depth_tx)};
|
||||
/* Reuse texture. */
|
||||
gbuffer.ray_radiance_tx.acquire(extent, GPU_RGBA16F, gbuffer.owner);
|
||||
/* Copy combined buffer so we can sample from it. */
|
||||
GPU_texture_copy(gbuffer.ray_radiance_tx, gbuffer.combined_tx);
|
||||
|
||||
input_radiance_tx_ = gbuffer.ray_radiance_tx;
|
||||
|
||||
hiz.prepare(gbuffer.depth_tx);
|
||||
/* TODO(fclem): Avoid this if possible. */
|
||||
hiz.update(gbuffer.depth_tx);
|
||||
|
||||
input_hiz_tx_ = hiz.texture_get();
|
||||
|
||||
GPU_framebuffer_bind(view_fb);
|
||||
}
|
||||
|
||||
DRW_stats_group_start("ForwardOpaque");
|
||||
DRW_draw_pass(prepass_ps_);
|
||||
inst_.shadows.set_view(view, gbuffer.depth_tx);
|
||||
DRW_draw_pass(opaque_ps_);
|
||||
DRW_stats_group_end();
|
||||
|
||||
DRW_stats_group_start("ForwardTransparent");
|
||||
/* TODO(fclem) This is suboptimal. We could sort during sync. */
|
||||
/* FIXME(fclem) This wont work for panoramic, where we need
|
||||
* to sort by distance to camera, not by z. */
|
||||
DRW_pass_sort_shgroup_z(transparent_ps_);
|
||||
DRW_draw_pass(transparent_ps_);
|
||||
DRW_stats_group_end();
|
||||
|
||||
if (inst_.raytracing.enabled()) {
|
||||
gbuffer.ray_radiance_tx.release();
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name DeferredLayer
|
||||
* \{ */
|
||||
|
||||
void DeferredLayer::sync(void)
|
||||
{
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
|
||||
prepass_ps_ = DRW_pass_create("Gbuffer.Prepass", state);
|
||||
|
||||
state |= DRW_STATE_CULL_BACK;
|
||||
prepass_culled_ps_ = DRW_pass_create("Gbuffer.Prepass.Culled", state);
|
||||
|
||||
DRW_pass_link(prepass_ps_, prepass_culled_ps_);
|
||||
}
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_STENCIL_ALWAYS |
|
||||
DRW_STATE_WRITE_STENCIL;
|
||||
gbuffer_ps_ = DRW_pass_create("Gbuffer", state);
|
||||
|
||||
state |= DRW_STATE_CULL_BACK;
|
||||
gbuffer_culled_ps_ = DRW_pass_create("Gbuffer.Culled", state);
|
||||
|
||||
DRW_pass_link(gbuffer_ps_, gbuffer_culled_ps_);
|
||||
}
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL |
|
||||
DRW_STATE_CULL_BACK | DRW_STATE_STENCIL_ALWAYS | DRW_STATE_WRITE_STENCIL;
|
||||
volume_ps_ = DRW_pass_create("VolumesHeterogeneous", state);
|
||||
}
|
||||
}
|
||||
|
||||
DRWShadingGroup *DeferredLayer::material_add(::Material *blender_mat, GPUMaterial *gpumat)
|
||||
{
|
||||
/* TODO/OPTI(fclem) Set the right mask for each effect based on gpumat flags. */
|
||||
uint stencil_mask = CLOSURE_DIFFUSE | CLOSURE_SSS | CLOSURE_REFLECTION | CLOSURE_TRANSPARENCY |
|
||||
CLOSURE_EMISSION | CLOSURE_REFRACTION;
|
||||
DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? gbuffer_culled_ps_ :
|
||||
gbuffer_ps_;
|
||||
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
|
||||
DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
|
||||
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
|
||||
DRW_shgroup_stencil_set(grp, stencil_mask, 0xFF, 0xFF);
|
||||
return grp;
|
||||
}
|
||||
|
||||
DRWShadingGroup *DeferredLayer::prepass_add(::Material *blender_mat, GPUMaterial *gpumat)
|
||||
{
|
||||
DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? prepass_culled_ps_ :
|
||||
prepass_ps_;
|
||||
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
|
||||
return grp;
|
||||
}
|
||||
|
||||
void DeferredLayer::volume_add(Object *ob)
|
||||
{
|
||||
LightModule &lights = inst_.lights;
|
||||
DeferredPass &deferred_pass = inst_.shading_passes.deferred;
|
||||
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_VOLUME);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, volume_ps_);
|
||||
lights.shgroup_resources(grp);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "depth_max_tx", &deferred_pass.input_depth_behind_tx_);
|
||||
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
|
||||
DRW_shgroup_stencil_set(grp, CLOSURE_VOLUME | CLOSURE_TRANSPARENCY, 0xFF, 0xFF);
|
||||
DRW_shgroup_call(grp, DRW_cache_cube_get(), ob);
|
||||
}
|
||||
|
||||
void DeferredLayer::render(const DRWView *view,
|
||||
GBuffer &gbuffer,
|
||||
HiZBuffer &hiz_front,
|
||||
HiZBuffer &hiz_back,
|
||||
RaytraceBuffer &rt_buffer,
|
||||
GPUFrameBuffer *view_fb)
|
||||
{
|
||||
DeferredPass &deferred_pass = inst_.shading_passes.deferred;
|
||||
|
||||
const bool no_surfaces = DRW_pass_is_empty(gbuffer_ps_);
|
||||
const bool no_volumes = DRW_pass_is_empty(volume_ps_);
|
||||
if (no_surfaces && no_volumes) {
|
||||
return;
|
||||
}
|
||||
/* TODO(fclem): detect these cases. */
|
||||
const bool use_diffuse = true;
|
||||
const bool use_subsurface = true;
|
||||
const bool use_transparency = true;
|
||||
const bool use_holdout = true;
|
||||
const bool use_refraction = true;
|
||||
const bool use_glossy = true;
|
||||
const bool use_ao = false;
|
||||
|
||||
gbuffer.prepare((eClosureBits)0xFFFFFFFFu);
|
||||
if (use_ao || use_glossy || use_diffuse) {
|
||||
hiz_front.prepare(gbuffer.depth_tx);
|
||||
}
|
||||
if (use_refraction) {
|
||||
hiz_back.prepare(gbuffer.depth_tx);
|
||||
}
|
||||
|
||||
update_pass_inputs(gbuffer, hiz_front, hiz_back);
|
||||
|
||||
if (use_refraction) {
|
||||
/* TODO(fclem) Only update if needed.
|
||||
* i.e: No need when SSR from previous layer has already updated hiz. */
|
||||
hiz_back.update(gbuffer.depth_tx);
|
||||
}
|
||||
|
||||
gbuffer.bind();
|
||||
|
||||
if (!no_surfaces) {
|
||||
DRW_draw_pass(prepass_ps_);
|
||||
|
||||
/* TODO(fclem): Ambient Occlusion texture node. */
|
||||
if (use_ao) {
|
||||
hiz_front.update(gbuffer.depth_tx);
|
||||
gbuffer.bind();
|
||||
}
|
||||
|
||||
DRW_draw_pass(gbuffer_ps_);
|
||||
}
|
||||
|
||||
inst_.shadows.set_view(view, gbuffer.depth_tx);
|
||||
|
||||
if (!no_volumes) {
|
||||
// gbuffer.copy_depth_behind();
|
||||
// deferred_pass.input_depth_behind_tx_ = gbuffer.depth_behind_tx;
|
||||
|
||||
gbuffer.bind_volume();
|
||||
DRW_draw_pass(volume_ps_);
|
||||
}
|
||||
|
||||
if (use_holdout) {
|
||||
gbuffer.bind_holdout();
|
||||
DRW_draw_pass(deferred_pass.eval_holdout_ps_);
|
||||
}
|
||||
|
||||
/* TODO(fclem) We could bypass update if ao already updated it and if there is no volume. */
|
||||
hiz_front.update(gbuffer.depth_tx);
|
||||
|
||||
if (!no_surfaces && use_refraction) {
|
||||
/* Launch and shade refraction rays before transparency changes the combined pass. */
|
||||
rt_buffer.trace(CLOSURE_REFRACTION, gbuffer, hiz_back, hiz_front);
|
||||
}
|
||||
|
||||
GPU_framebuffer_bind(view_fb);
|
||||
if (use_transparency) {
|
||||
DRW_draw_pass(deferred_pass.eval_transparency_ps_);
|
||||
}
|
||||
|
||||
gbuffer.clear_radiance();
|
||||
|
||||
if (!no_surfaces && use_refraction) {
|
||||
rt_buffer.denoise(CLOSURE_REFRACTION);
|
||||
rt_buffer.resolve(CLOSURE_REFRACTION, gbuffer);
|
||||
}
|
||||
|
||||
if (!no_volumes) {
|
||||
/* TODO(fclem) volume fb. */
|
||||
GPU_framebuffer_bind(view_fb);
|
||||
DRW_draw_pass(deferred_pass.eval_volume_homogeneous_ps_);
|
||||
}
|
||||
|
||||
if (!no_surfaces) {
|
||||
gbuffer.bind_radiance();
|
||||
DRW_draw_pass(deferred_pass.eval_direct_ps_);
|
||||
|
||||
if (use_diffuse) {
|
||||
rt_buffer.trace(CLOSURE_DIFFUSE, gbuffer, hiz_front, hiz_front);
|
||||
rt_buffer.denoise(CLOSURE_DIFFUSE);
|
||||
rt_buffer.resolve(CLOSURE_DIFFUSE, gbuffer);
|
||||
}
|
||||
|
||||
if (use_subsurface) {
|
||||
GPU_framebuffer_bind(view_fb);
|
||||
DRW_draw_pass(deferred_pass.eval_subsurface_ps_);
|
||||
}
|
||||
|
||||
if (use_glossy) {
|
||||
rt_buffer.trace(CLOSURE_REFLECTION, gbuffer, hiz_front, hiz_front);
|
||||
rt_buffer.denoise(CLOSURE_REFLECTION);
|
||||
rt_buffer.resolve(CLOSURE_REFLECTION, gbuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeferredLayer::update_pass_inputs(GBuffer &gbuffer, HiZBuffer &hiz_front, HiZBuffer &hiz_back)
|
||||
{
|
||||
DeferredPass &deferred_pass = inst_.shading_passes.deferred;
|
||||
deferred_pass.input_combined_tx_ = gbuffer.combined_tx;
|
||||
deferred_pass.input_emission_data_tx_ = gbuffer.emission_tx;
|
||||
deferred_pass.input_transmit_color_tx_ = gbuffer.transmit_color_tx;
|
||||
deferred_pass.input_transmit_normal_tx_ = gbuffer.transmit_normal_tx;
|
||||
deferred_pass.input_transmit_data_tx_ = gbuffer.transmit_data_tx;
|
||||
deferred_pass.input_reflect_color_tx_ = gbuffer.reflect_color_tx;
|
||||
deferred_pass.input_reflect_normal_tx_ = gbuffer.reflect_normal_tx;
|
||||
deferred_pass.input_diffuse_tx_ = gbuffer.diffuse_tx;
|
||||
deferred_pass.input_transparency_data_tx_ = gbuffer.transparency_tx;
|
||||
deferred_pass.input_volume_data_tx_ = gbuffer.volume_tx;
|
||||
deferred_pass.input_hiz_front_tx_ = hiz_front.texture_get();
|
||||
deferred_pass.input_hiz_back_tx_ = hiz_back.texture_get();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name DeferredLayer
|
||||
* \{ */
|
||||
|
||||
void DeferredPass::sync(void)
|
||||
{
|
||||
opaque_layer_.sync();
|
||||
refraction_layer_.sync();
|
||||
volumetric_layer_.sync();
|
||||
|
||||
LightModule &lights = inst_.lights;
|
||||
LightProbeModule &lightprobes = inst_.lightprobes;
|
||||
|
||||
eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
|
||||
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_ADD_FULL;
|
||||
eval_direct_ps_ = DRW_pass_create("DeferredDirect", state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_DIRECT);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_direct_ps_);
|
||||
lights.shgroup_resources(grp);
|
||||
DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "probes_buf", lightprobes.info_ubo_get());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
|
||||
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
|
||||
DRW_shgroup_uniform_texture_ref_ex(
|
||||
grp, "emission_data_tx", &input_emission_data_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(
|
||||
grp, "transmit_color_tx", &input_transmit_color_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(
|
||||
grp, "transmit_normal_tx", &input_transmit_normal_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(
|
||||
grp, "transmit_data_tx", &input_transmit_data_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(
|
||||
grp, "reflect_color_tx", &input_reflect_color_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(
|
||||
grp, "reflect_normal_tx", &input_reflect_normal_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_front_tx_);
|
||||
DRW_shgroup_uniform_texture_ref(
|
||||
grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get());
|
||||
DRW_shgroup_stencil_set(
|
||||
grp, 0x0, 0x0, CLOSURE_DIFFUSE | CLOSURE_REFLECTION | CLOSURE_EMISSION);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL | DRW_STATE_BLEND_ADD_FULL;
|
||||
eval_subsurface_ps_ = DRW_pass_create("DeferredSubsurface", state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(SUBSURFACE_EVAL);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_subsurface_ps_);
|
||||
DRW_shgroup_uniform_block(grp, "sss_buf", inst_.subsurface.ubo_get());
|
||||
DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get());
|
||||
DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_front_tx_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_diffuse_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(
|
||||
grp, "transmit_color_tx", &input_transmit_color_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(
|
||||
grp, "transmit_normal_tx", &input_transmit_normal_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(
|
||||
grp, "transmit_data_tx", &input_transmit_data_tx_, no_interp);
|
||||
DRW_shgroup_stencil_set(grp, 0x0, 0xFF, CLOSURE_SSS);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_ADD_FULL;
|
||||
eval_volume_homogeneous_ps_ = DRW_pass_create("DeferredVolume", state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_VOLUME);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_volume_homogeneous_ps_);
|
||||
lights.shgroup_resources(grp);
|
||||
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
|
||||
DRW_shgroup_uniform_texture_ref_ex(
|
||||
grp, "transparency_data_tx", &input_transparency_data_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "volume_data_tx", &input_volume_data_tx_, no_interp);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_front_tx_);
|
||||
DRW_shgroup_stencil_set(grp, 0x0, 0x0, CLOSURE_VOLUME);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_MUL;
|
||||
eval_transparency_ps_ = DRW_pass_create("DeferredTransparency", state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_TRANSPARENT);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_transparency_ps_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "transparency_data_tx", &input_transparency_data_tx_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "volume_data_tx", &input_volume_data_tx_, no_interp);
|
||||
DRW_shgroup_stencil_set(grp, 0x0, 0x0, CLOSURE_TRANSPARENCY);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL;
|
||||
eval_holdout_ps_ = DRW_pass_create("DeferredHoldout", state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_HOLDOUT);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_volume_homogeneous_ps_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "combined_tx", &input_combined_tx_);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "transparency_data_tx", &input_transparency_data_tx_);
|
||||
DRW_shgroup_stencil_set(grp, 0x0, 0x0, CLOSURE_TRANSPARENCY);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
DRWShadingGroup *DeferredPass::material_add(::Material *material, GPUMaterial *gpumat)
|
||||
{
|
||||
if (material->blend_flag & MA_BL_SS_REFRACTION) {
|
||||
return refraction_layer_.material_add(material, gpumat);
|
||||
}
|
||||
else {
|
||||
return opaque_layer_.material_add(material, gpumat);
|
||||
}
|
||||
}
|
||||
|
||||
DRWShadingGroup *DeferredPass::prepass_add(::Material *material, GPUMaterial *gpumat)
|
||||
{
|
||||
if (material->blend_flag & MA_BL_SS_REFRACTION) {
|
||||
return refraction_layer_.prepass_add(material, gpumat);
|
||||
}
|
||||
else {
|
||||
return opaque_layer_.prepass_add(material, gpumat);
|
||||
}
|
||||
}
|
||||
|
||||
void DeferredPass::volume_add(Object *ob)
|
||||
{
|
||||
volumetric_layer_.volume_add(ob);
|
||||
}
|
||||
|
||||
void DeferredPass::render(const DRWView *drw_view,
|
||||
GBuffer &gbuffer,
|
||||
HiZBuffer &hiz_front,
|
||||
HiZBuffer &hiz_back,
|
||||
RaytraceBuffer &rt_buffer_opaque_,
|
||||
RaytraceBuffer &rt_buffer_refract_,
|
||||
GPUFrameBuffer *view_fb)
|
||||
{
|
||||
DRW_stats_group_start("OpaqueLayer");
|
||||
opaque_layer_.render(drw_view, gbuffer, hiz_front, hiz_back, rt_buffer_opaque_, view_fb);
|
||||
DRW_stats_group_end();
|
||||
|
||||
DRW_stats_group_start("RefractionLayer");
|
||||
refraction_layer_.render(drw_view, gbuffer, hiz_front, hiz_back, rt_buffer_refract_, view_fb);
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* NOTE(fclem): Reuse the same rtbuf_buf as refraction but should not use it. */
|
||||
DRW_stats_group_start("VolumetricLayer");
|
||||
volumetric_layer_.render(drw_view, gbuffer, hiz_front, hiz_back, rt_buffer_refract_, view_fb);
|
||||
DRW_stats_group_end();
|
||||
|
||||
gbuffer.render_end();
|
||||
rt_buffer_opaque_.render_end(drw_view);
|
||||
rt_buffer_refract_.render_end(drw_view);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user