Curves: cage overlay for sculpt mode #104467
|
@ -40,7 +40,8 @@ ExternalProject_Add(external_igc_llvm
|
|||
${PATCH_CMD} -p 1 -d ${IGC_LLVM_SOURCE_DIR} < ${IGC_OPENCL_CLANG_PATCH_DIR}/clang/0004-OpenCL-support-cl_ext_float_atomics.patch &&
|
||||
${PATCH_CMD} -p 1 -d ${IGC_LLVM_SOURCE_DIR} < ${IGC_OPENCL_CLANG_PATCH_DIR}/clang/0005-OpenCL-Add-cl_khr_integer_dot_product.patch &&
|
||||
${PATCH_CMD} -p 1 -d ${IGC_LLVM_SOURCE_DIR} < ${IGC_OPENCL_CLANG_PATCH_DIR}/llvm/0001-Memory-leak-fix-for-Managed-Static-Mutex.patch &&
|
||||
${PATCH_CMD} -p 1 -d ${IGC_LLVM_SOURCE_DIR} < ${IGC_OPENCL_CLANG_PATCH_DIR}/llvm/0002-Remove-repo-name-in-LLVM-IR.patch
|
||||
${PATCH_CMD} -p 1 -d ${IGC_LLVM_SOURCE_DIR} < ${IGC_OPENCL_CLANG_PATCH_DIR}/llvm/0002-Remove-repo-name-in-LLVM-IR.patch &&
|
||||
${PATCH_CMD} -p 1 -d ${IGC_LLVM_SOURCE_DIR} < ${IGC_OPENCL_CLANG_PATCH_DIR}/llvm/0003-Add-missing-include-limit-in-benchmark.patch
|
||||
)
|
||||
add_dependencies(
|
||||
external_igc_llvm
|
||||
|
@ -55,9 +56,6 @@ ExternalProject_Add(external_igc_spirv_translator
|
|||
CONFIGURE_COMMAND echo .
|
||||
BUILD_COMMAND echo .
|
||||
INSTALL_COMMAND echo .
|
||||
PATCH_COMMAND ${PATCH_CMD} -p 1 -d ${IGC_SPIRV_TRANSLATOR_SOURCE_DIR} < ${IGC_OPENCL_CLANG_PATCH_DIR}/spirv/0001-update-SPIR-V-headers-for-SPV_INTEL_split_barrier.patch &&
|
||||
${PATCH_CMD} -p 1 -d ${IGC_SPIRV_TRANSLATOR_SOURCE_DIR} < ${IGC_OPENCL_CLANG_PATCH_DIR}/spirv/0002-Add-support-for-split-barriers-extension-SPV_INTEL_s.patch &&
|
||||
${PATCH_CMD} -p 1 -d ${IGC_SPIRV_TRANSLATOR_SOURCE_DIR} < ${IGC_OPENCL_CLANG_PATCH_DIR}/spirv/0003-Support-cl_bf16_conversions.patch
|
||||
)
|
||||
add_dependencies(
|
||||
external_igc_spirv_translator
|
||||
|
|
|
@ -88,6 +88,19 @@ else()
|
|||
export LDFLAGS=${PYTHON_LDFLAGS} &&
|
||||
export PKG_CONFIG_PATH=${LIBDIR}/ffi/lib/pkgconfig)
|
||||
|
||||
# NOTE: untested on APPLE so far.
|
||||
if(NOT APPLE)
|
||||
set(PYTHON_CONFIGURE_EXTRA_ARGS
|
||||
${PYTHON_CONFIGURE_EXTRA_ARGS}
|
||||
# Used on most release Linux builds (Fedora for e.g.),
|
||||
# increases build times noticeably with the benefit of a modest speedup at runtime.
|
||||
--enable-optimizations
|
||||
# While LTO is OK when building on the same system, it's incompatible across GCC versions,
|
||||
# making it impractical for developers to build against, so keep it disabled.
|
||||
# `--with-lto`
|
||||
)
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(external_python
|
||||
URL file://${PACKAGE_DIR}/${PYTHON_FILE}
|
||||
DOWNLOAD_DIR ${DOWNLOAD_DIR}
|
||||
|
|
|
@ -668,9 +668,9 @@ set(SPIRV_HEADERS_FILE SPIR-V-Headers-${SPIRV_HEADERS_VERSION}.tar.gz)
|
|||
# compiler, the versions used are taken from the following location
|
||||
# https://github.com/intel/intel-graphics-compiler/releases
|
||||
|
||||
set(IGC_VERSION 1.0.12149.1)
|
||||
set(IGC_VERSION 1.0.13064.7)
|
||||
set(IGC_URI https://github.com/intel/intel-graphics-compiler/archive/refs/tags/igc-${IGC_VERSION}.tar.gz)
|
||||
set(IGC_HASH 44f67f24e3bc5130f9f062533abf8154782a9d0a992bc19b498639a8521ae836)
|
||||
set(IGC_HASH a929abd4cca2b293961ec0437ee4b3b2147bd3b2c8a3c423af78c0c359b2e5ae)
|
||||
set(IGC_HASH_TYPE SHA256)
|
||||
set(IGC_FILE igc-${IGC_VERSION}.tar.gz)
|
||||
|
||||
|
@ -690,15 +690,15 @@ set(IGC_LLVM_FILE ${IGC_LLVM_VERSION}.tar.gz)
|
|||
#
|
||||
# WARNING WARNING WARNING
|
||||
|
||||
set(IGC_OPENCL_CLANG_VERSION 363a5262d8c7cff3fb28f3bdb5d85c8d7e91c1bb)
|
||||
set(IGC_OPENCL_CLANG_VERSION ee31812ea8b89d08c2918f045d11a19bd33525c5)
|
||||
set(IGC_OPENCL_CLANG_URI https://github.com/intel/opencl-clang/archive/${IGC_OPENCL_CLANG_VERSION}.tar.gz)
|
||||
set(IGC_OPENCL_CLANG_HASH aa8cf72bb239722ce8ce44f79413c6887ecc8ca18477dd520aa5c4809756da9a)
|
||||
set(IGC_OPENCL_CLANG_HASH 1db6735bbcfaa31e8a9ba39f121d6bafa806ea8919e9f56782d6aaa67771ddda)
|
||||
set(IGC_OPENCL_CLANG_HASH_TYPE SHA256)
|
||||
set(IGC_OPENCL_CLANG_FILE opencl-clang-${IGC_OPENCL_CLANG_VERSION}.tar.gz)
|
||||
|
||||
set(IGC_VCINTRINSICS_VERSION v0.5.0)
|
||||
set(IGC_VCINTRINSICS_VERSION v0.11.0)
|
||||
set(IGC_VCINTRINSICS_URI https://github.com/intel/vc-intrinsics/archive/refs/tags/${IGC_VCINTRINSICS_VERSION}.tar.gz)
|
||||
set(IGC_VCINTRINSICS_HASH 70bb47c5e32173cf61514941e83ae7c7eb4485e6d2fca60cfa1f50d4f42c41f2)
|
||||
set(IGC_VCINTRINSICS_HASH e5acd5626ce7fa6d41ce154c50ac805eda734ee66af94ef28e680ac2ad81bb9f)
|
||||
set(IGC_VCINTRINSICS_HASH_TYPE SHA256)
|
||||
set(IGC_VCINTRINSICS_FILE vc-intrinsics-${IGC_VCINTRINSICS_VERSION}.tar.gz)
|
||||
|
||||
|
@ -714,9 +714,9 @@ set(IGC_SPIRV_TOOLS_HASH 6e19900e948944243024aedd0a201baf3854b377b9cc7a386553bc1
|
|||
set(IGC_SPIRV_TOOLS_HASH_TYPE SHA256)
|
||||
set(IGC_SPIRV_TOOLS_FILE SPIR-V-Tools-${IGC_SPIRV_TOOLS_VERSION}.tar.gz)
|
||||
|
||||
set(IGC_SPIRV_TRANSLATOR_VERSION a31ffaeef77e23d500b3ea3d35e0c42ff5648ad9)
|
||||
set(IGC_SPIRV_TRANSLATOR_VERSION d739c01d65ec00dee64dedd40deed805216a7193)
|
||||
set(IGC_SPIRV_TRANSLATOR_URI https://github.com/KhronosGroup/SPIRV-LLVM-Translator/archive/${IGC_SPIRV_TRANSLATOR_VERSION}.tar.gz)
|
||||
set(IGC_SPIRV_TRANSLATOR_HASH 9e26c96a45341b8f8af521bacea20e752623346340addd02af95d669f6e89252)
|
||||
set(IGC_SPIRV_TRANSLATOR_HASH ddc0cc9ccbe59dadeaf291012d59de142b2e9f2b124dbb634644d39daddaa13e)
|
||||
set(IGC_SPIRV_TRANSLATOR_HASH_TYPE SHA256)
|
||||
set(IGC_SPIRV_TRANSLATOR_FILE SPIR-V-Translator-${IGC_SPIRV_TRANSLATOR_VERSION}.tar.gz)
|
||||
|
||||
|
@ -724,15 +724,15 @@ set(IGC_SPIRV_TRANSLATOR_FILE SPIR-V-Translator-${IGC_SPIRV_TRANSLATOR_VERSION}.
|
|||
### Intel Graphics Compiler DEPS END ###
|
||||
########################################
|
||||
|
||||
set(GMMLIB_VERSION intel-gmmlib-22.1.8)
|
||||
set(GMMLIB_VERSION intel-gmmlib-22.3.0)
|
||||
set(GMMLIB_URI https://github.com/intel/gmmlib/archive/refs/tags/${GMMLIB_VERSION}.tar.gz)
|
||||
set(GMMLIB_HASH bf23e9a3742b4fb98c7666c9e9b29f3219e4b2fb4d831aaf4eed71f5e2d17368)
|
||||
set(GMMLIB_HASH c1f33e1519edfc527127baeb0436b783430dfd256c643130169a3a71dc86aff9)
|
||||
set(GMMLIB_HASH_TYPE SHA256)
|
||||
set(GMMLIB_FILE ${GMMLIB_VERSION}.tar.gz)
|
||||
|
||||
set(OCLOC_VERSION 22.38.24278)
|
||||
set(OCLOC_VERSION 22.49.25018.21)
|
||||
set(OCLOC_URI https://github.com/intel/compute-runtime/archive/refs/tags/${OCLOC_VERSION}.tar.gz)
|
||||
set(OCLOC_HASH db0c542fccd651e6404b15a74d46027f1ce0eda8dc9e25a40cbb6c0faef257ee)
|
||||
set(OCLOC_HASH 92362dae08b503a34e5d3820ed284198c452bcd5e7504d90eb69887b20492c06)
|
||||
set(OCLOC_HASH_TYPE SHA256)
|
||||
set(OCLOC_FILE ocloc-${OCLOC_VERSION}.tar.gz)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
diff -Naur external_igc_opencl_clang.orig/CMakeLists.txt external_igc_opencl_clang/CMakeLists.txt
|
||||
--- external_igc_opencl_clang.orig/CMakeLists.txt 2022-03-16 05:51:10 -0600
|
||||
+++ external_igc_opencl_clang/CMakeLists.txt 2022-05-23 10:40:09 -0600
|
||||
@@ -126,22 +126,24 @@
|
||||
@@ -147,22 +147,24 @@
|
||||
)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -648,7 +648,7 @@ GPUDevice::Mem *GPUDevice::generic_alloc(device_memory &mem, size_t pitch_paddin
|
|||
}
|
||||
|
||||
if (mem_alloc_result) {
|
||||
assert(transform_host_pointer(&device_pointer, shared_pointer));
|
||||
assert(transform_host_pointer(device_pointer, shared_pointer));
|
||||
map_host_used += size;
|
||||
status = " in host memory";
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "scene/light.h"
|
||||
#include "scene/mesh.h"
|
||||
#include "scene/object.h"
|
||||
#include "scene/osl.h"
|
||||
#include "scene/pointcloud.h"
|
||||
#include "scene/scene.h"
|
||||
#include "scene/shader.h"
|
||||
|
@ -25,7 +26,6 @@
|
|||
|
||||
#ifdef WITH_OSL
|
||||
# include "kernel/osl/globals.h"
|
||||
# include "kernel/osl/services.h"
|
||||
#endif
|
||||
|
||||
#include "util/foreach.h"
|
||||
|
@ -1717,20 +1717,7 @@ void GeometryManager::device_update_displacement_images(Device *device,
|
|||
/* If any OSL node is used for displacement, it may reference a texture. But it's
|
||||
* unknown which ones, so have to load them all. */
|
||||
if (has_osl_node) {
|
||||
set<OSLRenderServices *> services_shared;
|
||||
device->foreach_device([&services_shared](Device *sub_device) {
|
||||
OSLGlobals *og = (OSLGlobals *)sub_device->get_cpu_osl_memory();
|
||||
services_shared.insert(og->services);
|
||||
});
|
||||
|
||||
for (OSLRenderServices *services : services_shared) {
|
||||
for (auto it = services->textures.begin(); it != services->textures.end(); ++it) {
|
||||
if (it->second->handle.get_manager() == image_manager) {
|
||||
const int slot = it->second->handle.svm_slot();
|
||||
bump_images.insert(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
OSLShaderManager::osl_image_slots(device, image_manager, bump_images);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -665,6 +665,27 @@ OSLNode *OSLShaderManager::osl_node(ShaderGraph *graph,
|
|||
return node;
|
||||
}
|
||||
|
||||
/* Static function, so only this file needs to be compile with RTTT. */
|
||||
void OSLShaderManager::osl_image_slots(Device *device,
|
||||
ImageManager *image_manager,
|
||||
set<int> &image_slots)
|
||||
{
|
||||
set<OSLRenderServices *> services_shared;
|
||||
device->foreach_device([&services_shared](Device *sub_device) {
|
||||
OSLGlobals *og = (OSLGlobals *)sub_device->get_cpu_osl_memory();
|
||||
services_shared.insert(og->services);
|
||||
});
|
||||
|
||||
for (OSLRenderServices *services : services_shared) {
|
||||
for (auto it = services->textures.begin(); it != services->textures.end(); ++it) {
|
||||
if (it->second->handle.get_manager() == image_manager) {
|
||||
const int slot = it->second->handle.svm_slot();
|
||||
image_slots.insert(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Graph Compiler */
|
||||
|
||||
OSLCompiler::OSLCompiler(OSLShaderManager *manager, OSL::ShadingSystem *ss, Scene *scene)
|
||||
|
|
|
@ -92,6 +92,9 @@ class OSLShaderManager : public ShaderManager {
|
|||
const std::string &bytecode_hash = "",
|
||||
const std::string &bytecode = "");
|
||||
|
||||
/* Get image slots used by OSL services on device. */
|
||||
static void osl_image_slots(Device *device, ImageManager *image_manager, set<int> &image_slots);
|
||||
|
||||
private:
|
||||
void texture_system_init();
|
||||
void texture_system_free();
|
||||
|
|
|
@ -602,7 +602,7 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
|
|||
/* NOTE: the `sdl_sub_evt.keysym.sym` is truncated,
|
||||
* for unicode support ghost has to be modified. */
|
||||
|
||||
/* TODO(@campbellbarton): support full unicode, SDL supports this but it needs to be
|
||||
/* TODO(@ideasman42): support full unicode, SDL supports this but it needs to be
|
||||
* explicitly enabled via #SDL_StartTextInput which GHOST would have to wrap. */
|
||||
char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
|
||||
if (type == GHOST_kEventKeyDown) {
|
||||
|
|
|
@ -241,7 +241,7 @@ enum {
|
|||
BTN_STYLUS = 0x14b,
|
||||
/** Use as right-mouse. */
|
||||
BTN_STYLUS2 = 0x14c,
|
||||
/** NOTE(@campbellbarton): Map to an additional button (not sure which hardware uses this). */
|
||||
/** NOTE(@ideasman42): Map to an additional button (not sure which hardware uses this). */
|
||||
BTN_STYLUS3 = 0x149,
|
||||
};
|
||||
|
||||
|
@ -916,7 +916,7 @@ struct GWL_Display {
|
|||
* The main purpose of having an active seat is an alternative from always using the first
|
||||
* seat which prevents events from any other seat.
|
||||
*
|
||||
* NOTE(@campbellbarton): This could be extended and developed further extended to support
|
||||
* NOTE(@ideasman42): This could be extended and developed further extended to support
|
||||
* an active seat per window (for e.g.), basic support is sufficient for now as currently isn't
|
||||
* a widely used feature.
|
||||
*/
|
||||
|
@ -957,7 +957,7 @@ struct GWL_Display {
|
|||
* Needed because #GHOST_System::dispatchEvents fires timers
|
||||
* outside of WAYLAND (without locking the `timer_mutex`).
|
||||
*/
|
||||
GHOST_TimerManager *ghost_timer_manager;
|
||||
GHOST_TimerManager *ghost_timer_manager = nullptr;
|
||||
|
||||
#endif /* USE_EVENT_BACKGROUND_THREAD */
|
||||
};
|
||||
|
@ -1014,8 +1014,10 @@ static void gwl_display_destroy(GWL_Display *display)
|
|||
}
|
||||
|
||||
/* Important to remove after the seats which may have key repeat timers active. */
|
||||
delete display->ghost_timer_manager;
|
||||
display->ghost_timer_manager = nullptr;
|
||||
if (display->ghost_timer_manager) {
|
||||
delete display->ghost_timer_manager;
|
||||
display->ghost_timer_manager = nullptr;
|
||||
}
|
||||
|
||||
#endif /* USE_EVENT_BACKGROUND_THREAD */
|
||||
|
||||
|
@ -1237,7 +1239,7 @@ static void gwl_registry_entry_remove_all(GWL_Display *display)
|
|||
{
|
||||
const bool on_exit = true;
|
||||
|
||||
/* NOTE(@campbellbarton): Free by slot instead of simply looping over
|
||||
/* NOTE(@ideasman42): Free by slot instead of simply looping over
|
||||
* `display->registry_entry` so the order of freeing is always predictable.
|
||||
* Otherwise global objects would be feed in the order they are registered.
|
||||
* While this works in my tests, it could cause difficult to reproduce bugs
|
||||
|
@ -1267,7 +1269,7 @@ static void gwl_registry_entry_remove_all(GWL_Display *display)
|
|||
* so there is no reason to update all other outputs that an output was removed (for e.g.).
|
||||
* Pass as -1 to update all slots.
|
||||
*
|
||||
* NOTE(@campbellbarton): Updating all other items on a single change is typically worth avoiding.
|
||||
* NOTE(@ideasman42): Updating all other items on a single change is typically worth avoiding.
|
||||
* In practice this isn't a problem as so there are so few elements in `display->registry_entry`,
|
||||
* so few use update functions and adding/removal at runtime is rarely called (plugging/unplugging)
|
||||
* hardware for e.g. So while it's possible to store dependency links to avoid unnecessary
|
||||
|
@ -1316,7 +1318,7 @@ static void ghost_wl_display_report_error(struct wl_display *display)
|
|||
fprintf(stderr, "The Wayland connection experienced a fatal error: %s\n", strerror(ecode));
|
||||
}
|
||||
|
||||
/* NOTE(@campbellbarton): The application is running,
|
||||
/* NOTE(@ideasman42): The application is running,
|
||||
* however an error closes all windows and most importantly:
|
||||
* shuts down the GPU context (loosing all GPU state - shaders, bind codes etc),
|
||||
* so recovering from this effectively involves restarting.
|
||||
|
@ -2968,7 +2970,7 @@ static void gesture_pinch_handle_begin(void *data,
|
|||
if (wl_surface *wl_surface_focus = seat->pointer.wl_surface_window) {
|
||||
win = ghost_wl_surface_user_data(wl_surface_focus);
|
||||
}
|
||||
/* NOTE(@campbellbarton): Blender's use of track-pad coordinates is inconsistent and needs work.
|
||||
/* NOTE(@ideasman42): Blender's use of track-pad coordinates is inconsistent and needs work.
|
||||
* This isn't specific to WAYLAND, in practice they tend to work well enough in most cases.
|
||||
* Some operators scale by the UI scale, some don't.
|
||||
* Even this window scale is not correct because it doesn't account for:
|
||||
|
@ -2982,7 +2984,7 @@ static void gesture_pinch_handle_begin(void *data,
|
|||
*/
|
||||
const wl_fixed_t win_scale = win ? win->scale() : 1;
|
||||
|
||||
/* NOTE(@campbellbarton): Scale factors match Blender's operators & default preferences.
|
||||
/* NOTE(@ideasman42): Scale factors match Blender's operators & default preferences.
|
||||
* For these values to work correctly, operator logic will need to be changed not to scale input
|
||||
* by the region size (as with 3D view zoom) or preference for 3D view orbit sensitivity.
|
||||
*
|
||||
|
@ -3145,7 +3147,7 @@ static const struct zwp_pointer_gesture_swipe_v1_listener gesture_swipe_listener
|
|||
/* -------------------------------------------------------------------- */
|
||||
/** \name Listener (Touch Seat), #wl_touch_listener
|
||||
*
|
||||
* NOTE(@campbellbarton): It's not clear if this interface is used by popular compositors.
|
||||
* NOTE(@ideasman42): It's not clear if this interface is used by popular compositors.
|
||||
* It looks like GNOME/KDE only support `zwp_pointer_gestures_v1_interface`.
|
||||
* If this isn't used anywhere, it could be removed.
|
||||
* \{ */
|
||||
|
@ -3806,7 +3808,7 @@ static xkb_keysym_t xkb_state_key_get_one_sym_without_modifiers(
|
|||
/* Use an empty keyboard state to access key symbol without modifiers. */
|
||||
xkb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state_empty, key);
|
||||
|
||||
/* NOTE(@campbellbarton): Only perform the number-locked lookup as a fallback
|
||||
/* NOTE(@ideasman42): Only perform the number-locked lookup as a fallback
|
||||
* when a number-pad key has been pressed. This is important as some key-maps use number lock
|
||||
* for switching other layers (in particular `de(neo_qwertz)` turns on layer-4), see: T96170.
|
||||
* Alternative solutions could be to inspect the layout however this could get involved
|
||||
|
@ -3936,7 +3938,7 @@ static void keyboard_handle_key(void *data,
|
|||
else {
|
||||
/* Key-up from keys that were not repeating cause the repeat timer to pause.
|
||||
*
|
||||
* NOTE(@campbellbarton): This behavior isn't universal, some text input systems will
|
||||
* NOTE(@ideasman42): This behavior isn't universal, some text input systems will
|
||||
* stop the repeat entirely. Choose to pause repeat instead as this is what GTK/WIN32 do,
|
||||
* and it fits better for keyboard input that isn't related to text entry. */
|
||||
timer_action = RESET;
|
||||
|
@ -7035,7 +7037,7 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
|||
UNPACK2(xy_next));
|
||||
wl_surface_commit(wl_surface);
|
||||
|
||||
/* NOTE(@campbellbarton): The new cursor position is a hint,
|
||||
/* NOTE(@ideasman42): The new cursor position is a hint,
|
||||
* it's possible the hint is ignored. It doesn't seem like there is a good way to
|
||||
* know if the hint will be used or not, at least not immediately. */
|
||||
xy_motion[0] = xy_next[0];
|
||||
|
@ -7078,7 +7080,7 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
|||
if (mode != GHOST_kGrabDisable) {
|
||||
if (grab_state_next.use_lock) {
|
||||
if (!grab_state_prev.use_lock) {
|
||||
/* TODO(@campbellbarton): As WAYLAND does not support warping the pointer it may not be
|
||||
/* TODO(@ideasman42): As WAYLAND does not support warping the pointer it may not be
|
||||
* possible to support #GHOST_kGrabWrap by pragmatically settings it's coordinates.
|
||||
* An alternative could be to draw the cursor in software (and hide the real cursor),
|
||||
* or just accept a locked cursor on WAYLAND. */
|
||||
|
|
|
@ -1080,7 +1080,7 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
|
|||
if (window->getCursorGrabMode() == GHOST_kGrabHide) {
|
||||
window->getClientBounds(bounds);
|
||||
|
||||
/* WARNING(@campbellbarton): The current warping logic fails to warp on every event,
|
||||
/* WARNING(@ideasman42): The current warping logic fails to warp on every event,
|
||||
* so the box needs to small enough not to let the cursor escape the window but large
|
||||
* enough that the cursor isn't being warped every time.
|
||||
* If this was not the case it would be less trouble to simply warp the cursor to the
|
||||
|
@ -1179,7 +1179,7 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA
|
|||
GHOST_TKey key = system->hardKey(raw, &key_down);
|
||||
GHOST_EventKey *event;
|
||||
|
||||
/* NOTE(@campbellbarton): key repeat in WIN32 also applies to modifier-keys.
|
||||
/* NOTE(@ideasman42): key repeat in WIN32 also applies to modifier-keys.
|
||||
* Check for this case and filter out modifier-repeat.
|
||||
* Typically keyboard events are *not* filtered as part of GHOST's event handling.
|
||||
* As other GHOST back-ends don't have the behavior, it's simplest not to send them through.
|
||||
|
|
|
@ -278,7 +278,7 @@ uint8_t GHOST_SystemX11::getNumDisplays() const
|
|||
void GHOST_SystemX11::getMainDisplayDimensions(uint32_t &width, uint32_t &height) const
|
||||
{
|
||||
if (m_display) {
|
||||
/* NOTE(@campbellbarton): for this to work as documented,
|
||||
/* NOTE(@ideasman42): for this to work as documented,
|
||||
* we would need to use Xinerama check r54370 for code that did this,
|
||||
* we've since removed since its not worth the extra dependency. */
|
||||
getAllDisplayDimensions(width, height);
|
||||
|
@ -927,7 +927,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
|
|||
if (window->getCursorGrabMode() == GHOST_kGrabHide) {
|
||||
window->getClientBounds(bounds);
|
||||
|
||||
/* TODO(@campbellbarton): warp the cursor to `window->getCursorGrabInitPos`,
|
||||
/* TODO(@ideasman42): warp the cursor to `window->getCursorGrabInitPos`,
|
||||
* on every motion event, see: D16557 (alternative fix for T102346). */
|
||||
const int32_t subregion_div = 4; /* One quarter of the region. */
|
||||
const int32_t size[2] = {bounds.getWidth(), bounds.getHeight()};
|
||||
|
|
|
@ -669,7 +669,7 @@ static void xdg_surface_handle_configure(void *data,
|
|||
GHOST_SystemWayland *system = win->ghost_system;
|
||||
const bool is_main_thread = system->main_thread_id == std::this_thread::get_id();
|
||||
if (!is_main_thread) {
|
||||
/* NOTE(@campbellbarton): this only gets one redraw,
|
||||
/* NOTE(@ideasman42): this only gets one redraw,
|
||||
* I could not find a case where this causes problems. */
|
||||
gwl_window_pending_actions_tag(win, PENDING_FRAME_CONFIGURE);
|
||||
}
|
||||
|
@ -774,7 +774,7 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
|
|||
window_->ghost_window = this;
|
||||
window_->ghost_system = system;
|
||||
|
||||
/* NOTE(@campbellbarton): The scale set here to avoid flickering on startup.
|
||||
/* NOTE(@ideasman42): The scale set here to avoid flickering on startup.
|
||||
* When all monitors use the same scale (which is quite common) there aren't any problems.
|
||||
*
|
||||
* When monitors have different scales there may still be a visible window resize on startup.
|
||||
|
@ -1078,7 +1078,7 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
|
|||
|
||||
wl_surface_destroy(window_->wl_surface);
|
||||
|
||||
/* NOTE(@campbellbarton): Flushing will often run the appropriate handlers event
|
||||
/* NOTE(@ideasman42): Flushing will often run the appropriate handlers event
|
||||
* (#wl_surface_listener.leave in particular) to avoid attempted access to the freed surfaces.
|
||||
* This is not fool-proof though, hence the call to #window_surface_unref, see: T99078. */
|
||||
wl_display_flush(system_->wl_display());
|
||||
|
|
|
@ -1002,11 +1002,11 @@ def unregister_tool(tool_cls):
|
|||
|
||||
# we start with the built-in default mapping
|
||||
def _blender_default_map():
|
||||
import rna_manual_reference as ref_mod
|
||||
ret = (ref_mod.url_manual_prefix, ref_mod.url_manual_mapping)
|
||||
# avoid storing in memory
|
||||
del _sys.modules["rna_manual_reference"]
|
||||
return ret
|
||||
# NOTE(@ideasman42): Avoid importing this as there is no need to keep the lookup table in memory.
|
||||
# As this runs when the user accesses the "Online Manual", the overhead loading the file is acceptable.
|
||||
# In my tests it's under 1/100th of a second loading from a `pyc`.
|
||||
ref_mod = execfile(_os.path.join(_script_base_dir, "modules", "rna_manual_reference.py"))
|
||||
return (ref_mod.url_manual_prefix, ref_mod.url_manual_mapping)
|
||||
|
||||
|
||||
# hooks for doc lookups
|
||||
|
|
|
@ -140,7 +140,7 @@ class DATA_PT_EEVEE_light_distance(DataButtonsPanel, Panel):
|
|||
class DATA_PT_EEVEE_shadow(DataButtonsPanel, Panel):
|
||||
bl_label = "Shadow"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
@ -168,7 +168,8 @@ class DATA_PT_EEVEE_shadow(DataButtonsPanel, Panel):
|
|||
if light.type != 'SUN':
|
||||
sub.prop(light, "shadow_buffer_clip_start", text="Clip Start")
|
||||
|
||||
col.prop(light, "shadow_buffer_bias", text="Bias")
|
||||
if context.engine != 'BLENDER_EEVEE_NEXT':
|
||||
col.prop(light, "shadow_buffer_bias", text="Bias")
|
||||
|
||||
|
||||
class DATA_PT_EEVEE_shadow_cascaded_shadow_map(DataButtonsPanel, Panel):
|
||||
|
|
|
@ -460,6 +460,32 @@ class RENDER_PT_eevee_shadows(RenderButtonsPanel, Panel):
|
|||
col.prop(props, "light_threshold")
|
||||
|
||||
|
||||
class RENDER_PT_eevee_next_shadows(RenderButtonsPanel, Panel):
|
||||
bl_label = "Shadows"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.engine in cls.COMPAT_ENGINES)
|
||||
|
||||
def draw_header(self, context):
|
||||
scene = context.scene
|
||||
props = scene.eevee
|
||||
self.layout.prop(props, "use_shadows", text="")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
scene = context.scene
|
||||
props = scene.eevee
|
||||
|
||||
col = layout.column()
|
||||
col.prop(props, "shadow_pool_size", text="Pool Size")
|
||||
col.prop(props, "light_threshold")
|
||||
|
||||
|
||||
class RENDER_PT_eevee_sampling(RenderButtonsPanel, Panel):
|
||||
bl_label = "Sampling"
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
||||
|
@ -808,6 +834,10 @@ class RENDER_PT_simplify_viewport(RenderButtonsPanel, Panel):
|
|||
col = flow.column()
|
||||
col.prop(rd, "simplify_volumes", text="Volume Resolution")
|
||||
|
||||
if context.engine in 'BLENDER_EEVEE_NEXT':
|
||||
col = flow.column()
|
||||
col.prop(rd, "simplify_shadows", text="Shadow Resolution")
|
||||
|
||||
|
||||
class RENDER_PT_simplify_render(RenderButtonsPanel, Panel):
|
||||
bl_label = "Render"
|
||||
|
@ -835,6 +865,10 @@ class RENDER_PT_simplify_render(RenderButtonsPanel, Panel):
|
|||
col = flow.column()
|
||||
col.prop(rd, "simplify_child_particles_render", text="Max Child Particles")
|
||||
|
||||
if context.engine in 'BLENDER_EEVEE_NEXT':
|
||||
col = flow.column()
|
||||
col.prop(rd, "simplify_shadows_render", text="Shadow Resolution")
|
||||
|
||||
|
||||
class RENDER_PT_simplify_greasepencil(RenderButtonsPanel, Panel, GreasePencilSimplifyPanel):
|
||||
bl_label = "Grease Pencil"
|
||||
|
@ -869,6 +903,7 @@ classes = (
|
|||
RENDER_PT_eevee_performance,
|
||||
RENDER_PT_eevee_hair,
|
||||
RENDER_PT_eevee_shadows,
|
||||
RENDER_PT_eevee_next_shadows,
|
||||
RENDER_PT_eevee_indirect_lighting,
|
||||
RENDER_PT_eevee_indirect_lighting_display,
|
||||
RENDER_PT_eevee_film,
|
||||
|
|
|
@ -40,7 +40,7 @@ typedef int32_t ft_pix;
|
|||
/* Macros copied from `include/freetype/internal/ftobjs.h`. */
|
||||
|
||||
/**
|
||||
* FIXME(@campbellbarton): Follow rounding from Blender 3.1x and older.
|
||||
* FIXME(@ideasman42): Follow rounding from Blender 3.1x and older.
|
||||
* This is what users will expect and changing this creates wider spaced text.
|
||||
* Use this macro to communicate that rounding should be used, using floor is to avoid
|
||||
* user visible changes, which can be reviewed and handled separately.
|
||||
|
|
|
@ -2,17 +2,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_curves.h"
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
* \brief Low-level operations for curves.
|
||||
*/
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "BLI_bounds_types.hh"
|
||||
#include "BLI_cache_mutex.hh"
|
||||
#include "BLI_generic_virtual_array.hh"
|
||||
#include "BLI_index_mask.hh"
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
|
@ -20,12 +15,12 @@
|
|||
#include "BLI_offset_indices.hh"
|
||||
#include "BLI_shared_cache.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_curves.h"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
|
|
|
@ -425,7 +425,7 @@ int set_listbasepointers(struct Main *main, struct ListBase *lb[]);
|
|||
/**
|
||||
* The size of thumbnails (optionally) stored in the `.blend` files header.
|
||||
*
|
||||
* NOTE(@campbellbarton): This is kept small as it's stored uncompressed in the `.blend` file,
|
||||
* NOTE(@ideasman42): This is kept small as it's stored uncompressed in the `.blend` file,
|
||||
* where a larger size would increase the size of every `.blend` file unreasonably.
|
||||
* If we wanted to increase the size, we'd want to use compression (JPEG or similar).
|
||||
*/
|
||||
|
|
|
@ -1467,7 +1467,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
|
|||
|
||||
/* Add orco coordinates to final and deformed mesh if requested. */
|
||||
if (final_datamask.vmask & CD_MASK_ORCO) {
|
||||
/* FIXME(@campbellbarton): avoid the need to convert to mesh data just to add an orco layer. */
|
||||
/* FIXME(@ideasman42): avoid the need to convert to mesh data just to add an orco layer. */
|
||||
BKE_mesh_wrapper_ensure_mdata(mesh_final);
|
||||
|
||||
add_orco_mesh(ob, em_input, mesh_final, mesh_orco, CD_ORCO);
|
||||
|
|
|
@ -884,7 +884,7 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name)
|
|||
void BKE_appdir_program_path_init(const char *argv0)
|
||||
{
|
||||
#ifdef WITH_PYTHON_MODULE
|
||||
/* NOTE(@campbellbarton): Always use `argv[0]` as is, when building as a Python module.
|
||||
/* NOTE(@ideasman42): Always use `argv[0]` as is, when building as a Python module.
|
||||
* Otherwise other methods of detecting the binary that override this argument
|
||||
* which must point to the Python module for data-files to be detected. */
|
||||
STRNCPY(g_app.program_filepath, argv0);
|
||||
|
|
|
@ -144,7 +144,7 @@ static float color_sample_remove_cost(const struct ColorResampleElem *c)
|
|||
return area;
|
||||
}
|
||||
|
||||
/* TODO(@campbellbarton): create `BLI_math_filter` ? */
|
||||
/* TODO(@ideasman42): create `BLI_math_filter` ? */
|
||||
static float filter_gauss(float x)
|
||||
{
|
||||
const float gaussfac = 1.6f;
|
||||
|
|
|
@ -1558,7 +1558,7 @@ static void followpath_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *
|
|||
|
||||
/* un-apply scaling caused by path */
|
||||
if ((data->followflag & FOLLOWPATH_RADIUS) == 0) {
|
||||
/* XXX(@campbellbarton): Assume that scale correction means that radius
|
||||
/* XXX(@ideasman42): Assume that scale correction means that radius
|
||||
* will have some scale error in it. */
|
||||
float obsize[3];
|
||||
|
||||
|
@ -3835,7 +3835,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
|
|||
|
||||
unit_m4(targetMatrix);
|
||||
INIT_MINMAX(curveMin, curveMax);
|
||||
/* XXX(@campbellbarton): don't think this is good calling this here because
|
||||
/* XXX(@ideasman42): don't think this is good calling this here because
|
||||
* the other object's data is lazily initializing bounding-box information.
|
||||
* This could cause issues when evaluating from a thread.
|
||||
* If the depsgraph ensures the bound-box is always available, a code-path could
|
||||
|
|
|
@ -2251,7 +2251,7 @@ static void make_bevel_list_3D_minimum_twist(BevList *bl)
|
|||
int nr;
|
||||
float q[4];
|
||||
const bool is_cyclic = bl->poly != -1;
|
||||
/* NOTE(@campbellbarton): For non-cyclic curves only initialize the first direction
|
||||
/* NOTE(@ideasman42): For non-cyclic curves only initialize the first direction
|
||||
* (via `vec_to_quat`), necessary for symmetry, see T71137.
|
||||
* Otherwise initialize the first and second points before propagating rotation forward.
|
||||
* This is historical as changing this can cause significantly different output.
|
||||
|
@ -2480,7 +2480,7 @@ static void make_bevel_list_segment_2D(BevList *bl)
|
|||
|
||||
static void make_bevel_list_2D(BevList *bl)
|
||||
{
|
||||
/* NOTE(@campbellbarton): `bevp->dir` and `bevp->quat` are not needed for beveling but are
|
||||
/* NOTE(@ideasman42): `bevp->dir` and `bevp->quat` are not needed for beveling but are
|
||||
* used when making a path from a 2D curve, therefore they need to be set. */
|
||||
|
||||
BevPoint *bevp0, *bevp1, *bevp2;
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_curves.hh"
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_curves.hh"
|
||||
|
||||
|
|
|
@ -1122,7 +1122,7 @@ void BKE_effectors_apply(ListBase *effectors,
|
|||
float *wind_force,
|
||||
float *impulse)
|
||||
{
|
||||
/* WARNING(@campbellbarton): historic comment?
|
||||
/* WARNING(@ideasman42): historic comment?
|
||||
* Many of these parameters don't exist!
|
||||
*
|
||||
* scene = scene where it runs in, for time and stuff.
|
||||
|
|
|
@ -74,7 +74,7 @@ ImBuf *BKE_icon_geom_rasterize(const struct Icon_Geom *geom, const uint size_x,
|
|||
const uchar(*pos)[2] = geom->coords;
|
||||
const uint *col = (void *)geom->colors;
|
||||
|
||||
/* TODO(@campbellbarton): Currently rasterizes to fixed size, then scales.
|
||||
/* TODO(@ideasman42): Currently rasterizes to fixed size, then scales.
|
||||
* Should rasterize to double size for eg instead. */
|
||||
const int rect_size[2] = {max_ii(256, (int)size_x * 2), max_ii(256, (int)size_y * 2)};
|
||||
|
||||
|
|
|
@ -797,7 +797,7 @@ IDProperty *IDP_GetProperties(ID *id, const bool create_if_needed)
|
|||
if (create_if_needed) {
|
||||
id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
|
||||
id->properties->type = IDP_GROUP;
|
||||
/* NOTE(@campbellbarton): Don't overwrite the data's name and type
|
||||
/* NOTE(@ideasman42): Don't overwrite the data's name and type
|
||||
* some functions might need this if they
|
||||
* don't have a real ID, should be named elsewhere. */
|
||||
// strcpy(id->name, "top_level_group");
|
||||
|
|
|
@ -121,7 +121,7 @@ void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath)
|
|||
/* Not essential but set `filepath_abs` is an absolute copy of value which
|
||||
* is more useful if its kept in sync. */
|
||||
if (BLI_path_is_rel(lib->filepath_abs)) {
|
||||
/* NOTE(@campbellbarton): the file may be unsaved, in this case, setting the
|
||||
/* NOTE(@ideasman42): the file may be unsaved, in this case, setting the
|
||||
* `filepath_abs` on an indirectly linked path is not allowed from the
|
||||
* outliner, and its not really supported but allow from here for now
|
||||
* since making local could cause this to be directly linked.
|
||||
|
|
|
@ -850,7 +850,7 @@ void BKE_object_material_resize(Main *bmain, Object *ob, const short totcol, boo
|
|||
ob->mat = newmatar;
|
||||
ob->matbits = newmatbits;
|
||||
}
|
||||
/* XXX(@campbellbarton): why not realloc on shrink? */
|
||||
/* XXX(@ideasman42): why not realloc on shrink? */
|
||||
|
||||
ob->totcol = totcol;
|
||||
if (ob->totcol && ob->actcol == 0) {
|
||||
|
|
|
@ -277,7 +277,7 @@ bool BKE_mball_is_basis(const Object *ob)
|
|||
/* Meta-Ball Basis Notes from Blender-2.5x
|
||||
* =======================================
|
||||
*
|
||||
* NOTE(@campbellbarton): This is a can of worms.
|
||||
* NOTE(@ideasman42): This is a can of worms.
|
||||
*
|
||||
* This really needs a rewrite/refactor its totally broken in anything other than basic cases
|
||||
* Multiple Scenes + Set Scenes & mixing meta-ball basis _should_ work but fails to update the
|
||||
|
@ -485,7 +485,7 @@ bool BKE_mball_minmax_ex(
|
|||
copy_v3_v3(centroid, &ml->x);
|
||||
}
|
||||
|
||||
/* TODO(@campbellbarton): non circle shapes cubes etc, probably nobody notices. */
|
||||
/* TODO(@ideasman42): non circle shapes cubes etc, probably nobody notices. */
|
||||
for (int i = -1; i != 3; i += 2) {
|
||||
copy_v3_v3(vec, centroid);
|
||||
add_v3_fl(vec, scale_mb * i);
|
||||
|
|
|
@ -34,7 +34,7 @@ static int compare_v2_classify(const float uv_a[2], const float uv_b[2])
|
|||
if (uv_a[0] == uv_b[0] && uv_a[1] == uv_b[1]) {
|
||||
return CMP_EQUAL;
|
||||
}
|
||||
/* NOTE(@campbellbarton): that the ULP value is the primary value used to compare relative
|
||||
/* NOTE(@ideasman42): that the ULP value is the primary value used to compare relative
|
||||
* values as the absolute value doesn't account for float precision at difference scales.
|
||||
* - For subdivision-surface ULP of 3 is sufficient,
|
||||
* although this value is extremely small.
|
||||
|
|
|
@ -573,7 +573,7 @@ void BKE_mesh_calc_loop_tangents(Mesh *me_eval,
|
|||
const char (*tangent_names)[MAX_CUSTOMDATA_LAYER_NAME],
|
||||
int tangent_names_len)
|
||||
{
|
||||
/* TODO(@campbellbarton): store in Mesh.runtime to avoid recalculation. */
|
||||
/* TODO(@ideasman42): store in Mesh.runtime to avoid recalculation. */
|
||||
short tangent_mask = 0;
|
||||
BKE_mesh_calc_loop_tangent_ex(
|
||||
BKE_mesh_vert_positions(me_eval),
|
||||
|
|
|
@ -2437,7 +2437,7 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys, const int f
|
|||
psysn->pointcache = BKE_ptcache_copy_list(&psysn->ptcaches, &psys->ptcaches, flag);
|
||||
}
|
||||
|
||||
/* XXX(@campbellbarton): from reading existing code this seems correct but intended usage of
|
||||
/* XXX(@ideasman42): from reading existing code this seems correct but intended usage of
|
||||
* point-cache should with cloth should be added in 'ParticleSystem'. */
|
||||
if (psysn->clmd) {
|
||||
psysn->clmd->point_cache = psysn->pointcache;
|
||||
|
|
|
@ -1382,7 +1382,7 @@ void BKE_ocean_bake(struct Ocean *o,
|
|||
void (*update_cb)(void *, float progress, int *cancel),
|
||||
void *update_cb_data)
|
||||
{
|
||||
/* NOTE(@campbellbarton): some of these values remain uninitialized unless certain options
|
||||
/* NOTE(@ideasman42): some of these values remain uninitialized unless certain options
|
||||
* are enabled, take care that #BKE_ocean_eval_ij() initializes a member before use. */
|
||||
OceanResult ocr;
|
||||
|
||||
|
@ -1437,7 +1437,7 @@ void BKE_ocean_bake(struct Ocean *o,
|
|||
rgb_to_rgba_unit_alpha(&ibuf_disp->rect_float[4 * (res_x * y + x)], ocr.disp);
|
||||
|
||||
if (o->_do_jacobian) {
|
||||
/* TODO(@campbellbarton): cleanup unused code. */
|
||||
/* TODO(@ideasman42): cleanup unused code. */
|
||||
|
||||
float /* r, */ /* UNUSED */ pr = 0.0f, foam_result;
|
||||
float neg_disp, neg_eplus;
|
||||
|
|
|
@ -5470,7 +5470,7 @@ void BKE_particle_system_blend_read_lib(BlendLibReader *reader,
|
|||
BLO_read_id_address(reader, id->lib, &psys->target_ob);
|
||||
|
||||
if (psys->clmd) {
|
||||
/* XXX(@campbellbarton): from reading existing code this seems correct but intended usage
|
||||
/* XXX(@ideasman42): from reading existing code this seems correct but intended usage
|
||||
* of point-cache with cloth should be added in #ParticleSystem. */
|
||||
psys->clmd->point_cache = psys->pointcache;
|
||||
psys->clmd->ptcaches.first = psys->clmd->ptcaches.last = nullptr;
|
||||
|
|
|
@ -967,7 +967,7 @@ void psys_get_birth_coords(
|
|||
float tmat[3][3];
|
||||
|
||||
/* NOTE: utan_local is not taken from 'utan', we calculate from rot_vec/vtan. */
|
||||
/* NOTE(@campbellbarton): it looks like rotation phase may be applied twice
|
||||
/* NOTE(@ideasman42): it looks like rotation phase may be applied twice
|
||||
* (once with vtan, again below) however this isn't the case. */
|
||||
float *rot_vec_local = tmat[0];
|
||||
float *vtan_local = tmat[1];
|
||||
|
|
|
@ -2588,7 +2588,7 @@ static bool check_rendered_viewport_visible(Main *bmain)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* TODO(@campbellbarton): shouldn't we be able to use 'DEG_get_view_layer' here?
|
||||
/* TODO(@ideasman42): shouldn't we be able to use 'DEG_get_view_layer' here?
|
||||
* Currently this is nullptr on load, so don't. */
|
||||
static void prepare_mesh_for_viewport_render(Main *bmain,
|
||||
const Scene *scene,
|
||||
|
|
|
@ -893,7 +893,7 @@ static void ccgDM_copyFinalVertArray(DerivedMesh *dm, float (*r_positions)[3])
|
|||
int x;
|
||||
|
||||
for (x = 1; x < edgeSize - 1; x++) {
|
||||
/* NOTE(@campbellbarton): This gives errors with `--debug-fpe` the normals don't seem to be
|
||||
/* NOTE(@ideasman42): This gives errors with `--debug-fpe` the normals don't seem to be
|
||||
* unit length. This is most likely caused by edges with no faces which are now zeroed out,
|
||||
* see comment in: `ccgSubSurf__calcVertNormals()`. */
|
||||
vd = static_cast<CCGElem *>(ccgSubSurf_getEdgeData(ss, e, x));
|
||||
|
|
|
@ -757,7 +757,7 @@ template<typename T> detail::Quaternion<T> normalized_to_quat_fast(const MatBase
|
|||
}
|
||||
}
|
||||
else {
|
||||
/* NOTE(@campbellbarton): A zero matrix will fall through to this block,
|
||||
/* NOTE(@ideasman42): A zero matrix will fall through to this block,
|
||||
* needed so a zero scaled matrices to return a quaternion without rotation, see: T101848.
|
||||
*/
|
||||
const T trace = 1.0f + mat[0][0] + mat[1][1] + mat[2][2];
|
||||
|
|
|
@ -910,6 +910,27 @@ struct MutableMatView
|
|||
unroll<NumCol>([&](auto i) { (*this)[i] *= b; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Vector operators. Need to be redefined to avoid operator priority issue. */
|
||||
|
||||
friend col_type operator*(MutableMatView &a, const row_type &b)
|
||||
{
|
||||
/* This is the reference implementation.
|
||||
* Might be overloaded with vectorized / optimized code. */
|
||||
col_type result(0);
|
||||
unroll<NumCol>([&](auto c) { result += b[c] * a[c]; });
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Multiply by the transposed. */
|
||||
friend row_type operator*(const col_type &a, MutableMatView &b)
|
||||
{
|
||||
/* This is the reference implementation.
|
||||
* Might be overloaded with vectorized / optimized code. */
|
||||
row_type result(0);
|
||||
unroll<NumCol>([&](auto c) { unroll<NumRow>([&](auto r) { result[c] += b[c][r] * a[r]; }); });
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
using float2x2 = MatBase<float, 2, 2>;
|
||||
|
|
|
@ -252,6 +252,16 @@ template<typename T, int Size>
|
|||
return result;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
[[nodiscard]] inline VecBase<T, Size> round(const VecBase<T, Size> &a)
|
||||
{
|
||||
VecBase<T, Size> result;
|
||||
for (int i = 0; i < Size; i++) {
|
||||
result[i] = std::round(a[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, int Size>
|
||||
[[nodiscard]] inline VecBase<T, Size> ceil(const VecBase<T, Size> &a)
|
||||
{
|
||||
|
|
|
@ -82,7 +82,7 @@ struct ScanFillEdge *BLI_scanfill_edge_add(ScanFillContext *sf_ctx,
|
|||
struct ScanFillVert *v2);
|
||||
|
||||
enum {
|
||||
/* NOTE(@campbellbarton): using #BLI_SCANFILL_CALC_REMOVE_DOUBLES
|
||||
/* NOTE(@ideasman42): using #BLI_SCANFILL_CALC_REMOVE_DOUBLES
|
||||
* Assumes ordered edges, otherwise we risk an eternal loop
|
||||
* removing double verts. */
|
||||
BLI_SCANFILL_CALC_REMOVE_DOUBLES = (1 << 1),
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
*/
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* NOTE(@campbellbarton): CLANG behaves slightly differently to GCC,
|
||||
/* NOTE(@ideasman42): CLANG behaves slightly differently to GCC,
|
||||
* these can be enabled but do so carefully as they can introduce build-errors. */
|
||||
# if !defined(__clang__)
|
||||
# pragma GCC diagnostic error "-Wsign-compare"
|
||||
|
|
|
@ -1667,7 +1667,7 @@ bool isect_ray_tri_v3(const float ray_origin[3],
|
|||
float *r_lambda,
|
||||
float r_uv[2])
|
||||
{
|
||||
/* NOTE(@campbellbarton): these values were 0.000001 in 2.4x but for projection snapping on
|
||||
/* NOTE(@ideasman42): these values were 0.000001 in 2.4x but for projection snapping on
|
||||
* a human head `(1BU == 1m)`, subdivision-surface level 2, this gave many errors. */
|
||||
const float epsilon = 0.00000001f;
|
||||
float p[3], s[3], e1[3], e2[3], q[3];
|
||||
|
@ -3773,7 +3773,7 @@ void barycentric_weights_v2_quad(const float v1[2],
|
|||
const float co[2],
|
||||
float w[4])
|
||||
{
|
||||
/* NOTE(@campbellbarton): fabsf() here is not needed for convex quads
|
||||
/* NOTE(@ideasman42): fabsf() here is not needed for convex quads
|
||||
* (and not used in #interp_weights_poly_v2).
|
||||
* But in the case of concave/bow-tie quads for the mask rasterizer it
|
||||
* gives unreliable results without adding `absf()`. If this becomes an issue for more general
|
||||
|
|
|
@ -334,7 +334,7 @@ void mat3_normalized_to_quat_fast(float q[4], const float mat[3][3])
|
|||
}
|
||||
}
|
||||
else {
|
||||
/* NOTE(@campbellbarton): A zero matrix will fall through to this block,
|
||||
/* NOTE(@ideasman42): A zero matrix will fall through to this block,
|
||||
* needed so a zero scaled matrices to return a quaternion without rotation, see: T101848. */
|
||||
const float trace = 1.0f + mat[0][0] + mat[1][1] + mat[2][2];
|
||||
float s = 2.0f * sqrtf(trace);
|
||||
|
|
|
@ -329,7 +329,7 @@ void **BLI_smallhash_iternew_p(const SmallHash *sh, SmallHashIter *iter, uintptr
|
|||
/** \name Debugging & Introspection
|
||||
* \{ */
|
||||
|
||||
/* NOTE(@campbellbarton): useful for debugging but may not be intended for general use. */
|
||||
/* NOTE(@ideasman42): useful for debugging but may not be intended for general use. */
|
||||
#if 0
|
||||
void BLI_smallhash_print(SmallHash *sh)
|
||||
{
|
||||
|
|
|
@ -402,7 +402,7 @@ bool BLI_str_quoted_substr_range(const char *__restrict str,
|
|||
return true;
|
||||
}
|
||||
|
||||
/* NOTE(@campbellbarton): in principal it should be possible to access a quoted string
|
||||
/* NOTE(@ideasman42): in principal it should be possible to access a quoted string
|
||||
* with an arbitrary size, currently all callers for this functionality
|
||||
* happened to use a fixed size buffer, so only #BLI_str_quoted_substr is needed. */
|
||||
#if 0
|
||||
|
|
|
@ -45,7 +45,7 @@ static const size_t utf8_skip_data[256] = {
|
|||
|
||||
ptrdiff_t BLI_str_utf8_invalid_byte(const char *str, size_t length)
|
||||
{
|
||||
/* NOTE(@campbellbarton): from libswish3, originally called u8_isvalid(),
|
||||
/* NOTE(@ideasman42): from libswish3, originally called u8_isvalid(),
|
||||
* modified to return the index of the bad character (byte index not UTF).
|
||||
* http://svn.swish-e.org/libswish3/trunk/src/libswish3/utf8.c r3044.
|
||||
*
|
||||
|
@ -403,7 +403,7 @@ int BLI_str_utf8_char_width_safe(const char *p)
|
|||
|
||||
/* copied from glib's gutf8.c, added 'Err' arg */
|
||||
|
||||
/* NOTE(@campbellbarton): glib uses uint for unicode, best we do the same,
|
||||
/* NOTE(@ideasman42): glib uses uint for unicode, best we do the same,
|
||||
* though we don't typedef it. */
|
||||
|
||||
#define UTF8_COMPUTE(Char, Mask, Len, Err) \
|
||||
|
|
|
@ -388,6 +388,21 @@ TEST(math_matrix_types, ViewMatrixMultiplyOperator)
|
|||
EXPECT_EQ(view[1][1], 46);
|
||||
}
|
||||
|
||||
TEST(math_matrix_types, ViewVectorMultiplyOperator)
|
||||
{
|
||||
float4x4 mat = float4x4({1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16});
|
||||
auto view = mat.view<2, 3, 1, 1>();
|
||||
|
||||
float3 result = view * float2(4, 5);
|
||||
EXPECT_EQ(result[0], 74);
|
||||
EXPECT_EQ(result[1], 83);
|
||||
EXPECT_EQ(result[2], 92);
|
||||
|
||||
float2 result2 = float3(1, 2, 3) * view;
|
||||
EXPECT_EQ(result2[0], 44);
|
||||
EXPECT_EQ(result2[1], 68);
|
||||
}
|
||||
|
||||
TEST(math_matrix_types, ViewMatrixNormalize)
|
||||
{
|
||||
float4x4 mat = float4x4({1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16});
|
||||
|
|
|
@ -288,7 +288,7 @@ void BLO_expand_id(BlendExpander *expander, struct ID *id);
|
|||
* This function ensures that reports are printed,
|
||||
* in the case of library linking errors this is important!
|
||||
*
|
||||
* NOTE(@campbellbarton) a kludge but better than doubling up on prints,
|
||||
* NOTE(@ideasman42) a kludge but better than doubling up on prints,
|
||||
* we could alternatively have a versions of a report function which forces printing.
|
||||
*/
|
||||
void BLO_reportf_wrap(struct BlendFileReadReport *reports,
|
||||
|
|
|
@ -3911,6 +3911,15 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
* \note Keep this message at the bottom of the function.
|
||||
*/
|
||||
{
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "int", "shadow_pool_size")) {
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
scene->eevee.flag |= SCE_EEVEE_SHADOW_ENABLED;
|
||||
scene->eevee.shadow_pool_size = 512;
|
||||
scene->r.simplify_shadows = 1.0f;
|
||||
scene->r.simplify_shadows_render = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep this block, even when empty. */
|
||||
|
||||
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
|
||||
|
|
|
@ -859,7 +859,7 @@ static void bm_to_mesh_shape(BMesh *bm,
|
|||
((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) &&
|
||||
(keyi < currkey->totelem)) {
|
||||
/* Reconstruct keys via vertices original key indices.
|
||||
* WARNING(@campbellbarton): `currkey->data` is known to be unreliable as the edit-mesh
|
||||
* WARNING(@ideasman42): `currkey->data` is known to be unreliable as the edit-mesh
|
||||
* coordinates may be flushed back to the shape-key when exporting or rendering.
|
||||
* This is a last resort! If this branch is running as part of regular usage
|
||||
* it can be considered a bug. */
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "intern/bmesh_operators_private.h" /* own include */
|
||||
|
||||
/**
|
||||
* TODO(@campbellbarton): Many connected edge loops can cause an error attempting
|
||||
* TODO(@ideasman42): Many connected edge loops can cause an error attempting
|
||||
* to create faces with duplicate vertices. While this needs to be investigated,
|
||||
* it's simple enough to check for this case, see: T102232.
|
||||
*/
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
void bmo_contextual_create_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
/* NOTE(@campbellbarton): doing the best thing here isn't always easy create vs dissolve,
|
||||
/* NOTE(@ideasman42): doing the best thing here isn't always easy create vs dissolve,
|
||||
* its nice to support but it _really_ gives issues we might have to not call dissolve. */
|
||||
|
||||
BMOIter oiter;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
namespace blender::compositor {
|
||||
|
||||
#define USE_FORCE_BILINEAR
|
||||
/* XXX(@campbellbarton): ignore input and use default from old compositor,
|
||||
/* XXX(@ideasman42): ignore input and use default from old compositor,
|
||||
* could become an option like the transform node.
|
||||
*
|
||||
* NOTE: use bilinear because bicubic makes fuzzy even when not scaling at all (1:1)
|
||||
|
|
|
@ -151,6 +151,7 @@ set(SRC
|
|||
engines/eevee_next/eevee_renderbuffers.cc
|
||||
engines/eevee_next/eevee_sampling.cc
|
||||
engines/eevee_next/eevee_shader.cc
|
||||
engines/eevee_next/eevee_shadow.cc
|
||||
engines/eevee_next/eevee_sync.cc
|
||||
engines/eevee_next/eevee_velocity.cc
|
||||
engines/eevee_next/eevee_view.cc
|
||||
|
@ -281,6 +282,7 @@ set(SRC
|
|||
engines/eevee_next/eevee_renderbuffers.hh
|
||||
engines/eevee_next/eevee_sampling.hh
|
||||
engines/eevee_next/eevee_shader.hh
|
||||
engines/eevee_next/eevee_shadow.hh
|
||||
engines/eevee_next/eevee_sync.hh
|
||||
engines/eevee_next/eevee_velocity.hh
|
||||
engines/eevee_next/eevee_view.hh
|
||||
|
@ -422,6 +424,7 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_camera_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_colorspace_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_transparency_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl
|
||||
|
@ -462,10 +465,29 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_motion_blur_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_nodetree_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_sampling_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_page_allocate_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_page_clear_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_page_defrag_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_page_free_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_page_mask_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_page_ops_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_tag_update_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_tag_usage_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_tag_usage_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_tag_usage_vert.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_test.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_tilemap_bounds_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_tilemap_init_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_surf_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_surf_world_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_velocity_lib.glsl
|
||||
|
||||
|
@ -798,6 +820,7 @@ if(WITH_GTESTS)
|
|||
set(TEST_SRC
|
||||
tests/draw_pass_test.cc
|
||||
tests/draw_testing.cc
|
||||
tests/eevee_test.cc
|
||||
tests/shaders_test.cc
|
||||
|
||||
tests/draw_testing.hh
|
||||
|
|
|
@ -135,8 +135,9 @@ void Camera::sync()
|
|||
#endif
|
||||
}
|
||||
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);
|
||||
/* \note: Follow camera parameters where distances are positive in front of the camera. */
|
||||
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);
|
||||
|
@ -144,6 +145,57 @@ void Camera::sync()
|
|||
|
||||
data_.initialized = true;
|
||||
data_.push_update();
|
||||
|
||||
update_bounds();
|
||||
}
|
||||
|
||||
void Camera::update_bounds()
|
||||
{
|
||||
float left, right, bottom, top, near, far;
|
||||
projmat_dimensions(data_.winmat.ptr(), &left, &right, &bottom, &top, &near, &far);
|
||||
|
||||
BoundBox bbox;
|
||||
bbox.vec[0][2] = bbox.vec[3][2] = bbox.vec[7][2] = bbox.vec[4][2] = -near;
|
||||
bbox.vec[0][0] = bbox.vec[3][0] = left;
|
||||
bbox.vec[4][0] = bbox.vec[7][0] = right;
|
||||
bbox.vec[0][1] = bbox.vec[4][1] = bottom;
|
||||
bbox.vec[7][1] = bbox.vec[3][1] = top;
|
||||
|
||||
/* Get the coordinates of the far plane. */
|
||||
if (!this->is_orthographic()) {
|
||||
float sca_far = far / near;
|
||||
left *= sca_far;
|
||||
right *= sca_far;
|
||||
bottom *= sca_far;
|
||||
top *= sca_far;
|
||||
}
|
||||
|
||||
bbox.vec[1][2] = bbox.vec[2][2] = bbox.vec[6][2] = bbox.vec[5][2] = -far;
|
||||
bbox.vec[1][0] = bbox.vec[2][0] = left;
|
||||
bbox.vec[6][0] = bbox.vec[5][0] = right;
|
||||
bbox.vec[1][1] = bbox.vec[5][1] = bottom;
|
||||
bbox.vec[2][1] = bbox.vec[6][1] = top;
|
||||
|
||||
bound_sphere.center = {0.0f, 0.0f, 0.0f};
|
||||
bound_sphere.radius = 0.0f;
|
||||
|
||||
for (auto i : IndexRange(8)) {
|
||||
bound_sphere.center += float3(bbox.vec[i]);
|
||||
}
|
||||
bound_sphere.center /= 8.0f;
|
||||
for (auto i : IndexRange(8)) {
|
||||
float dist_sqr = math::distance_squared(bound_sphere.center, float3(bbox.vec[i]));
|
||||
bound_sphere.radius = max_ff(bound_sphere.radius, dist_sqr);
|
||||
}
|
||||
bound_sphere.radius = sqrtf(bound_sphere.radius);
|
||||
|
||||
/* Transform into world space. */
|
||||
bound_sphere.center = math::transform_point(data_.viewinv, bound_sphere.center);
|
||||
|
||||
/* Compute diagonal length. */
|
||||
float2 p0 = float2(bbox.vec[0]) / (this->is_perspective() ? bbox.vec[0][2] : 1.0f);
|
||||
float2 p1 = float2(bbox.vec[7]) / (this->is_perspective() ? bbox.vec[7][2] : 1.0f);
|
||||
data_.screen_diagonal_length = math::distance(p0, p1);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -94,6 +94,11 @@ class Camera {
|
|||
|
||||
CameraDataBuf data_;
|
||||
|
||||
struct {
|
||||
float3 center;
|
||||
float radius;
|
||||
} bound_sphere;
|
||||
|
||||
public:
|
||||
Camera(Instance &inst) : inst_(inst){};
|
||||
~Camera(){};
|
||||
|
@ -133,6 +138,17 @@ class Camera {
|
|||
{
|
||||
return data_.viewinv.z_axis();
|
||||
}
|
||||
const float3 &bound_center() const
|
||||
{
|
||||
return bound_sphere.center;
|
||||
}
|
||||
const float &bound_radius() const
|
||||
{
|
||||
return bound_sphere.radius;
|
||||
}
|
||||
|
||||
private:
|
||||
void update_bounds();
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -32,15 +32,29 @@
|
|||
* 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_RES 32
|
||||
#define SHADOW_TILEMAP_LOD 5 /* LOG2(SHADOW_TILEMAP_RES) */
|
||||
#define SHADOW_TILEMAP_LOD0_LEN ((SHADOW_TILEMAP_RES / 1) * (SHADOW_TILEMAP_RES / 1))
|
||||
#define SHADOW_TILEMAP_LOD1_LEN ((SHADOW_TILEMAP_RES / 2) * (SHADOW_TILEMAP_RES / 2))
|
||||
#define SHADOW_TILEMAP_LOD2_LEN ((SHADOW_TILEMAP_RES / 4) * (SHADOW_TILEMAP_RES / 4))
|
||||
#define SHADOW_TILEMAP_LOD3_LEN ((SHADOW_TILEMAP_RES / 8) * (SHADOW_TILEMAP_RES / 8))
|
||||
#define SHADOW_TILEMAP_LOD4_LEN ((SHADOW_TILEMAP_RES / 16) * (SHADOW_TILEMAP_RES / 16))
|
||||
#define SHADOW_TILEMAP_LOD5_LEN ((SHADOW_TILEMAP_RES / 32) * (SHADOW_TILEMAP_RES / 32))
|
||||
#define SHADOW_TILEMAP_PER_ROW 64
|
||||
#define SHADOW_PAGE_COPY_GROUP_SIZE 32
|
||||
#define SHADOW_DEPTH_SCAN_GROUP_SIZE 32
|
||||
#define SHADOW_TILEDATA_PER_TILEMAP \
|
||||
(SHADOW_TILEMAP_LOD0_LEN + SHADOW_TILEMAP_LOD1_LEN + SHADOW_TILEMAP_LOD2_LEN + \
|
||||
SHADOW_TILEMAP_LOD3_LEN + SHADOW_TILEMAP_LOD4_LEN + SHADOW_TILEMAP_LOD5_LEN)
|
||||
#define SHADOW_PAGE_CLEAR_GROUP_SIZE 32
|
||||
#define SHADOW_PAGE_RES 256
|
||||
#define SHADOW_DEPTH_SCAN_GROUP_SIZE 8
|
||||
#define SHADOW_AABB_TAG_GROUP_SIZE 64
|
||||
#define SHADOW_MAX_TILEMAP 4096
|
||||
#define SHADOW_MAX_TILE (SHADOW_MAX_TILEMAP * SHADOW_TILEDATA_PER_TILEMAP)
|
||||
#define SHADOW_MAX_PAGE 4096
|
||||
#define SHADOW_PAGE_PER_ROW 64
|
||||
#define SHADOW_ATLAS_SLOT 5
|
||||
#define SHADOW_BOUNDS_GROUP_SIZE 64
|
||||
#define SHADOW_VIEW_MAX 64 /* Must match DRW_VIEW_MAX. */
|
||||
|
||||
/* Ray-tracing. */
|
||||
#define RAYTRACE_GROUP_SIZE 16
|
||||
|
@ -74,6 +88,11 @@
|
|||
/* Resource bindings. */
|
||||
|
||||
/* Texture. */
|
||||
#define SHADOW_TILEMAPS_TEX_SLOT 12
|
||||
/* Only during surface shading. */
|
||||
#define SHADOW_ATLAS_TEX_SLOT 13
|
||||
/* Only during shadow rendering. */
|
||||
#define SHADOW_RENDER_MAP_SLOT 13
|
||||
#define RBUFS_UTILITY_TEX_SLOT 14
|
||||
|
||||
/* Images. */
|
||||
|
@ -99,7 +118,10 @@
|
|||
#define LIGHT_BUF_SLOT 1
|
||||
#define LIGHT_ZBIN_BUF_SLOT 2
|
||||
#define LIGHT_TILE_BUF_SLOT 3
|
||||
/* Only during surface shading. */
|
||||
#define RBUFS_AOV_BUF_SLOT 5
|
||||
/* Only during shadow rendering. */
|
||||
#define SHADOW_PAGE_INFO_SLOT 5
|
||||
#define SAMPLING_BUF_SLOT 6
|
||||
#define CRYPTOMATTE_BUF_SLOT 7
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ void Instance::init(const int2 &output_res,
|
|||
film.init(output_res, output_rect);
|
||||
velocity.init();
|
||||
depth_of_field.init();
|
||||
shadows.init();
|
||||
motion_blur.init();
|
||||
main_view.init();
|
||||
}
|
||||
|
@ -102,6 +103,7 @@ void Instance::begin_sync()
|
|||
materials.begin_sync();
|
||||
velocity.begin_sync(); /* NOTE: Also syncs camera. */
|
||||
lights.begin_sync();
|
||||
shadows.begin_sync();
|
||||
cryptomatte.begin_sync();
|
||||
|
||||
gpencil_engine_enabled = false;
|
||||
|
@ -197,6 +199,7 @@ void Instance::object_sync_render(void *instance_,
|
|||
void Instance::end_sync()
|
||||
{
|
||||
velocity.end_sync();
|
||||
shadows.end_sync(); /** \note: Needs to be before lights. */
|
||||
lights.end_sync();
|
||||
sampling.end_sync();
|
||||
film.end_sync();
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "eevee_renderbuffers.hh"
|
||||
#include "eevee_sampling.hh"
|
||||
#include "eevee_shader.hh"
|
||||
#include "eevee_shadow.hh"
|
||||
#include "eevee_sync.hh"
|
||||
#include "eevee_view.hh"
|
||||
#include "eevee_world.hh"
|
||||
|
@ -46,6 +47,7 @@ class Instance {
|
|||
SyncModule sync;
|
||||
MaterialModule materials;
|
||||
PipelineModule pipelines;
|
||||
ShadowModule shadows;
|
||||
LightModule lights;
|
||||
VelocityModule velocity;
|
||||
MotionBlurModule motion_blur;
|
||||
|
@ -89,6 +91,7 @@ class Instance {
|
|||
sync(*this),
|
||||
materials(*this),
|
||||
pipelines(*this),
|
||||
shadows(*this),
|
||||
lights(*this),
|
||||
velocity(*this),
|
||||
motion_blur(*this),
|
||||
|
|
|
@ -41,7 +41,7 @@ static eLightType to_light_type(short blender_light_type, short blender_area_typ
|
|||
/** \name Light Object
|
||||
* \{ */
|
||||
|
||||
void Light::sync(/* ShadowModule &shadows , */ const Object *ob, float threshold)
|
||||
void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
|
||||
{
|
||||
const ::Light *la = (const ::Light *)ob->data;
|
||||
float scale[3];
|
||||
|
@ -75,67 +75,49 @@ void Light::sync(/* ShadowModule &shadows , */ const Object *ob, float threshold
|
|||
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 (assign_if_different(this->type, new_type)) {
|
||||
shadow_discard_safe(shadows);
|
||||
}
|
||||
|
||||
#if 0
|
||||
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);
|
||||
shadow_ensure(shadows);
|
||||
if (is_sun_light(this->type)) {
|
||||
this->directional->sync(this->object_mat, 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);
|
||||
this->punctual->sync(
|
||||
this->type, this->object_mat, la->spotsize, la->clipsta, this->influence_radius_max);
|
||||
}
|
||||
}
|
||||
else {
|
||||
shadow_discard_safe(shadows);
|
||||
}
|
||||
#endif
|
||||
|
||||
this->initialized = true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
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;
|
||||
if (this->directional != nullptr) {
|
||||
shadows.directional_pool.destruct(*directional);
|
||||
this->directional = nullptr;
|
||||
}
|
||||
if (this->punctual != nullptr) {
|
||||
shadows.punctual_pool.destruct(*punctual);
|
||||
this->punctual = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Light::shadow_ensure(ShadowModule &shadows)
|
||||
{
|
||||
if (is_sun_light(this->type) && this->directional == nullptr) {
|
||||
this->directional = &shadows.directional_pool.construct(shadows);
|
||||
}
|
||||
else if (this->punctual == nullptr) {
|
||||
this->punctual = &shadows.punctual_pool.construct(shadows);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Returns attenuation radius inverted & 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) {
|
||||
|
@ -265,6 +247,14 @@ void Light::debug_draw()
|
|||
/** \name LightModule
|
||||
* \{ */
|
||||
|
||||
LightModule::~LightModule()
|
||||
{
|
||||
/* WATCH: Destructor order. Expect shadow module to be destructed later. */
|
||||
for (Light &light : light_map_.values()) {
|
||||
light.shadow_discard_safe(inst_.shadows);
|
||||
}
|
||||
};
|
||||
|
||||
void LightModule::begin_sync()
|
||||
{
|
||||
use_scene_lights_ = inst_.use_scene_lights();
|
||||
|
@ -286,61 +276,44 @@ void LightModule::sync_light(const Object *ob, ObjectHandle &handle)
|
|||
Light &light = light_map_.lookup_or_add_default(handle.object_key);
|
||||
light.used = true;
|
||||
if (handle.recalc != 0 || !light.initialized) {
|
||||
light.sync(/* inst_.shadows, */ ob, light_threshold_);
|
||||
light.initialized = true;
|
||||
light.sync(inst_.shadows, ob, light_threshold_);
|
||||
}
|
||||
sun_lights_len_ += int(light.type == LIGHT_SUN);
|
||||
local_lights_len_ += int(light.type != LIGHT_SUN);
|
||||
sun_lights_len_ += int(is_sun_light(light.type));
|
||||
local_lights_len_ += int(!is_sun_light(light.type));
|
||||
}
|
||||
|
||||
void LightModule::end_sync()
|
||||
{
|
||||
// ShadowModule &shadows = inst_.shadows;
|
||||
|
||||
/* NOTE: We resize this buffer before removing deleted lights. */
|
||||
int lights_allocated = ceil_to_multiple_u(max_ii(light_map_.size(), 1), LIGHT_CHUNK);
|
||||
light_buf_.resize(lights_allocated);
|
||||
|
||||
/* Track light deletion. */
|
||||
Vector<ObjectKey, 0> deleted_keys;
|
||||
/* Indices inside GPU data array. */
|
||||
int sun_lights_idx = 0;
|
||||
int local_lights_idx = sun_lights_len_;
|
||||
|
||||
/* Fill GPU data with scene data. */
|
||||
for (auto item : light_map_.items()) {
|
||||
Light &light = item.value;
|
||||
auto it_end = light_map_.items().end();
|
||||
for (auto it = light_map_.items().begin(); it != it_end; ++it) {
|
||||
Light &light = (*it).value;
|
||||
|
||||
if (!light.used) {
|
||||
/* Deleted light. */
|
||||
deleted_keys.append(item.key);
|
||||
// light.shadow_discard_safe(shadows);
|
||||
light_map_.remove(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
int dst_idx = (light.type == LIGHT_SUN) ? sun_lights_idx++ : local_lights_idx++;
|
||||
int dst_idx = is_sun_light(light.type) ? sun_lights_idx++ : local_lights_idx++;
|
||||
/* Put all light data into global data SSBO. */
|
||||
light_buf_[dst_idx] = light;
|
||||
|
||||
#if 0
|
||||
if (light.shadow_id != LIGHT_NO_SHADOW) {
|
||||
if (light.type == LIGHT_SUN) {
|
||||
light_buf_[dst_idx].shadow_data = shadows.directionals[light.shadow_id];
|
||||
}
|
||||
else {
|
||||
light_buf_[dst_idx].shadow_data = shadows.punctuals[light.shadow_id];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* Untag for next sync. */
|
||||
light.used = false;
|
||||
}
|
||||
/* This scene data buffer is then immutable after this point. */
|
||||
light_buf_.push_update();
|
||||
|
||||
for (auto &key : deleted_keys) {
|
||||
light_map_.remove(key);
|
||||
}
|
||||
|
||||
/* Update sampling on deletion or un-hiding (use_scene_lights). */
|
||||
if (assign_if_different(light_map_size_, light_map_.size())) {
|
||||
inst_.sampling.reset();
|
||||
|
|
|
@ -34,25 +34,52 @@
|
|||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
class ShadowModule;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Light Object
|
||||
* \{ */
|
||||
|
||||
struct Light : public LightData {
|
||||
struct Light : public LightData, NonCopyable {
|
||||
public:
|
||||
bool initialized = false;
|
||||
bool used = false;
|
||||
|
||||
/** Pointers to source Shadow. Type depends on `LightData::type`. */
|
||||
ShadowDirectional *directional = nullptr;
|
||||
ShadowPunctual *punctual = nullptr;
|
||||
|
||||
public:
|
||||
Light()
|
||||
{
|
||||
shadow_id = LIGHT_NO_SHADOW;
|
||||
/* Avoid valgrind warning. */
|
||||
this->type = LIGHT_SUN;
|
||||
}
|
||||
|
||||
void sync(/* ShadowModule &shadows, */ const Object *ob, float threshold);
|
||||
/* Only used for debugging. */
|
||||
#ifndef NDEBUG
|
||||
Light(Light &&other)
|
||||
{
|
||||
*static_cast<LightData *>(this) = other;
|
||||
this->initialized = other.initialized;
|
||||
this->used = other.used;
|
||||
this->directional = other.directional;
|
||||
this->punctual = other.punctual;
|
||||
other.directional = nullptr;
|
||||
other.punctual = nullptr;
|
||||
}
|
||||
|
||||
// void shadow_discard_safe(ShadowModule &shadows);
|
||||
~Light()
|
||||
{
|
||||
BLI_assert(directional == nullptr);
|
||||
BLI_assert(punctual == nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
void sync(ShadowModule &shadows, const Object *ob, float threshold);
|
||||
|
||||
void shadow_ensure(ShadowModule &shadows);
|
||||
void shadow_discard_safe(ShadowModule &shadows);
|
||||
|
||||
void debug_draw();
|
||||
|
||||
|
@ -73,7 +100,7 @@ struct Light : public LightData {
|
|||
* The light module manages light data buffers and light culling system.
|
||||
*/
|
||||
class LightModule {
|
||||
// friend ShadowModule;
|
||||
friend ShadowModule;
|
||||
|
||||
private:
|
||||
/* Keep tile count reasonable for memory usage and 2D culling performance. */
|
||||
|
@ -125,7 +152,7 @@ class LightModule {
|
|||
|
||||
public:
|
||||
LightModule(Instance &inst) : inst_(inst){};
|
||||
~LightModule(){};
|
||||
~LightModule();
|
||||
|
||||
void begin_sync();
|
||||
void sync_light(const Object *ob, ObjectHandle &handle);
|
||||
|
@ -138,21 +165,8 @@ class LightModule {
|
|||
|
||||
void debug_draw(View &view, GPUFrameBuffer *view_fb);
|
||||
|
||||
void bind_resources(DRWShadingGroup *grp)
|
||||
{
|
||||
DRW_shgroup_storage_block_ref(grp, "light_buf", &culling_light_buf_);
|
||||
DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_);
|
||||
DRW_shgroup_storage_block_ref(grp, "light_zbin_buf", &culling_zbin_buf_);
|
||||
DRW_shgroup_storage_block_ref(grp, "light_tile_buf", &culling_tile_buf_);
|
||||
#if 0
|
||||
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());
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T> void bind_resources(draw::detail::PassBase<T> *pass)
|
||||
{
|
||||
/* Storage Buf. */
|
||||
pass->bind_ssbo(LIGHT_CULL_BUF_SLOT, &culling_data_buf_);
|
||||
pass->bind_ssbo(LIGHT_BUF_SLOT, &culling_light_buf_);
|
||||
pass->bind_ssbo(LIGHT_ZBIN_BUF_SLOT, &culling_zbin_buf_);
|
||||
|
|
|
@ -300,7 +300,9 @@ MaterialArray &MaterialModule::material_array_get(Object *ob, bool has_motion)
|
|||
for (auto i : IndexRange(materials_len)) {
|
||||
::Material *blender_mat = material_from_slot(ob, i);
|
||||
Material &mat = material_sync(ob, blender_mat, to_material_geometry(ob), has_motion);
|
||||
material_array_.materials.append(&mat);
|
||||
/* \note: Perform a whole copy since next material_sync() can move the Material memory location
|
||||
* (i.e: because of its container growing) */
|
||||
material_array_.materials.append(mat);
|
||||
material_array_.gpu_materials.append(mat.shading.gpumat);
|
||||
}
|
||||
return material_array_;
|
||||
|
|
|
@ -213,7 +213,7 @@ struct Material {
|
|||
};
|
||||
|
||||
struct MaterialArray {
|
||||
Vector<Material *> materials;
|
||||
Vector<Material> materials;
|
||||
Vector<GPUMaterial *> gpu_materials;
|
||||
};
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@ void WorldPipeline::sync(GPUMaterial *gpumat)
|
|||
world_ps_.bind_image("rp_emission_img", &rbufs.emission_tx);
|
||||
world_ps_.bind_image("rp_cryptomatte_img", &rbufs.cryptomatte_tx);
|
||||
|
||||
world_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get());
|
||||
|
||||
world_ps_.draw(DRW_cache_fullscreen_quad_get(), handle);
|
||||
/* To allow opaque pass rendering over it. */
|
||||
world_ps_.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
|
||||
|
@ -58,6 +60,39 @@ void WorldPipeline::render(View &view)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shadow Pipeline
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void ShadowPipeline::sync()
|
||||
{
|
||||
surface_ps_.init();
|
||||
/* TODO(fclem): Add state for rendering to empty framebuffer without depth test.
|
||||
* For now this is only here for avoiding the rasterizer discard state. */
|
||||
surface_ps_.state_set(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
|
||||
surface_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
|
||||
surface_ps_.bind_texture(SHADOW_RENDER_MAP_SLOT, &inst_.shadows.render_map_tx_);
|
||||
surface_ps_.bind_image(SHADOW_ATLAS_SLOT, &inst_.shadows.atlas_tx_);
|
||||
surface_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get());
|
||||
surface_ps_.bind_ssbo(SHADOW_PAGE_INFO_SLOT, &inst_.shadows.pages_infos_data_);
|
||||
inst_.sampling.bind_resources(&surface_ps_);
|
||||
|
||||
surface_ps_.framebuffer_set(&inst_.shadows.render_fb_);
|
||||
}
|
||||
|
||||
PassMain::Sub *ShadowPipeline::surface_material_add(GPUMaterial *gpumat)
|
||||
{
|
||||
return &surface_ps_.sub(GPU_material_get_name(gpumat));
|
||||
}
|
||||
|
||||
void ShadowPipeline::render(View &view)
|
||||
{
|
||||
inst_.manager->submit(surface_ps_, view);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Forward Pass
|
||||
*
|
||||
|
@ -123,6 +158,7 @@ void ForwardPipeline::sync()
|
|||
opaque_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get());
|
||||
|
||||
inst_.lights.bind_resources(&opaque_ps_);
|
||||
inst_.shadows.bind_resources(&opaque_ps_);
|
||||
inst_.sampling.bind_resources(&opaque_ps_);
|
||||
inst_.cryptomatte.bind_resources(&opaque_ps_);
|
||||
}
|
||||
|
@ -145,9 +181,10 @@ void ForwardPipeline::sync()
|
|||
/* Textures. */
|
||||
sub.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
|
||||
/* Uniform Buf. */
|
||||
opaque_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get());
|
||||
sub.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get());
|
||||
|
||||
inst_.lights.bind_resources(&sub);
|
||||
inst_.shadows.bind_resources(&sub);
|
||||
inst_.sampling.bind_resources(&sub);
|
||||
}
|
||||
}
|
||||
|
@ -225,7 +262,7 @@ void ForwardPipeline::render(View &view,
|
|||
// inst_.hiz_buffer.update();
|
||||
// }
|
||||
|
||||
// inst_.shadows.set_view(view, depth_tx);
|
||||
inst_.shadows.set_view(view);
|
||||
|
||||
GPU_framebuffer_bind(combined_fb);
|
||||
inst_.manager->submit(opaque_ps_, view);
|
||||
|
|
|
@ -43,6 +43,28 @@ class WorldPipeline {
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shadow Pass
|
||||
*
|
||||
* \{ */
|
||||
|
||||
class ShadowPipeline {
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
PassMain surface_ps_ = {"Shadow.Surface"};
|
||||
|
||||
public:
|
||||
ShadowPipeline(Instance &inst) : inst_(inst){};
|
||||
|
||||
PassMain::Sub *surface_material_add(GPUMaterial *gpumat);
|
||||
|
||||
void sync();
|
||||
void render(View &view);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Forward Pass
|
||||
*
|
||||
|
@ -177,19 +199,19 @@ class PipelineModule {
|
|||
WorldPipeline world;
|
||||
// DeferredPipeline deferred;
|
||||
ForwardPipeline forward;
|
||||
// ShadowPipeline shadow;
|
||||
ShadowPipeline shadow;
|
||||
// VelocityPipeline velocity;
|
||||
|
||||
UtilityTexture utility_tx;
|
||||
|
||||
public:
|
||||
PipelineModule(Instance &inst) : world(inst), forward(inst){};
|
||||
PipelineModule(Instance &inst) : world(inst), forward(inst), shadow(inst){};
|
||||
|
||||
void sync()
|
||||
{
|
||||
// deferred.sync();
|
||||
forward.sync();
|
||||
// shadow.sync();
|
||||
shadow.sync();
|
||||
// velocity.sync();
|
||||
}
|
||||
|
||||
|
@ -227,8 +249,7 @@ class PipelineModule {
|
|||
/* TODO(fclem) volume pass. */
|
||||
return nullptr;
|
||||
case MAT_PIPE_SHADOW:
|
||||
// return shadow.material_add(blender_mat, gpumat);
|
||||
break;
|
||||
return shadow.surface_material_add(gpumat);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -142,6 +142,30 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
|
|||
return "eevee_light_culling_tile";
|
||||
case LIGHT_CULLING_ZBIN:
|
||||
return "eevee_light_culling_zbin";
|
||||
case SHADOW_DEBUG:
|
||||
return "eevee_shadow_debug";
|
||||
case SHADOW_PAGE_ALLOCATE:
|
||||
return "eevee_shadow_page_allocate";
|
||||
case SHADOW_PAGE_CLEAR:
|
||||
return "eevee_shadow_page_clear";
|
||||
case SHADOW_PAGE_DEFRAG:
|
||||
return "eevee_shadow_page_defrag";
|
||||
case SHADOW_PAGE_FREE:
|
||||
return "eevee_shadow_page_free";
|
||||
case SHADOW_PAGE_MASK:
|
||||
return "eevee_shadow_page_mask";
|
||||
case SHADOW_TILEMAP_BOUNDS:
|
||||
return "eevee_shadow_tilemap_bounds";
|
||||
case SHADOW_TILEMAP_FINALIZE:
|
||||
return "eevee_shadow_tilemap_finalize";
|
||||
case SHADOW_TILEMAP_INIT:
|
||||
return "eevee_shadow_tilemap_init";
|
||||
case SHADOW_TILEMAP_TAG_UPDATE:
|
||||
return "eevee_shadow_tag_update";
|
||||
case SHADOW_TILEMAP_TAG_USAGE_OPAQUE:
|
||||
return "eevee_shadow_tag_usage_opaque";
|
||||
case SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT:
|
||||
return "eevee_shadow_tag_usage_transparent";
|
||||
/* To avoid compiler warning about missing case. */
|
||||
case MAX_SHADER_TYPE:
|
||||
return "";
|
||||
|
@ -198,8 +222,17 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
|
|||
/* WORKAROUND: Avoid utility texture merge error. TODO: find a cleaner fix. */
|
||||
for (auto &resource : info.batch_resources_) {
|
||||
if (resource.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) {
|
||||
if (resource.slot == RBUFS_UTILITY_TEX_SLOT) {
|
||||
resource.slot = GPU_max_textures_frag() - 1;
|
||||
switch (resource.slot) {
|
||||
case RBUFS_UTILITY_TEX_SLOT:
|
||||
resource.slot = GPU_max_textures_frag() - 1;
|
||||
break;
|
||||
// case SHADOW_RENDER_MAP_SLOT: /* Does not compile because it is a define. */
|
||||
case SHADOW_ATLAS_TEX_SLOT:
|
||||
resource.slot = GPU_max_textures_frag() - 2;
|
||||
break;
|
||||
case SHADOW_TILEMAPS_TEX_SLOT:
|
||||
resource.slot = GPU_max_textures_frag() - 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,9 +247,10 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
|
|||
|
||||
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT) == false &&
|
||||
pipeline_type == MAT_PIPE_FORWARD) {
|
||||
/* Opaque forward do support AOVs and render pass. */
|
||||
/* Opaque forward do support AOVs and render pass if not using transparency. */
|
||||
info.additional_info("eevee_aov_out");
|
||||
info.additional_info("eevee_render_pass_out");
|
||||
info.additional_info("eevee_cryptomatte_out");
|
||||
}
|
||||
|
||||
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_BARYCENTRIC)) {
|
||||
|
@ -389,9 +423,11 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
|
|||
break;
|
||||
case MAT_PIPE_FORWARD_PREPASS:
|
||||
case MAT_PIPE_DEFERRED_PREPASS:
|
||||
case MAT_PIPE_SHADOW:
|
||||
info.additional_info("eevee_surf_depth");
|
||||
break;
|
||||
case MAT_PIPE_SHADOW:
|
||||
info.additional_info("eevee_surf_shadow");
|
||||
break;
|
||||
case MAT_PIPE_DEFERRED:
|
||||
info.additional_info("eevee_surf_deferred");
|
||||
break;
|
||||
|
|
|
@ -62,6 +62,19 @@ enum eShaderType {
|
|||
MOTION_BLUR_TILE_FLATTEN_RENDER,
|
||||
MOTION_BLUR_TILE_FLATTEN_VIEWPORT,
|
||||
|
||||
SHADOW_DEBUG,
|
||||
SHADOW_PAGE_ALLOCATE,
|
||||
SHADOW_PAGE_CLEAR,
|
||||
SHADOW_PAGE_DEFRAG,
|
||||
SHADOW_PAGE_FREE,
|
||||
SHADOW_PAGE_MASK,
|
||||
SHADOW_TILEMAP_BOUNDS,
|
||||
SHADOW_TILEMAP_FINALIZE,
|
||||
SHADOW_TILEMAP_INIT,
|
||||
SHADOW_TILEMAP_TAG_UPDATE,
|
||||
SHADOW_TILEMAP_TAG_USAGE_OPAQUE,
|
||||
SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT,
|
||||
|
||||
MAX_SHADER_TYPE,
|
||||
};
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
|
||||
namespace blender::eevee {
|
||||
|
||||
class ShadowDirectional;
|
||||
class ShadowPunctual;
|
||||
|
||||
using namespace draw;
|
||||
|
||||
constexpr eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
|
@ -46,36 +49,21 @@ enum eDebugMode : uint32_t {
|
|||
*/
|
||||
DEBUG_HIZ_VALIDATION = 2u,
|
||||
/**
|
||||
* Tile-maps to screen. Is also present in other modes.
|
||||
* - Black pixels, no pages allocated.
|
||||
* - Green pixels, pages cached.
|
||||
* - Red pixels, pages allocated.
|
||||
* Show tiles depending on their status.
|
||||
*/
|
||||
DEBUG_SHADOW_TILEMAPS = 10u,
|
||||
/**
|
||||
* Random color per pages. Validates page density allocation and sampling.
|
||||
* Show content of shadow map. Used to verify projection code.
|
||||
*/
|
||||
DEBUG_SHADOW_PAGES = 11u,
|
||||
DEBUG_SHADOW_VALUES = 11u,
|
||||
/**
|
||||
* Outputs random color per tile-map (or tile-map level). Validates tile-maps coverage.
|
||||
* Black means not covered by any tile-maps LOD of the shadow.
|
||||
* Show random color for each tile. Verify allocation and LOD assignment.
|
||||
*/
|
||||
DEBUG_SHADOW_LOD = 12u,
|
||||
DEBUG_SHADOW_TILE_RANDOM_COLOR = 12u,
|
||||
/**
|
||||
* Outputs white pixels for pages allocated and black pixels for unused pages.
|
||||
* This needs DEBUG_SHADOW_PAGE_ALLOCATION_ENABLED defined in order to work.
|
||||
* Show random color for each tile. Verify distribution and LOD transitions.
|
||||
*/
|
||||
DEBUG_SHADOW_PAGE_ALLOCATION = 13u,
|
||||
/**
|
||||
* Outputs the tile-map atlas. Default tile-map is too big for the usual screen resolution.
|
||||
* Try lowering SHADOW_TILEMAP_PER_ROW and SHADOW_MAX_TILEMAP before using this option.
|
||||
*/
|
||||
DEBUG_SHADOW_TILE_ALLOCATION = 14u,
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
DEBUG_SHADOW_SHADOW_DEPTH = 15u
|
||||
DEBUG_SHADOW_TILEMAP_RANDOM_COLOR = 13u,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
@ -176,6 +164,11 @@ struct CameraData {
|
|||
float clip_near;
|
||||
float clip_far;
|
||||
eCameraType type;
|
||||
/** World space distance between view corners at unit distance from camera. */
|
||||
float screen_diagonal_length;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
|
||||
bool1 initialized;
|
||||
|
||||
|
@ -501,8 +494,7 @@ static inline float regular_polygon_side_length(float sides_count)
|
|||
* 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). */
|
||||
/* 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)));
|
||||
|
@ -582,10 +574,11 @@ BLI_STATIC_ASSERT_ALIGN(LightCullingData, 16)
|
|||
|
||||
enum eLightType : uint32_t {
|
||||
LIGHT_SUN = 0u,
|
||||
LIGHT_POINT = 1u,
|
||||
LIGHT_SPOT = 2u,
|
||||
LIGHT_RECT = 3u,
|
||||
LIGHT_ELLIPSE = 4u
|
||||
LIGHT_SUN_ORTHO = 1u,
|
||||
LIGHT_POINT = 10u,
|
||||
LIGHT_SPOT = 11u,
|
||||
LIGHT_RECT = 20u,
|
||||
LIGHT_ELLIPSE = 21u
|
||||
};
|
||||
|
||||
static inline bool is_area_light(eLightType type)
|
||||
|
@ -593,6 +586,11 @@ static inline bool is_area_light(eLightType type)
|
|||
return type >= LIGHT_RECT;
|
||||
}
|
||||
|
||||
static inline bool is_sun_light(eLightType type)
|
||||
{
|
||||
return type < LIGHT_POINT;
|
||||
}
|
||||
|
||||
struct LightData {
|
||||
/** Normalized object matrix. Last column contains data accessible using the following macros. */
|
||||
float4x4 object_mat;
|
||||
|
@ -602,6 +600,9 @@ struct LightData {
|
|||
#define _radius _area_size_x
|
||||
#define _spot_mul object_mat[2][3]
|
||||
#define _spot_bias object_mat[3][3]
|
||||
/** Scale to convert from world units to tile space of the clipmap_lod_max. */
|
||||
#define _clipmap_origin_x object_mat[2][3]
|
||||
#define _clipmap_origin_y object_mat[3][3]
|
||||
/** Aliases for axes. */
|
||||
#ifndef USE_GPU_SHADER_CREATE_INFO
|
||||
# define _right object_mat[0].xyz()
|
||||
|
@ -614,34 +615,210 @@ struct LightData {
|
|||
# define _back object_mat[2].xyz
|
||||
# define _position object_mat[3].xyz
|
||||
#endif
|
||||
/** Influence radius (inverted and squared) adjusted for Surface / Volume power. */
|
||||
/** Punctual : Influence radius (inverted and squared) adjusted for Surface / Volume power. */
|
||||
float influence_radius_invsqr_surface;
|
||||
float influence_radius_invsqr_volume;
|
||||
/** Maximum influence radius. Used for culling. */
|
||||
/** Punctual : Maximum influence radius. Used for culling. Equal to clip far distance. */
|
||||
float influence_radius_max;
|
||||
/** Index of the shadow struct on CPU. -1 means no shadow. */
|
||||
int shadow_id;
|
||||
/** Special radius factor for point lighting. */
|
||||
float radius_squared;
|
||||
/** NOTE: It is ok to use float3 here. A float is declared right after it.
|
||||
* float3 is also aligned to 16 bytes. */
|
||||
float3 color;
|
||||
/** Light Type. */
|
||||
eLightType type;
|
||||
/** Spot size. Aligned to size of float2. */
|
||||
float2 spot_size_inv;
|
||||
/** Spot angle tangent. */
|
||||
float spot_tan;
|
||||
/** Reuse for directional LOD bias. */
|
||||
#define _clipmap_lod_bias spot_tan
|
||||
/** 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;
|
||||
/** Spot angle tangent. */
|
||||
float spot_tan;
|
||||
/** 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;
|
||||
|
||||
/** --- Shadow Data --- */
|
||||
/** Directional : Near clip distance. Float stored as int for atomic operations. */
|
||||
int clip_near;
|
||||
int clip_far;
|
||||
/** Directional : Clip-map LOD range to avoid sampling outside of valid range. */
|
||||
int clipmap_lod_min;
|
||||
int clipmap_lod_max;
|
||||
/** Index of the first tile-map. */
|
||||
int tilemap_index;
|
||||
/** Directional : Offset of the LOD min in LOD min tile units. */
|
||||
int2 clipmap_base_offset;
|
||||
/** Punctual & Directional : Normal matrix packed for automatic bias. */
|
||||
float2 normal_mat_packed;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(LightData, 16)
|
||||
|
||||
static inline int light_tilemap_max_get(LightData light)
|
||||
{
|
||||
/* This is not something we need in performance critical code. */
|
||||
return light.tilemap_index + (light.clipmap_lod_max - light.clipmap_lod_min);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \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 clip-maps with each level
|
||||
* covering twice as much area as the previous one.
|
||||
* \{ */
|
||||
|
||||
enum eShadowProjectionType : uint32_t {
|
||||
SHADOW_PROJECTION_CUBEFACE = 0u,
|
||||
SHADOW_PROJECTION_CLIPMAP = 1u,
|
||||
SHADOW_PROJECTION_CASCADE = 2u,
|
||||
};
|
||||
|
||||
static inline int2 shadow_cascade_grid_offset(int2 base_offset, int level_relative)
|
||||
{
|
||||
return (base_offset * level_relative) / (1 << 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Small descriptor used for the tile update phase. Updated by CPU & uploaded to GPU each redraw.
|
||||
*/
|
||||
struct ShadowTileMapData {
|
||||
/** Cached, used for rendering. */
|
||||
float4x4 viewmat, winmat;
|
||||
/** Punctual : Corners of the frustum. (vec3 padded to vec4) */
|
||||
float4 corners[4];
|
||||
/** Integer offset of the center of the 16x16 tiles from the origin of the tile space. */
|
||||
int2 grid_offset;
|
||||
/** Shift between previous and current grid_offset. Allows update tagging. */
|
||||
int2 grid_shift;
|
||||
/** True for punctual lights. */
|
||||
eShadowProjectionType projection_type;
|
||||
/** Multiple of SHADOW_TILEDATA_PER_TILEMAP. Offset inside the tile buffer. */
|
||||
int tiles_index;
|
||||
/** Index of persistent data in the persistent data buffer. */
|
||||
int clip_data_index;
|
||||
/** Bias LOD to tag for usage to lower the amount of tile used. */
|
||||
float lod_bias;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowTileMapData, 16)
|
||||
|
||||
/**
|
||||
* Per tilemap data persistent on GPU.
|
||||
*/
|
||||
struct ShadowTileMapClip {
|
||||
/** Clip distances that were used to render the pages. */
|
||||
float clip_near_stored;
|
||||
float clip_far_stored;
|
||||
/** Near and far clip distances for directional. Float stored as int for atomic operations. */
|
||||
int clip_near;
|
||||
int clip_far;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowTileMapClip, 16)
|
||||
|
||||
struct ShadowPagesInfoData {
|
||||
/** Number of free pages in the free page buffer. */
|
||||
int page_free_count;
|
||||
/** Number of page allocations needed for this cycle. */
|
||||
int page_alloc_count;
|
||||
/** Index of the next cache page in the cached page buffer. */
|
||||
uint page_cached_next;
|
||||
/** Index of the first page in the buffer since the last defrag. */
|
||||
uint page_cached_start;
|
||||
/** Index of the last page in the buffer since the last defrag. */
|
||||
uint page_cached_end;
|
||||
/** Number of views to be rendered during the shadow update pass. */
|
||||
int view_count;
|
||||
/** Physical page size in pixel. Pages are all squares. */
|
||||
int page_size;
|
||||
|
||||
int _pad0;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowPagesInfoData, 16)
|
||||
|
||||
struct ShadowStatistics {
|
||||
/** Statistics that are read back to CPU after a few frame (to avoid stall). */
|
||||
int page_used_count;
|
||||
int page_update_count;
|
||||
int page_allocated_count;
|
||||
int page_rendered_count;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowStatistics, 16)
|
||||
|
||||
/** Decoded tile data structure. */
|
||||
struct ShadowTileData {
|
||||
/** Page inside the virtual shadow map atlas. */
|
||||
uint2 page;
|
||||
/** Page index inside pages_cached_buf. Only valid if `is_cached` is true. */
|
||||
uint cache_index;
|
||||
/** LOD pointed to LOD 0 tile page. (cube-map only). */
|
||||
uint lod;
|
||||
/** If the tile is needed for rendering. */
|
||||
bool is_used;
|
||||
/** True if an update is needed. This persists even if the tile gets unused. */
|
||||
bool do_update;
|
||||
/** True if the tile owns the page (mutually exclusive with `is_cached`). */
|
||||
bool is_allocated;
|
||||
/** True if the tile has been staged for rendering. This will remove the `do_update` flag. */
|
||||
bool is_rendered;
|
||||
/** True if the tile is inside the pages_cached_buf (mutually exclusive with `is_allocated`). */
|
||||
bool is_cached;
|
||||
};
|
||||
/** \note Stored packed as a uint. */
|
||||
#define ShadowTileDataPacked uint
|
||||
|
||||
enum eShadowFlag : uint32_t {
|
||||
SHADOW_NO_DATA = 0u,
|
||||
SHADOW_IS_CACHED = (1u << 27u),
|
||||
SHADOW_IS_ALLOCATED = (1u << 28u),
|
||||
SHADOW_DO_UPDATE = (1u << 29u),
|
||||
SHADOW_IS_RENDERED = (1u << 30u),
|
||||
SHADOW_IS_USED = (1u << 31u)
|
||||
};
|
||||
|
||||
static inline ShadowTileData shadow_tile_unpack(ShadowTileDataPacked data)
|
||||
{
|
||||
ShadowTileData tile;
|
||||
/* Tweaked for SHADOW_PAGE_PER_ROW = 64. */
|
||||
tile.page.x = data & 63u;
|
||||
tile.page.y = (data >> 6u) & 63u;
|
||||
/* -- 12 bits -- */
|
||||
/* Tweaked for SHADOW_TILEMAP_LOD < 8. */
|
||||
tile.lod = (data >> 12u) & 7u;
|
||||
/* -- 15 bits -- */
|
||||
/* Tweaked for SHADOW_MAX_TILEMAP = 4096. */
|
||||
tile.cache_index = (data >> 15u) & 4095u;
|
||||
/* -- 27 bits -- */
|
||||
tile.is_used = (data & SHADOW_IS_USED) != 0;
|
||||
tile.is_cached = (data & SHADOW_IS_CACHED) != 0;
|
||||
tile.is_allocated = (data & SHADOW_IS_ALLOCATED) != 0;
|
||||
tile.is_rendered = (data & SHADOW_IS_RENDERED) != 0;
|
||||
tile.do_update = (data & SHADOW_DO_UPDATE) != 0;
|
||||
return tile;
|
||||
}
|
||||
|
||||
static inline ShadowTileDataPacked shadow_tile_pack(ShadowTileData tile)
|
||||
{
|
||||
uint data;
|
||||
data = (tile.page.x & 63u);
|
||||
data |= (tile.page.y & 63u) << 6u;
|
||||
data |= (tile.lod & 7u) << 12u;
|
||||
data |= (tile.cache_index & 4095u) << 15u;
|
||||
data |= (tile.is_used ? uint(SHADOW_IS_USED) : 0);
|
||||
data |= (tile.is_allocated ? uint(SHADOW_IS_ALLOCATED) : 0);
|
||||
data |= (tile.is_cached ? uint(SHADOW_IS_CACHED) : 0);
|
||||
data |= (tile.is_rendered ? uint(SHADOW_IS_RENDERED) : 0);
|
||||
data |= (tile.do_update ? uint(SHADOW_DO_UPDATE) : 0);
|
||||
return data;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -761,6 +938,13 @@ using LightDataBuf = draw::StorageArrayBuffer<LightData, LIGHT_CHUNK>;
|
|||
using MotionBlurDataBuf = draw::UniformBuffer<MotionBlurData>;
|
||||
using MotionBlurTileIndirectionBuf = draw::StorageBuffer<MotionBlurTileIndirection, true>;
|
||||
using SamplingDataBuf = draw::StorageBuffer<SamplingData>;
|
||||
using ShadowStatisticsBuf = draw::StorageBuffer<ShadowStatistics>;
|
||||
using ShadowPagesInfoDataBuf = draw::StorageBuffer<ShadowPagesInfoData>;
|
||||
using ShadowPageHeapBuf = draw::StorageVectorBuffer<uint, SHADOW_MAX_PAGE>;
|
||||
using ShadowPageCacheBuf = draw::StorageArrayBuffer<uint2, SHADOW_MAX_PAGE, true>;
|
||||
using ShadowTileMapDataBuf = draw::StorageVectorBuffer<ShadowTileMapData, SHADOW_MAX_TILEMAP>;
|
||||
using ShadowTileMapClipBuf = draw::StorageArrayBuffer<ShadowTileMapClip, SHADOW_MAX_TILEMAP, true>;
|
||||
using ShadowTileDataBuf = draw::StorageArrayBuffer<ShadowTileDataPacked, SHADOW_MAX_TILE, true>;
|
||||
using VelocityGeometryBuf = draw::StorageArrayBuffer<float4, 16, true>;
|
||||
using VelocityIndexBuf = draw::StorageArrayBuffer<VelocityIndex, 16>;
|
||||
using VelocityObjectBuf = draw::StorageArrayBuffer<float4x4, 16>;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,451 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2022 Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* The shadow module manages shadow update tagging & shadow rendering.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_pool.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
|
||||
#include "eevee_material.hh"
|
||||
#include "eevee_shader.hh"
|
||||
#include "eevee_shader_shared.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
class ShadowModule;
|
||||
class ShadowPipeline;
|
||||
struct Light;
|
||||
|
||||
enum eCubeFace {
|
||||
/* Ordering by culling order. If cone aperture is shallow, we cull the later view. */
|
||||
Z_NEG = 0,
|
||||
X_POS,
|
||||
X_NEG,
|
||||
Y_POS,
|
||||
Y_NEG,
|
||||
Z_POS,
|
||||
};
|
||||
|
||||
/* To be applied after view matrix. Follow same order as eCubeFace. */
|
||||
constexpr static const float shadow_face_mat[6][4][4] = {
|
||||
{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}, /* Z_NEG */
|
||||
{{0, 0, -1, 0}, {-1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* X_POS */
|
||||
{{0, 0, 1, 0}, {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* X_NEG */
|
||||
{{1, 0, 0, 0}, {0, 0, -1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* Y_POS */
|
||||
{{-1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* Y_NEG */
|
||||
{{1, 0, 0, 0}, {0, -1, 0, 0}, {0, 0, -1, 0}, {0, 0, 0, 1}}, /* Z_POS */
|
||||
};
|
||||
|
||||
/* Converts to [-SHADOW_TILEMAP_RES / 2..SHADOW_TILEMAP_RES / 2] for XY and [0..1] for Z. */
|
||||
constexpr static const float shadow_clipmap_scale_mat[4][4] = {{SHADOW_TILEMAP_RES / 2, 0, 0, 0},
|
||||
{0, SHADOW_TILEMAP_RES / 2, 0, 0},
|
||||
{0, 0, 0.5, 0},
|
||||
{0, 0, 0.5, 1}};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Tile-Map
|
||||
*
|
||||
* Stores indirection table and states of each tile of a virtual shadow-map.
|
||||
* One tile-map has the effective resolution of `pagesize * tile_map_resolution`.
|
||||
* Each tile-map overhead is quite small if they do not have any pages allocated.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
struct ShadowTileMap : public ShadowTileMapData {
|
||||
static constexpr int64_t tile_map_resolution = SHADOW_TILEMAP_RES;
|
||||
static constexpr int64_t tiles_count = tile_map_resolution * tile_map_resolution;
|
||||
|
||||
/** Level of detail for clipmap. */
|
||||
int level = INT_MAX;
|
||||
/** Cube face index. */
|
||||
eCubeFace cubeface = Z_NEG;
|
||||
/** Cached, used for detecting updates. */
|
||||
float4x4 object_mat;
|
||||
/** Near and far clip distances. For clip-map, computed on the GPU using casters BBoxes. */
|
||||
float near, far;
|
||||
|
||||
public:
|
||||
ShadowTileMap(int tiles_index_)
|
||||
{
|
||||
tiles_index = tiles_index_;
|
||||
/* For now just the same index. */
|
||||
clip_data_index = tiles_index_ / SHADOW_TILEDATA_PER_TILEMAP;
|
||||
this->set_dirty();
|
||||
}
|
||||
|
||||
void sync_orthographic(const float4x4 &object_mat_,
|
||||
int2 origin_offset,
|
||||
int clipmap_level,
|
||||
float lod_bias_,
|
||||
eShadowProjectionType projection_type_);
|
||||
|
||||
void sync_cubeface(
|
||||
const float4x4 &object_mat, float near, float far, eCubeFace face, float lod_bias_);
|
||||
|
||||
void debug_draw() const;
|
||||
|
||||
void set_dirty()
|
||||
{
|
||||
grid_shift = int2(SHADOW_TILEMAP_RES);
|
||||
}
|
||||
|
||||
void set_updated()
|
||||
{
|
||||
grid_shift = int2(0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The tile-maps are managed on CPU and associated with each light shadow object.
|
||||
*
|
||||
* The number of tile-maps & tiles is unbounded (to the limit of SSBOs), but the actual number
|
||||
* used for rendering is caped to 4096. This is to simplify tile-maps management on CPU.
|
||||
*
|
||||
* At sync end, all tile-maps are grouped by light inside the ShadowTileMapDataBuf so that each
|
||||
* light has a contiguous range of tile-maps to refer to.
|
||||
*/
|
||||
struct ShadowTileMapPool {
|
||||
public:
|
||||
/** Limit the width of the texture. */
|
||||
static constexpr int64_t maps_per_row = SHADOW_TILEMAP_PER_ROW;
|
||||
|
||||
/** Vector containing available offset to tile range in the ShadowTileDataBuf. */
|
||||
Vector<uint> free_indices;
|
||||
/** Pool containing shadow tile structure on CPU. */
|
||||
Pool<ShadowTileMap> tilemap_pool;
|
||||
/** Sorted descriptions for each tile-map in the pool. Updated each frame. */
|
||||
ShadowTileMapDataBuf tilemaps_data = {"tilemaps_data"};
|
||||
/** Previously used tile-maps that needs to release their tiles/pages. Updated each frame. */
|
||||
ShadowTileMapDataBuf tilemaps_unused = {"tilemaps_unused"};
|
||||
/** All possible tiles. A range of tiles tile is referenced by a tile-map. */
|
||||
ShadowTileDataBuf tiles_data = {"tiles_data"};
|
||||
/** Clip range for directional shadows. Updated on GPU. Persistent. */
|
||||
ShadowTileMapClipBuf tilemaps_clip = {"tilemaps_clip"};
|
||||
/** Texture equivalent of ShadowTileDataBuf but grouped by light. */
|
||||
Texture tilemap_tx = {"tilemap_tx"};
|
||||
/** Number of free tile-maps at the end of the previous sync. */
|
||||
int64_t last_free_len = 0;
|
||||
|
||||
public:
|
||||
ShadowTileMapPool();
|
||||
|
||||
ShadowTileMap *acquire();
|
||||
|
||||
/**
|
||||
* Push the given list of ShadowTileMap onto the free stack. Their pages will be free.
|
||||
*/
|
||||
void release(Span<ShadowTileMap *> free_list);
|
||||
|
||||
void end_sync(ShadowModule &module);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shadow Casters & Receivers
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/* Can be either a shadow caster or a shadow receiver. */
|
||||
struct ShadowObject {
|
||||
ResourceHandle resource_handle = {0};
|
||||
bool used = true;
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name ShadowModule
|
||||
*
|
||||
* Manages shadow atlas and shadow region data.
|
||||
* \{ */
|
||||
|
||||
class ShadowModule {
|
||||
friend ShadowPunctual;
|
||||
friend ShadowDirectional;
|
||||
friend ShadowPipeline;
|
||||
friend ShadowTileMapPool;
|
||||
|
||||
public:
|
||||
/** Need to be first because of destructor order. */
|
||||
ShadowTileMapPool tilemap_pool;
|
||||
|
||||
Pool<ShadowPunctual> punctual_pool;
|
||||
Pool<ShadowDirectional> directional_pool;
|
||||
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
/** Map of shadow casters to track deletion & update of intersected shadows. */
|
||||
Map<ObjectKey, ShadowObject> objects_;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Tile-map Management
|
||||
* \{ */
|
||||
|
||||
PassSimple tilemap_setup_ps_ = {"TilemapSetup"};
|
||||
PassMain tilemap_usage_ps_ = {"TagUsage"};
|
||||
PassSimple tilemap_update_ps_ = {"TilemapUpdate"};
|
||||
|
||||
PassMain::Sub *tilemap_usage_transparent_ps_ = nullptr;
|
||||
GPUBatch *box_batch_ = nullptr;
|
||||
|
||||
Framebuffer usage_tag_fb;
|
||||
|
||||
/** List of Resource IDs (to get bounds) for tagging passes. */
|
||||
StorageVectorBuffer<uint, 128> past_casters_updated_ = {"PastCastersUpdated"};
|
||||
StorageVectorBuffer<uint, 128> curr_casters_updated_ = {"CurrCastersUpdated"};
|
||||
/** List of Resource IDs (to get bounds) for getting minimum clip-maps bounds. */
|
||||
StorageVectorBuffer<uint, 128> curr_casters_ = {"CurrCasters"};
|
||||
|
||||
/** Indirect arguments for page clearing. */
|
||||
StorageBuffer<DispatchCommand> clear_dispatch_buf_;
|
||||
/** Pages to clear. */
|
||||
StorageArrayBuffer<uint, SHADOW_MAX_PAGE> clear_page_buf_ = {"clear_page_buf"};
|
||||
|
||||
int3 dispatch_depth_scan_size_;
|
||||
/* Ratio between tile-map pixel world "radius" and film pixel world "radius". */
|
||||
float tilemap_projection_ratio_;
|
||||
|
||||
/* Statistics that are read back to CPU after a few frame (to avoid stall). */
|
||||
SwapChain<ShadowStatisticsBuf, 5> statistics_buf_;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Page Management
|
||||
* \{ */
|
||||
|
||||
static constexpr eGPUTextureFormat atlas_type = GPU_R32UI;
|
||||
/** Atlas containing all physical pages. */
|
||||
Texture atlas_tx_ = {"shadow_atlas_tx_"};
|
||||
|
||||
/** Pool of unallocated pages waiting to be assigned to specific tiles in the tile-map atlas. */
|
||||
ShadowPageHeapBuf pages_free_data_ = {"PagesFreeBuf"};
|
||||
/** Pool of cached tiles waiting to be reused. */
|
||||
ShadowPageCacheBuf pages_cached_data_ = {"PagesCachedBuf"};
|
||||
/** Infos for book keeping and debug. */
|
||||
ShadowPagesInfoDataBuf pages_infos_data_ = {"PagesInfosBuf"};
|
||||
|
||||
int3 copy_dispatch_size_;
|
||||
int3 scan_dispatch_size_;
|
||||
int rendering_tilemap_;
|
||||
int rendering_lod_;
|
||||
bool do_full_update = true;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Rendering
|
||||
* \{ */
|
||||
|
||||
/** Multi-View containing a maximum of 64 view to be rendered with the shadow pipeline. */
|
||||
View shadow_multi_view_ = {"ShadowMultiView", SHADOW_VIEW_MAX, true};
|
||||
/** Tile to physical page mapping. This is an array texture with one layer per view. */
|
||||
Texture render_map_tx_ = {"ShadowRenderMap",
|
||||
GPU_R32UI,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE,
|
||||
int2(SHADOW_TILEMAP_RES),
|
||||
64,
|
||||
nullptr,
|
||||
SHADOW_TILEMAP_LOD + 1};
|
||||
/** An empty frame-buffer (no attachment) the size of a whole tile-map. */
|
||||
Framebuffer render_fb_;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debugging
|
||||
* \{ */
|
||||
|
||||
/** Display informations about the virtual shadows. */
|
||||
PassSimple debug_draw_ps_ = {"Shadow.Debug"};
|
||||
|
||||
/** \} */
|
||||
|
||||
/** Scene immutable parameters. */
|
||||
|
||||
/** For now, needs to be hardcoded. */
|
||||
int shadow_page_size_ = SHADOW_PAGE_RES;
|
||||
/** Amount of bias to apply to the LOD computed at the tile usage tagging stage. */
|
||||
float lod_bias_ = 0.0f;
|
||||
/** Maximum number of allocated pages. Maximum value is SHADOW_MAX_TILEMAP. */
|
||||
int shadow_page_len_ = SHADOW_MAX_TILEMAP;
|
||||
/** Global switch. */
|
||||
bool enabled_ = true;
|
||||
|
||||
public:
|
||||
ShadowModule(Instance &inst);
|
||||
~ShadowModule(){};
|
||||
|
||||
void init();
|
||||
|
||||
void begin_sync();
|
||||
/** Register a shadow caster or receiver. */
|
||||
void sync_object(const ObjectHandle &handle,
|
||||
const ResourceHandle &resource_handle,
|
||||
bool is_shadow_caster,
|
||||
bool is_alpha_blend);
|
||||
void end_sync();
|
||||
|
||||
void set_lights_data();
|
||||
|
||||
void set_view(View &view);
|
||||
|
||||
void debug_end_sync();
|
||||
void debug_draw(View &view, GPUFrameBuffer *view_fb);
|
||||
|
||||
template<typename T> void bind_resources(draw::detail::PassBase<T> *pass)
|
||||
{
|
||||
pass->bind_texture(SHADOW_ATLAS_TEX_SLOT, &atlas_tx_);
|
||||
pass->bind_texture(SHADOW_TILEMAPS_TEX_SLOT, &tilemap_pool.tilemap_tx);
|
||||
}
|
||||
|
||||
private:
|
||||
void remove_unused();
|
||||
void debug_page_map_call(DRWPass *pass);
|
||||
|
||||
/** Compute approximate screen pixel space radius. */
|
||||
float screen_pixel_radius(const View &view, const int2 &extent);
|
||||
/** Compute approximate punctual shadow pixel world space radius, 1 unit away of the light. */
|
||||
float tilemap_pixel_radius();
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shadow
|
||||
*
|
||||
* A shadow component is associated to a `eevee::Light` and manages its associated Tile-maps.
|
||||
* \{ */
|
||||
|
||||
class ShadowPunctual : public NonCopyable, NonMovable {
|
||||
private:
|
||||
ShadowModule &shadows_;
|
||||
/** Tile-map for each cube-face needed (in eCubeFace order). */
|
||||
Vector<ShadowTileMap *> tilemaps_;
|
||||
/** Area light size. */
|
||||
float size_x_, size_y_;
|
||||
/** Shape type. */
|
||||
eLightType light_type_;
|
||||
/** Random position on the light. In world space. */
|
||||
float3 random_offset_;
|
||||
/** Light position. */
|
||||
float3 position_;
|
||||
/** Near and far clip distances. */
|
||||
float far_, near_;
|
||||
/** Number of tile-maps needed to cover the light angular extents. */
|
||||
int tilemaps_needed_;
|
||||
/** Visibility cone angle from the light source. */
|
||||
int cone_aperture_;
|
||||
|
||||
public:
|
||||
ShadowPunctual(ShadowModule &module) : shadows_(module){};
|
||||
ShadowPunctual(ShadowPunctual &&other)
|
||||
: shadows_(other.shadows_), tilemaps_(std::move(other.tilemaps_)){};
|
||||
|
||||
~ShadowPunctual()
|
||||
{
|
||||
shadows_.tilemap_pool.release(tilemaps_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync shadow parameters but do not allocate any shadow tile-maps.
|
||||
*/
|
||||
void sync(eLightType light_type,
|
||||
const float4x4 &object_mat,
|
||||
float cone_aperture,
|
||||
float near_clip,
|
||||
float far_clip);
|
||||
|
||||
/**
|
||||
* Release the tile-maps that will not be used in the current frame.
|
||||
*/
|
||||
void release_excess_tilemaps();
|
||||
|
||||
/**
|
||||
* Allocate shadow tile-maps and setup views for rendering.
|
||||
*/
|
||||
void end_sync(Light &light, float lod_bias);
|
||||
};
|
||||
|
||||
class ShadowDirectional : public NonCopyable, NonMovable {
|
||||
private:
|
||||
ShadowModule &shadows_;
|
||||
/** Tile-map for each clip-map level. */
|
||||
Vector<ShadowTileMap *> tilemaps_;
|
||||
/** User minimum resolution. */
|
||||
float min_resolution_;
|
||||
/** Copy of object matrix. Normalized. */
|
||||
float4x4 object_mat_;
|
||||
/** Current range of clip-map / cascades levels covered by this shadow. */
|
||||
IndexRange levels_range;
|
||||
|
||||
public:
|
||||
ShadowDirectional(ShadowModule &module) : shadows_(module){};
|
||||
ShadowDirectional(ShadowDirectional &&other)
|
||||
: shadows_(other.shadows_), tilemaps_(std::move(other.tilemaps_)){};
|
||||
|
||||
~ShadowDirectional()
|
||||
{
|
||||
shadows_.tilemap_pool.release(tilemaps_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync shadow parameters but do not allocate any shadow tile-maps.
|
||||
*/
|
||||
void sync(const float4x4 &object_mat, float min_resolution);
|
||||
|
||||
/**
|
||||
* Release the tile-maps that will not be used in the current frame.
|
||||
*/
|
||||
void release_excess_tilemaps(const Camera &camera, float lod_bias);
|
||||
|
||||
/**
|
||||
* Allocate shadow tile-maps and setup views for rendering.
|
||||
*/
|
||||
void end_sync(Light &light, const Camera &camera, float lod_bias);
|
||||
|
||||
/* Return coverage of the whole tile-map in world unit. */
|
||||
static float coverage_get(int lvl)
|
||||
{
|
||||
/* This function should be kept in sync with shadow_directional_level(). */
|
||||
/* \note: If we would to introduce a global scaling option it would be here. */
|
||||
return exp2(lvl);
|
||||
}
|
||||
|
||||
/* Return coverage of a single tile for a tile-map of this LOD in world unit. */
|
||||
static float tile_size_get(int lvl)
|
||||
{
|
||||
return coverage_get(lvl) / SHADOW_TILEMAP_RES;
|
||||
}
|
||||
|
||||
private:
|
||||
IndexRange clipmap_level_range(const Camera &camera);
|
||||
IndexRange cascade_level_range(const Camera &camera, float lod_bias);
|
||||
|
||||
void cascade_tilemaps_distribution(Light &light, const Camera &camera);
|
||||
void clipmap_tilemaps_distribution(Light &light, const Camera &camera, float lod_bias);
|
||||
|
||||
void cascade_tilemaps_distribution_near_far_points(const Camera &camera,
|
||||
float3 &near_point,
|
||||
float3 &far_point);
|
||||
|
||||
/* Choose between clip-map and cascade distribution of shadow-map precision depending on the
|
||||
* camera projection type and bounds. */
|
||||
static eShadowProjectionType directional_distribution_type_get(const Camera &camera);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
|
@ -41,6 +41,7 @@ ObjectHandle &SyncModule::sync_object(Object *ob)
|
|||
ObjectHandle &eevee_dd = *reinterpret_cast<ObjectHandle *>(dd);
|
||||
|
||||
if (eevee_dd.object_key.ob == nullptr) {
|
||||
ob = DEG_get_original_object(ob);
|
||||
eevee_dd.object_key = ObjectKey(ob);
|
||||
}
|
||||
|
||||
|
@ -48,7 +49,6 @@ ObjectHandle &SyncModule::sync_object(Object *ob)
|
|||
ID_RECALC_GEOMETRY;
|
||||
if ((eevee_dd.recalc & recalc_flags) != 0) {
|
||||
inst_.sampling.reset();
|
||||
UNUSED_VARS(inst_);
|
||||
}
|
||||
|
||||
return eevee_dd;
|
||||
|
@ -127,13 +127,13 @@ void SyncModule::sync_mesh(Object *ob,
|
|||
if (geom == nullptr) {
|
||||
continue;
|
||||
}
|
||||
Material *material = material_array.materials[i];
|
||||
geometry_call(material->shading.sub_pass, geom, res_handle);
|
||||
geometry_call(material->prepass.sub_pass, geom, res_handle);
|
||||
geometry_call(material->shadow.sub_pass, geom, res_handle);
|
||||
Material &material = material_array.materials[i];
|
||||
geometry_call(material.shading.sub_pass, geom, res_handle);
|
||||
geometry_call(material.prepass.sub_pass, geom, res_handle);
|
||||
geometry_call(material.shadow.sub_pass, geom, res_handle);
|
||||
|
||||
is_shadow_caster = is_shadow_caster || material->shadow.sub_pass != nullptr;
|
||||
is_alpha_blend = is_alpha_blend || material->is_alpha_blend_transparent;
|
||||
is_shadow_caster = is_shadow_caster || material.shadow.sub_pass != nullptr;
|
||||
is_alpha_blend = is_alpha_blend || material.is_alpha_blend_transparent;
|
||||
|
||||
GPUMaterial *gpu_material = material_array.gpu_materials[i];
|
||||
::Material *mat = GPU_material_get_material(gpu_material);
|
||||
|
@ -141,8 +141,9 @@ void SyncModule::sync_mesh(Object *ob,
|
|||
}
|
||||
|
||||
inst_.manager->extract_object_attributes(res_handle, ob_ref, material_array.gpu_materials);
|
||||
|
||||
inst_.shadows.sync_object(ob_handle, res_handle, is_shadow_caster, is_alpha_blend);
|
||||
inst_.cryptomatte.sync_object(ob, res_handle);
|
||||
// shadows.sync_object(ob, ob_handle, is_shadow_caster, is_alpha_blend);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -236,7 +237,7 @@ static void gpencil_stroke_sync(bGPDlayer * /*gpl*/,
|
|||
{
|
||||
gpIterData &iter = *(gpIterData *)thunk;
|
||||
|
||||
Material *material = iter.material_array.materials[gps->mat_nr];
|
||||
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;
|
||||
|
@ -280,9 +281,9 @@ void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandl
|
|||
|
||||
gpencil_drawcall_flush(iter);
|
||||
|
||||
// bool is_caster = true; /* TODO material.shadow.sub_pass. */
|
||||
// bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */
|
||||
// shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend);
|
||||
bool is_caster = true; /* TODO material.shadow.sub_pass. */
|
||||
bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */
|
||||
inst_.shadows.sync_object(ob_handle, res_handle, is_caster, is_alpha_blend);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -347,9 +348,9 @@ void SyncModule::sync_curves(Object *ob,
|
|||
/* TODO(fclem) Hair velocity. */
|
||||
// shading_passes.velocity.gpencil_add(ob, ob_handle);
|
||||
|
||||
// bool is_caster = material.shadow.sub_pass != nullptr;
|
||||
// bool is_alpha_blend = material.is_alpha_blend_transparent;
|
||||
// shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend);
|
||||
bool is_caster = material.shadow.sub_pass != nullptr;
|
||||
bool is_alpha_blend = material.is_alpha_blend_transparent;
|
||||
inst_.shadows.sync_object(ob_handle, res_handle, is_caster, is_alpha_blend);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -261,7 +261,7 @@ void VelocityModule::end_sync()
|
|||
{
|
||||
Vector<ObjectKey, 0> deleted_obj;
|
||||
|
||||
uint32_t max_resource_id_ = 0u;
|
||||
uint32_t max_resource_id_ = 1u;
|
||||
|
||||
for (Map<ObjectKey, VelocityObjectData>::Item item : velocity_map.items()) {
|
||||
if (item.value.obj.resource_id == uint32_t(-1)) {
|
||||
|
|
|
@ -134,13 +134,12 @@ void ShadingView::render()
|
|||
|
||||
inst_.lights.debug_draw(render_view_new_, combined_fb_);
|
||||
inst_.hiz_buffer.debug_draw(render_view_new_, combined_fb_);
|
||||
inst_.shadows.debug_draw(render_view_new_, combined_fb_);
|
||||
|
||||
GPUTexture *combined_final_tx = render_postfx(rbufs.combined_tx);
|
||||
|
||||
inst_.film.accumulate(sub_view_, combined_final_tx);
|
||||
|
||||
// inst_.shadows.debug_draw();
|
||||
|
||||
rbufs.release();
|
||||
postfx_tx_.release();
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
# ifdef OBINFO_LIB
|
||||
vec3 attr_load_orco(vec4 orco)
|
||||
{
|
||||
# ifdef GPU_VERTEX_SHADER
|
||||
/* We know when there is no orco layer when orco.w is 1.0 because it uses the generic vertex
|
||||
* attribute (which is [0,0,0,1]). */
|
||||
if (orco.w == 1.0) {
|
||||
|
@ -23,6 +24,7 @@ vec3 attr_load_orco(vec4 orco)
|
|||
* using the orco_madd factors. */
|
||||
return OrcoTexCoFactors[0].xyz + pos * OrcoTexCoFactors[1].xyz;
|
||||
}
|
||||
# endif
|
||||
return orco.xyz * 0.5 + 0.5;
|
||||
}
|
||||
# endif
|
||||
|
|
|
@ -9,6 +9,11 @@
|
|||
|
||||
void main()
|
||||
{
|
||||
DRW_VIEW_FROM_RESOURCE_ID;
|
||||
#ifdef MAT_SHADOW
|
||||
shadow_interp.view_id = drw_view_id;
|
||||
#endif
|
||||
|
||||
init_interface();
|
||||
|
||||
vec3 T;
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
|
||||
void main()
|
||||
{
|
||||
DRW_VIEW_FROM_RESOURCE_ID;
|
||||
#ifdef MAT_SHADOW
|
||||
shadow_interp.view_id = drw_view_id;
|
||||
#endif
|
||||
|
||||
init_interface();
|
||||
|
||||
/* TODO(fclem): Expose through a node? */
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
|
||||
void main()
|
||||
{
|
||||
DRW_VIEW_FROM_RESOURCE_ID;
|
||||
#ifdef MAT_SHADOW
|
||||
shadow_interp.view_id = drw_view_id;
|
||||
#endif
|
||||
|
||||
init_interface();
|
||||
|
||||
interp.P = point_object_to_world(pos);
|
||||
|
|
|
@ -22,7 +22,7 @@ void main()
|
|||
}
|
||||
|
||||
/* Sun lights are packed at the end of the array. Perform early copy. */
|
||||
if (light.type == LIGHT_SUN) {
|
||||
if (is_sun_light(light.type)) {
|
||||
/* NOTE: We know the index because sun lights are packed at the start of the input buffer. */
|
||||
out_light_buf[light_cull_buf.local_lights_len + l_idx] = light;
|
||||
return;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
|
||||
|
||||
/* TODO(fclem): We could reduce register pressure by only having static branches for sun lights. */
|
||||
|
@ -19,6 +20,7 @@ void light_eval_ex(ClosureDiffuse diffuse,
|
|||
ClosureReflection reflection,
|
||||
const bool is_directional,
|
||||
vec3 P,
|
||||
vec3 Ng,
|
||||
vec3 V,
|
||||
float vP_z,
|
||||
float thickness,
|
||||
|
@ -34,17 +36,17 @@ void light_eval_ex(ClosureDiffuse diffuse,
|
|||
|
||||
float visibility = light_attenuation(light, L, dist);
|
||||
|
||||
#if 0 /* TODO(fclem): Shadows */
|
||||
if ((light.shadow_id != LIGHT_NO_SHADOW) && (visibility > 0.0)) {
|
||||
if (light.tilemap_index != LIGHT_NO_SHADOW && (visibility > 0.0)) {
|
||||
vec3 lL = light_world_to_local(light, -L) * dist;
|
||||
vec3 lNg = light_world_to_local(light, Ng);
|
||||
|
||||
float shadow_delta = shadow_delta_get(
|
||||
shadow_atlas_tx, shadow_tilemaps_tx, light, light.shadow_data, lL, dist, P);
|
||||
ShadowSample samp = shadow_sample(
|
||||
is_directional, shadow_atlas_tx, shadow_tilemaps_tx, light, lL, lNg, P);
|
||||
|
||||
# ifdef SSS_TRANSMITTANCE
|
||||
/* Transmittance evaluation first to use initial visibility. */
|
||||
#ifdef SSS_TRANSMITTANCE
|
||||
/* Transmittance evaluation first to use initial visibility without shadow. */
|
||||
if (diffuse.sss_id != 0u && light.diffuse_power > 0.0) {
|
||||
float delta = max(thickness, shadow_delta);
|
||||
float delta = max(thickness, samp.occluder_delta + samp.bias);
|
||||
|
||||
vec3 intensity = visibility * light.transmit_power *
|
||||
light_translucent(sss_transmittance_tx,
|
||||
|
@ -57,11 +59,9 @@ void light_eval_ex(ClosureDiffuse diffuse,
|
|||
delta);
|
||||
out_diffuse += light.color * intensity;
|
||||
}
|
||||
# endif
|
||||
|
||||
visibility *= float(shadow_delta - light.shadow_data.bias <= 0.0);
|
||||
}
|
||||
#endif
|
||||
visibility *= float(samp.occluder_delta + samp.bias >= 0.0);
|
||||
}
|
||||
|
||||
if (visibility < 1e-6) {
|
||||
return;
|
||||
|
@ -84,6 +84,7 @@ void light_eval_ex(ClosureDiffuse diffuse,
|
|||
void light_eval(ClosureDiffuse diffuse,
|
||||
ClosureReflection reflection,
|
||||
vec3 P,
|
||||
vec3 Ng,
|
||||
vec3 V,
|
||||
float vP_z,
|
||||
float thickness,
|
||||
|
@ -100,6 +101,7 @@ void light_eval(ClosureDiffuse diffuse,
|
|||
reflection,
|
||||
true,
|
||||
P,
|
||||
Ng,
|
||||
V,
|
||||
vP_z,
|
||||
thickness,
|
||||
|
@ -117,6 +119,7 @@ void light_eval(ClosureDiffuse diffuse,
|
|||
reflection,
|
||||
false,
|
||||
P,
|
||||
Ng,
|
||||
V,
|
||||
vP_z,
|
||||
thickness,
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
|
||||
void light_vector_get(LightData ld, vec3 P, out vec3 L, out float dist)
|
||||
{
|
||||
if (ld.type == LIGHT_SUN) {
|
||||
/* TODO(fclem): Static branching. */
|
||||
if (is_sun_light(ld.type)) {
|
||||
L = ld._back;
|
||||
dist = 1.0;
|
||||
}
|
||||
|
@ -57,10 +58,13 @@ float light_attenuation(LightData ld, vec3 L, float dist)
|
|||
if (ld.type == LIGHT_SPOT) {
|
||||
vis *= light_spot_attenuation(ld, L);
|
||||
}
|
||||
|
||||
if (ld.type >= LIGHT_SPOT) {
|
||||
vis *= step(0.0, -dot(L, -ld._back));
|
||||
}
|
||||
if (ld.type != LIGHT_SUN) {
|
||||
|
||||
/* TODO(fclem): Static branching. */
|
||||
if (!is_sun_light(ld.type)) {
|
||||
#ifdef VOLUME_LIGHTING
|
||||
vis *= light_influence_attenuation(dist, ld.influence_radius_invsqr_volume);
|
||||
#else
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
|
||||
/**
|
||||
* Debug drawing for virtual shadowmaps.
|
||||
* See eShadowDebug for more information.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_debug_print_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
|
||||
|
||||
/** Control the scaling of the tilemap splat. */
|
||||
const float pixel_scale = 4.0;
|
||||
|
||||
vec3 debug_random_color(ivec2 v)
|
||||
{
|
||||
float r = interlieved_gradient_noise(vec2(v), 0.0, 0.0);
|
||||
return hue_gradient(r);
|
||||
}
|
||||
|
||||
vec3 debug_random_color(int v)
|
||||
{
|
||||
return debug_random_color(ivec2(v, 0));
|
||||
}
|
||||
|
||||
void debug_tile_print(ShadowTileData tile, ivec4 tile_coord)
|
||||
{
|
||||
drw_print("Tile (", tile_coord.x, ",", tile_coord.y, ") in Tilemap ", tile_coord.z, " : ");
|
||||
drw_print(tile.lod);
|
||||
drw_print(tile.page);
|
||||
drw_print(tile.cache_index);
|
||||
}
|
||||
|
||||
vec3 debug_tile_state_color(ShadowTileData tile)
|
||||
{
|
||||
if (tile.lod > 0) {
|
||||
/* Uses data from another LOD. */
|
||||
return neon_gradient(float(tile.lod) / float(SHADOW_TILEMAP_LOD));
|
||||
}
|
||||
if (tile.do_update && tile.is_used) {
|
||||
/* Updated. */
|
||||
return vec3(0.5, 1, 0);
|
||||
}
|
||||
if (tile.is_used) {
|
||||
/* Used but was cached. */
|
||||
return vec3(0, 1, 0);
|
||||
}
|
||||
vec3 col = vec3(0);
|
||||
if (tile.is_cached) {
|
||||
col += vec3(0.2, 0, 0.5);
|
||||
if (tile.do_update) {
|
||||
col += vec3(0.8, 0, 0);
|
||||
}
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
||||
ShadowSample debug_tile_get(vec3 P, LightData light)
|
||||
{
|
||||
vec3 lNg = vec3(1.0, 0.0, 0.0);
|
||||
if (is_sun_light(light.type)) {
|
||||
return shadow_directional_sample_get(shadow_atlas_tx, shadow_tilemaps_tx, light, P, lNg);
|
||||
}
|
||||
else {
|
||||
vec3 lL = light_world_to_local(light, P - light._position);
|
||||
return shadow_punctual_sample_get(shadow_atlas_tx, shadow_tilemaps_tx, light, lL, lNg);
|
||||
}
|
||||
}
|
||||
|
||||
LightData debug_light_get()
|
||||
{
|
||||
LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(light_cull_buf, l_idx)
|
||||
{
|
||||
LightData light = light_buf[l_idx];
|
||||
if (light.tilemap_index == debug_tilemap_index) {
|
||||
return light;
|
||||
}
|
||||
}
|
||||
LIGHT_FOREACH_END
|
||||
|
||||
LIGHT_FOREACH_BEGIN_DIRECTIONAL(light_cull_buf, l_idx)
|
||||
{
|
||||
LightData light = light_buf[l_idx];
|
||||
if (light.tilemap_index == debug_tilemap_index) {
|
||||
return light;
|
||||
}
|
||||
}
|
||||
LIGHT_FOREACH_END
|
||||
}
|
||||
|
||||
/** Return true if a pixel was written. */
|
||||
bool debug_tilemaps(vec3 P, LightData light)
|
||||
{
|
||||
const int debug_tile_size_px = 4;
|
||||
ivec2 px = ivec2(gl_FragCoord.xy) / debug_tile_size_px;
|
||||
int tilemap = px.x / SHADOW_TILEMAP_RES;
|
||||
int tilemap_index = light.tilemap_index + tilemap;
|
||||
if ((px.y < SHADOW_TILEMAP_RES) && (tilemap_index <= light_tilemap_max_get(light))) {
|
||||
/* Debug actual values in the tilemap buffer. */
|
||||
ShadowTileMapData tilemap = tilemaps_buf[tilemap_index];
|
||||
int tile_index = shadow_tile_offset(px % SHADOW_TILEMAP_RES, tilemap.tiles_index, 0);
|
||||
ShadowTileData tile = shadow_tile_unpack(tiles_buf[tile_index]);
|
||||
/* Leave 1 px border between tilemaps. */
|
||||
if (!any(
|
||||
equal(ivec2(gl_FragCoord.xy) % (SHADOW_TILEMAP_RES * debug_tile_size_px), ivec2(0)))) {
|
||||
gl_FragDepth = 0.0;
|
||||
out_color_add = vec4(debug_tile_state_color(tile), 0.0);
|
||||
out_color_mul = vec4(0.0);
|
||||
|
||||
if (ivec2(gl_FragCoord.xy) == ivec2(0)) {
|
||||
drw_print(light.object_mat);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void debug_tile_state(vec3 P, LightData light)
|
||||
{
|
||||
ShadowSample samp = debug_tile_get(P, light);
|
||||
out_color_add = vec4(debug_tile_state_color(samp.tile), 0) * 0.5;
|
||||
out_color_mul = vec4(0.5);
|
||||
}
|
||||
|
||||
void debug_atlas_values(vec3 P, LightData light)
|
||||
{
|
||||
ShadowSample samp = debug_tile_get(P, light);
|
||||
out_color_add = vec4(vec3(samp.occluder_dist), 0);
|
||||
out_color_mul = vec4(0.0);
|
||||
}
|
||||
|
||||
void debug_random_tile_color(vec3 P, LightData light)
|
||||
{
|
||||
ShadowSample samp = debug_tile_get(P, light);
|
||||
out_color_add = vec4(debug_random_color(ivec2(samp.tile.page)), 0) * 0.5;
|
||||
out_color_mul = vec4(0.5);
|
||||
}
|
||||
|
||||
void debug_random_tilemap_color(vec3 P, LightData light)
|
||||
{
|
||||
ShadowCoordinates coord;
|
||||
if (is_sun_light(light.type)) {
|
||||
vec3 lP = shadow_world_to_local(light, P);
|
||||
coord = shadow_directional_coordinates(light, lP);
|
||||
}
|
||||
else {
|
||||
vec3 lP = light_world_to_local(light, P - light._position);
|
||||
int face_id = shadow_punctual_face_index_get(lP);
|
||||
lP = shadow_punctual_local_position_to_face_local(face_id, lP);
|
||||
coord = shadow_punctual_coordinates(light, lP, face_id);
|
||||
}
|
||||
|
||||
out_color_add = vec4(debug_random_color(ivec2(coord.tilemap_index)), 0) * 0.5;
|
||||
out_color_mul = vec4(0.5);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
/* Default to no output. */
|
||||
gl_FragDepth = 1.0;
|
||||
out_color_add = vec4(0.0);
|
||||
out_color_mul = vec4(1.0);
|
||||
|
||||
float depth = texelFetch(hiz_tx, ivec2(gl_FragCoord.xy), 0).r;
|
||||
vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth);
|
||||
/* Make it pass the depth test. */
|
||||
gl_FragDepth = depth - 1e-6;
|
||||
|
||||
LightData light = debug_light_get();
|
||||
|
||||
if (debug_tilemaps(P, light)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (depth != 1.0) {
|
||||
switch (eDebugMode(debug_mode)) {
|
||||
case DEBUG_SHADOW_TILEMAPS:
|
||||
debug_tile_state(P, light);
|
||||
break;
|
||||
case DEBUG_SHADOW_VALUES:
|
||||
debug_atlas_values(P, light);
|
||||
break;
|
||||
case DEBUG_SHADOW_TILE_RANDOM_COLOR:
|
||||
debug_random_tile_color(P, light);
|
||||
break;
|
||||
case DEBUG_SHADOW_TILEMAP_RANDOM_COLOR:
|
||||
debug_random_tilemap_color(P, light);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
|
||||
|
||||
/** \a unormalized_uv is the uv coordinates for the whole tilemap [0..SHADOW_TILEMAP_RES]. */
|
||||
vec2 shadow_page_uv_transform(
|
||||
vec2 atlas_size, uvec2 page, uint lod, vec2 unormalized_uv, ivec2 tile_lod0_coord)
|
||||
{
|
||||
/* Bias uv sample for LODs since custom raster aligns LOD pixels instead of centering them. */
|
||||
if (lod != 0) {
|
||||
unormalized_uv += 0.5 / float(SHADOW_PAGE_RES * SHADOW_TILEMAP_RES);
|
||||
}
|
||||
float lod_scaling = exp2(-float(lod));
|
||||
vec2 target_tile = vec2(tile_lod0_coord >> lod);
|
||||
vec2 page_uv = unormalized_uv * lod_scaling - target_tile;
|
||||
/* Assumes atlas is squared. */
|
||||
vec2 atlas_uv = (vec2(page) + min(page_uv, 0.99999)) * float(SHADOW_PAGE_RES) / atlas_size;
|
||||
return atlas_uv;
|
||||
}
|
||||
|
||||
/* Rotate vector to light's local space . Used for directional shadows. */
|
||||
vec3 shadow_world_to_local(LightData ld, vec3 L)
|
||||
{
|
||||
/* Avoid relying on compiler to optimize this.
|
||||
* vec3 lL = transpose(mat3(ld.object_mat)) * L; */
|
||||
vec3 lL;
|
||||
lL.x = dot(ld.object_mat[0].xyz, L);
|
||||
lL.y = dot(ld.object_mat[1].xyz, L);
|
||||
lL.z = dot(ld.object_mat[2].xyz, L);
|
||||
return lL;
|
||||
}
|
||||
|
||||
/* TODO(fclem) use utildef version. */
|
||||
float shadow_orderedIntBitsToFloat(int int_value)
|
||||
{
|
||||
return intBitsToFloat((int_value < 0) ? (int_value ^ 0x7FFFFFFF) : int_value);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Shadow Sampling Functions
|
||||
* \{ */
|
||||
|
||||
/* Turns local light coordinate into shadow region index. Matches eCubeFace order.
|
||||
* \note lL does not need to be normalized. */
|
||||
int shadow_punctual_face_index_get(vec3 lL)
|
||||
{
|
||||
vec3 aP = abs(lL);
|
||||
if (all(greaterThan(aP.xx, aP.yz))) {
|
||||
return (lL.x > 0.0) ? 1 : 2;
|
||||
}
|
||||
else if (all(greaterThan(aP.yy, aP.xz))) {
|
||||
return (lL.y > 0.0) ? 3 : 4;
|
||||
}
|
||||
else {
|
||||
return (lL.z > 0.0) ? 5 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
mat4x4 shadow_load_normal_matrix(LightData light)
|
||||
{
|
||||
if (!is_sun_light(light.type)) {
|
||||
/* FIXME: Why? */
|
||||
float scale = 0.5;
|
||||
return mat4x4(vec4(scale, 0.0, 0.0, 0.0),
|
||||
vec4(0.0, scale, 0.0, 0.0),
|
||||
vec4(0.0, 0.0, 0.0, -1.0),
|
||||
vec4(0.0, 0.0, light.normal_mat_packed.x, light.normal_mat_packed.y));
|
||||
}
|
||||
else {
|
||||
float near = shadow_orderedIntBitsToFloat(light.clip_near);
|
||||
float far = shadow_orderedIntBitsToFloat(light.clip_far);
|
||||
/* Could be store precomputed inside the light struct. Just have to find a how to update it. */
|
||||
float z_scale = (far - near) * 0.5;
|
||||
return mat4x4(vec4(light.normal_mat_packed.x, 0.0, 0.0, 0.0),
|
||||
vec4(0.0, light.normal_mat_packed.x, 0.0, 0.0),
|
||||
vec4(0.0, 0.0, z_scale, 0.0),
|
||||
vec4(0.0, 0.0, 0.0, 1.0));
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns minimum bias (in world space unit) needed for a given geometry normal and a shadowmap
|
||||
* page to avoid self shadowing artifacts. Note that this can return a negative bias to better
|
||||
* match the surface. */
|
||||
float shadow_slope_bias_get(vec2 atlas_size, LightData light, vec3 lNg, vec3 lP, vec2 uv, uint lod)
|
||||
{
|
||||
/* Compute coordinate inside the pixel we are sampling. */
|
||||
vec2 uv_subpixel_coord = fract(uv * atlas_size);
|
||||
/* Bias uv sample for LODs since custom raster aligns LOD pixels instead of centering them. */
|
||||
uv_subpixel_coord += (lod > 0) ? -exp2(-1.0 - float(lod)) : 0.0;
|
||||
/* Compute delta to the texel center (where the sample is). */
|
||||
vec2 ndc_texel_center_delta = uv_subpixel_coord * 2.0 - 1.0;
|
||||
/* Create a normal plane equation and go through the normal projection matrix. */
|
||||
vec4 lNg_plane = vec4(lNg, -dot(lNg, lP));
|
||||
vec4 ndc_Ng = shadow_load_normal_matrix(light) * lNg_plane;
|
||||
/* Get slope from normal vector. Note that this is signed. */
|
||||
vec2 ndc_slope = ndc_Ng.xy / abs(ndc_Ng.z);
|
||||
/* Clamp out to avoid the bias going to infinity. Remember this is in NDC space. */
|
||||
ndc_slope = clamp(ndc_slope, -100.0, 100.0);
|
||||
/* Compute slope to where the receiver should be by extending the plane to the texel center. */
|
||||
float bias = dot(ndc_slope, ndc_texel_center_delta);
|
||||
/* Bias for 1 pixel of the sampled LOD. */
|
||||
bias /= ((SHADOW_TILEMAP_RES * SHADOW_PAGE_RES) >> lod);
|
||||
return bias;
|
||||
}
|
||||
|
||||
struct ShadowSample {
|
||||
/* Signed delta in world units from the shading point to the occluder. Negative if occluded. */
|
||||
float occluder_delta;
|
||||
/* Tile coordinate inside the tilemap [0..SHADOW_TILEMAP_RES). */
|
||||
ivec2 tile_coord;
|
||||
/* UV coordinate inside the tilemap [0..SHADOW_TILEMAP_RES). */
|
||||
vec2 uv;
|
||||
/* Minimum slope bias to apply during comparison. */
|
||||
float bias;
|
||||
/* Distance from near clip plane in world space units. */
|
||||
float occluder_dist;
|
||||
/* Tile used loaded for page indirection. */
|
||||
ShadowTileData tile;
|
||||
};
|
||||
|
||||
float shadow_tile_depth_get(usampler2D atlas_tx, ShadowTileData tile, vec2 atlas_uv)
|
||||
{
|
||||
if (!tile.is_allocated) {
|
||||
/* Far plane distance but with a bias to make sure there will be no shadowing.
|
||||
* But also not FLT_MAX since it can cause issue with projection. */
|
||||
return 1.1;
|
||||
}
|
||||
return uintBitsToFloat(texture(atlas_tx, atlas_uv).r);
|
||||
}
|
||||
|
||||
vec2 shadow_punctual_linear_depth(vec2 z, float near, float far)
|
||||
{
|
||||
vec2 d = z * 2.0 - 1.0;
|
||||
float z_delta = far - near;
|
||||
/* Can we simplify? */
|
||||
return ((-2.0 * near * far) / z_delta) / (d + (-(far + near) / z_delta));
|
||||
}
|
||||
|
||||
float shadow_directional_linear_depth(float z, float near, float far)
|
||||
{
|
||||
return z * (near - far) - near;
|
||||
}
|
||||
|
||||
ShadowSample shadow_punctual_sample_get(
|
||||
usampler2D atlas_tx, usampler2D tilemaps_tx, LightData light, vec3 lP, vec3 lNg)
|
||||
{
|
||||
int face_id = shadow_punctual_face_index_get(lP);
|
||||
lNg = shadow_punctual_local_position_to_face_local(face_id, lNg);
|
||||
lP = shadow_punctual_local_position_to_face_local(face_id, lP);
|
||||
|
||||
ShadowCoordinates coord = shadow_punctual_coordinates(light, lP, face_id);
|
||||
|
||||
vec2 atlas_size = vec2(textureSize(atlas_tx, 0).xy);
|
||||
|
||||
ShadowSample samp;
|
||||
samp.tile = shadow_tile_load(tilemaps_tx, coord.tile_coord, coord.tilemap_index);
|
||||
samp.uv = shadow_page_uv_transform(
|
||||
atlas_size, samp.tile.page, samp.tile.lod, coord.uv, coord.tile_coord);
|
||||
samp.bias = shadow_slope_bias_get(atlas_size, light, lNg, lP, samp.uv, samp.tile.lod);
|
||||
|
||||
float occluder_ndc = shadow_tile_depth_get(atlas_tx, samp.tile, samp.uv);
|
||||
/* Depth is cleared to FLT_MAX, clamp it to 1 to avoid issues when converting to linear. */
|
||||
occluder_ndc = saturate(occluder_ndc);
|
||||
|
||||
/* NOTE: Given to be both positive, so can use intBitsToFloat instead of orderedInt version. */
|
||||
float near = intBitsToFloat(light.clip_near);
|
||||
float far = intBitsToFloat(light.clip_far);
|
||||
/* Shadow is stored as gl_FragCoord.z. Convert to radial distance along with the bias. */
|
||||
vec2 occluder = vec2(occluder_ndc, saturate(occluder_ndc + samp.bias));
|
||||
vec2 occluder_z = shadow_punctual_linear_depth(occluder, near, far);
|
||||
float receiver_dist = length(lP);
|
||||
float radius_divisor = receiver_dist / abs(lP.z);
|
||||
samp.occluder_dist = occluder_z.x * radius_divisor;
|
||||
samp.bias = (occluder_z.y - occluder_z.x) * radius_divisor;
|
||||
samp.occluder_delta = samp.occluder_dist - receiver_dist;
|
||||
return samp;
|
||||
}
|
||||
|
||||
ShadowSample shadow_directional_sample_get(
|
||||
usampler2D atlas_tx, usampler2D tilemaps_tx, LightData light, vec3 P, vec3 lNg)
|
||||
{
|
||||
vec3 lP = shadow_world_to_local(light, P);
|
||||
ShadowCoordinates coord = shadow_directional_coordinates(light, lP);
|
||||
|
||||
vec2 atlas_size = vec2(textureSize(atlas_tx, 0).xy);
|
||||
|
||||
ShadowSample samp;
|
||||
samp.tile = shadow_tile_load(tilemaps_tx, coord.tile_coord, coord.tilemap_index);
|
||||
samp.uv = shadow_page_uv_transform(
|
||||
atlas_size, samp.tile.page, samp.tile.lod, coord.uv, coord.tile_coord);
|
||||
samp.bias = shadow_slope_bias_get(atlas_size, light, lNg, lP, samp.uv, samp.tile.lod);
|
||||
samp.bias *= exp2(float(coord.lod_relative));
|
||||
|
||||
float occluder_ndc = shadow_tile_depth_get(atlas_tx, samp.tile, samp.uv);
|
||||
|
||||
float near = shadow_orderedIntBitsToFloat(light.clip_near);
|
||||
float far = shadow_orderedIntBitsToFloat(light.clip_far);
|
||||
samp.occluder_dist = shadow_directional_linear_depth(occluder_ndc, near, far);
|
||||
/* Receiver distance needs to also be increasing.
|
||||
* Negate since Z distance follows blender camera convention of -Z as forward. */
|
||||
float receiver_dist = -lP.z;
|
||||
samp.bias *= near - far;
|
||||
samp.occluder_delta = samp.occluder_dist - receiver_dist;
|
||||
return samp;
|
||||
}
|
||||
|
||||
ShadowSample shadow_sample(const bool is_directional,
|
||||
usampler2D atlas_tx,
|
||||
usampler2D tilemaps_tx,
|
||||
LightData light,
|
||||
vec3 lL,
|
||||
vec3 lNg,
|
||||
vec3 P)
|
||||
{
|
||||
if (is_directional) {
|
||||
return shadow_directional_sample_get(atlas_tx, tilemaps_tx, light, P, lNg);
|
||||
}
|
||||
else {
|
||||
return shadow_punctual_sample_get(atlas_tx, tilemaps_tx, light, lL, lNg);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
/**
|
||||
* Virtual shadowmapping: Allocation.
|
||||
*
|
||||
* Allocates pages to tiles needing them.
|
||||
* Note that allocation can fail, in this case the tile is left with no page.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_page_ops_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
|
||||
|
||||
void main()
|
||||
{
|
||||
ShadowTileMapData tilemap_data = tilemaps_buf[gl_GlobalInvocationID.z];
|
||||
|
||||
int tile_start = tilemap_data.tiles_index;
|
||||
for (int lod = 0; lod <= SHADOW_TILEMAP_LOD; lod++) {
|
||||
int lod_len = SHADOW_TILEMAP_LOD0_LEN >> (lod * 2);
|
||||
int local_tile = int(gl_LocalInvocationID.x);
|
||||
if (local_tile < lod_len) {
|
||||
int tile_index = tile_start + local_tile;
|
||||
|
||||
ShadowTileData tile = shadow_tile_unpack(tiles_buf[tile_index]);
|
||||
if (tile.is_used && !tile.is_allocated) {
|
||||
shadow_page_alloc(tile);
|
||||
tiles_buf[tile_index] = shadow_tile_pack(tile);
|
||||
}
|
||||
|
||||
if (tile.is_used) {
|
||||
atomicAdd(statistics_buf.page_used_count, 1);
|
||||
}
|
||||
if (tile.is_used && tile.do_update) {
|
||||
atomicAdd(statistics_buf.page_update_count, 1);
|
||||
}
|
||||
if (tile.is_allocated) {
|
||||
atomicAdd(statistics_buf.page_allocated_count, 1);
|
||||
}
|
||||
}
|
||||
tile_start += lod_len;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
/**
|
||||
* Virtual shadowmapping: Page Clear.
|
||||
*
|
||||
* Equivalent to a framebuffer depth clear but only for pages pushed to the clear_page_buf.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
|
||||
|
||||
void main()
|
||||
{
|
||||
uvec2 page_co = unpackUvec2x16(clear_page_buf[gl_GlobalInvocationID.z]);
|
||||
uvec2 page_texel = page_co * pages_infos_buf.page_size + gl_GlobalInvocationID.xy;
|
||||
|
||||
/* Clear to FLT_MAX instead of 1 so the far plane doesn't cast shadows onto farther objects. */
|
||||
imageStore(atlas_img, ivec2(page_texel), uvec4(floatBitsToUint(FLT_MAX)));
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
|
||||
/**
|
||||
* Virtual shadowmapping: Defrag.
|
||||
*
|
||||
* Defragment the cached page buffer making one continuous array.
|
||||
*
|
||||
* Also pop_front the cached pages if there is not enough free pages for the needed allocations.
|
||||
* Here is an example of the behavior of this buffer during one update cycle:
|
||||
*
|
||||
* Initial state: 5 cached pages. Buffer starts at index 2 and ends at 6.
|
||||
* [--xxxxx---------]
|
||||
* After page free step: 2 cached pages were removed (r), 3 pages were inserted in the cache (i).
|
||||
* [--xrxrxiii------]
|
||||
* After page defrag step: The buffer is compressed into only 6 pages.
|
||||
* [----xxxxxx------]
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_page_ops_lib.glsl)
|
||||
|
||||
const uint max_page = SHADOW_MAX_PAGE;
|
||||
|
||||
void find_first_valid(inout uint src, uint dst)
|
||||
{
|
||||
for (; src < dst; src++) {
|
||||
if (pages_cached_buf[src % max_page].x != uint(-1)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void page_cached_free(uint page_index)
|
||||
{
|
||||
uint tile_index = pages_cached_buf[page_index].y;
|
||||
ShadowTileData tile = shadow_tile_unpack(tiles_buf[tile_index]);
|
||||
|
||||
shadow_page_cache_remove(tile);
|
||||
shadow_page_free(tile);
|
||||
|
||||
tiles_buf[tile_index] = shadow_tile_pack(tile);
|
||||
}
|
||||
|
||||
#if 0 /* Can be used to debug heap and invalid pages inside the free buffer. */
|
||||
# define check_heap_integrity(heap, start, size, invalid_val, result) \
|
||||
result = true; \
|
||||
for (int i = 0; i < max_page; i++) { \
|
||||
if ((i >= start) && (i < (start + size))) { \
|
||||
result = result && (heap[i].x != invalid_val); \
|
||||
} \
|
||||
else { \
|
||||
result = result && (heap[i].x == invalid_val); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
# define check_heap_integrity(heap, start, size, invalid_val, result)
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
/* Pages we need to get off the cache for the allocation pass. */
|
||||
int additional_pages = pages_infos_buf.page_alloc_count - pages_infos_buf.page_free_count;
|
||||
|
||||
uint src = pages_infos_buf.page_cached_start;
|
||||
uint end = pages_infos_buf.page_cached_end;
|
||||
|
||||
find_first_valid(src, end);
|
||||
|
||||
bool valid_pre;
|
||||
check_heap_integrity(pages_free_buf, 0, pages_infos_buf.page_free_count, uint(-1), valid_pre);
|
||||
|
||||
/* First free as much pages as needed from the end of the cached range to fulfill the allocation.
|
||||
* Avoid defragmenting to then free them. */
|
||||
for (; additional_pages > 0 && src < end; additional_pages--) {
|
||||
page_cached_free(src % max_page);
|
||||
find_first_valid(src, end);
|
||||
}
|
||||
|
||||
/* Defrag page in "old" range. */
|
||||
bool is_empty = (src == end);
|
||||
if (!is_empty) {
|
||||
/* `page_cached_end` refers to the next empty slot.
|
||||
* Decrement by one to refer to the first slot we can defrag. */
|
||||
for (uint dst = end - 1; dst > src; dst--) {
|
||||
/* Find hole. */
|
||||
if (pages_cached_buf[dst % max_page].x != uint(-1)) {
|
||||
continue;
|
||||
}
|
||||
/* Update corresponding reference in tile. */
|
||||
shadow_page_cache_update_page_ref(src % max_page, dst % max_page);
|
||||
/* Move page. */
|
||||
pages_cached_buf[dst % max_page] = pages_cached_buf[src % max_page];
|
||||
pages_cached_buf[src % max_page] = uvec2(-1);
|
||||
|
||||
find_first_valid(src, dst);
|
||||
}
|
||||
}
|
||||
|
||||
end = pages_infos_buf.page_cached_next;
|
||||
/* Free pages in the "new" range (these are compact). */
|
||||
for (; additional_pages > 0 && src < end; additional_pages--, src++) {
|
||||
page_cached_free(src % max_page);
|
||||
}
|
||||
|
||||
bool valid_post;
|
||||
check_heap_integrity(pages_free_buf, 0, pages_infos_buf.page_free_count, uint(-1), valid_post);
|
||||
|
||||
pages_infos_buf.page_cached_start = src;
|
||||
pages_infos_buf.page_cached_end = end;
|
||||
pages_infos_buf.page_alloc_count = 0;
|
||||
pages_infos_buf.view_count = 0;
|
||||
|
||||
/* Stats. */
|
||||
statistics_buf.page_used_count = 0;
|
||||
statistics_buf.page_update_count = 0;
|
||||
statistics_buf.page_allocated_count = 0;
|
||||
statistics_buf.page_rendered_count = 0;
|
||||
|
||||
/* Wrap the cursor to avoid unsigned overflow. We do not do modulo arithmetic because it would
|
||||
* produce a 0 length buffer if the buffer is full. */
|
||||
if (pages_infos_buf.page_cached_start > max_page) {
|
||||
pages_infos_buf.page_cached_next -= max_page;
|
||||
pages_infos_buf.page_cached_start -= max_page;
|
||||
pages_infos_buf.page_cached_end -= max_page;
|
||||
}
|
||||
|
||||
/* Reset clear command indirect buffer. */
|
||||
clear_dispatch_buf.num_groups_x = pages_infos_buf.page_size / SHADOW_PAGE_CLEAR_GROUP_SIZE;
|
||||
clear_dispatch_buf.num_groups_y = pages_infos_buf.page_size / SHADOW_PAGE_CLEAR_GROUP_SIZE;
|
||||
clear_dispatch_buf.num_groups_z = 0;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
/**
|
||||
* Virtual shadowmapping: Tile page freeing.
|
||||
*
|
||||
* Releases the allocated pages held by tilemaps that have been become unused.
|
||||
* Also reclaim cached pages if the tiles needs them.
|
||||
* Note that we also count the number of new page allocations needed.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_page_ops_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
|
||||
|
||||
void main()
|
||||
{
|
||||
ShadowTileMapData tilemap_data = tilemaps_buf[gl_GlobalInvocationID.z];
|
||||
|
||||
int tile_start = tilemap_data.tiles_index;
|
||||
for (int lod = 0; lod <= SHADOW_TILEMAP_LOD; lod++) {
|
||||
int lod_len = SHADOW_TILEMAP_LOD0_LEN >> (lod * 2);
|
||||
int local_tile = int(gl_LocalInvocationID.x);
|
||||
if (local_tile < lod_len) {
|
||||
int tile_index = tile_start + local_tile;
|
||||
|
||||
ShadowTileData tile = shadow_tile_unpack(tiles_buf[tile_index]);
|
||||
|
||||
bool is_orphaned = !tile.is_used && tile.do_update;
|
||||
if (is_orphaned) {
|
||||
if (tile.is_cached) {
|
||||
shadow_page_cache_remove(tile);
|
||||
}
|
||||
if (tile.is_allocated) {
|
||||
shadow_page_free(tile);
|
||||
}
|
||||
}
|
||||
|
||||
if (tile.is_used) {
|
||||
if (tile.is_cached) {
|
||||
shadow_page_cache_remove(tile);
|
||||
}
|
||||
if (!tile.is_allocated) {
|
||||
atomicAdd(pages_infos_buf.page_alloc_count, 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (tile.is_allocated) {
|
||||
shadow_page_cache_append(tile, tile_index);
|
||||
}
|
||||
}
|
||||
|
||||
tiles_buf[tile_index] = shadow_tile_pack(tile);
|
||||
}
|
||||
tile_start += lod_len;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
/**
|
||||
* Virtual shadowmapping: Usage un-tagging
|
||||
*
|
||||
* Remove used tag from masked tiles (LOD overlap).
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
|
||||
|
||||
shared uint usage_grid[SHADOW_TILEMAP_RES / 2][SHADOW_TILEMAP_RES / 2];
|
||||
|
||||
void main()
|
||||
{
|
||||
uint tilemap_index = gl_GlobalInvocationID.z;
|
||||
ShadowTileMapData tilemap = tilemaps_buf[tilemap_index];
|
||||
|
||||
if (tilemap.projection_type == SHADOW_PROJECTION_CUBEFACE) {
|
||||
/* For each level collect the number of used (or masked) tile that are covering the tile from
|
||||
* the level underneath. If this adds up to 4 the underneath tile is flag unused as its data
|
||||
* is not needed for rendering.
|
||||
*
|
||||
* This is because 2 receivers can tag used the same area of the shadowmap but with different
|
||||
* LODs. */
|
||||
bool is_used = false;
|
||||
ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy);
|
||||
uint lod_size = uint(SHADOW_TILEMAP_RES);
|
||||
for (int lod = 0; lod <= SHADOW_TILEMAP_LOD; lod++, lod_size >>= 1u) {
|
||||
bool thread_active = all(lessThan(tile_co, ivec2(lod_size)));
|
||||
|
||||
barrier();
|
||||
|
||||
ShadowTileData tile;
|
||||
if (thread_active) {
|
||||
int tile_offset = shadow_tile_offset(tile_co, tilemap.tiles_index, lod);
|
||||
tile = shadow_tile_unpack(tiles_buf[tile_offset]);
|
||||
if (lod > 0 && usage_grid[tile_co.y][tile_co.x] == 4u) {
|
||||
/* Remove the usage flag as this tile is completely covered by higher LOD tiles. */
|
||||
tiles_buf[tile_offset] &= ~SHADOW_IS_USED;
|
||||
/* Consider this tile occluding lower levels. */
|
||||
tile.is_used = true;
|
||||
}
|
||||
/* Reset count for next level. */
|
||||
usage_grid[tile_co.y][tile_co.x] = 0u;
|
||||
}
|
||||
|
||||
barrier();
|
||||
|
||||
if (thread_active) {
|
||||
if (tile.is_used) {
|
||||
atomicAdd(usage_grid[tile_co.y / 2][tile_co.x / 2], 1u);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
|
||||
/**
|
||||
* Operations to move virtual shadow map pages between heaps and tiles.
|
||||
* We reuse the blender::vector class denomination.
|
||||
*
|
||||
* The needed resources for this lib are:
|
||||
* - tiles_buf
|
||||
* - pages_free_buf
|
||||
* - pages_cached_buf
|
||||
* - pages_infos_buf
|
||||
*
|
||||
* A page is can be in 3 state (free, cached, acquired). Each one correspond to a different owner.
|
||||
*
|
||||
* - The pages_free_buf works in a regular stack containing only the page coordinates.
|
||||
*
|
||||
* - The pages_cached_buf is a ring buffer where newly cached pages gets added at the end and the
|
||||
* old cached pages gets defragmented at the start of the used portion.
|
||||
*
|
||||
* - The tiles_buf only owns a page if it is used. If the page is cached, the tile contains a
|
||||
* reference index inside the pages_cached_buf.
|
||||
*
|
||||
* IMPORTANT: Do not forget to manually store the tile data after doing operations on them.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
|
||||
|
||||
/* TODO(@fclem): Implement. */
|
||||
#define assert(check)
|
||||
|
||||
/* Remove page ownership from the tile and append it to the cache. */
|
||||
void shadow_page_free(inout ShadowTileData tile)
|
||||
{
|
||||
assert(tile.is_allocated);
|
||||
|
||||
int index = atomicAdd(pages_infos_buf.page_free_count, 1);
|
||||
assert(index < SHADOW_MAX_PAGE);
|
||||
/* Insert in heap. */
|
||||
pages_free_buf[index] = packUvec2x16(tile.page);
|
||||
/* Remove from tile. */
|
||||
tile.page = uvec2(-1);
|
||||
tile.is_cached = false;
|
||||
tile.is_allocated = false;
|
||||
}
|
||||
|
||||
/* Remove last page from the free heap and give ownership to the tile. */
|
||||
void shadow_page_alloc(inout ShadowTileData tile)
|
||||
{
|
||||
assert(!tile.is_allocated);
|
||||
|
||||
int index = atomicAdd(pages_infos_buf.page_free_count, -1) - 1;
|
||||
/* This can easily happen in really big scene. */
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
/* Insert in tile. */
|
||||
tile.page = unpackUvec2x16(pages_free_buf[index]);
|
||||
tile.is_allocated = true;
|
||||
tile.do_update = true;
|
||||
/* Remove from heap. */
|
||||
pages_free_buf[index] = uint(-1);
|
||||
}
|
||||
|
||||
/* Remove page ownership from the tile cache and append it to the cache. */
|
||||
void shadow_page_cache_append(inout ShadowTileData tile, uint tile_index)
|
||||
{
|
||||
assert(tile.is_allocated);
|
||||
|
||||
/* The page_cached_next is also wrapped in the defrag phase to avoid unsigned overflow. */
|
||||
uint index = atomicAdd(pages_infos_buf.page_cached_next, 1u) % uint(SHADOW_MAX_PAGE);
|
||||
/* Insert in heap. */
|
||||
pages_cached_buf[index] = uvec2(packUvec2x16(tile.page), tile_index);
|
||||
/* Remove from tile. */
|
||||
tile.page = uvec2(-1);
|
||||
tile.cache_index = index;
|
||||
tile.is_cached = true;
|
||||
tile.is_allocated = false;
|
||||
}
|
||||
|
||||
/* Remove page from cache and give ownership to the tile. */
|
||||
void shadow_page_cache_remove(inout ShadowTileData tile)
|
||||
{
|
||||
assert(!tile.is_allocated);
|
||||
assert(tile.is_cached);
|
||||
|
||||
uint index = tile.cache_index;
|
||||
/* Insert in tile. */
|
||||
tile.page = unpackUvec2x16(pages_cached_buf[index].x);
|
||||
tile.cache_index = uint(-1);
|
||||
tile.is_cached = false;
|
||||
tile.is_allocated = true;
|
||||
/* Remove from heap. Leaves hole in the buffer. This is handled by the defrag phase. */
|
||||
pages_cached_buf[index] = uvec2(-1);
|
||||
}
|
||||
|
||||
/* Update cached page reference when a cached page moves inside the cached page buffer. */
|
||||
void shadow_page_cache_update_page_ref(uint page_index, uint new_page_index)
|
||||
{
|
||||
uint tile_index = pages_cached_buf[page_index].y;
|
||||
ShadowTileData tile = shadow_tile_unpack(tiles_buf[tile_index]);
|
||||
tile.cache_index = new_page_index;
|
||||
tiles_buf[tile_index] = shadow_tile_pack(tile);
|
||||
}
|
||||
|
||||
/* Update cached page reference when a tile referencing a cached page moves inside the tilemap. */
|
||||
void shadow_page_cache_update_tile_ref(uint page_index, uint new_tile_index)
|
||||
{
|
||||
pages_cached_buf[page_index].y = new_tile_index;
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
|
||||
/**
|
||||
* Virtual shadowmapping: Update tagging
|
||||
*
|
||||
* Any updated shadow caster needs to tag the shadow map tiles it was in and is now into.
|
||||
* This is done in 2 pass of this same shader. One for past object bounds and one for new object
|
||||
* bounds. The bounding boxes are roughly software rasterized (just a plain rect) in order to tag
|
||||
* the appropriate tiles.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_intersect_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_aabb_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
|
||||
|
||||
vec3 safe_project(mat4 winmat, mat4 viewmat, inout int clipped, vec3 v)
|
||||
{
|
||||
vec4 tmp = winmat * (viewmat * vec4(v, 1.0));
|
||||
/* Detect case when point is behind the camera. */
|
||||
clipped += int(tmp.w < 0.0);
|
||||
return tmp.xyz / tmp.w;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ShadowTileMapData tilemap = tilemaps_buf[gl_GlobalInvocationID.z];
|
||||
|
||||
IsectPyramid frustum;
|
||||
if (tilemap.projection_type == SHADOW_PROJECTION_CUBEFACE) {
|
||||
Pyramid pyramid = shadow_tilemap_cubeface_bounds(tilemap, ivec2(0), ivec2(SHADOW_TILEMAP_RES));
|
||||
frustum = isect_data_setup(pyramid);
|
||||
}
|
||||
|
||||
uint resource_id = resource_ids_buf[gl_GlobalInvocationID.x];
|
||||
|
||||
IsectBox box = isect_data_setup(bounds_buf[resource_id].bounding_corners[0].xyz,
|
||||
bounds_buf[resource_id].bounding_corners[1].xyz,
|
||||
bounds_buf[resource_id].bounding_corners[2].xyz,
|
||||
bounds_buf[resource_id].bounding_corners[3].xyz);
|
||||
|
||||
int clipped = 0;
|
||||
/* NDC space post projection [-1..1] (unclamped). */
|
||||
AABB aabb_ndc = aabb_init_min_max();
|
||||
for (int v = 0; v < 8; v++) {
|
||||
aabb_merge(aabb_ndc, safe_project(tilemap.winmat, tilemap.viewmat, clipped, box.corners[v]));
|
||||
}
|
||||
|
||||
if (tilemap.projection_type == SHADOW_PROJECTION_CUBEFACE) {
|
||||
if (clipped == 8) {
|
||||
/* All verts are behind the camera. */
|
||||
return;
|
||||
}
|
||||
else if (clipped > 0) {
|
||||
/* Not all verts are behind the near clip plane. */
|
||||
if (intersect(frustum, box)) {
|
||||
/* We cannot correctly handle this case so we fallback by covering the whole view. */
|
||||
aabb_ndc.max = vec3(1.0);
|
||||
aabb_ndc.min = vec3(-1.0);
|
||||
}
|
||||
else {
|
||||
/* Still out of the frustum. Ignore. */
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AABB aabb_tag;
|
||||
AABB aabb_map = AABB(vec3(-0.99999), vec3(0.99999));
|
||||
|
||||
/* Directionnal winmat have no correct near/far in the Z dimension at this point.
|
||||
* Do not clip in this dimension. */
|
||||
if (tilemap.projection_type != SHADOW_PROJECTION_CUBEFACE) {
|
||||
aabb_map.min.z = -FLT_MAX;
|
||||
aabb_map.max.z = FLT_MAX;
|
||||
}
|
||||
|
||||
if (!aabb_clip(aabb_map, aabb_ndc, aabb_tag)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Raster the bounding rectangle of the Box projection. */
|
||||
const float tilemap_half_res = float(SHADOW_TILEMAP_RES / 2);
|
||||
ivec2 box_min = ivec2(aabb_tag.min.xy * tilemap_half_res + tilemap_half_res);
|
||||
ivec2 box_max = ivec2(aabb_tag.max.xy * tilemap_half_res + tilemap_half_res);
|
||||
|
||||
for (int lod = 0; lod <= SHADOW_TILEMAP_LOD; lod++, box_min >>= 1, box_max >>= 1) {
|
||||
for (int y = box_min.y; y <= box_max.y; y++) {
|
||||
for (int x = box_min.x; x <= box_max.x; x++) {
|
||||
int tile_index = shadow_tile_offset(ivec2(x, y), tilemap.tiles_index, lod);
|
||||
atomicOr(tiles_buf[tile_index], SHADOW_DO_UPDATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
/**
|
||||
* Virtual shadowmapping: Usage tagging
|
||||
*
|
||||
* Shadow pages are only allocated if they are visible.
|
||||
* This pass scan the depth buffer and tag all tiles that are needed for light shadowing as
|
||||
* needed.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tag_usage_lib.glsl)
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 tex_size = textureSize(depth_tx, 0).xy;
|
||||
|
||||
if (!in_range_inclusive(texel, ivec2(0), ivec2(tex_size - 1))) {
|
||||
return;
|
||||
}
|
||||
|
||||
float depth = texelFetch(depth_tx, texel, 0).r;
|
||||
if (depth == 1.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 uv = vec2(texel) / vec2(tex_size);
|
||||
vec3 vP = get_view_space_from_depth(uv, depth);
|
||||
vec3 P = transform_point(ViewMatrixInverse, vP);
|
||||
vec2 pixel = vec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
shadow_tag_usage(vP, P, pixel);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
/**
|
||||
* Virtual shadowmapping: Usage tagging
|
||||
*
|
||||
* Shadow pages are only allocated if they are visible.
|
||||
* This pass scan the depth buffer and tag all tiles that are needed for light shadowing as
|
||||
* needed.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tag_usage_lib.glsl)
|
||||
|
||||
void main()
|
||||
{
|
||||
shadow_tag_usage(interp.vP, interp.P, gl_FragCoord.xy);
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
|
||||
/**
|
||||
* Virtual shadowmapping: Usage tagging
|
||||
*
|
||||
* Shadow pages are only allocated if they are visible.
|
||||
* This pass scan the depth buffer and tag all tiles that are needed for light shadowing as
|
||||
* needed.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_intersect_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
|
||||
|
||||
void shadow_tag_usage_tilemap(uint l_idx, vec3 P, float dist_to_cam, const bool is_directional)
|
||||
{
|
||||
LightData light = light_buf[l_idx];
|
||||
|
||||
if (light.tilemap_index == LIGHT_NO_SHADOW) {
|
||||
return;
|
||||
}
|
||||
|
||||
int lod = 0;
|
||||
ivec2 tile_co;
|
||||
int tilemap_index = light.tilemap_index;
|
||||
if (is_directional) {
|
||||
vec3 lP = shadow_world_to_local(light, P);
|
||||
|
||||
ShadowCoordinates coord = shadow_directional_coordinates(light, lP);
|
||||
|
||||
tile_co = coord.tile_coord;
|
||||
tilemap_index = coord.tilemap_index;
|
||||
}
|
||||
else {
|
||||
vec3 lP = light_world_to_local(light, P - light._position);
|
||||
float dist_to_light = length(lP);
|
||||
if (dist_to_light > light.influence_radius_max) {
|
||||
return;
|
||||
}
|
||||
if (light.type == LIGHT_SPOT) {
|
||||
/* Early out if out of cone. */
|
||||
float angle_tan = length(lP.xy / dist_to_light);
|
||||
if (angle_tan > light.spot_tan) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (is_area_light(light.type)) {
|
||||
/* Early out if on the wrong side. */
|
||||
if (lP.z > 0.0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* How much a shadow map pixel covers a final image pixel.
|
||||
* We project a shadow map pixel (as a sphere for simplicity) to the receiver plane.
|
||||
* We then reproject this sphere onto the camera screen and compare it to the film pixel size.
|
||||
* This gives a good approximation of what LOD to select to get a somewhat uniform shadow map
|
||||
* resolution in screen space. */
|
||||
float footprint_ratio = dist_to_light;
|
||||
/* Project the radius to the screen. 1 unit away from the camera the same way
|
||||
* pixel_world_radius_inv was computed. Not needed in orthographic mode. */
|
||||
bool is_persp = (ProjectionMatrix[3][3] == 0.0);
|
||||
if (is_persp) {
|
||||
footprint_ratio /= dist_to_cam;
|
||||
}
|
||||
/* Apply resolution ratio. */
|
||||
footprint_ratio *= tilemap_projection_ratio;
|
||||
|
||||
int face_id = shadow_punctual_face_index_get(lP);
|
||||
lP = shadow_punctual_local_position_to_face_local(face_id, lP);
|
||||
|
||||
ShadowCoordinates coord = shadow_punctual_coordinates(light, lP, face_id);
|
||||
tile_co = coord.tile_coord;
|
||||
tilemap_index = coord.tilemap_index;
|
||||
|
||||
lod = int(ceil(-log2(footprint_ratio) + tilemaps_buf[tilemap_index].lod_bias));
|
||||
lod = clamp(lod, 0, SHADOW_TILEMAP_LOD);
|
||||
}
|
||||
tile_co >>= lod;
|
||||
|
||||
if (tilemap_index > light_tilemap_max_get(light)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int tile_index = shadow_tile_offset(tile_co, tilemaps_buf[tilemap_index].tiles_index, lod);
|
||||
atomicOr(tiles_buf[tile_index], SHADOW_IS_USED);
|
||||
}
|
||||
|
||||
void shadow_tag_usage(vec3 vP, vec3 P, vec2 pixel)
|
||||
{
|
||||
float dist_to_cam = length(vP);
|
||||
|
||||
LIGHT_FOREACH_BEGIN_DIRECTIONAL(light_cull_buf, l_idx)
|
||||
{
|
||||
shadow_tag_usage_tilemap(l_idx, P, dist_to_cam, true);
|
||||
}
|
||||
LIGHT_FOREACH_END
|
||||
|
||||
LIGHT_FOREACH_BEGIN_LOCAL(light_cull_buf, light_zbin_buf, light_tile_buf, pixel, vP.z, l_idx)
|
||||
{
|
||||
shadow_tag_usage_tilemap(l_idx, P, dist_to_cam, false);
|
||||
}
|
||||
LIGHT_FOREACH_END
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
/**
|
||||
* Virtual shadowmapping: Usage tagging
|
||||
*
|
||||
* Shadow pages are only allocated if they are visible.
|
||||
* This renders bounding boxes for transparent objects in order to tag the correct shadows.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
|
||||
void main()
|
||||
{
|
||||
ObjectBounds bounds = bounds_buf[drw_ResourceID];
|
||||
|
||||
interp.P = bounds.bounding_corners[0].xyz;
|
||||
interp.P += bounds.bounding_corners[1].xyz * pos.x;
|
||||
interp.P += bounds.bounding_corners[2].xyz * pos.y;
|
||||
interp.P += bounds.bounding_corners[3].xyz * pos.z;
|
||||
interp.vP = point_world_to_view(interp.P);
|
||||
|
||||
gl_Position = point_world_to_ndc(interp.P);
|
||||
}
|
|
@ -0,0 +1,398 @@
|
|||
/* Directive for resetting the line numbering so the failing tests lines can be printed.
|
||||
* This conflict with the shader compiler error logging scheme.
|
||||
* Comment out for correct compilation error line. */
|
||||
#line 5
|
||||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_test_lib.glsl)
|
||||
|
||||
#define TEST(a, b) if (true)
|
||||
|
||||
void main()
|
||||
{
|
||||
TEST(eevee_shadow, DirectionalClipmapLevel)
|
||||
{
|
||||
LightData light;
|
||||
light.type = LIGHT_SUN;
|
||||
light.clipmap_lod_min = -5;
|
||||
light.clipmap_lod_max = 8;
|
||||
light._clipmap_lod_bias = 0.0;
|
||||
float fac = float(SHADOW_TILEMAP_RES - 1) / float(SHADOW_TILEMAP_RES);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 0.0)), light.clipmap_lod_min);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 0.49)), 1);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 0.5)), 1);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 0.51)), 1);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 0.99)), 2);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 1.0)), 2);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 1.01)), 2);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 12.5)), 6);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 12.51)), 6);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 15.9999)), 6);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 16.0)), 6);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 16.00001)), 6);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 5000.0)), light.clipmap_lod_max);
|
||||
/* Produces NaN / Inf, Undefined behavior. */
|
||||
// EXPECT_EQ(shadow_directional_level(light, vec3(FLT_MAX)), light.clipmap_lod_max);
|
||||
}
|
||||
|
||||
TEST(eevee_shadow, DirectionalCascadeLevel)
|
||||
{
|
||||
LightData light;
|
||||
light.type = LIGHT_SUN_ORTHO;
|
||||
light.clipmap_lod_min = 2;
|
||||
light.clipmap_lod_max = 8;
|
||||
float half_size = exp2(float(light.clipmap_lod_min - 1));
|
||||
light._clipmap_lod_bias = light.clipmap_lod_min - 1;
|
||||
float fac = float(SHADOW_TILEMAP_RES - 1) / float(SHADOW_TILEMAP_RES);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * half_size * 0.0, 0.0, 0.0)), 2);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * half_size * 0.5, 0.0, 0.0)), 2);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * half_size * 1.0, 0.0, 0.0)), 3);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * half_size * 1.5, 0.0, 0.0)), 3);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * half_size * 2.0, 0.0, 0.0)), 4);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 5000.0)), light.clipmap_lod_max);
|
||||
/* Produces NaN / Inf, Undefined behavior. */
|
||||
// EXPECT_EQ(shadow_directional_level(light, vec3(FLT_MAX)), light.clipmap_lod_max);
|
||||
}
|
||||
|
||||
TEST(eevee_shadow, DirectionalClipmapCoordinates)
|
||||
{
|
||||
ShadowCoordinates coords;
|
||||
vec3 lP, camera_lP;
|
||||
|
||||
LightData light;
|
||||
light.type = LIGHT_SUN;
|
||||
light.clipmap_lod_min = 0; /* Range [-0.5..0.5]. */
|
||||
light.clipmap_lod_max = 2; /* Range [-2..2]. */
|
||||
light.tilemap_index = light.clipmap_lod_min;
|
||||
light._position = vec3(0.0);
|
||||
float lod_min_tile_size = exp2(float(light.clipmap_lod_min)) / float(SHADOW_TILEMAP_RES);
|
||||
float lod_max_half_size = exp2(float(light.clipmap_lod_max)) / 2.0;
|
||||
|
||||
camera_lP = vec3(0.0, 0.0, 0.0);
|
||||
/* Follows ShadowDirectional::end_sync(). */
|
||||
light.clipmap_base_offset = ivec2(round(camera_lP.xy / lod_min_tile_size));
|
||||
EXPECT_EQ(light.clipmap_base_offset, ivec2(0));
|
||||
|
||||
/* Test UVs and tile mapping. */
|
||||
|
||||
lP = vec3(1e-5, 1e-5, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
|
||||
lP = vec3(-1e-5, -1e-5, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2((SHADOW_TILEMAP_RES / 2) - 1));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
|
||||
lP = vec3(-0.5, -0.5, 0.0); /* Min of first LOD. */
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(0));
|
||||
EXPECT_NEAR(coords.uv, vec2(0), 1e-3);
|
||||
|
||||
lP = vec3(0.5, 0.5, 0.0); /* Max of first LOD. */
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES - 1));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES), 1e-3);
|
||||
|
||||
/* Test clipmap level selection. */
|
||||
|
||||
camera_lP = vec3(2.0, 2.0, 0.0);
|
||||
/* Follows ShadowDirectional::end_sync(). */
|
||||
light.clipmap_base_offset = ivec2(round(camera_lP.xy / lod_min_tile_size));
|
||||
EXPECT_EQ(light.clipmap_base_offset, ivec2(32));
|
||||
|
||||
lP = vec3(2.00001, 2.00001, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
|
||||
lP = vec3(1.50001, 1.50001, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 1);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 4));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 4), 1e-3);
|
||||
|
||||
lP = vec3(1.00001, 1.00001, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 2);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 4));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 4), 1e-3);
|
||||
|
||||
lP = vec3(-0.0001, -0.0001, 0.0); /* Out of bounds. */
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 2);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(0));
|
||||
EXPECT_NEAR(coords.uv, vec2(0), 1e-3);
|
||||
|
||||
/* Test clipmap offset. */
|
||||
|
||||
light.clipmap_base_offset = ivec2(31, 1);
|
||||
lP = vec3(2.0001, 0.0001, 0.0);
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, -1));
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, 0));
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, 0));
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, 0));
|
||||
|
||||
/* Test clipmap negative offsets. */
|
||||
|
||||
light.clipmap_base_offset = ivec2(-31, -1);
|
||||
lP = vec3(-2.0001, -0.0001, 0.0);
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 1));
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 0));
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 0));
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 0));
|
||||
}
|
||||
TEST(eevee_shadow, DirectionalCascadeCoordinates)
|
||||
{
|
||||
ShadowCoordinates coords;
|
||||
vec3 lP, camera_lP;
|
||||
|
||||
LightData light;
|
||||
light.type = LIGHT_SUN_ORTHO;
|
||||
light.clipmap_lod_min = 0; /* Range [-0.5..0.5]. */
|
||||
light.clipmap_lod_max = 2; /* 3 tilemaps. */
|
||||
light.tilemap_index = 1;
|
||||
light._position = vec3(0.0);
|
||||
light._clipmap_lod_bias = light.clipmap_lod_min - 1;
|
||||
light._clipmap_origin_x = 0.0;
|
||||
light._clipmap_origin_y = 0.0;
|
||||
float lod_tile_size = exp2(float(light.clipmap_lod_min)) / float(SHADOW_TILEMAP_RES);
|
||||
float lod_half_size = exp2(float(light.clipmap_lod_min)) / 2.0;
|
||||
float narrowing = float(SHADOW_TILEMAP_RES - 1) / float(SHADOW_TILEMAP_RES);
|
||||
|
||||
camera_lP = vec3(0.0, 0.0, 0.0);
|
||||
int level_range_size = light.clipmap_lod_max - light.clipmap_lod_min + 1;
|
||||
vec2 farthest_tilemap_center = vec2(lod_half_size * float(level_range_size - 1), 0.0);
|
||||
light.clipmap_base_offset = floatBitsToInt(
|
||||
vec2(lod_half_size / float(level_range_size - 1), 0.0));
|
||||
|
||||
/* Test UVs and tile mapping. */
|
||||
|
||||
lP = vec3(1e-8, 1e-8, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 1);
|
||||
EXPECT_EQ(coords.lod_relative, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
|
||||
lP = vec3(lod_half_size * narrowing - 1e-5, 1e-8, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 1);
|
||||
EXPECT_EQ(coords.lod_relative, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES - 1, SHADOW_TILEMAP_RES / 2));
|
||||
EXPECT_NEAR(coords.uv, vec2(float(SHADOW_TILEMAP_RES) - 0.5, SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
|
||||
lP = vec3(lod_half_size + 1e-5, 1e-5, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 2);
|
||||
EXPECT_EQ(coords.lod_relative, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES - 1, SHADOW_TILEMAP_RES / 2));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES, SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
|
||||
// lP = vec3(-0.5, -0.5, 0.0); /* Min of first LOD. */
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tilemap_index, 0);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(0));
|
||||
// EXPECT_NEAR(coords.uv, vec2(0), 1e-3);
|
||||
|
||||
// lP = vec3(0.5, 0.5, 0.0); /* Max of first LOD. */
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tilemap_index, 0);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES - 1));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES), 1e-3);
|
||||
|
||||
/* Test clipmap level selection. */
|
||||
|
||||
// camera_lP = vec3(2.0, 2.0, 0.0);
|
||||
/* Follows ShadowDirectional::end_sync(). */
|
||||
// light.clipmap_base_offset = ivec2(round(camera_lP.xy / lod_min_tile_size));
|
||||
// EXPECT_EQ(light.clipmap_base_offset, ivec2(32));
|
||||
|
||||
// lP = vec3(2.00001, 2.00001, 0.0);
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tilemap_index, 0);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
|
||||
// lP = vec3(1.50001, 1.50001, 0.0);
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tilemap_index, 1);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 4));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 4), 1e-3);
|
||||
|
||||
// lP = vec3(1.00001, 1.00001, 0.0);
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tilemap_index, 2);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 4));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 4), 1e-3);
|
||||
|
||||
// lP = vec3(-0.0001, -0.0001, 0.0); /* Out of bounds. */
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tilemap_index, 2);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(0));
|
||||
// EXPECT_NEAR(coords.uv, vec2(0), 1e-3);
|
||||
|
||||
/* Test clipmap offset. */
|
||||
|
||||
// light.clipmap_base_offset = ivec2(31, 1);
|
||||
// lP = vec3(2.0001, 0.0001, 0.0);
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, -1));
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, 0));
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, 0));
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, 0));
|
||||
|
||||
/* Test clipmap negative offsets. */
|
||||
|
||||
// light.clipmap_base_offset = ivec2(-31, -1);
|
||||
// lP = vec3(-2.0001, -0.0001, 0.0);
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 1));
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 0));
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 0));
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 0));
|
||||
}
|
||||
|
||||
TEST(eevee_shadow, DirectionalSlopeBias)
|
||||
{
|
||||
float near = 0.0, far = 1.0;
|
||||
LightData light;
|
||||
light.type = LIGHT_SUN;
|
||||
light.clip_near = floatBitsToInt(near);
|
||||
light.clip_far = floatBitsToInt(far);
|
||||
light.clipmap_lod_min = 0;
|
||||
|
||||
/* Position has no effect for directionnal. */
|
||||
vec3 lP = vec3(0.0);
|
||||
vec2 atlas_size = vec2(SHADOW_TILEMAP_RES);
|
||||
{
|
||||
vec3 lNg = vec3(0.0, 0.0, 1.0);
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP, vec2(0.0), 0), 0.0, 3e-7);
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP, vec2(0.0), 1), 0.0, 3e-7);
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP, vec2(0.0), 2), 0.0, 3e-7);
|
||||
}
|
||||
{
|
||||
vec3 lNg = normalize(vec3(0.0, 1.0, 1.0));
|
||||
float expect = 1.0 / (SHADOW_TILEMAP_RES * SHADOW_PAGE_RES);
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP, vec2(0.0), 0), expect, 3e-7);
|
||||
EXPECT_NEAR(
|
||||
shadow_slope_bias_get(atlas_size, light, lNg, lP, vec2(0.0), 1), expect * 2.0, 3e-7);
|
||||
EXPECT_NEAR(
|
||||
shadow_slope_bias_get(atlas_size, light, lNg, lP, vec2(0.0), 2), expect * 4.0, 3e-7);
|
||||
}
|
||||
{
|
||||
vec3 lNg = normalize(vec3(1.0, 1.0, 1.0));
|
||||
float expect = 2.0 / (SHADOW_TILEMAP_RES * SHADOW_PAGE_RES);
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP, vec2(0.0), 0), expect, 3e-7);
|
||||
EXPECT_NEAR(
|
||||
shadow_slope_bias_get(atlas_size, light, lNg, lP, vec2(0.0), 1), expect * 2.0, 3e-7);
|
||||
EXPECT_NEAR(
|
||||
shadow_slope_bias_get(atlas_size, light, lNg, lP, vec2(0.0), 2), expect * 4.0, 3e-7);
|
||||
}
|
||||
light.clipmap_lod_min = -1;
|
||||
{
|
||||
vec3 lNg = normalize(vec3(1.0, 1.0, 1.0));
|
||||
float expect = 0.5 * (2.0 / (SHADOW_TILEMAP_RES * SHADOW_PAGE_RES));
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP, vec2(0.0), 0), expect, 3e-7);
|
||||
EXPECT_NEAR(
|
||||
shadow_slope_bias_get(atlas_size, light, lNg, lP, vec2(0.0), 1), expect * 2.0, 3e-7);
|
||||
EXPECT_NEAR(
|
||||
shadow_slope_bias_get(atlas_size, light, lNg, lP, vec2(0.0), 2), expect * 4.0, 3e-7);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(eevee_shadow, PunctualSlopeBias)
|
||||
{
|
||||
float near = 0.5, far = 1.0;
|
||||
mat4 pers_mat = projection_perspective(-near, near, -near, near, near, far);
|
||||
mat4 normal_mat = invert(transpose(pers_mat));
|
||||
|
||||
LightData light;
|
||||
light.clip_near = floatBitsToInt(near);
|
||||
light.clip_far = floatBitsToInt(far);
|
||||
light.influence_radius_max = far;
|
||||
light.type = LIGHT_SPOT;
|
||||
light.normal_mat_packed.x = normal_mat[3][2];
|
||||
light.normal_mat_packed.y = normal_mat[3][3];
|
||||
|
||||
vec2 atlas_size = vec2(SHADOW_TILEMAP_RES);
|
||||
{
|
||||
/* Simulate a "2D" plane crossing the frustum diagonaly. */
|
||||
vec3 lP0 = vec3(-1.0, 0.0, -1.0);
|
||||
vec3 lP1 = vec3(0.5, 0.0, -0.5);
|
||||
vec3 lTg = normalize(lP1 - lP0);
|
||||
vec3 lNg = vec3(-lTg.z, 0.0, lTg.x);
|
||||
|
||||
float expect = 1.0 / (SHADOW_TILEMAP_RES * SHADOW_PAGE_RES);
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 0), expect, 1e-4);
|
||||
EXPECT_NEAR(
|
||||
shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 1), expect * 2.0, 1e-4);
|
||||
EXPECT_NEAR(
|
||||
shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 2), expect * 4.0, 1e-4);
|
||||
}
|
||||
{
|
||||
/* Simulate a "2D" plane crossing the near plane at the center diagonaly. */
|
||||
vec3 lP0 = vec3(-1.0, 0.0, -1.0);
|
||||
vec3 lP1 = vec3(0.0, 0.0, -0.5);
|
||||
vec3 lTg = normalize(lP1 - lP0);
|
||||
vec3 lNg = vec3(-lTg.z, 0.0, lTg.x);
|
||||
|
||||
float expect = 2.0 / (SHADOW_TILEMAP_RES * SHADOW_PAGE_RES);
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 0), expect, 1e-4);
|
||||
EXPECT_NEAR(
|
||||
shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 1), expect * 2.0, 1e-4);
|
||||
EXPECT_NEAR(
|
||||
shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 2), expect * 4.0, 1e-4);
|
||||
}
|
||||
{
|
||||
/* Simulate a "2D" plane parallel to near clip plane. */
|
||||
vec3 lP0 = vec3(-1.0, 0.0, -0.75);
|
||||
vec3 lP1 = vec3(0.0, 0.0, -0.75);
|
||||
vec3 lTg = normalize(lP1 - lP0);
|
||||
vec3 lNg = vec3(-lTg.z, 0.0, lTg.x);
|
||||
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 0), 0.0, 1e-4);
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 1), 0.0, 1e-4);
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 2), 0.0, 1e-4);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
|
||||
/**
|
||||
* Virtual shadowmapping: Bounds computation for directional shadows.
|
||||
*
|
||||
* Iterate through all shadow casters and extract min/max per directional shadow.
|
||||
* This needs to happen first in the pipeline to allow tagging all relevant tilemap as dirty if
|
||||
* their range changes.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_intersect_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
|
||||
|
||||
shared int global_min;
|
||||
shared int global_max;
|
||||
|
||||
void main()
|
||||
{
|
||||
uint index = gl_GlobalInvocationID.x;
|
||||
/* Keep uniform control flow. Do not return. */
|
||||
index = min(index, uint(resource_len) - 1);
|
||||
|
||||
uint resource_id = casters_id_buf[index];
|
||||
ObjectBounds bounds = bounds_buf[resource_id];
|
||||
IsectBox box = isect_data_setup(bounds.bounding_corners[0].xyz,
|
||||
bounds.bounding_corners[1].xyz,
|
||||
bounds.bounding_corners[2].xyz,
|
||||
bounds.bounding_corners[3].xyz);
|
||||
|
||||
LIGHT_FOREACH_BEGIN_DIRECTIONAL(light_cull_buf, l_idx)
|
||||
{
|
||||
LightData light = light_buf[l_idx];
|
||||
|
||||
float local_min = FLT_MAX;
|
||||
float local_max = -FLT_MAX;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
float z = dot(box.corners[i].xyz, light._back);
|
||||
local_min = min(local_min, z);
|
||||
local_max = max(local_max, z);
|
||||
}
|
||||
|
||||
if (gl_LocalInvocationID.x == 0) {
|
||||
global_min = floatBitsToOrderedInt(FLT_MAX);
|
||||
global_max = floatBitsToOrderedInt(-FLT_MAX);
|
||||
}
|
||||
|
||||
barrier();
|
||||
|
||||
/* Quantization bias. */
|
||||
local_min -= abs(local_min) * 0.01;
|
||||
local_max += abs(local_max) * 0.01;
|
||||
|
||||
/* Intermediate result. Min/Max of a compute group. */
|
||||
atomicMin(global_min, floatBitsToOrderedInt(local_min));
|
||||
atomicMax(global_max, floatBitsToOrderedInt(local_max));
|
||||
|
||||
barrier();
|
||||
|
||||
if (gl_LocalInvocationID.x == 0) {
|
||||
/* Final result. Min/Max of the whole dispatch. */
|
||||
atomicMin(light_buf[l_idx].clip_far, global_min);
|
||||
atomicMax(light_buf[l_idx].clip_near, global_max);
|
||||
/* TODO(fclem): This feel unecessary but we currently have no indexing from
|
||||
* tilemap to lights. This is because the lights are selected by culling phase. */
|
||||
for (int i = light.tilemap_index; i <= light_tilemap_max_get(light); i++) {
|
||||
int index = tilemaps_buf[i].clip_data_index;
|
||||
atomicMin(tilemaps_clip_buf[index].clip_far, global_min);
|
||||
atomicMax(tilemaps_clip_buf[index].clip_near, global_max);
|
||||
}
|
||||
}
|
||||
|
||||
/* No need for barrier here since global_min/max is only read by thread 0 before being reset by
|
||||
* thread 0. */
|
||||
}
|
||||
LIGHT_FOREACH_END
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
|
||||
/**
|
||||
* Virtual shadowmapping: Tilemap to texture conversion.
|
||||
*
|
||||
* For all visible light tilemaps, copy page coordinate to a texture.
|
||||
* This avoids one level of indirection when evaluating shadows and allows
|
||||
* to use a sampler instead of a SSBO bind.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
|
||||
|
||||
shared uint tile_updates_count;
|
||||
shared int view_index;
|
||||
|
||||
void page_clear_buf_append(uint page_packed)
|
||||
{
|
||||
uint clear_page_index = atomicAdd(clear_dispatch_buf.num_groups_z, 1u);
|
||||
clear_page_buf[clear_page_index] = page_packed;
|
||||
}
|
||||
|
||||
void page_tag_as_rendered(ivec2 tile_co, int tiles_index, int lod)
|
||||
{
|
||||
int tile_index = shadow_tile_offset(tile_co, tiles_index, lod);
|
||||
tiles_buf[tile_index] |= SHADOW_IS_RENDERED;
|
||||
atomicAdd(statistics_buf.page_rendered_count, 1);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (all(equal(gl_LocalInvocationID, uvec3(0)))) {
|
||||
tile_updates_count = uint(0);
|
||||
}
|
||||
barrier();
|
||||
|
||||
int tilemap_index = int(gl_GlobalInvocationID.z);
|
||||
ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
ivec2 atlas_texel = shadow_tile_coord_in_atlas(tile_co, tilemap_index);
|
||||
|
||||
ShadowTileMapData tilemap_data = tilemaps_buf[tilemap_index];
|
||||
int lod_max = (tilemap_data.projection_type == SHADOW_PROJECTION_CUBEFACE) ? SHADOW_TILEMAP_LOD :
|
||||
0;
|
||||
|
||||
int lod_valid = 0;
|
||||
/* One bit per lod. */
|
||||
int do_lod_update = 0;
|
||||
/* Packed page (packUvec2x16) to render per LOD. */
|
||||
uint updated_lod_page[SHADOW_TILEMAP_LOD + 1];
|
||||
uvec2 page_valid;
|
||||
/* With all threads (LOD0 size dispatch) load each lod tile from the highest lod
|
||||
* to the lowest, keeping track of the lowest one allocated which will be use for shadowing.
|
||||
* Also save which page are to be updated. */
|
||||
for (int lod = SHADOW_TILEMAP_LOD; lod >= 0; lod--) {
|
||||
if (lod > lod_max) {
|
||||
updated_lod_page[lod] = 0xFFFFFFFFu;
|
||||
continue;
|
||||
}
|
||||
|
||||
int tile_index = shadow_tile_offset(tile_co >> lod, tilemap_data.tiles_index, lod);
|
||||
|
||||
ShadowTileData tile = shadow_tile_unpack(tiles_buf[tile_index]);
|
||||
|
||||
if (tile.is_used && tile.do_update) {
|
||||
do_lod_update = 1 << lod;
|
||||
updated_lod_page[lod] = packUvec2x16(tile.page);
|
||||
}
|
||||
else {
|
||||
updated_lod_page[lod] = 0xFFFFFFFFu;
|
||||
}
|
||||
|
||||
/* Save highest lod for this thread. */
|
||||
if (tile.is_used && lod > 0) {
|
||||
/* Reload the page in case there was an allocation in the valid thread. */
|
||||
page_valid = tile.page;
|
||||
lod_valid = lod;
|
||||
}
|
||||
else if (lod == 0 && lod_valid != 0 && !tile.is_allocated) {
|
||||
/* If the tile is not used, store the valid LOD level in LOD0. */
|
||||
tile.page = page_valid;
|
||||
tile.lod = lod_valid;
|
||||
/* This is not a real ownership. It is just a tag so that the shadowing is deemed correct. */
|
||||
tile.is_allocated = true;
|
||||
}
|
||||
|
||||
if (lod == 0) {
|
||||
imageStore(tilemaps_img, atlas_texel, uvec4(shadow_tile_pack(tile)));
|
||||
}
|
||||
}
|
||||
|
||||
if (do_lod_update > 0) {
|
||||
atomicAdd(tile_updates_count, 1u);
|
||||
}
|
||||
|
||||
barrier();
|
||||
|
||||
if (all(equal(gl_LocalInvocationID, uvec3(0)))) {
|
||||
/* No update by default. */
|
||||
view_index = 64;
|
||||
|
||||
if (tile_updates_count > 0) {
|
||||
view_index = atomicAdd(pages_infos_buf.view_count, 1);
|
||||
if (view_index < 64) {
|
||||
view_infos_buf[view_index].viewmat = tilemap_data.viewmat;
|
||||
view_infos_buf[view_index].viewinv = inverse(tilemap_data.viewmat);
|
||||
|
||||
if (tilemap_data.projection_type != SHADOW_PROJECTION_CUBEFACE) {
|
||||
int clip_index = tilemap_data.clip_data_index;
|
||||
/* For directionnal, we need to modify winmat to encompass all casters. */
|
||||
float clip_far = -tilemaps_clip_buf[clip_index].clip_far_stored;
|
||||
float clip_near = -tilemaps_clip_buf[clip_index].clip_near_stored;
|
||||
tilemap_data.winmat[2][2] = -2.0 / (clip_far - clip_near);
|
||||
tilemap_data.winmat[3][2] = -(clip_far + clip_near) / (clip_far - clip_near);
|
||||
}
|
||||
view_infos_buf[view_index].winmat = tilemap_data.winmat;
|
||||
view_infos_buf[view_index].wininv = inverse(tilemap_data.winmat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
barrier();
|
||||
|
||||
if (view_index < 64) {
|
||||
ivec3 render_map_texel = ivec3(tile_co, view_index);
|
||||
|
||||
/* Store page indirection for rendering. Update every texel in the view array level. */
|
||||
if (true) {
|
||||
imageStore(render_map_lod0_img, render_map_texel, uvec4(updated_lod_page[0]));
|
||||
if (updated_lod_page[0] != 0xFFFFFFFFu) {
|
||||
page_clear_buf_append(updated_lod_page[0]);
|
||||
page_tag_as_rendered(render_map_texel.xy, tilemap_data.tiles_index, 0);
|
||||
}
|
||||
}
|
||||
render_map_texel.xy >>= 1;
|
||||
if (all(equal(tile_co, render_map_texel.xy << 1u))) {
|
||||
imageStore(render_map_lod1_img, render_map_texel, uvec4(updated_lod_page[1]));
|
||||
if (updated_lod_page[1] != 0xFFFFFFFFu) {
|
||||
page_clear_buf_append(updated_lod_page[1]);
|
||||
page_tag_as_rendered(render_map_texel.xy, tilemap_data.tiles_index, 1);
|
||||
}
|
||||
}
|
||||
render_map_texel.xy >>= 1;
|
||||
if (all(equal(tile_co, render_map_texel.xy << 2u))) {
|
||||
imageStore(render_map_lod2_img, render_map_texel, uvec4(updated_lod_page[2]));
|
||||
if (updated_lod_page[2] != 0xFFFFFFFFu) {
|
||||
page_clear_buf_append(updated_lod_page[2]);
|
||||
page_tag_as_rendered(render_map_texel.xy, tilemap_data.tiles_index, 2);
|
||||
}
|
||||
}
|
||||
render_map_texel.xy >>= 1;
|
||||
if (all(equal(tile_co, render_map_texel.xy << 3u))) {
|
||||
imageStore(render_map_lod3_img, render_map_texel, uvec4(updated_lod_page[3]));
|
||||
if (updated_lod_page[3] != 0xFFFFFFFFu) {
|
||||
page_clear_buf_append(updated_lod_page[3]);
|
||||
page_tag_as_rendered(render_map_texel.xy, tilemap_data.tiles_index, 3);
|
||||
}
|
||||
}
|
||||
render_map_texel.xy >>= 1;
|
||||
if (all(equal(tile_co, render_map_texel.xy << 4u))) {
|
||||
imageStore(render_map_lod4_img, render_map_texel, uvec4(updated_lod_page[4]));
|
||||
if (updated_lod_page[4] != 0xFFFFFFFFu) {
|
||||
page_clear_buf_append(updated_lod_page[4]);
|
||||
page_tag_as_rendered(render_map_texel.xy, tilemap_data.tiles_index, 4);
|
||||
}
|
||||
}
|
||||
render_map_texel.xy >>= 1;
|
||||
if (all(equal(tile_co, render_map_texel.xy << 5u))) {
|
||||
imageStore(render_map_lod5_img, render_map_texel, uvec4(updated_lod_page[5]));
|
||||
if (updated_lod_page[5] != 0xFFFFFFFFu) {
|
||||
page_clear_buf_append(updated_lod_page[5]);
|
||||
page_tag_as_rendered(render_map_texel.xy, tilemap_data.tiles_index, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (all(equal(gl_GlobalInvocationID, uvec3(0)))) {
|
||||
/* Clamp it as it can underflow if there is too much tile present on screen. */
|
||||
pages_infos_buf.page_free_count = max(pages_infos_buf.page_free_count, 0);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue