This is a port of the previous implementation but using compute shaders instead of using the raster pipeline for every steps. Only the scatter passes is kept as a raster pass for obvious performance reasons. Many steps have been rewritten to take advantage of LDS which allows faster and simpler downsampling and filtering for some passes. A new stabilize phase has been separated from another setup pass in order to improve it in the future with better stabilization. The scatter pass shaders and pipeline also changed. We now use indirect drawcall to draw quads using triangle strips primitives. This reduces fragment shader invocation count & overdraw compared to a bounding triangle. This also reduces the amount of vertex shader invocation drastically to the bare minimum instead of having always 3 verts per 4 pixels (for each ground).
525 lines
18 KiB
Python
525 lines
18 KiB
Python
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
import bpy
|
|
from bpy.types import Panel
|
|
from rna_prop_ui import PropertyPanel
|
|
from bl_ui.utils import PresetPanel
|
|
|
|
|
|
class CameraButtonsPanel:
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "data"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.engine
|
|
return context.camera and (engine in cls.COMPAT_ENGINES)
|
|
|
|
|
|
class CAMERA_PT_presets(PresetPanel, Panel):
|
|
bl_label = "Camera Presets"
|
|
preset_subdir = "camera"
|
|
preset_operator = "script.execute_preset"
|
|
preset_add_operator = "camera.preset_add"
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
|
|
|
|
|
|
class SAFE_AREAS_PT_presets(PresetPanel, Panel):
|
|
bl_label = "Camera Presets"
|
|
preset_subdir = "safe_areas"
|
|
preset_operator = "script.execute_preset"
|
|
preset_add_operator = "safe_areas.preset_add"
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
|
|
|
|
|
|
class DATA_PT_context_camera(CameraButtonsPanel, Panel):
|
|
bl_label = ""
|
|
bl_options = {'HIDE_HEADER'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
ob = context.object
|
|
cam = context.camera
|
|
space = context.space_data
|
|
|
|
if ob:
|
|
layout.template_ID(ob, "data")
|
|
elif cam:
|
|
layout.template_ID(space, "pin_id")
|
|
|
|
|
|
class DATA_PT_lens(CameraButtonsPanel, Panel):
|
|
bl_label = "Lens"
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
cam = context.camera
|
|
|
|
layout.prop(cam, "type")
|
|
|
|
col = layout.column()
|
|
col.separator()
|
|
|
|
if cam.type == 'PERSP':
|
|
if cam.lens_unit == 'MILLIMETERS':
|
|
col.prop(cam, "lens")
|
|
elif cam.lens_unit == 'FOV':
|
|
col.prop(cam, "angle")
|
|
col.prop(cam, "lens_unit")
|
|
|
|
elif cam.type == 'ORTHO':
|
|
col.prop(cam, "ortho_scale")
|
|
|
|
elif cam.type == 'PANO':
|
|
engine = context.engine
|
|
if engine == 'CYCLES':
|
|
ccam = cam.cycles
|
|
col.prop(ccam, "panorama_type")
|
|
if ccam.panorama_type == 'FISHEYE_EQUIDISTANT':
|
|
col.prop(ccam, "fisheye_fov")
|
|
elif ccam.panorama_type == 'FISHEYE_EQUISOLID':
|
|
col.prop(ccam, "fisheye_lens", text="Lens")
|
|
col.prop(ccam, "fisheye_fov")
|
|
elif ccam.panorama_type == 'EQUIRECTANGULAR':
|
|
sub = col.column(align=True)
|
|
sub.prop(ccam, "latitude_min", text="Latitude Min")
|
|
sub.prop(ccam, "latitude_max", text="Max")
|
|
sub = col.column(align=True)
|
|
sub.prop(ccam, "longitude_min", text="Longitude Min")
|
|
sub.prop(ccam, "longitude_max", text="Max")
|
|
elif ccam.panorama_type == 'FISHEYE_LENS_POLYNOMIAL':
|
|
col.prop(ccam, "fisheye_fov")
|
|
col.prop(ccam, "fisheye_polynomial_k0", text="K0")
|
|
col.prop(ccam, "fisheye_polynomial_k1", text="K1")
|
|
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_EEVEE_NEXT', 'BLENDER_WORKBENCH'}:
|
|
if cam.lens_unit == 'MILLIMETERS':
|
|
col.prop(cam, "lens")
|
|
elif cam.lens_unit == 'FOV':
|
|
col.prop(cam, "angle")
|
|
col.prop(cam, "lens_unit")
|
|
|
|
col = layout.column()
|
|
col.separator()
|
|
|
|
sub = col.column(align=True)
|
|
sub.prop(cam, "shift_x", text="Shift X")
|
|
sub.prop(cam, "shift_y", text="Y")
|
|
|
|
col.separator()
|
|
sub = col.column(align=True)
|
|
sub.prop(cam, "clip_start", text="Clip Start")
|
|
sub.prop(cam, "clip_end", text="End")
|
|
|
|
|
|
class DATA_PT_camera_stereoscopy(CameraButtonsPanel, Panel):
|
|
bl_label = "Stereoscopy"
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
render = context.scene.render
|
|
return (super().poll(context) and render.use_multiview and
|
|
render.views_format == 'STEREO_3D')
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
render = context.scene.render
|
|
st = context.camera.stereo
|
|
cam = context.camera
|
|
|
|
is_spherical_stereo = cam.type != 'ORTHO' and render.use_spherical_stereo
|
|
use_spherical_stereo = is_spherical_stereo and st.use_spherical_stereo
|
|
|
|
layout.prop(st, "convergence_mode")
|
|
|
|
col = layout.column()
|
|
sub = col.column()
|
|
sub.active = st.convergence_mode != 'PARALLEL'
|
|
sub.prop(st, "convergence_distance")
|
|
|
|
col.prop(st, "interocular_distance")
|
|
|
|
if is_spherical_stereo:
|
|
col.separator()
|
|
col.prop(st, "use_spherical_stereo")
|
|
sub = col.column()
|
|
sub.active = st.use_spherical_stereo
|
|
sub.prop(st, "use_pole_merge")
|
|
|
|
sub = col.column(align=True)
|
|
sub.active = st.use_pole_merge
|
|
sub.prop(st, "pole_merge_angle_from", text="Pole Merge Angle Start")
|
|
sub.prop(st, "pole_merge_angle_to", text="End")
|
|
|
|
col = layout.column()
|
|
col.active = not use_spherical_stereo
|
|
col.separator()
|
|
col.prop(st, "pivot")
|
|
|
|
|
|
class DATA_PT_camera(CameraButtonsPanel, Panel):
|
|
bl_label = "Camera"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw_header_preset(self, _context):
|
|
CAMERA_PT_presets.draw_panel_header(self.layout)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
cam = context.camera
|
|
|
|
layout.use_property_split = True
|
|
|
|
col = layout.column()
|
|
col.prop(cam, "sensor_fit")
|
|
|
|
if cam.sensor_fit == 'AUTO':
|
|
col.prop(cam, "sensor_width", text="Size")
|
|
else:
|
|
sub = col.column(align=True)
|
|
sub.active = cam.sensor_fit == 'HORIZONTAL'
|
|
sub.prop(cam, "sensor_width", text="Width")
|
|
|
|
sub = col.column(align=True)
|
|
sub.active = cam.sensor_fit == 'VERTICAL'
|
|
sub.prop(cam, "sensor_height", text="Height")
|
|
|
|
|
|
class DATA_PT_camera_dof(CameraButtonsPanel, Panel):
|
|
bl_label = "Depth of Field"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw_header(self, context):
|
|
cam = context.camera
|
|
dof = cam.dof
|
|
self.layout.prop(dof, "use_dof", text="")
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
cam = context.camera
|
|
dof = cam.dof
|
|
layout.active = dof.use_dof
|
|
|
|
col = layout.column()
|
|
col.prop(dof, "focus_object", text="Focus on Object")
|
|
if dof.focus_object and dof.focus_object.type == 'ARMATURE':
|
|
col.prop_search(dof, "focus_subtarget", dof.focus_object.data, "bones", text="Focus on Bone")
|
|
sub = col.column()
|
|
sub.active = (dof.focus_object is None)
|
|
sub.prop(dof, "focus_distance", text="Focus Distance")
|
|
|
|
|
|
class DATA_PT_camera_dof_aperture(CameraButtonsPanel, Panel):
|
|
bl_label = "Aperture"
|
|
bl_parent_id = "DATA_PT_camera_dof"
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
cam = context.camera
|
|
dof = cam.dof
|
|
layout.active = dof.use_dof
|
|
|
|
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
|
|
|
|
col = flow.column()
|
|
col.prop(dof, "aperture_fstop")
|
|
|
|
col = flow.column()
|
|
col.prop(dof, "aperture_blades")
|
|
col.prop(dof, "aperture_rotation")
|
|
col.prop(dof, "aperture_ratio")
|
|
|
|
|
|
class DATA_PT_camera_background_image(CameraButtonsPanel, Panel):
|
|
bl_label = "Background Images"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw_header(self, context):
|
|
cam = context.camera
|
|
|
|
self.layout.prop(cam, "show_background_images", text="")
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
cam = context.camera
|
|
use_multiview = context.scene.render.use_multiview
|
|
|
|
col = layout.column()
|
|
col.operator("view3d.background_image_add", text="Add Image")
|
|
|
|
for i, bg in enumerate(cam.background_images):
|
|
layout.active = cam.show_background_images
|
|
box = layout.box()
|
|
row = box.row(align=True)
|
|
row.prop(bg, "show_expanded", text="", emboss=False)
|
|
if bg.source == 'IMAGE' and bg.image:
|
|
row.prop(bg.image, "name", text="", emboss=False)
|
|
elif bg.source == 'MOVIE_CLIP' and bg.clip:
|
|
row.prop(bg.clip, "name", text="", emboss=False)
|
|
elif bg.source and bg.use_camera_clip:
|
|
row.label(text="Active Clip")
|
|
else:
|
|
row.label(text="Not Set")
|
|
|
|
row.prop(
|
|
bg,
|
|
"show_background_image",
|
|
text="",
|
|
emboss=False,
|
|
icon='RESTRICT_VIEW_OFF' if bg.show_background_image else 'RESTRICT_VIEW_ON',
|
|
)
|
|
|
|
row.operator("view3d.background_image_remove", text="", emboss=False, icon='X').index = i
|
|
|
|
if bg.show_expanded:
|
|
row = box.row()
|
|
row.prop(bg, "source", expand=True)
|
|
|
|
has_bg = False
|
|
if bg.source == 'IMAGE':
|
|
row = box.row()
|
|
row.template_ID(bg, "image", open="image.open")
|
|
if bg.image is not None:
|
|
box.template_image(bg, "image", bg.image_user, compact=True)
|
|
has_bg = True
|
|
|
|
if use_multiview:
|
|
box.prop(bg.image, "use_multiview")
|
|
|
|
column = box.column()
|
|
column.active = bg.image.use_multiview
|
|
|
|
column.label(text="Views Format:")
|
|
column.row().prop(bg.image, "views_format", expand=True)
|
|
|
|
sub = column.box()
|
|
sub.active = bg.image.views_format == 'STEREO_3D'
|
|
sub.template_image_stereo_3d(bg.image.stereo_3d_format)
|
|
|
|
elif bg.source == 'MOVIE_CLIP':
|
|
box.prop(bg, "use_camera_clip", text="Active Clip")
|
|
|
|
column = box.column()
|
|
column.active = not bg.use_camera_clip
|
|
column.template_ID(bg, "clip", open="clip.open")
|
|
|
|
if bg.clip:
|
|
column.template_movieclip(bg, "clip", compact=True)
|
|
|
|
if bg.use_camera_clip or bg.clip:
|
|
has_bg = True
|
|
|
|
column = box.column()
|
|
column.active = has_bg
|
|
column.prop(bg.clip_user, "use_render_undistorted")
|
|
column.prop(bg.clip_user, "proxy_render_size")
|
|
|
|
if has_bg:
|
|
col = box.column()
|
|
col.prop(bg, "alpha", slider=True)
|
|
col.row().prop(bg, "display_depth", expand=True)
|
|
|
|
col.row().prop(bg, "frame_method", expand=True)
|
|
|
|
row = box.row()
|
|
row.prop(bg, "offset")
|
|
|
|
col = box.column()
|
|
col.prop(bg, "rotation")
|
|
col.prop(bg, "scale")
|
|
|
|
col = box.column(heading="Flip")
|
|
col.prop(bg, "use_flip_x", text="X")
|
|
col.prop(bg, "use_flip_y", text="Y")
|
|
|
|
|
|
class DATA_PT_camera_display(CameraButtonsPanel, Panel):
|
|
bl_label = "Viewport Display"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
cam = context.camera
|
|
|
|
col = layout.column(align=True)
|
|
|
|
col.prop(cam, "display_size", text="Size")
|
|
|
|
col = layout.column(heading="Show")
|
|
col.prop(cam, "show_limits", text="Limits")
|
|
col.prop(cam, "show_mist", text="Mist")
|
|
col.prop(cam, "show_sensor", text="Sensor")
|
|
col.prop(cam, "show_name", text="Name")
|
|
|
|
col = layout.column(align=False, heading="Passepartout")
|
|
col.use_property_decorate = False
|
|
row = col.row(align=True)
|
|
sub = row.row(align=True)
|
|
sub.prop(cam, "show_passepartout", text="")
|
|
sub = sub.row(align=True)
|
|
sub.active = cam.show_passepartout
|
|
sub.prop(cam, "passepartout_alpha", text="")
|
|
row.prop_decorator(cam, "passepartout_alpha")
|
|
|
|
|
|
class DATA_PT_camera_display_composition_guides(CameraButtonsPanel, Panel):
|
|
bl_label = "Composition Guides"
|
|
bl_parent_id = "DATA_PT_camera_display"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
cam = context.camera
|
|
|
|
layout.prop(cam, "show_composition_thirds")
|
|
|
|
col = layout.column(heading="Center", align=True)
|
|
col.prop(cam, "show_composition_center")
|
|
col.prop(cam, "show_composition_center_diagonal", text="Diagonal")
|
|
|
|
col = layout.column(heading="Golden", align=True)
|
|
col.prop(cam, "show_composition_golden", text="Ratio")
|
|
col.prop(cam, "show_composition_golden_tria_a", text="Triangle A")
|
|
col.prop(cam, "show_composition_golden_tria_b", text="Triangle B")
|
|
|
|
col = layout.column(heading="Harmony", align=True)
|
|
col.prop(cam, "show_composition_harmony_tri_a", text="Triangle A")
|
|
col.prop(cam, "show_composition_harmony_tri_b", text="Triangle B")
|
|
|
|
|
|
class DATA_PT_camera_safe_areas(CameraButtonsPanel, Panel):
|
|
bl_label = "Safe Areas"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw_header(self, context):
|
|
cam = context.camera
|
|
|
|
self.layout.prop(cam, "show_safe_areas", text="")
|
|
|
|
def draw_header_preset(self, _context):
|
|
SAFE_AREAS_PT_presets.draw_panel_header(self.layout)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
safe_data = context.scene.safe_areas
|
|
camera = context.camera
|
|
|
|
layout.use_property_split = True
|
|
|
|
layout.active = camera.show_safe_areas
|
|
|
|
col = layout.column()
|
|
|
|
sub = col.column()
|
|
sub.prop(safe_data, "title", slider=True)
|
|
sub.prop(safe_data, "action", slider=True)
|
|
|
|
|
|
class DATA_PT_camera_safe_areas_center_cut(CameraButtonsPanel, Panel):
|
|
bl_label = "Center-Cut Safe Areas"
|
|
bl_parent_id = "DATA_PT_camera_safe_areas"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw_header(self, context):
|
|
cam = context.camera
|
|
|
|
layout = self.layout
|
|
layout.active = cam.show_safe_areas
|
|
layout.prop(cam, "show_safe_center", text="")
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
safe_data = context.scene.safe_areas
|
|
camera = context.camera
|
|
|
|
layout.use_property_split = True
|
|
|
|
layout.active = camera.show_safe_areas and camera.show_safe_center
|
|
|
|
col = layout.column()
|
|
col.prop(safe_data, "title_center", slider=True)
|
|
col.prop(safe_data, "action_center", slider=True)
|
|
|
|
|
|
class DATA_PT_custom_props_camera(CameraButtonsPanel, PropertyPanel, Panel):
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
|
|
_context_path = "object.data"
|
|
_property_type = bpy.types.Camera
|
|
|
|
|
|
def draw_display_safe_settings(layout, safe_data, settings):
|
|
show_safe_areas = settings.show_safe_areas
|
|
show_safe_center = settings.show_safe_center
|
|
|
|
layout.use_property_split = True
|
|
|
|
col = layout.column()
|
|
col.active = show_safe_areas
|
|
|
|
sub = col.column()
|
|
sub.prop(safe_data, "title", slider=True)
|
|
sub.prop(safe_data, "action", slider=True)
|
|
|
|
col.separator()
|
|
|
|
col.prop(settings, "show_safe_center", text="Center-Cut Safe Areas")
|
|
|
|
sub = col.column()
|
|
sub.active = show_safe_areas and show_safe_center
|
|
sub.prop(safe_data, "title_center", slider=True)
|
|
sub.prop(safe_data, "action_center", slider=True)
|
|
|
|
|
|
classes = (
|
|
CAMERA_PT_presets,
|
|
SAFE_AREAS_PT_presets,
|
|
DATA_PT_context_camera,
|
|
DATA_PT_lens,
|
|
DATA_PT_camera_dof,
|
|
DATA_PT_camera_dof_aperture,
|
|
DATA_PT_camera,
|
|
DATA_PT_camera_stereoscopy,
|
|
DATA_PT_camera_safe_areas,
|
|
DATA_PT_camera_safe_areas_center_cut,
|
|
DATA_PT_camera_background_image,
|
|
DATA_PT_camera_display,
|
|
DATA_PT_camera_display_composition_guides,
|
|
DATA_PT_custom_props_camera,
|
|
)
|
|
|
|
if __name__ == "__main__": # only for live edit.
|
|
from bpy.utils import register_class
|
|
for cls in classes:
|
|
register_class(cls)
|