Compare commits
273 Commits
tmp-workbe
...
tmp-eevee-
Author | SHA1 | Date | |
---|---|---|---|
9cc6d1dc32 | |||
8e7b535ce6 | |||
9b35b77716 | |||
423bbbbaae | |||
![]() |
3775615aea | ||
ea577c499f | |||
0ea5f3fb5d | |||
a21f1e81e0 | |||
37cfceb7eb | |||
87c13ac68c | |||
4710f3346a | |||
74d68e50d3 | |||
89dbad9085 | |||
834b966b41 | |||
d7ac659e02 | |||
9c341153a2 | |||
279a73e429 | |||
32a96b80a3 | |||
25ac6aa5e4 | |||
c75f7143b6 | |||
658ae5d63f | |||
ea214f128e | |||
848d35c57e | |||
![]() |
58e0aa36ea | ||
a3c416f864 | |||
99ffe1153a | |||
6c483479b8 | |||
d0e86c1b78 | |||
![]() |
c85c52f2ce | ||
d518550c46 | |||
aa2164da33 | |||
5730668dea | |||
8a20aec403 | |||
5400018106 | |||
338c0e5f90 | |||
6f1ab97c53 | |||
78f29c0467 | |||
1f2b3cdd73 | |||
032baf06f3 | |||
0379ddac7d | |||
cf31c4ba18 | |||
a99e43b2b7 | |||
5abab0a41a | |||
a7f7b0b77e | |||
1edf520439 | |||
0e86c60c28 | |||
0bdf574ea2 | |||
bb1e2a80e4 | |||
4226c484bd | |||
710e46cb2d | |||
cab1f1d9a2 | |||
af87b6d8cb | |||
7b615ca186 | |||
9dc0379dc0 | |||
f6296e502a | |||
fdd84d36ce | |||
b57db4b79e | |||
fc0dd5583c | |||
08e2885796 | |||
da1b6c4c02 | |||
d74c2b5c1f | |||
57dfec79f4 | |||
13f2df3c28 | |||
489b484b7b | |||
b42adab3a2 | |||
e729abb0e2 | |||
![]() |
74afc86d4b | ||
![]() |
ae44070341 | ||
![]() |
a9bb460766 | ||
974981a637 | |||
79927e730e | |||
990ed109f2 | |||
37848d1c8e | |||
12b26d21b0 | |||
6738ecb64e | |||
5b299e5999 | |||
9bce134e56 | |||
1bf6a880ab | |||
![]() |
a21bca0e20 | ||
b3bf46b78d | |||
55a6a8900a | |||
36bec765e2 | |||
0a7c4afd21 | |||
021bf5b171 | |||
181bc60214 | |||
b23d9519d0 | |||
7303a453aa | |||
26335dfc57 | |||
1b00ca3575 | |||
68b0195bf3 | |||
68602f3d87 | |||
a8a4f5f805 | |||
9bd070fbc2 | |||
7664e1dd79 | |||
55af3361bf | |||
fbfbc9f15d | |||
bf42246984 | |||
761ba97601 | |||
ebb2b1120c | |||
2b61ca8f34 | |||
9db0734a1e | |||
9c4cf35414 | |||
c92b2d8bc4 | |||
2a6a06fab7 | |||
fc6a430d8e | |||
c746eeae93 | |||
e5fadd8c84 | |||
682e1bea7e | |||
75db7522c6 | |||
96d354a84d | |||
5905b11c07 | |||
a97234574d | |||
282a0c90d1 | |||
b8ab3f2f52 | |||
92a92fc60f | |||
52a81b8175 | |||
55a85af05d | |||
3db3006d4c | |||
008fb3eed9 | |||
b304514bd5 | |||
b0da401292 | |||
ee7deb09cf | |||
9f85107fef | |||
bcd5bd6cd5 | |||
42d2c96d4c | |||
941fdefdb3 | |||
a0df3c4d51 | |||
6f773e289b | |||
d5f91a68c0 | |||
4984cba10d | |||
e28ae32461 | |||
59a0099b9f | |||
225c1b0708 | |||
f8cfd7e288 | |||
dc0c074ac4 | |||
2994b6dd98 | |||
ab6a6ffed4 | |||
d3a825678a | |||
41c84bb08b | |||
9711cddbe0 | |||
85b6e6da4a | |||
c51604e308 | |||
9207920c1e | |||
f79788f767 | |||
c4287db151 | |||
62aa72dac8 | |||
0053d2fc81 | |||
6206a30519 | |||
e3ff83a4a8 | |||
80b92467f0 | |||
169a2a54d7 | |||
d8ec228a76 | |||
27adad0b0d | |||
c4a3ba6f83 | |||
e962002db2 | |||
3ad7832a8d | |||
209ab8c424 | |||
94f813db70 | |||
c844497aee | |||
a6ae942540 | |||
1f262a461c | |||
4c816924e7 | |||
a1459e1fcf | |||
4260823e1e | |||
89a002c4e3 | |||
6c1e7868c7 | |||
9b153666e7 | |||
93881a2a8e | |||
04f053c6a4 | |||
d66b98e9c8 | |||
ae529ed93e | |||
581cb48c9e | |||
dc64186d75 | |||
308d42d072 | |||
e6d94b83ba | |||
3caf7ba32d | |||
b3084d23bf | |||
7f5d787952 | |||
79a5322fa4 | |||
92aedc5eda | |||
33ff463ea1 | |||
7d3f65a044 | |||
6d3c7a8281 | |||
8e6deba985 | |||
d31e74d3f8 | |||
bdcf0ccead | |||
c8b40c5fd6 | |||
2dc9db65d7 | |||
ff00c1d6eb | |||
1f5c3c9d74 | |||
d87161e574 | |||
14df74ea8b | |||
060c462f3a | |||
25dd16a8cd | |||
0fb1621594 | |||
5fb1b27d17 | |||
6376b575d9 | |||
31963c8d86 | |||
4495060185 | |||
f4dbdd7b52 | |||
e19c028cc4 | |||
f04011dd87 | |||
1bc0a70d94 | |||
2b6c70a780 | |||
113c16d7a9 | |||
06ca1818d0 | |||
6c0d8c4b75 | |||
44bb4be66c | |||
f7f1ee9e99 | |||
e91df656f5 | |||
10cf16270a | |||
7d36a00d14 | |||
9a857d83a6 | |||
25806227e8 | |||
6dc49ec992 | |||
0c71240f14 | |||
f46661428b | |||
4500a90cdc | |||
99a5d49a38 | |||
c59156dac7 | |||
c7fb0d5c7b | |||
f1a5c5f6cb | |||
556478c20e | |||
5df8d5af8a | |||
1d3de154e2 | |||
4090bac8c8 | |||
0932d508c8 | |||
0fdd8a64b4 | |||
9dddfe2ef6 | |||
e808500ba1 | |||
2fd359684d | |||
da91f87764 | |||
610294205f | |||
5697f96608 | |||
93b774a661 | |||
bf0ca28494 | |||
89af2b0485 | |||
ddc1be5556 | |||
23584ee52f | |||
520962b3d6 | |||
017e9d852e | |||
ab55a1b67b | |||
9bf6fa974d | |||
431a662f4f | |||
9c74f9322d | |||
a4ae2b91c9 | |||
0cd4896037 | |||
e540c9c47a | |||
f9b15edbde | |||
1857fa8bc9 | |||
ac6e2b0083 | |||
d18e74d822 | |||
8c753a2c80 | |||
2a7d9d4515 | |||
89f2d3427e | |||
309b90c955 | |||
a766ee6d5d | |||
ebf455daba | |||
fe9ea5d5cb | |||
8a2f400cf3 | |||
448d10a31a | |||
e0e1dd73bb | |||
fa88f5af4c | |||
fcb85633ea | |||
935da83e2c | |||
ef174ffcb3 | |||
ad23570fa2 | |||
8777497c4b | |||
25af8b801d | |||
829e2e4a24 | |||
09e1f276ff | |||
81632de706 | |||
f7cb19956f |
@@ -264,6 +264,9 @@ ForEachMacros:
|
||||
- SET_SLOT_PROBING_BEGIN
|
||||
- MAP_SLOT_PROBING_BEGIN
|
||||
- VECTOR_SET_SLOT_PROBING_BEGIN
|
||||
- LIGHT_FOREACH_BEGIN_DIRECTIONAL
|
||||
- LIGHT_FOREACH_BEGIN_LOCAL
|
||||
- LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL
|
||||
|
||||
StatementMacros:
|
||||
- PyObject_HEAD
|
||||
|
@@ -31,7 +31,7 @@ ExternalProject_Add(external_python_site_packages
|
||||
CONFIGURE_COMMAND ${PIP_CONFIGURE_COMMAND}
|
||||
BUILD_COMMAND ""
|
||||
PREFIX ${BUILD_DIR}/site_packages
|
||||
INSTALL_COMMAND ${PYTHON_BINARY} -m pip install ${SITE_PACKAGES_EXTRA} cython==${CYTHON_VERSION} idna==${IDNA_VERSION} charset-normalizer==${CHARSET_NORMALIZER_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} zstandard==${ZSTANDARD_VERSION} --no-binary :all:
|
||||
INSTALL_COMMAND ${PYTHON_BINARY} -m pip install --no-cache-dir ${SITE_PACKAGES_EXTRA} cython==${CYTHON_VERSION} idna==${IDNA_VERSION} charset-normalizer==${CHARSET_NORMALIZER_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} zstandard==${ZSTANDARD_VERSION} --no-binary :all:
|
||||
)
|
||||
|
||||
if(USE_PIP_NUMPY)
|
||||
|
@@ -189,11 +189,11 @@ set(OSL_HASH 1abd7ce40481771a9fa937f19595d2f2)
|
||||
set(OSL_HASH_TYPE MD5)
|
||||
set(OSL_FILE OpenShadingLanguage-${OSL_VERSION}.tar.gz)
|
||||
|
||||
set(PYTHON_VERSION 3.9.7)
|
||||
set(PYTHON_SHORT_VERSION 3.9)
|
||||
set(PYTHON_SHORT_VERSION_NO_DOTS 39)
|
||||
set(PYTHON_VERSION 3.10.2)
|
||||
set(PYTHON_SHORT_VERSION 3.10)
|
||||
set(PYTHON_SHORT_VERSION_NO_DOTS 310)
|
||||
set(PYTHON_URI https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz)
|
||||
set(PYTHON_HASH fddb060b483bc01850a3f412eea1d954)
|
||||
set(PYTHON_HASH 14e8c22458ed7779a1957b26cde01db9)
|
||||
set(PYTHON_HASH_TYPE MD5)
|
||||
set(PYTHON_FILE Python-${PYTHON_VERSION}.tar.xz)
|
||||
|
||||
@@ -215,18 +215,20 @@ set(NANOVDB_HASH e7b9e863ec2f3b04ead171dec2322807)
|
||||
set(NANOVDB_HASH_TYPE MD5)
|
||||
set(NANOVDB_FILE nano-vdb-${NANOVDB_GIT_UID}.tar.gz)
|
||||
|
||||
set(IDNA_VERSION 3.2)
|
||||
set(CHARSET_NORMALIZER_VERSION 2.0.6)
|
||||
set(URLLIB3_VERSION 1.26.7)
|
||||
set(IDNA_VERSION 3.3)
|
||||
set(CHARSET_NORMALIZER_VERSION 2.0.10)
|
||||
set(URLLIB3_VERSION 1.26.8)
|
||||
set(CERTIFI_VERSION 2021.10.8)
|
||||
set(REQUESTS_VERSION 2.26.0)
|
||||
set(CYTHON_VERSION 0.29.24)
|
||||
set(ZSTANDARD_VERSION 0.15.2 )
|
||||
set(REQUESTS_VERSION 2.27.1)
|
||||
set(CYTHON_VERSION 0.29.26)
|
||||
# The version of the zstd library used to build the Python package should match ZSTD_VERSION defined below.
|
||||
# At this time of writing, 0.17.0 was already released, but built against zstd 1.5.1, while we use 1.5.0.
|
||||
set(ZSTANDARD_VERSION 0.16.0)
|
||||
|
||||
set(NUMPY_VERSION 1.21.2)
|
||||
set(NUMPY_SHORT_VERSION 1.21)
|
||||
set(NUMPY_VERSION 1.22.0)
|
||||
set(NUMPY_SHORT_VERSION 1.22)
|
||||
set(NUMPY_URI https://github.com/numpy/numpy/releases/download/v${NUMPY_VERSION}/numpy-${NUMPY_VERSION}.zip)
|
||||
set(NUMPY_HASH 5638d5dae3ca387be562912312db842e)
|
||||
set(NUMPY_HASH 252de134862a27bd66705d29622edbfe)
|
||||
set(NUMPY_HASH_TYPE MD5)
|
||||
set(NUMPY_FILE numpy-${NUMPY_VERSION}.zip)
|
||||
|
||||
|
@@ -379,27 +379,27 @@ USE_CXX11=true
|
||||
CLANG_FORMAT_VERSION_MIN="6.0"
|
||||
CLANG_FORMAT_VERSION_MEX="10.0"
|
||||
|
||||
PYTHON_VERSION="3.9.7"
|
||||
PYTHON_VERSION_SHORT="3.9"
|
||||
PYTHON_VERSION_MIN="3.7"
|
||||
PYTHON_VERSION_MEX="3.11"
|
||||
PYTHON_VERSION="3.10.2"
|
||||
PYTHON_VERSION_SHORT="3.10"
|
||||
PYTHON_VERSION_MIN="3.9"
|
||||
PYTHON_VERSION_MEX="3.12"
|
||||
PYTHON_VERSION_INSTALLED=$PYTHON_VERSION_SHORT
|
||||
PYTHON_FORCE_BUILD=false
|
||||
PYTHON_FORCE_REBUILD=false
|
||||
PYTHON_SKIP=false
|
||||
|
||||
# Additional Python modules.
|
||||
PYTHON_IDNA_VERSION="3.2"
|
||||
PYTHON_IDNA_VERSION="3.3"
|
||||
PYTHON_IDNA_VERSION_MIN="2.0"
|
||||
PYTHON_IDNA_VERSION_MEX="4.0"
|
||||
PYTHON_IDNA_NAME="idna"
|
||||
|
||||
PYTHON_CHARSET_NORMALIZER_VERSION="2.0.6"
|
||||
PYTHON_CHARSET_NORMALIZER_VERSION="2.0.10"
|
||||
PYTHON_CHARSET_NORMALIZER_VERSION_MIN="2.0.6"
|
||||
PYTHON_CHARSET_NORMALIZER_VERSION_MEX="2.1.0" # requests uses `charset_normalizer~=2.0.0`
|
||||
PYTHON_CHARSET_NORMALIZER_NAME="charset-normalizer"
|
||||
|
||||
PYTHON_URLLIB3_VERSION="1.26.7"
|
||||
PYTHON_URLLIB3_VERSION="1.26.8"
|
||||
PYTHON_URLLIB3_VERSION_MIN="1.0"
|
||||
PYTHON_URLLIB3_VERSION_MEX="2.0"
|
||||
PYTHON_URLLIB3_NAME="urllib3"
|
||||
@@ -409,17 +409,17 @@ PYTHON_CERTIFI_VERSION_MIN="2021.0"
|
||||
PYTHON_CERTIFI_VERSION_MEX="2023.0"
|
||||
PYTHON_CERTIFI_NAME="certifi"
|
||||
|
||||
PYTHON_REQUESTS_VERSION="2.23.0"
|
||||
PYTHON_REQUESTS_VERSION="2.27.1"
|
||||
PYTHON_REQUESTS_VERSION_MIN="2.0"
|
||||
PYTHON_REQUESTS_VERSION_MEX="3.0"
|
||||
PYTHON_REQUESTS_NAME="requests"
|
||||
|
||||
PYTHON_ZSTANDARD_VERSION="0.15.2"
|
||||
PYTHON_ZSTANDARD_VERSION="0.16.0"
|
||||
PYTHON_ZSTANDARD_VERSION_MIN="0.15.2"
|
||||
PYTHON_ZSTANDARD_VERSION_MEX="0.16.0"
|
||||
PYTHON_ZSTANDARD_VERSION_MEX="0.20.0"
|
||||
PYTHON_ZSTANDARD_NAME="zstandard"
|
||||
|
||||
PYTHON_NUMPY_VERSION="1.21.2"
|
||||
PYTHON_NUMPY_VERSION="1.22.0"
|
||||
PYTHON_NUMPY_VERSION_MIN="1.14"
|
||||
PYTHON_NUMPY_VERSION_MEX="2.0"
|
||||
PYTHON_NUMPY_NAME="numpy"
|
||||
@@ -499,7 +499,7 @@ LLVM_FORCE_REBUILD=false
|
||||
LLVM_SKIP=false
|
||||
|
||||
# OSL needs to be compiled for now!
|
||||
OSL_VERSION="1.11.14.1"
|
||||
OSL_VERSION="1.11.17.0"
|
||||
OSL_VERSION_SHORT="1.11"
|
||||
OSL_VERSION_MIN="1.11"
|
||||
OSL_VERSION_MEX="2.0"
|
||||
|
@@ -128,25 +128,20 @@ if(WITH_CODEC_SNDFILE)
|
||||
endif()
|
||||
|
||||
if(WITH_PYTHON)
|
||||
# we use precompiled libraries for py 3.9 and up by default
|
||||
set(PYTHON_VERSION 3.9)
|
||||
# Use precompiled libraries by default.
|
||||
set(PYTHON_VERSION 3.10)
|
||||
if(NOT WITH_PYTHON_MODULE AND NOT WITH_PYTHON_FRAMEWORK)
|
||||
# normally cached but not since we include them with blender
|
||||
# Normally cached but not since we include them with blender.
|
||||
set(PYTHON_INCLUDE_DIR "${LIBDIR}/python/include/python${PYTHON_VERSION}")
|
||||
set(PYTHON_EXECUTABLE "${LIBDIR}/python/bin/python${PYTHON_VERSION}")
|
||||
set(PYTHON_LIBRARY ${LIBDIR}/python/lib/libpython${PYTHON_VERSION}.a)
|
||||
set(PYTHON_LIBPATH "${LIBDIR}/python/lib/python${PYTHON_VERSION}")
|
||||
# set(PYTHON_LINKFLAGS "-u _PyMac_Error") # won't build with this enabled
|
||||
else()
|
||||
# module must be compiled against Python framework
|
||||
# Module must be compiled against Python framework.
|
||||
set(_py_framework "/Library/Frameworks/Python.framework/Versions/${PYTHON_VERSION}")
|
||||
|
||||
set(PYTHON_INCLUDE_DIR "${_py_framework}/include/python${PYTHON_VERSION}")
|
||||
set(PYTHON_EXECUTABLE "${_py_framework}/bin/python${PYTHON_VERSION}")
|
||||
set(PYTHON_LIBPATH "${_py_framework}/lib/python${PYTHON_VERSION}")
|
||||
# set(PYTHON_LIBRARY python${PYTHON_VERSION})
|
||||
# set(PYTHON_LINKFLAGS "-u _PyMac_Error -framework Python") # won't build with this enabled
|
||||
|
||||
unset(_py_framework)
|
||||
endif()
|
||||
|
||||
|
@@ -109,9 +109,14 @@ if(NOT WITH_SYSTEM_FREETYPE)
|
||||
find_package_wrapper(Freetype REQUIRED)
|
||||
if(EXISTS ${LIBDIR})
|
||||
find_package_wrapper(Brotli REQUIRED)
|
||||
list(APPEND FREETYPE_LIBRARIES
|
||||
${BROTLI_LIBRARIES}
|
||||
)
|
||||
|
||||
# NOTE: This is done on WIN32 & APPLE but fails on some Linux systems.
|
||||
# See: https://devtalk.blender.org/t/22536
|
||||
# So `BROTLI_LIBRARIES` need to be added `FREETYPE_LIBRARIES`.
|
||||
#
|
||||
# list(APPEND FREETYPE_LIBRARIES
|
||||
# ${BROTLI_LIBRARIES}
|
||||
# )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@@ -55,6 +55,10 @@ if(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
message(WARNING "stripped pdb not supported with clang, disabling..")
|
||||
set(WITH_WINDOWS_STRIPPED_PDB OFF)
|
||||
endif()
|
||||
else()
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.28.29921) # MSVC 2019 16.9.16
|
||||
message(FATAL_ERROR "Compiler is unsupported, MSVC 2019 16.9.16 or newer is required for building blender.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT WITH_PYTHON_MODULE)
|
||||
@@ -265,12 +269,6 @@ if(NOT DEFINED LIBDIR)
|
||||
elseif(MSVC_VERSION GREATER 1919)
|
||||
message(STATUS "Visual Studio 2019 detected.")
|
||||
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15)
|
||||
elseif(MSVC_VERSION GREATER 1909)
|
||||
message(STATUS "Visual Studio 2017 detected.")
|
||||
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15)
|
||||
elseif(MSVC_VERSION EQUAL 1900)
|
||||
message(STATUS "Visual Studio 2015 detected.")
|
||||
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Using pre-compiled LIBDIR: ${LIBDIR}")
|
||||
@@ -465,7 +463,7 @@ if(WITH_JACK)
|
||||
endif()
|
||||
|
||||
if(WITH_PYTHON)
|
||||
set(PYTHON_VERSION 3.9) # CACHE STRING)
|
||||
set(PYTHON_VERSION 3.10) # CACHE STRING)
|
||||
|
||||
string(REPLACE "." "" _PYTHON_VERSION_NO_DOTS ${PYTHON_VERSION})
|
||||
set(PYTHON_LIBRARY ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/libs/python${_PYTHON_VERSION_NO_DOTS}.lib)
|
||||
|
@@ -3,9 +3,6 @@ echo No explicit msvc version requested, autodetecting version.
|
||||
call "%~dp0\detect_msvc2019.cmd"
|
||||
if %ERRORLEVEL% EQU 0 goto DetectionComplete
|
||||
|
||||
call "%~dp0\detect_msvc2017.cmd"
|
||||
if %ERRORLEVEL% EQU 0 goto DetectionComplete
|
||||
|
||||
call "%~dp0\detect_msvc2022.cmd"
|
||||
if %ERRORLEVEL% EQU 0 goto DetectionComplete
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
if "%BUILD_VS_YEAR%"=="2017" set BUILD_VS_LIBDIRPOST=vc15
|
||||
if "%BUILD_VS_YEAR%"=="2019" set BUILD_VS_LIBDIRPOST=vc15
|
||||
if "%BUILD_VS_YEAR%"=="2022" set BUILD_VS_LIBDIRPOST=vc15
|
||||
|
||||
|
@@ -19,12 +19,6 @@ if "%WITH_PYDEBUG%"=="1" (
|
||||
set PYDEBUG_CMAKE_ARGS=-DWINDOWS_PYTHON_DEBUG=On
|
||||
)
|
||||
|
||||
if "%BUILD_VS_YEAR%"=="2017" (
|
||||
set BUILD_GENERATOR_POST=%WINDOWS_ARCH%
|
||||
) else (
|
||||
set BUILD_PLATFORM_SELECT=-A %MSBUILD_PLATFORM%
|
||||
)
|
||||
|
||||
set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% -G "Visual Studio %BUILD_VS_VER% %BUILD_VS_YEAR%%BUILD_GENERATOR_POST%" %BUILD_PLATFORM_SELECT% %TESTS_CMAKE_ARGS% %CLANG_CMAKE_ARGS% %ASAN_CMAKE_ARGS% %PYDEBUG_CMAKE_ARGS%
|
||||
|
||||
if NOT EXIST %BUILD_DIR%\nul (
|
||||
|
@@ -37,15 +37,9 @@ set LLVM_DIR=
|
||||
:DetectionComplete
|
||||
set CC=%LLVM_DIR%\bin\clang-cl
|
||||
set CXX=%LLVM_DIR%\bin\clang-cl
|
||||
if "%BUILD_VS_YEAR%" == "2019" (
|
||||
rem build and tested against 2019 16.2
|
||||
set CFLAGS=-m64 -fmsc-version=1922
|
||||
set CXXFLAGS=-m64 -fmsc-version=1922
|
||||
) else (
|
||||
rem build and tested against 2017 15.7
|
||||
set CFLAGS=-m64 -fmsc-version=1914
|
||||
set CXXFLAGS=-m64 -fmsc-version=1914
|
||||
)
|
||||
rem build and tested against 2019 16.2
|
||||
set CFLAGS=-m64 -fmsc-version=1922
|
||||
set CXXFLAGS=-m64 -fmsc-version=1922
|
||||
)
|
||||
|
||||
if "%WITH_ASAN%"=="1" (
|
||||
|
@@ -1,3 +0,0 @@
|
||||
set BUILD_VS_VER=15
|
||||
set BUILD_VS_YEAR=2017
|
||||
call "%~dp0\detect_msvc_vswhere.cmd"
|
@@ -50,14 +50,6 @@ if NOT "%1" == "" (
|
||||
goto ERR
|
||||
) else if "%1" == "x64" (
|
||||
set BUILD_ARCH=x64
|
||||
) else if "%1" == "2017" (
|
||||
set BUILD_VS_YEAR=2017
|
||||
) else if "%1" == "2017pre" (
|
||||
set BUILD_VS_YEAR=2017
|
||||
set VSWHERE_ARGS=-prerelease
|
||||
) else if "%1" == "2017b" (
|
||||
set BUILD_VS_YEAR=2017
|
||||
set VSWHERE_ARGS=-products Microsoft.VisualStudio.Product.BuildTools
|
||||
) else if "%1" == "2019" (
|
||||
set BUILD_VS_YEAR=2019
|
||||
) else if "%1" == "2019pre" (
|
||||
|
@@ -24,12 +24,12 @@ echo - nobuildinfo ^(disable buildinfo^)
|
||||
echo - debug ^(Build an unoptimized debuggable build^)
|
||||
echo - packagename [newname] ^(override default cpack package name^)
|
||||
echo - builddir [newdir] ^(override default build folder^)
|
||||
echo - 2017 ^(build with visual studio 2017^)
|
||||
echo - 2017pre ^(build with visual studio 2017 pre-release^)
|
||||
echo - 2017b ^(build with visual studio 2017 Build Tools^)
|
||||
echo - 2019 ^(build with visual studio 2019^)
|
||||
echo - 2019pre ^(build with visual studio 2019 pre-release^)
|
||||
echo - 2019b ^(build with visual studio 2019 Build Tools^)
|
||||
echo - 2022 ^(build with visual studio 2022^)
|
||||
echo - 2022pre ^(build with visual studio 2022 pre-release^)
|
||||
echo - 2022b ^(build with visual studio 2022 Build Tools^)
|
||||
|
||||
echo.
|
||||
echo Documentation Targets ^(Not associated with building^)
|
||||
|
@@ -1,4 +1,3 @@
|
||||
if "%BUILD_VS_YEAR%"=="2017" set BUILD_VS_LIBDIRPOST=vc15
|
||||
if "%BUILD_VS_YEAR%"=="2019" set BUILD_VS_LIBDIRPOST=vc15
|
||||
if "%BUILD_VS_YEAR%"=="2022" set BUILD_VS_LIBDIRPOST=vc15
|
||||
|
||||
|
@@ -61,6 +61,26 @@ static_assert(Object::MAX_MOTION_STEPS == Geometry::MAX_MOTION_STEPS,
|
||||
|
||||
# define IS_HAIR(x) (x & 1)
|
||||
|
||||
/* This gets called by Embree at every valid ray/object intersection.
|
||||
* Things like recording subsurface or shadow hits for later evaluation
|
||||
* as well as filtering for volume objects happen here.
|
||||
* Cycles' own BVH does that directly inside the traversal calls.
|
||||
*/
|
||||
static void rtc_filter_intersection_func(const RTCFilterFunctionNArguments *args)
|
||||
{
|
||||
/* Current implementation in Cycles assumes only single-ray intersection queries. */
|
||||
assert(args->N == 1);
|
||||
|
||||
RTCHit *hit = (RTCHit *)args->hit;
|
||||
CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt;
|
||||
const KernelGlobalsCPU *kg = ctx->kg;
|
||||
const Ray *cray = ctx->ray;
|
||||
|
||||
if (kernel_embree_is_self_intersection(kg, hit, cray)) {
|
||||
*args->valid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* This gets called by Embree at every valid ray/object intersection.
|
||||
* Things like recording subsurface or shadow hits for later evaluation
|
||||
* as well as filtering for volume objects happen here.
|
||||
@@ -75,12 +95,16 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
|
||||
RTCHit *hit = (RTCHit *)args->hit;
|
||||
CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt;
|
||||
const KernelGlobalsCPU *kg = ctx->kg;
|
||||
const Ray *cray = ctx->ray;
|
||||
|
||||
switch (ctx->type) {
|
||||
case CCLIntersectContext::RAY_SHADOW_ALL: {
|
||||
Intersection current_isect;
|
||||
kernel_embree_convert_hit(kg, ray, hit, ¤t_isect);
|
||||
|
||||
if (intersection_skip_self_shadow(cray->self, current_isect.object, current_isect.prim)) {
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
/* If no transparent shadows or max number of hits exceeded, all light is blocked. */
|
||||
const int flags = intersection_get_shader_flags(kg, current_isect.prim, current_isect.type);
|
||||
if (!(flags & (SD_HAS_TRANSPARENT_SHADOW)) || ctx->num_hits >= ctx->max_hits) {
|
||||
@@ -160,6 +184,10 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (intersection_skip_self_local(cray->self, current_isect.prim)) {
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* No intersection information requested, just return a hit. */
|
||||
if (ctx->max_hits == 0) {
|
||||
@@ -225,6 +253,11 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
|
||||
if (ctx->num_hits < ctx->max_hits) {
|
||||
Intersection current_isect;
|
||||
kernel_embree_convert_hit(kg, ray, hit, ¤t_isect);
|
||||
if (intersection_skip_self(cray->self, current_isect.object, current_isect.prim)) {
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
Intersection *isect = &ctx->isect_s[ctx->num_hits];
|
||||
++ctx->num_hits;
|
||||
*isect = current_isect;
|
||||
@@ -236,12 +269,15 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
|
||||
}
|
||||
/* This tells Embree to continue tracing. */
|
||||
*args->valid = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CCLIntersectContext::RAY_REGULAR:
|
||||
default:
|
||||
/* Nothing to do here. */
|
||||
if (kernel_embree_is_self_intersection(kg, hit, cray)) {
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -257,6 +293,14 @@ static void rtc_filter_func_backface_cull(const RTCFilterFunctionNArguments *arg
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt;
|
||||
const KernelGlobalsCPU *kg = ctx->kg;
|
||||
const Ray *cray = ctx->ray;
|
||||
|
||||
if (kernel_embree_is_self_intersection(kg, hit, cray)) {
|
||||
*args->valid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void rtc_filter_occluded_func_backface_cull(const RTCFilterFunctionNArguments *args)
|
||||
@@ -505,6 +549,7 @@ void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i)
|
||||
|
||||
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
|
||||
rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func);
|
||||
rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_intersection_func);
|
||||
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
|
||||
|
||||
rtcCommitGeometry(geom_id);
|
||||
@@ -767,6 +812,7 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
|
||||
|
||||
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
|
||||
if (hair->curve_shape == CURVE_RIBBON) {
|
||||
rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_intersection_func);
|
||||
rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func);
|
||||
}
|
||||
else {
|
||||
|
@@ -226,7 +226,7 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
|
||||
pipeline_options.usesMotionBlur = false;
|
||||
pipeline_options.traversableGraphFlags =
|
||||
OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_LEVEL_INSTANCING;
|
||||
pipeline_options.numPayloadValues = 6;
|
||||
pipeline_options.numPayloadValues = 8;
|
||||
pipeline_options.numAttributeValues = 2; /* u, v */
|
||||
pipeline_options.exceptionFlags = OPTIX_EXCEPTION_FLAG_NONE;
|
||||
pipeline_options.pipelineLaunchParamsVariableName = "__params"; /* See globals.h */
|
||||
|
@@ -173,15 +173,16 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
|
||||
uint p3 = 0;
|
||||
uint p4 = visibility;
|
||||
uint p5 = PRIMITIVE_NONE;
|
||||
uint p6 = ((uint64_t)ray) & 0xFFFFFFFF;
|
||||
uint p7 = (((uint64_t)ray) >> 32) & 0xFFFFFFFF;
|
||||
|
||||
uint ray_mask = visibility & 0xFF;
|
||||
uint ray_flags = OPTIX_RAY_FLAG_NONE;
|
||||
uint ray_flags = OPTIX_RAY_FLAG_ENFORCE_ANYHIT;
|
||||
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
|
||||
ray_mask = 0xFF;
|
||||
ray_flags = OPTIX_RAY_FLAG_ENFORCE_ANYHIT;
|
||||
}
|
||||
else if (visibility & PATH_RAY_SHADOW_OPAQUE) {
|
||||
ray_flags = OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT;
|
||||
ray_flags |= OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT;
|
||||
}
|
||||
|
||||
optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0,
|
||||
@@ -200,7 +201,9 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
|
||||
p2,
|
||||
p3,
|
||||
p4,
|
||||
p5);
|
||||
p5,
|
||||
p6,
|
||||
p7);
|
||||
|
||||
isect->t = __uint_as_float(p0);
|
||||
isect->u = __uint_as_float(p1);
|
||||
@@ -242,6 +245,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
|
||||
}
|
||||
|
||||
MetalRTIntersectionPayload payload;
|
||||
payload.self = ray->self;
|
||||
payload.u = 0.0f;
|
||||
payload.v = 0.0f;
|
||||
payload.visibility = visibility;
|
||||
@@ -309,6 +313,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
|
||||
CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_REGULAR);
|
||||
IntersectContext rtc_ctx(&ctx);
|
||||
RTCRayHit ray_hit;
|
||||
ctx.ray = ray;
|
||||
kernel_embree_setup_rayhit(*ray, ray_hit, visibility);
|
||||
rtcIntersect1(kernel_data.bvh.scene, &rtc_ctx.context, &ray_hit);
|
||||
if (ray_hit.hit.geomID != RTC_INVALID_GEOMETRY_ID &&
|
||||
@@ -356,6 +361,9 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
|
||||
uint p2 = pointer_pack_to_uint_0(local_isect);
|
||||
uint p3 = pointer_pack_to_uint_1(local_isect);
|
||||
uint p4 = local_object;
|
||||
uint p6 = ((uint64_t)ray) & 0xFFFFFFFF;
|
||||
uint p7 = (((uint64_t)ray) >> 32) & 0xFFFFFFFF;
|
||||
|
||||
/* Is set to zero on miss or if ray is aborted, so can be used as return value. */
|
||||
uint p5 = max_hits;
|
||||
|
||||
@@ -379,7 +387,9 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
|
||||
p2,
|
||||
p3,
|
||||
p4,
|
||||
p5);
|
||||
p5,
|
||||
p6,
|
||||
p7);
|
||||
|
||||
return p5;
|
||||
# elif defined(__METALRT__)
|
||||
@@ -417,6 +427,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
|
||||
}
|
||||
|
||||
MetalRTIntersectionLocalPayload payload;
|
||||
payload.self = ray->self;
|
||||
payload.local_object = local_object;
|
||||
payload.max_hits = max_hits;
|
||||
payload.local_isect.num_hits = 0;
|
||||
@@ -460,6 +471,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
|
||||
kg, has_bvh ? CCLIntersectContext::RAY_SSS : CCLIntersectContext::RAY_LOCAL);
|
||||
ctx.lcg_state = lcg_state;
|
||||
ctx.max_hits = max_hits;
|
||||
ctx.ray = ray;
|
||||
ctx.local_isect = local_isect;
|
||||
if (local_isect) {
|
||||
local_isect->num_hits = 0;
|
||||
@@ -532,6 +544,8 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
|
||||
uint p3 = max_hits;
|
||||
uint p4 = visibility;
|
||||
uint p5 = false;
|
||||
uint p6 = ((uint64_t)ray) & 0xFFFFFFFF;
|
||||
uint p7 = (((uint64_t)ray) >> 32) & 0xFFFFFFFF;
|
||||
|
||||
uint ray_mask = visibility & 0xFF;
|
||||
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
|
||||
@@ -555,7 +569,9 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
|
||||
p2,
|
||||
p3,
|
||||
p4,
|
||||
p5);
|
||||
p5,
|
||||
p6,
|
||||
p7);
|
||||
|
||||
*num_recorded_hits = uint16_unpack_from_uint_0(p2);
|
||||
*throughput = __uint_as_float(p1);
|
||||
@@ -588,6 +604,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
|
||||
}
|
||||
|
||||
MetalRTIntersectionShadowPayload payload;
|
||||
payload.self = ray->self;
|
||||
payload.visibility = visibility;
|
||||
payload.max_hits = max_hits;
|
||||
payload.num_hits = 0;
|
||||
@@ -634,6 +651,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
|
||||
Intersection *isect_array = (Intersection *)state->shadow_isect;
|
||||
ctx.isect_s = isect_array;
|
||||
ctx.max_hits = max_hits;
|
||||
ctx.ray = ray;
|
||||
IntersectContext rtc_ctx(&ctx);
|
||||
RTCRay rtc_ray;
|
||||
kernel_embree_setup_ray(*ray, rtc_ray, visibility);
|
||||
@@ -685,6 +703,8 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
|
||||
uint p3 = 0;
|
||||
uint p4 = visibility;
|
||||
uint p5 = PRIMITIVE_NONE;
|
||||
uint p6 = ((uint64_t)ray) & 0xFFFFFFFF;
|
||||
uint p7 = (((uint64_t)ray) >> 32) & 0xFFFFFFFF;
|
||||
|
||||
uint ray_mask = visibility & 0xFF;
|
||||
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
|
||||
@@ -708,7 +728,9 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
|
||||
p2,
|
||||
p3,
|
||||
p4,
|
||||
p5);
|
||||
p5,
|
||||
p6,
|
||||
p7);
|
||||
|
||||
isect->t = __uint_as_float(p0);
|
||||
isect->u = __uint_as_float(p1);
|
||||
@@ -744,6 +766,7 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
|
||||
}
|
||||
|
||||
MetalRTIntersectionPayload payload;
|
||||
payload.self = ray->self;
|
||||
payload.visibility = visibility;
|
||||
|
||||
typename metalrt_intersector_type::result_type intersection;
|
||||
@@ -820,6 +843,7 @@ ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals kg,
|
||||
ctx.isect_s = isect;
|
||||
ctx.max_hits = max_hits;
|
||||
ctx.num_hits = 0;
|
||||
ctx.ray = ray;
|
||||
IntersectContext rtc_ctx(&ctx);
|
||||
RTCRay rtc_ray;
|
||||
kernel_embree_setup_ray(*ray, rtc_ray, visibility);
|
||||
|
@@ -22,6 +22,8 @@
|
||||
#include "kernel/device/cpu/compat.h"
|
||||
#include "kernel/device/cpu/globals.h"
|
||||
|
||||
#include "kernel/bvh/util.h"
|
||||
|
||||
#include "util/vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
@@ -38,6 +40,9 @@ struct CCLIntersectContext {
|
||||
KernelGlobals kg;
|
||||
RayType type;
|
||||
|
||||
/* For avoiding self intersections */
|
||||
const Ray *ray;
|
||||
|
||||
/* for shadow rays */
|
||||
Intersection *isect_s;
|
||||
uint max_hits;
|
||||
@@ -56,6 +61,7 @@ struct CCLIntersectContext {
|
||||
{
|
||||
kg = kg_;
|
||||
type = type_;
|
||||
ray = NULL;
|
||||
max_hits = 1;
|
||||
num_hits = 0;
|
||||
num_recorded_hits = 0;
|
||||
@@ -102,7 +108,34 @@ ccl_device_inline void kernel_embree_setup_rayhit(const Ray &ray,
|
||||
{
|
||||
kernel_embree_setup_ray(ray, rayhit.ray, visibility);
|
||||
rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID;
|
||||
rayhit.hit.primID = RTC_INVALID_GEOMETRY_ID;
|
||||
rayhit.hit.instID[0] = RTC_INVALID_GEOMETRY_ID;
|
||||
}
|
||||
|
||||
ccl_device_inline bool kernel_embree_is_self_intersection(const KernelGlobals kg,
|
||||
const RTCHit *hit,
|
||||
const Ray *ray)
|
||||
{
|
||||
bool status = false;
|
||||
if (hit->instID[0] != RTC_INVALID_GEOMETRY_ID) {
|
||||
const int oID = hit->instID[0] / 2;
|
||||
if ((ray->self.object == oID) || (ray->self.light_object == oID)) {
|
||||
RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData(
|
||||
rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0]));
|
||||
const int pID = hit->primID +
|
||||
(intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID));
|
||||
status = intersection_skip_self_shadow(ray->self, oID, pID);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const int oID = hit->geomID / 2;
|
||||
if ((ray->self.object == oID) || (ray->self.light_object == oID)) {
|
||||
const int pID = hit->primID + (intptr_t)rtcGetGeometryUserData(
|
||||
rtcGetGeometry(kernel_data.bvh.scene, hit->geomID));
|
||||
status = intersection_skip_self_shadow(ray->self, oID, pID);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ccl_device_inline void kernel_embree_convert_hit(KernelGlobals kg,
|
||||
|
@@ -157,7 +157,11 @@ ccl_device_inline
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip self intersection. */
|
||||
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
|
||||
if (intersection_skip_self_local(ray->self, prim)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (triangle_intersect_local(kg,
|
||||
local_isect,
|
||||
@@ -188,7 +192,11 @@ ccl_device_inline
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip self intersection. */
|
||||
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
|
||||
if (intersection_skip_self_local(ray->self, prim)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (motion_triangle_intersect_local(kg,
|
||||
local_isect,
|
||||
|
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
struct MetalRTIntersectionPayload {
|
||||
RaySelfPrimitives self;
|
||||
uint visibility;
|
||||
float u, v;
|
||||
int prim;
|
||||
@@ -25,6 +26,7 @@ struct MetalRTIntersectionPayload {
|
||||
};
|
||||
|
||||
struct MetalRTIntersectionLocalPayload {
|
||||
RaySelfPrimitives self;
|
||||
uint local_object;
|
||||
uint lcg_state;
|
||||
short max_hits;
|
||||
@@ -34,6 +36,7 @@ struct MetalRTIntersectionLocalPayload {
|
||||
};
|
||||
|
||||
struct MetalRTIntersectionShadowPayload {
|
||||
RaySelfPrimitives self;
|
||||
uint visibility;
|
||||
#if defined(__METALRT_MOTION__)
|
||||
float time;
|
||||
|
@@ -160,6 +160,9 @@ ccl_device_inline
|
||||
kernel_tex_fetch(__prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
|
||||
if (intersection_skip_self_shadow(ray->self, prim_object, prim)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (type & PRIMITIVE_ALL) {
|
||||
case PRIMITIVE_TRIANGLE: {
|
||||
|
@@ -133,35 +133,29 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
|
||||
--stack_ptr;
|
||||
|
||||
/* primitive intersection */
|
||||
switch (type & PRIMITIVE_ALL) {
|
||||
case PRIMITIVE_TRIANGLE: {
|
||||
for (; prim_addr < prim_addr2; prim_addr++) {
|
||||
kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
|
||||
for (; prim_addr < prim_addr2; prim_addr++) {
|
||||
kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
|
||||
|
||||
const int prim_object = (object == OBJECT_NONE) ?
|
||||
kernel_tex_fetch(__prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
|
||||
const int prim_object = (object == OBJECT_NONE) ?
|
||||
kernel_tex_fetch(__prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
|
||||
if (intersection_skip_self_shadow(ray->self, prim_object, prim)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (type & PRIMITIVE_ALL) {
|
||||
case PRIMITIVE_TRIANGLE: {
|
||||
if (triangle_intersect(
|
||||
kg, isect, P, dir, isect->t, visibility, prim_object, prim, prim_addr)) {
|
||||
/* shadow ray early termination */
|
||||
if (visibility & PATH_RAY_SHADOW_OPAQUE)
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#if BVH_FEATURE(BVH_MOTION)
|
||||
case PRIMITIVE_MOTION_TRIANGLE: {
|
||||
for (; prim_addr < prim_addr2; prim_addr++) {
|
||||
kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type);
|
||||
|
||||
const int prim_object = (object == OBJECT_NONE) ?
|
||||
kernel_tex_fetch(__prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
|
||||
|
||||
case PRIMITIVE_MOTION_TRIANGLE: {
|
||||
if (motion_triangle_intersect(kg,
|
||||
isect,
|
||||
P,
|
||||
@@ -176,28 +170,21 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
|
||||
if (visibility & PATH_RAY_SHADOW_OPAQUE)
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* BVH_FEATURE(BVH_MOTION) */
|
||||
#if BVH_FEATURE(BVH_HAIR)
|
||||
case PRIMITIVE_CURVE_THICK:
|
||||
case PRIMITIVE_MOTION_CURVE_THICK:
|
||||
case PRIMITIVE_CURVE_RIBBON:
|
||||
case PRIMITIVE_MOTION_CURVE_RIBBON: {
|
||||
for (; prim_addr < prim_addr2; prim_addr++) {
|
||||
case PRIMITIVE_CURVE_THICK:
|
||||
case PRIMITIVE_MOTION_CURVE_THICK:
|
||||
case PRIMITIVE_CURVE_RIBBON:
|
||||
case PRIMITIVE_MOTION_CURVE_RIBBON: {
|
||||
if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {
|
||||
const float2 prim_time = kernel_tex_fetch(__prim_time, prim_addr);
|
||||
if (ray->time < prim_time.x || ray->time > prim_time.y) {
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const int prim_object = (object == OBJECT_NONE) ?
|
||||
kernel_tex_fetch(__prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
|
||||
|
||||
const int curve_type = kernel_tex_fetch(__prim_type, prim_addr);
|
||||
const bool hit = curve_intersect(
|
||||
kg, isect, P, dir, isect->t, prim_object, prim, ray->time, curve_type);
|
||||
@@ -206,26 +193,19 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
|
||||
if (visibility & PATH_RAY_SHADOW_OPAQUE)
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* BVH_FEATURE(BVH_HAIR) */
|
||||
#if BVH_FEATURE(BVH_POINTCLOUD)
|
||||
case PRIMITIVE_POINT:
|
||||
case PRIMITIVE_MOTION_POINT: {
|
||||
for (; prim_addr < prim_addr2; prim_addr++) {
|
||||
case PRIMITIVE_POINT:
|
||||
case PRIMITIVE_MOTION_POINT: {
|
||||
if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {
|
||||
const float2 prim_time = kernel_tex_fetch(__prim_time, prim_addr);
|
||||
if (ray->time < prim_time.x || ray->time > prim_time.y) {
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const int prim_object = (object == OBJECT_NONE) ?
|
||||
kernel_tex_fetch(__prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
|
||||
|
||||
const int point_type = kernel_tex_fetch(__prim_type, prim_addr);
|
||||
const bool hit = point_intersect(
|
||||
kg, isect, P, dir, isect->t, prim_object, prim, ray->time, point_type);
|
||||
@@ -234,10 +214,10 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
|
||||
if (visibility & PATH_RAY_SHADOW_OPAQUE)
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* BVH_FEATURE(BVH_POINTCLOUD) */
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@@ -21,54 +21,22 @@ CCL_NAMESPACE_BEGIN
|
||||
/* Ray offset to avoid self intersection.
|
||||
*
|
||||
* This function should be used to compute a modified ray start position for
|
||||
* rays leaving from a surface. */
|
||||
|
||||
* rays leaving from a surface. This is from "A Fast and Robust Method for Avoiding
|
||||
* Self-Intersection" see https://research.nvidia.com/publication/2019-03_A-Fast-and
|
||||
*/
|
||||
ccl_device_inline float3 ray_offset(float3 P, float3 Ng)
|
||||
{
|
||||
#ifdef __INTERSECTION_REFINE__
|
||||
const float epsilon_f = 1e-5f;
|
||||
/* ideally this should match epsilon_f, but instancing and motion blur
|
||||
* precision makes it problematic */
|
||||
const float epsilon_test = 1.0f;
|
||||
const int epsilon_i = 32;
|
||||
const float int_scale = 256.0f;
|
||||
int3 of_i = make_int3((int)(int_scale * Ng.x), (int)(int_scale * Ng.y), (int)(int_scale * Ng.z));
|
||||
|
||||
float3 res;
|
||||
|
||||
/* x component */
|
||||
if (fabsf(P.x) < epsilon_test) {
|
||||
res.x = P.x + Ng.x * epsilon_f;
|
||||
}
|
||||
else {
|
||||
uint ix = __float_as_uint(P.x);
|
||||
ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i;
|
||||
res.x = __uint_as_float(ix);
|
||||
}
|
||||
|
||||
/* y component */
|
||||
if (fabsf(P.y) < epsilon_test) {
|
||||
res.y = P.y + Ng.y * epsilon_f;
|
||||
}
|
||||
else {
|
||||
uint iy = __float_as_uint(P.y);
|
||||
iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i;
|
||||
res.y = __uint_as_float(iy);
|
||||
}
|
||||
|
||||
/* z component */
|
||||
if (fabsf(P.z) < epsilon_test) {
|
||||
res.z = P.z + Ng.z * epsilon_f;
|
||||
}
|
||||
else {
|
||||
uint iz = __float_as_uint(P.z);
|
||||
iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i;
|
||||
res.z = __uint_as_float(iz);
|
||||
}
|
||||
|
||||
return res;
|
||||
#else
|
||||
const float epsilon_f = 1e-4f;
|
||||
return P + epsilon_f * Ng;
|
||||
#endif
|
||||
float3 p_i = make_float3(__int_as_float(__float_as_int(P.x) + ((P.x < 0) ? -of_i.x : of_i.x)),
|
||||
__int_as_float(__float_as_int(P.y) + ((P.y < 0) ? -of_i.y : of_i.y)),
|
||||
__int_as_float(__float_as_int(P.z) + ((P.z < 0) ? -of_i.z : of_i.z)));
|
||||
const float origin = 1.0f / 32.0f;
|
||||
const float float_scale = 1.0f / 65536.0f;
|
||||
return make_float3(fabsf(P.x) < origin ? P.x + float_scale * Ng.x : p_i.x,
|
||||
fabsf(P.y) < origin ? P.y + float_scale * Ng.y : p_i.y,
|
||||
fabsf(P.z) < origin ? P.z + float_scale * Ng.z : p_i.z);
|
||||
}
|
||||
|
||||
#if defined(__KERNEL_CPU__)
|
||||
@@ -227,4 +195,25 @@ ccl_device_inline float intersection_curve_shadow_transparency(KernelGlobals kg,
|
||||
return (1.0f - u) * f0 + u * f1;
|
||||
}
|
||||
|
||||
ccl_device_inline bool intersection_skip_self(ccl_private const RaySelfPrimitives &self,
|
||||
const int object,
|
||||
const int prim)
|
||||
{
|
||||
return (self.prim == prim) && (self.object == object);
|
||||
}
|
||||
|
||||
ccl_device_inline bool intersection_skip_self_shadow(ccl_private const RaySelfPrimitives &self,
|
||||
const int object,
|
||||
const int prim)
|
||||
{
|
||||
return ((self.prim == prim) && (self.object == object)) ||
|
||||
((self.light_prim == prim) && (self.light_object == object));
|
||||
}
|
||||
|
||||
ccl_device_inline bool intersection_skip_self_local(ccl_private const RaySelfPrimitives &self,
|
||||
const int prim)
|
||||
{
|
||||
return (self.prim == prim);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -144,6 +144,9 @@ ccl_device_inline
|
||||
kernel_tex_fetch(__prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
|
||||
if (intersection_skip_self(ray->self, prim_object, prim)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int object_flag = kernel_tex_fetch(__object_flag, prim_object);
|
||||
if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
|
||||
@@ -164,6 +167,9 @@ ccl_device_inline
|
||||
kernel_tex_fetch(__prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
|
||||
if (intersection_skip_self(ray->self, prim_object, prim)) {
|
||||
continue;
|
||||
}
|
||||
int object_flag = kernel_tex_fetch(__object_flag, prim_object);
|
||||
if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
|
||||
continue;
|
||||
|
@@ -147,6 +147,9 @@ ccl_device_inline
|
||||
kernel_tex_fetch(__prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
|
||||
if (intersection_skip_self(ray->self, prim_object, prim)) {
|
||||
continue;
|
||||
}
|
||||
int object_flag = kernel_tex_fetch(__object_flag, prim_object);
|
||||
if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
|
||||
continue;
|
||||
@@ -188,6 +191,9 @@ ccl_device_inline
|
||||
kernel_tex_fetch(__prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_tex_fetch(__prim_index, prim_addr);
|
||||
if (intersection_skip_self(ray->self, prim_object, prim)) {
|
||||
continue;
|
||||
}
|
||||
int object_flag = kernel_tex_fetch(__object_flag, prim_object);
|
||||
if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
|
||||
continue;
|
||||
|
@@ -40,6 +40,27 @@ struct TriangleIntersectionResult
|
||||
|
||||
enum { METALRT_HIT_TRIANGLE, METALRT_HIT_BOUNDING_BOX };
|
||||
|
||||
ccl_device_inline bool intersection_skip_self(ray_data const RaySelfPrimitives& self,
|
||||
const int object,
|
||||
const int prim)
|
||||
{
|
||||
return (self.prim == prim) && (self.object == object);
|
||||
}
|
||||
|
||||
ccl_device_inline bool intersection_skip_self_shadow(ray_data const RaySelfPrimitives& self,
|
||||
const int object,
|
||||
const int prim)
|
||||
{
|
||||
return ((self.prim == prim) && (self.object == object)) ||
|
||||
((self.light_prim == prim) && (self.light_object == object));
|
||||
}
|
||||
|
||||
ccl_device_inline bool intersection_skip_self_local(ray_data const RaySelfPrimitives& self,
|
||||
const int prim)
|
||||
{
|
||||
return (self.prim == prim);
|
||||
}
|
||||
|
||||
template<typename TReturn, uint intersection_type>
|
||||
TReturn metalrt_local_hit(constant KernelParamsMetal &launch_params_metal,
|
||||
ray_data MetalKernelContext::MetalRTIntersectionLocalPayload &payload,
|
||||
@@ -53,8 +74,8 @@ TReturn metalrt_local_hit(constant KernelParamsMetal &launch_params_metal,
|
||||
#ifdef __BVH_LOCAL__
|
||||
uint prim = primitive_id + kernel_tex_fetch(__object_prim_offset, object);
|
||||
|
||||
if (object != payload.local_object) {
|
||||
/* Only intersect with matching object */
|
||||
if ((object != payload.local_object) || intersection_skip_self_local(payload.self, prim)) {
|
||||
/* Only intersect with matching object and skip self-intersecton. */
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
@@ -166,6 +187,11 @@ bool metalrt_shadow_all_hit(constant KernelParamsMetal &launch_params_metal,
|
||||
}
|
||||
# endif
|
||||
|
||||
if (intersection_skip_self_shadow(payload.self, object, prim)) {
|
||||
/* continue search */
|
||||
return true;
|
||||
}
|
||||
|
||||
float u = 0.0f, v = 0.0f;
|
||||
int type = 0;
|
||||
if (intersection_type == METALRT_HIT_TRIANGLE) {
|
||||
@@ -322,21 +348,35 @@ inline TReturnType metalrt_visibility_test(constant KernelParamsMetal &launch_pa
|
||||
}
|
||||
# endif
|
||||
|
||||
# ifdef __VISIBILITY_FLAG__
|
||||
uint visibility = payload.visibility;
|
||||
# ifdef __VISIBILITY_FLAG__
|
||||
if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) {
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
# endif
|
||||
|
||||
/* Shadow ray early termination. */
|
||||
if (visibility & PATH_RAY_SHADOW_OPAQUE) {
|
||||
result.accept = true;
|
||||
result.continue_search = false;
|
||||
return result;
|
||||
if (intersection_skip_self_shadow(payload.self, object, prim)) {
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
result.accept = true;
|
||||
result.continue_search = false;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (intersection_skip_self(payload.self, object, prim)) {
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
result.accept = true;
|
||||
result.continue_search = true;
|
||||
|
@@ -45,6 +45,11 @@ template<typename T> ccl_device_forceinline T *get_payload_ptr_2()
|
||||
return pointer_unpack_from_uint<T>(optixGetPayload_2(), optixGetPayload_3());
|
||||
}
|
||||
|
||||
template<typename T> ccl_device_forceinline T *get_payload_ptr_6()
|
||||
{
|
||||
return (T *)(((uint64_t)optixGetPayload_7() << 32) | optixGetPayload_6());
|
||||
}
|
||||
|
||||
ccl_device_forceinline int get_object_id()
|
||||
{
|
||||
#ifdef __OBJECT_MOTION__
|
||||
@@ -111,6 +116,12 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit()
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
|
||||
const int prim = optixGetPrimitiveIndex();
|
||||
ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
|
||||
if (intersection_skip_self_local(ray->self, prim)) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
|
||||
const uint max_hits = optixGetPayload_5();
|
||||
if (max_hits == 0) {
|
||||
/* Special case for when no hit information is requested, just report that something was hit */
|
||||
@@ -149,8 +160,6 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit()
|
||||
local_isect->num_hits = 1;
|
||||
}
|
||||
|
||||
const int prim = optixGetPrimitiveIndex();
|
||||
|
||||
Intersection *isect = &local_isect->hits[hit];
|
||||
isect->t = optixGetRayTmax();
|
||||
isect->prim = prim;
|
||||
@@ -185,6 +194,11 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
|
||||
}
|
||||
# endif
|
||||
|
||||
ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
|
||||
if (intersection_skip_self_shadow(ray->self, object, prim)) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
|
||||
float u = 0.0f, v = 0.0f;
|
||||
int type = 0;
|
||||
if (optixIsTriangleHit()) {
|
||||
@@ -314,6 +328,12 @@ extern "C" __global__ void __anyhit__kernel_optix_volume_test()
|
||||
if ((kernel_tex_fetch(__object_flag, object) & SD_OBJECT_HAS_VOLUME) == 0) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
|
||||
const int prim = optixGetPrimitiveIndex();
|
||||
ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
|
||||
if (intersection_skip_self(ray->self, object, prim)) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void __anyhit__kernel_optix_visibility_test()
|
||||
@@ -330,18 +350,31 @@ extern "C" __global__ void __anyhit__kernel_optix_visibility_test()
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __VISIBILITY_FLAG__
|
||||
const uint object = get_object_id();
|
||||
const uint visibility = optixGetPayload_4();
|
||||
#ifdef __VISIBILITY_FLAG__
|
||||
if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
|
||||
/* Shadow ray early termination. */
|
||||
if (visibility & PATH_RAY_SHADOW_OPAQUE) {
|
||||
return optixTerminateRay();
|
||||
}
|
||||
#endif
|
||||
|
||||
const int prim = optixGetPrimitiveIndex();
|
||||
ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
|
||||
|
||||
if (visibility & PATH_RAY_SHADOW_OPAQUE) {
|
||||
if (intersection_skip_self_shadow(ray->self, object, prim)) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
else {
|
||||
/* Shadow ray early termination. */
|
||||
return optixTerminateRay();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (intersection_skip_self(ray->self, object, prim)) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void __closesthit__kernel_optix_hit()
|
||||
|
@@ -29,46 +29,19 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Refine triangle intersection to more precise hit point. For rays that travel
|
||||
* far the precision is often not so good, this reintersects the primitive from
|
||||
* a closer distance.
|
||||
/**
|
||||
* Use the barycentric coordinates to get the intersection location
|
||||
*/
|
||||
|
||||
ccl_device_inline float3 motion_triangle_refine(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
float3 P,
|
||||
float3 D,
|
||||
float t,
|
||||
const int isect_object,
|
||||
const int isect_prim,
|
||||
float3 verts[3])
|
||||
ccl_device_inline float3 motion_triangle_point_from_uv(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
const int isect_object,
|
||||
const int isect_prim,
|
||||
const float u,
|
||||
const float v,
|
||||
float3 verts[3])
|
||||
{
|
||||
#ifdef __INTERSECTION_REFINE__
|
||||
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
if (UNLIKELY(t == 0.0f)) {
|
||||
return P;
|
||||
}
|
||||
const Transform tfm = object_get_inverse_transform(kg, sd);
|
||||
|
||||
P = transform_point(&tfm, P);
|
||||
D = transform_direction(&tfm, D * t);
|
||||
D = normalize_len(D, &t);
|
||||
}
|
||||
|
||||
P = P + D * t;
|
||||
|
||||
/* Compute refined intersection distance. */
|
||||
const float3 e1 = verts[0] - verts[2];
|
||||
const float3 e2 = verts[1] - verts[2];
|
||||
const float3 s1 = cross(D, e2);
|
||||
|
||||
const float invdivisor = 1.0f / dot(s1, e1);
|
||||
const float3 d = P - verts[2];
|
||||
const float3 s2 = cross(d, e1);
|
||||
float rt = dot(e2, s2) * invdivisor;
|
||||
|
||||
/* Compute refined position. */
|
||||
P = P + D * rt;
|
||||
float w = 1.0f - u - v;
|
||||
float3 P = u * verts[0] + v * verts[1] + w * verts[2];
|
||||
|
||||
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
const Transform tfm = object_get_transform(kg, sd);
|
||||
@@ -76,71 +49,8 @@ ccl_device_inline float3 motion_triangle_refine(KernelGlobals kg,
|
||||
}
|
||||
|
||||
return P;
|
||||
#else
|
||||
return P + D * t;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Same as above, except that t is assumed to be in object space
|
||||
* for instancing.
|
||||
*/
|
||||
|
||||
#ifdef __BVH_LOCAL__
|
||||
# if defined(__KERNEL_CUDA__) && (defined(i386) || defined(_M_IX86))
|
||||
ccl_device_noinline
|
||||
# else
|
||||
ccl_device_inline
|
||||
# endif
|
||||
float3
|
||||
motion_triangle_refine_local(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
float3 P,
|
||||
float3 D,
|
||||
float t,
|
||||
const int isect_object,
|
||||
const int isect_prim,
|
||||
float3 verts[3])
|
||||
{
|
||||
# if defined(__KERNEL_GPU_RAYTRACING__)
|
||||
/* t is always in world space with OptiX and MetalRT. */
|
||||
return motion_triangle_refine(kg, sd, P, D, t, isect_object, isect_prim, verts);
|
||||
# else
|
||||
# ifdef __INTERSECTION_REFINE__
|
||||
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
const Transform tfm = object_get_inverse_transform(kg, sd);
|
||||
|
||||
P = transform_point(&tfm, P);
|
||||
D = transform_direction(&tfm, D);
|
||||
D = normalize(D);
|
||||
}
|
||||
|
||||
P = P + D * t;
|
||||
|
||||
/* compute refined intersection distance */
|
||||
const float3 e1 = verts[0] - verts[2];
|
||||
const float3 e2 = verts[1] - verts[2];
|
||||
const float3 s1 = cross(D, e2);
|
||||
|
||||
const float invdivisor = 1.0f / dot(s1, e1);
|
||||
const float3 d = P - verts[2];
|
||||
const float3 s2 = cross(d, e1);
|
||||
float rt = dot(e2, s2) * invdivisor;
|
||||
|
||||
P = P + D * rt;
|
||||
|
||||
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
const Transform tfm = object_get_transform(kg, sd);
|
||||
P = transform_point(&tfm, P);
|
||||
}
|
||||
|
||||
return P;
|
||||
# else /* __INTERSECTION_REFINE__ */
|
||||
return P + D * t;
|
||||
# endif /* __INTERSECTION_REFINE__ */
|
||||
# endif
|
||||
}
|
||||
#endif /* __BVH_LOCAL__ */
|
||||
|
||||
/* Ray intersection. We simply compute the vertex positions at the given ray
|
||||
* time and do a ray intersection with the resulting triangle.
|
||||
*/
|
||||
|
@@ -68,15 +68,7 @@ ccl_device_noinline void motion_triangle_shader_setup(KernelGlobals kg,
|
||||
verts[1] = (1.0f - t) * verts[1] + t * next_verts[1];
|
||||
verts[2] = (1.0f - t) * verts[2] + t * next_verts[2];
|
||||
/* Compute refined position. */
|
||||
#ifdef __BVH_LOCAL__
|
||||
if (is_local) {
|
||||
sd->P = motion_triangle_refine_local(kg, sd, P, D, ray_t, isect_object, isect_prim, verts);
|
||||
}
|
||||
else
|
||||
#endif /* __BVH_LOCAL__*/
|
||||
{
|
||||
sd->P = motion_triangle_refine(kg, sd, P, D, ray_t, isect_object, isect_prim, verts);
|
||||
}
|
||||
sd->P = motion_triangle_point_from_uv(kg, sd, isect_object, isect_prim, sd->u, sd->v, verts);
|
||||
/* Compute face normal. */
|
||||
float3 Ng;
|
||||
if (sd->object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) {
|
||||
|
@@ -89,7 +89,7 @@ ccl_device_inline void shader_setup_from_ray(KernelGlobals kg,
|
||||
sd->shader = kernel_tex_fetch(__tri_shader, sd->prim);
|
||||
|
||||
/* vectors */
|
||||
sd->P = triangle_refine(kg, sd, ray->P, ray->D, isect->t, isect->object, isect->prim);
|
||||
sd->P = triangle_point_from_uv(kg, sd, isect->object, isect->prim, isect->u, isect->v);
|
||||
sd->Ng = Ng;
|
||||
sd->N = Ng;
|
||||
|
||||
|
@@ -142,58 +142,23 @@ ccl_device_inline bool triangle_intersect_local(KernelGlobals kg,
|
||||
}
|
||||
#endif /* __BVH_LOCAL__ */
|
||||
|
||||
/* Refine triangle intersection to more precise hit point. For rays that travel
|
||||
* far the precision is often not so good, this reintersects the primitive from
|
||||
* a closer distance. */
|
||||
|
||||
/* Reintersections uses the paper:
|
||||
*
|
||||
* Tomas Moeller
|
||||
* Fast, minimum storage ray/triangle intersection
|
||||
* http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf
|
||||
/**
|
||||
* Use the barycentric coordinates to get the intersection location
|
||||
*/
|
||||
|
||||
ccl_device_inline float3 triangle_refine(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
float3 P,
|
||||
float3 D,
|
||||
float t,
|
||||
const int isect_object,
|
||||
const int isect_prim)
|
||||
ccl_device_inline float3 triangle_point_from_uv(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
const int isect_object,
|
||||
const int isect_prim,
|
||||
const float u,
|
||||
const float v)
|
||||
{
|
||||
#ifdef __INTERSECTION_REFINE__
|
||||
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
if (UNLIKELY(t == 0.0f)) {
|
||||
return P;
|
||||
}
|
||||
const Transform tfm = object_get_inverse_transform(kg, sd);
|
||||
|
||||
P = transform_point(&tfm, P);
|
||||
D = transform_direction(&tfm, D * t);
|
||||
D = normalize_len(D, &t);
|
||||
}
|
||||
|
||||
P = P + D * t;
|
||||
|
||||
const uint tri_vindex = kernel_tex_fetch(__tri_vindex, isect_prim).w;
|
||||
const packed_float3 tri_a = kernel_tex_fetch(__tri_verts, tri_vindex + 0),
|
||||
tri_b = kernel_tex_fetch(__tri_verts, tri_vindex + 1),
|
||||
tri_c = kernel_tex_fetch(__tri_verts, tri_vindex + 2);
|
||||
float3 edge1 = make_float3(tri_a.x - tri_c.x, tri_a.y - tri_c.y, tri_a.z - tri_c.z);
|
||||
float3 edge2 = make_float3(tri_b.x - tri_c.x, tri_b.y - tri_c.y, tri_b.z - tri_c.z);
|
||||
float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z);
|
||||
float3 qvec = cross(tvec, edge1);
|
||||
float3 pvec = cross(D, edge2);
|
||||
float det = dot(edge1, pvec);
|
||||
if (det != 0.0f) {
|
||||
/* If determinant is zero it means ray lies in the plane of
|
||||
* the triangle. It is possible in theory due to watertight
|
||||
* nature of triangle intersection. For such cases we simply
|
||||
* don't refine intersection hoping it'll go all fine.
|
||||
*/
|
||||
float rt = dot(edge2, qvec) / det;
|
||||
P = P + D * rt;
|
||||
}
|
||||
float w = 1.0f - u - v;
|
||||
|
||||
float3 P = u * tri_a + v * tri_b + w * tri_c;
|
||||
|
||||
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
const Transform tfm = object_get_transform(kg, sd);
|
||||
@@ -201,65 +166,6 @@ ccl_device_inline float3 triangle_refine(KernelGlobals kg,
|
||||
}
|
||||
|
||||
return P;
|
||||
#else
|
||||
return P + D * t;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Same as above, except that t is assumed to be in object space for
|
||||
* instancing.
|
||||
*/
|
||||
ccl_device_inline float3 triangle_refine_local(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
float3 P,
|
||||
float3 D,
|
||||
float t,
|
||||
const int isect_object,
|
||||
const int isect_prim)
|
||||
{
|
||||
#if defined(__KERNEL_GPU_RAYTRACING__)
|
||||
/* t is always in world space with OptiX and MetalRT. */
|
||||
return triangle_refine(kg, sd, P, D, t, isect_object, isect_prim);
|
||||
#else
|
||||
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
const Transform tfm = object_get_inverse_transform(kg, sd);
|
||||
|
||||
P = transform_point(&tfm, P);
|
||||
D = transform_direction(&tfm, D);
|
||||
D = normalize(D);
|
||||
}
|
||||
|
||||
P = P + D * t;
|
||||
|
||||
# ifdef __INTERSECTION_REFINE__
|
||||
const uint tri_vindex = kernel_tex_fetch(__tri_vindex, isect_prim).w;
|
||||
const packed_float3 tri_a = kernel_tex_fetch(__tri_verts, tri_vindex + 0),
|
||||
tri_b = kernel_tex_fetch(__tri_verts, tri_vindex + 1),
|
||||
tri_c = kernel_tex_fetch(__tri_verts, tri_vindex + 2);
|
||||
float3 edge1 = make_float3(tri_a.x - tri_c.x, tri_a.y - tri_c.y, tri_a.z - tri_c.z);
|
||||
float3 edge2 = make_float3(tri_b.x - tri_c.x, tri_b.y - tri_c.y, tri_b.z - tri_c.z);
|
||||
float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z);
|
||||
float3 qvec = cross(tvec, edge1);
|
||||
float3 pvec = cross(D, edge2);
|
||||
float det = dot(edge1, pvec);
|
||||
if (det != 0.0f) {
|
||||
/* If determinant is zero it means ray lies in the plane of
|
||||
* the triangle. It is possible in theory due to watertight
|
||||
* nature of triangle intersection. For such cases we simply
|
||||
* don't refine intersection hoping it'll go all fine.
|
||||
*/
|
||||
float rt = dot(edge2, qvec) / det;
|
||||
P = P + D * rt;
|
||||
}
|
||||
# endif /* __INTERSECTION_REFINE__ */
|
||||
|
||||
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
const Transform tfm = object_get_transform(kg, sd);
|
||||
P = transform_point(&tfm, P);
|
||||
}
|
||||
|
||||
return P;
|
||||
#endif
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -328,6 +328,12 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg,
|
||||
|
||||
/* Scene Intersection. */
|
||||
Intersection isect ccl_optional_struct_init;
|
||||
isect.object = OBJECT_NONE;
|
||||
isect.prim = PRIM_NONE;
|
||||
ray.self.object = last_isect_object;
|
||||
ray.self.prim = last_isect_prim;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = PRIM_NONE;
|
||||
bool hit = scene_intersect(kg, &ray, visibility, &isect);
|
||||
|
||||
/* TODO: remove this and do it in the various intersection functions instead. */
|
||||
|
@@ -156,7 +156,10 @@ ccl_device void integrator_intersect_shadow(KernelGlobals kg, IntegratorShadowSt
|
||||
/* Read ray from integrator state into local memory. */
|
||||
Ray ray ccl_optional_struct_init;
|
||||
integrator_state_read_shadow_ray(kg, state, &ray);
|
||||
|
||||
ray.self.object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, object);
|
||||
ray.self.prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, prim);
|
||||
ray.self.light_object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 1, object);
|
||||
ray.self.light_prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 1, prim);
|
||||
/* Compute visibility. */
|
||||
const uint visibility = integrate_intersect_shadow_visibility(kg, state);
|
||||
|
||||
|
@@ -38,7 +38,10 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg,
|
||||
Ray volume_ray ccl_optional_struct_init;
|
||||
volume_ray.P = from_P;
|
||||
volume_ray.D = normalize_len(to_P - from_P, &volume_ray.t);
|
||||
|
||||
volume_ray.self.object = INTEGRATOR_STATE(state, isect, object);
|
||||
volume_ray.self.prim = INTEGRATOR_STATE(state, isect, prim);
|
||||
volume_ray.self.light_object = OBJECT_NONE;
|
||||
volume_ray.self.light_prim = PRIM_NONE;
|
||||
/* Store to avoid global fetches on every intersection step. */
|
||||
const uint volume_stack_size = kernel_data.volume_stack_size;
|
||||
|
||||
@@ -68,7 +71,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg,
|
||||
volume_stack_enter_exit(kg, state, stack_sd);
|
||||
|
||||
/* Move ray forward. */
|
||||
volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng);
|
||||
volume_ray.P = stack_sd->P;
|
||||
if (volume_ray.t != FLT_MAX) {
|
||||
volume_ray.D = normalize_len(to_P - volume_ray.P, &volume_ray.t);
|
||||
}
|
||||
@@ -91,6 +94,10 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s
|
||||
* fewest hits. */
|
||||
volume_ray.D = make_float3(0.0f, 0.0f, 1.0f);
|
||||
volume_ray.t = FLT_MAX;
|
||||
volume_ray.self.object = OBJECT_NONE;
|
||||
volume_ray.self.prim = PRIM_NONE;
|
||||
volume_ray.self.light_object = OBJECT_NONE;
|
||||
volume_ray.self.light_prim = PRIM_NONE;
|
||||
|
||||
int stack_index = 0, enclosed_index = 0;
|
||||
|
||||
@@ -203,7 +210,7 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s
|
||||
}
|
||||
|
||||
/* Move ray forward. */
|
||||
volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng);
|
||||
volume_ray.P = stack_sd->P;
|
||||
++step;
|
||||
}
|
||||
#endif
|
||||
|
@@ -37,8 +37,9 @@ ccl_device_inline void integrate_light(KernelGlobals kg,
|
||||
|
||||
/* Advance ray beyond light. */
|
||||
/* TODO: can we make this more numerically robust to avoid reintersecting the
|
||||
* same light in some cases? */
|
||||
const float3 new_ray_P = ray_offset(ray_P + ray_D * isect.t, ray_D);
|
||||
* same light in some cases? Ray should not intersect surface anymore as the
|
||||
* object and prim ids will prevent self intersection. */
|
||||
const float3 new_ray_P = ray_P + ray_D * isect.t;
|
||||
INTEGRATOR_STATE_WRITE(state, ray, P) = new_ray_P;
|
||||
INTEGRATOR_STATE_WRITE(state, ray, t) -= isect.t;
|
||||
|
||||
@@ -46,7 +47,7 @@ ccl_device_inline void integrate_light(KernelGlobals kg,
|
||||
const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t);
|
||||
ray_P -= ray_D * mis_ray_t;
|
||||
isect.t += mis_ray_t;
|
||||
INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = mis_ray_t + isect.t;
|
||||
INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = isect.t;
|
||||
|
||||
LightSample ls ccl_optional_struct_init;
|
||||
const bool use_light_sample = light_sample_from_intersection(kg, &isect, ray_P, ray_D, &ls);
|
||||
|
@@ -83,7 +83,10 @@ ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg,
|
||||
/* Setup shader data. */
|
||||
Ray ray ccl_optional_struct_init;
|
||||
integrator_state_read_shadow_ray(kg, state, &ray);
|
||||
|
||||
ray.self.object = OBJECT_NONE;
|
||||
ray.self.prim = PRIM_NONE;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = PRIM_NONE;
|
||||
/* Modify ray position and length to match current segment. */
|
||||
const float start_t = (hit == 0) ? 0.0f :
|
||||
INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit - 1, t);
|
||||
@@ -149,7 +152,7 @@ ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg,
|
||||
const float last_hit_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, num_recorded_hits - 1, t);
|
||||
const float3 ray_P = INTEGRATOR_STATE(state, shadow_ray, P);
|
||||
const float3 ray_D = INTEGRATOR_STATE(state, shadow_ray, D);
|
||||
INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray_offset(ray_P + last_hit_t * ray_D, ray_D);
|
||||
INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray_P + last_hit_t * ray_D;
|
||||
INTEGRATOR_STATE_WRITE(state, shadow_ray, t) -= last_hit_t;
|
||||
}
|
||||
|
||||
|
@@ -182,6 +182,11 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
|
||||
|
||||
/* Write shadow ray and associated state to global memory. */
|
||||
integrator_state_write_shadow_ray(kg, shadow_state, &ray);
|
||||
// Save memory by storing the light and object indices in the shadow_isect
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, object) = ray.self.object;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, prim) = ray.self.prim;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, object) = ray.self.light_object;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, prim) = ray.self.light_prim;
|
||||
|
||||
/* Copy state from main path to shadow path. */
|
||||
const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce);
|
||||
@@ -266,13 +271,11 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
|
||||
}
|
||||
|
||||
/* Setup ray. Note that clipping works through transparent bounces. */
|
||||
INTEGRATOR_STATE_WRITE(state, ray, P) = ray_offset(sd->P,
|
||||
(label & LABEL_TRANSMIT) ? -sd->Ng : sd->Ng);
|
||||
INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
|
||||
INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(bsdf_omega_in);
|
||||
INTEGRATOR_STATE_WRITE(state, ray, t) = (label & LABEL_TRANSPARENT) ?
|
||||
INTEGRATOR_STATE(state, ray, t) - sd->ray_length :
|
||||
FLT_MAX;
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
|
||||
INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(bsdf_domega_in);
|
||||
@@ -316,7 +319,7 @@ ccl_device_forceinline bool integrate_surface_volume_only_bounce(IntegratorState
|
||||
}
|
||||
|
||||
/* Setup ray position, direction stays unchanged. */
|
||||
INTEGRATOR_STATE_WRITE(state, ray, P) = ray_offset(sd->P, -sd->Ng);
|
||||
INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
|
||||
|
||||
/* Clipping works through transparent. */
|
||||
INTEGRATOR_STATE_WRITE(state, ray, t) -= sd->ray_length;
|
||||
@@ -360,10 +363,14 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg,
|
||||
}
|
||||
|
||||
Ray ray ccl_optional_struct_init;
|
||||
ray.P = ray_offset(sd->P, sd->Ng);
|
||||
ray.P = sd->P;
|
||||
ray.D = ao_D;
|
||||
ray.t = kernel_data.integrator.ao_bounces_distance;
|
||||
ray.time = sd->time;
|
||||
ray.self.object = sd->object;
|
||||
ray.self.prim = sd->prim;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = PRIM_NONE;
|
||||
ray.dP = differential_zero_compact();
|
||||
ray.dD = differential_zero_compact();
|
||||
|
||||
@@ -375,6 +382,10 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg,
|
||||
|
||||
/* Write shadow ray and associated state to global memory. */
|
||||
integrator_state_write_shadow_ray(kg, shadow_state, &ray);
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, object) = ray.self.object;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, prim) = ray.self.prim;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, object) = ray.self.light_object;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, prim) = ray.self.light_prim;
|
||||
|
||||
/* Copy state from main path to shadow path. */
|
||||
const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce);
|
||||
|
@@ -791,6 +791,10 @@ ccl_device_forceinline void integrate_volume_direct_light(
|
||||
|
||||
/* Write shadow ray and associated state to global memory. */
|
||||
integrator_state_write_shadow_ray(kg, shadow_state, &ray);
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, object) = ray.self.object;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 0, prim) = ray.self.prim;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, object) = ray.self.light_object;
|
||||
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_isect, 1, prim) = ray.self.light_prim;
|
||||
|
||||
/* Copy state from main path to shadow path. */
|
||||
const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce);
|
||||
@@ -873,11 +877,13 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
|
||||
INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
|
||||
INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_omega_in);
|
||||
INTEGRATOR_STATE_WRITE(state, ray, t) = FLT_MAX;
|
||||
|
||||
# ifdef __RAY_DIFFERENTIALS__
|
||||
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
|
||||
INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(phase_domega_in);
|
||||
# endif
|
||||
// Save memory by storing last hit prim and object in isect
|
||||
INTEGRATOR_STATE_WRITE(state, isect, prim) = sd->prim;
|
||||
INTEGRATOR_STATE_WRITE(state, isect, object) = sd->object;
|
||||
|
||||
/* Update throughput. */
|
||||
const float3 throughput = INTEGRATOR_STATE(state, path, throughput);
|
||||
|
@@ -61,6 +61,7 @@ KERNEL_STRUCT_MEMBER(shadow_ray, packed_float3, D, KERNEL_FEATURE_PATH_TRACING)
|
||||
KERNEL_STRUCT_MEMBER(shadow_ray, float, t, KERNEL_FEATURE_PATH_TRACING)
|
||||
KERNEL_STRUCT_MEMBER(shadow_ray, float, time, KERNEL_FEATURE_PATH_TRACING)
|
||||
KERNEL_STRUCT_MEMBER(shadow_ray, float, dP, KERNEL_FEATURE_PATH_TRACING)
|
||||
KERNEL_STRUCT_MEMBER(shadow_ray, int, object, KERNEL_FEATURE_PATH_TRACING)
|
||||
KERNEL_STRUCT_END(shadow_ray)
|
||||
|
||||
/*********************** Shadow Intersection result **************************/
|
||||
|
@@ -57,7 +57,6 @@ ccl_device int subsurface_bounce(KernelGlobals kg,
|
||||
|
||||
/* Pass along object info, reusing isect to save memory. */
|
||||
INTEGRATOR_STATE_WRITE(state, subsurface, Ng) = sd->Ng;
|
||||
INTEGRATOR_STATE_WRITE(state, isect, object) = sd->object;
|
||||
|
||||
uint32_t path_flag = (INTEGRATOR_STATE(state, path, flag) & ~PATH_RAY_CAMERA) |
|
||||
((sc->type == CLOSURE_BSSRDF_BURLEY_ID) ? PATH_RAY_SUBSURFACE_DISK :
|
||||
@@ -165,10 +164,8 @@ ccl_device_inline bool subsurface_scatter(KernelGlobals kg, IntegratorState stat
|
||||
|
||||
if (object_flag & SD_OBJECT_INTERSECTS_VOLUME) {
|
||||
float3 P = INTEGRATOR_STATE(state, ray, P);
|
||||
const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng);
|
||||
const float3 offset_P = ray_offset(P, -Ng);
|
||||
|
||||
integrator_volume_stack_update_for_subsurface(kg, state, offset_P, ray.P);
|
||||
integrator_volume_stack_update_for_subsurface(kg, state, P, ray.P);
|
||||
}
|
||||
}
|
||||
# endif /* __VOLUME__ */
|
||||
|
@@ -99,6 +99,10 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg,
|
||||
ray.dP = ray_dP;
|
||||
ray.dD = differential_zero_compact();
|
||||
ray.time = time;
|
||||
ray.self.object = OBJECT_NONE;
|
||||
ray.self.prim = PRIM_NONE;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = OBJECT_NONE;
|
||||
|
||||
/* Intersect with the same object. if multiple intersections are found it
|
||||
* will use at most BSSRDF_MAX_HITS hits, a random subset of all hits. */
|
||||
|
@@ -195,6 +195,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
|
||||
const float time = INTEGRATOR_STATE(state, ray, time);
|
||||
const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng);
|
||||
const int object = INTEGRATOR_STATE(state, isect, object);
|
||||
const int prim = INTEGRATOR_STATE(state, isect, prim);
|
||||
|
||||
/* Sample diffuse surface scatter into the object. */
|
||||
float3 D;
|
||||
@@ -205,12 +206,16 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
|
||||
}
|
||||
|
||||
/* Setup ray. */
|
||||
ray.P = ray_offset(P, -Ng);
|
||||
ray.P = P;
|
||||
ray.D = D;
|
||||
ray.t = FLT_MAX;
|
||||
ray.time = time;
|
||||
ray.dP = ray_dP;
|
||||
ray.dD = differential_zero_compact();
|
||||
ray.self.object = object;
|
||||
ray.self.prim = prim;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = PRIM_NONE;
|
||||
|
||||
#ifndef __KERNEL_GPU_RAYTRACING__
|
||||
/* Compute or fetch object transforms. */
|
||||
@@ -377,7 +382,15 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
|
||||
* If yes, we will later use backwards guided sampling in order to have a decent
|
||||
* chance of connecting to it.
|
||||
* TODO: Maybe use less than 10 times the mean free path? */
|
||||
ray.t = (bounce == 0) ? max(t, 10.0f / (min3(sigma_t))) : t;
|
||||
if (bounce == 0) {
|
||||
ray.t = max(t, 10.0f / (min3(sigma_t)));
|
||||
}
|
||||
else {
|
||||
ray.t = t;
|
||||
/* After the first bounce the object can intersect the same surface again */
|
||||
ray.self.object = OBJECT_NONE;
|
||||
ray.self.prim = PRIM_NONE;
|
||||
}
|
||||
scene_intersect_local(kg, &ray, &ss_isect, object, NULL, 1);
|
||||
hit = (ss_isect.num_hits > 0);
|
||||
|
||||
@@ -408,13 +421,6 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
|
||||
if (hit) {
|
||||
t = ray.t;
|
||||
}
|
||||
else if (bounce == 0) {
|
||||
/* Restore original position if nothing was hit after the first bounce,
|
||||
* without the ray_offset() that was added to avoid self-intersection.
|
||||
* Otherwise if that offset is relatively large compared to the scattering
|
||||
* radius, we never go back up high enough to exit the surface. */
|
||||
ray.P = P;
|
||||
}
|
||||
|
||||
/* Advance to new scatter location. */
|
||||
ray.P += t * ray.D;
|
||||
|
@@ -418,8 +418,8 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
|
||||
LightType type = (LightType)klight->type;
|
||||
ls->type = type;
|
||||
ls->shader = klight->shader_id;
|
||||
ls->object = PRIM_NONE;
|
||||
ls->prim = PRIM_NONE;
|
||||
ls->object = isect->object;
|
||||
ls->prim = isect->prim;
|
||||
ls->lamp = lamp;
|
||||
/* todo: missing texture coordinates */
|
||||
ls->t = isect->t;
|
||||
|
@@ -198,7 +198,7 @@ ccl_device_inline float3 shadow_ray_offset(KernelGlobals kg,
|
||||
float NL = dot(sd->N, L);
|
||||
bool transmit = (NL < 0.0f);
|
||||
float3 Ng = (transmit ? -sd->Ng : sd->Ng);
|
||||
float3 P = ray_offset(sd->P, Ng);
|
||||
float3 P = sd->P;
|
||||
|
||||
if ((sd->type & PRIMITIVE_TRIANGLE) && (sd->shader & SHADER_SMOOTH_NORMAL)) {
|
||||
const float offset_cutoff =
|
||||
@@ -243,7 +243,7 @@ ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restri
|
||||
}
|
||||
else {
|
||||
/* other lights, avoid self-intersection */
|
||||
ray->D = ray_offset(ls->P, ls->Ng) - P;
|
||||
ray->D = ls->P - P;
|
||||
ray->D = normalize_len(ray->D, &ray->t);
|
||||
}
|
||||
}
|
||||
@@ -257,6 +257,12 @@ ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restri
|
||||
ray->dP = differential_make_compact(sd->dP);
|
||||
ray->dD = differential_zero_compact();
|
||||
ray->time = sd->time;
|
||||
|
||||
/* Fill in intersection surface and light details. */
|
||||
ray->self.prim = sd->prim;
|
||||
ray->self.object = sd->object;
|
||||
ray->self.light_prim = ls->prim;
|
||||
ray->self.light_object = ls->object;
|
||||
}
|
||||
|
||||
/* Create shadow ray towards light sample. */
|
||||
|
@@ -70,10 +70,14 @@ ccl_device float svm_ao(
|
||||
|
||||
/* Create ray. */
|
||||
Ray ray;
|
||||
ray.P = ray_offset(sd->P, N);
|
||||
ray.P = sd->P;
|
||||
ray.D = D.x * T + D.y * B + D.z * N;
|
||||
ray.t = max_dist;
|
||||
ray.time = sd->time;
|
||||
ray.self.object = sd->object;
|
||||
ray.self.prim = sd->prim;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = PRIM_NONE;
|
||||
ray.dP = differential_zero_compact();
|
||||
ray.dD = differential_zero_compact();
|
||||
|
||||
|
@@ -196,6 +196,10 @@ ccl_device float3 svm_bevel(
|
||||
ray.dP = differential_zero_compact();
|
||||
ray.dD = differential_zero_compact();
|
||||
ray.time = sd->time;
|
||||
ray.self.object = OBJECT_NONE;
|
||||
ray.self.prim = PRIM_NONE;
|
||||
ray.self.light_object = OBJECT_NONE;
|
||||
ray.self.light_prim = PRIM_NONE;
|
||||
|
||||
/* Intersect with the same object. if multiple intersections are found it
|
||||
* will use at most LOCAL_MAX_HITS hits, a random subset of all hits. */
|
||||
@@ -207,15 +211,24 @@ ccl_device float3 svm_bevel(
|
||||
/* Quickly retrieve P and Ng without setting up ShaderData. */
|
||||
float3 hit_P;
|
||||
if (sd->type == PRIMITIVE_TRIANGLE) {
|
||||
hit_P = triangle_refine_local(
|
||||
kg, sd, ray.P, ray.D, ray.t, isect.hits[hit].object, isect.hits[hit].prim);
|
||||
hit_P = triangle_point_from_uv(kg,
|
||||
sd,
|
||||
isect.hits[hit].object,
|
||||
isect.hits[hit].prim,
|
||||
isect.hits[hit].u,
|
||||
isect.hits[hit].v);
|
||||
}
|
||||
# ifdef __OBJECT_MOTION__
|
||||
else if (sd->type == PRIMITIVE_MOTION_TRIANGLE) {
|
||||
float3 verts[3];
|
||||
motion_triangle_vertices(kg, sd->object, isect.hits[hit].prim, sd->time, verts);
|
||||
hit_P = motion_triangle_refine_local(
|
||||
kg, sd, ray.P, ray.D, ray.t, isect.hits[hit].object, isect.hits[hit].prim, verts);
|
||||
hit_P = motion_triangle_point_from_uv(kg,
|
||||
sd,
|
||||
isect.hits[hit].object,
|
||||
isect.hits[hit].prim,
|
||||
isect.hits[hit].u,
|
||||
isect.hits[hit].v,
|
||||
verts);
|
||||
}
|
||||
# endif /* __OBJECT_MOTION__ */
|
||||
|
||||
|
@@ -512,12 +512,21 @@ typedef struct differential {
|
||||
|
||||
/* Ray */
|
||||
|
||||
typedef struct RaySelfPrimitives {
|
||||
int prim; /* Primitive the ray is starting from */
|
||||
int object; /* Instance prim is a part of */
|
||||
int light_prim; /* Light primitive */
|
||||
int light_object; /* Light object */
|
||||
} RaySelfPrimitives;
|
||||
|
||||
typedef struct Ray {
|
||||
float3 P; /* origin */
|
||||
float3 D; /* direction */
|
||||
float t; /* length of the ray */
|
||||
float time; /* time (for motion blur) */
|
||||
|
||||
RaySelfPrimitives self;
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
float dP;
|
||||
float dD;
|
||||
|
@@ -292,7 +292,7 @@ target_link_libraries(multitest_c
|
||||
guardedalloc_lib
|
||||
wcwidth_lib
|
||||
${OPENGL_gl_LIBRARY}
|
||||
${FREETYPE_LIBRARIES}
|
||||
${FREETYPE_LIBRARIES} ${BROTLI_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${CMAKE_DL_LIBS}
|
||||
${PLATFORM_LINKLIBS}
|
||||
|
@@ -117,8 +117,21 @@ class DATA_PT_lens(CameraButtonsPanel, Panel):
|
||||
col.prop(ccam, "fisheye_polynomial_k2", text="K2")
|
||||
col.prop(ccam, "fisheye_polynomial_k3", text="K3")
|
||||
col.prop(ccam, "fisheye_polynomial_k4", text="K4")
|
||||
|
||||
elif engine in {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}:
|
||||
elif engine == 'BLENDER_EEVEE':
|
||||
col.prop(cam, "panorama_type")
|
||||
if cam.panorama_type == 'FISHEYE_EQUIDISTANT':
|
||||
col.prop(cam, "fisheye_fov")
|
||||
elif cam.panorama_type == 'FISHEYE_EQUISOLID':
|
||||
col.prop(cam, "fisheye_lens", text="Lens")
|
||||
col.prop(cam, "fisheye_fov")
|
||||
elif cam.panorama_type == 'EQUIRECTANGULAR':
|
||||
sub = col.column(align=True)
|
||||
sub.prop(cam, "latitude_min", text="Latitude Min")
|
||||
sub.prop(cam, "latitude_max", text="Max")
|
||||
sub = col.column(align=True)
|
||||
sub.prop(cam, "longitude_min", text="Longitude Min")
|
||||
sub.prop(cam, "longitude_max", text="Max")
|
||||
elif engine in {'BLENDER_RENDER', 'BLENDER_WORKBENCH'}:
|
||||
if cam.lens_unit == 'MILLIMETERS':
|
||||
col.prop(cam, "lens")
|
||||
elif cam.lens_unit == 'FOV':
|
||||
|
@@ -227,8 +227,6 @@ def draw_material_settings(self, context):
|
||||
layout.prop(mat, "show_transparent_back")
|
||||
|
||||
layout.prop(mat, "use_screen_refraction")
|
||||
layout.prop(mat, "refraction_depth")
|
||||
layout.prop(mat, "use_sss_translucency")
|
||||
layout.prop(mat, "pass_index")
|
||||
|
||||
|
||||
|
@@ -180,6 +180,37 @@ class RENDER_PT_eevee_motion_blur(RenderButtonsPanel, Panel):
|
||||
col.prop(props, "motion_blur_max")
|
||||
col.prop(props, "motion_blur_steps", text="Steps")
|
||||
|
||||
class RENDER_PT_eevee_motion_blur_curve(RenderButtonsPanel, Panel):
|
||||
bl_label = "Shutter Curve"
|
||||
bl_parent_id = "RENDER_PT_eevee_motion_blur"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.engine in cls.COMPAT_ENGINES)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
scene = context.scene
|
||||
rd = scene.render
|
||||
props = scene.eevee
|
||||
|
||||
layout.active = props.use_motion_blur
|
||||
col = layout.column()
|
||||
|
||||
col.template_curve_mapping(rd, "motion_blur_shutter_curve")
|
||||
|
||||
col = layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
row.operator("render.shutter_curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
|
||||
row.operator("render.shutter_curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
|
||||
row.operator("render.shutter_curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
|
||||
row.operator("render.shutter_curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
|
||||
row.operator("render.shutter_curve_preset", icon='LINCURVE', text="").shape = 'LINE'
|
||||
row.operator("render.shutter_curve_preset", icon='NOCURVE', text="").shape = 'MAX'
|
||||
|
||||
|
||||
class RENDER_PT_eevee_depth_of_field(RenderButtonsPanel, Panel):
|
||||
bl_label = "Depth of Field"
|
||||
@@ -329,8 +360,8 @@ class RENDER_PT_eevee_subsurface_scattering(RenderButtonsPanel, Panel):
|
||||
col.prop(props, "sss_jitter_threshold")
|
||||
|
||||
|
||||
class RENDER_PT_eevee_screen_space_reflections(RenderButtonsPanel, Panel):
|
||||
bl_label = "Screen Space Reflections"
|
||||
class RENDER_PT_eevee_raytracing(RenderButtonsPanel, Panel):
|
||||
bl_label = "Raytracing"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
||||
|
||||
@@ -352,12 +383,9 @@ class RENDER_PT_eevee_screen_space_reflections(RenderButtonsPanel, Panel):
|
||||
|
||||
col = layout.column()
|
||||
col.active = props.use_ssr
|
||||
col.prop(props, "use_ssr_refraction", text="Refraction")
|
||||
col.prop(props, "use_ssr_halfres")
|
||||
col.prop(props, "ssr_quality")
|
||||
col.prop(props, "ssr_max_roughness")
|
||||
col.prop(props, "ssr_thickness")
|
||||
col.prop(props, "ssr_border_fade")
|
||||
col.prop(props, "ssr_firefly_fac")
|
||||
|
||||
|
||||
@@ -491,6 +519,7 @@ class RENDER_PT_eevee_film(RenderButtonsPanel, Panel):
|
||||
col = layout.column()
|
||||
col.prop(rd, "filter_size")
|
||||
col.prop(rd, "film_transparent", text="Transparent")
|
||||
col.prop(props, "use_log_space")
|
||||
|
||||
col = layout.column(align=False, heading="Overscan")
|
||||
row = col.row(align=True)
|
||||
@@ -703,8 +732,9 @@ classes = (
|
||||
RENDER_PT_eevee_bloom,
|
||||
RENDER_PT_eevee_depth_of_field,
|
||||
RENDER_PT_eevee_subsurface_scattering,
|
||||
RENDER_PT_eevee_screen_space_reflections,
|
||||
RENDER_PT_eevee_raytracing,
|
||||
RENDER_PT_eevee_motion_blur,
|
||||
RENDER_PT_eevee_motion_blur_curve,
|
||||
RENDER_PT_eevee_volumetric,
|
||||
RENDER_PT_eevee_volumetric_lighting,
|
||||
RENDER_PT_eevee_volumetric_shadows,
|
||||
|
@@ -76,6 +76,8 @@ class VIEWLAYER_PT_eevee_layer_passes_data(ViewLayerButtonsPanel, Panel):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
scene = context.scene
|
||||
scene_eevee = scene.eevee
|
||||
|
||||
view_layer = context.view_layer
|
||||
|
||||
@@ -84,6 +86,9 @@ class VIEWLAYER_PT_eevee_layer_passes_data(ViewLayerButtonsPanel, Panel):
|
||||
col.prop(view_layer, "use_pass_z")
|
||||
col.prop(view_layer, "use_pass_mist")
|
||||
col.prop(view_layer, "use_pass_normal")
|
||||
sub = col.column()
|
||||
sub.active = not scene_eevee.use_motion_blur
|
||||
sub.prop(view_layer, "use_pass_vector")
|
||||
|
||||
|
||||
class VIEWLAYER_PT_eevee_layer_passes_light(ViewLayerButtonsPanel, Panel):
|
||||
|
@@ -761,6 +761,15 @@ class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel):
|
||||
col.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="")
|
||||
col.separator()
|
||||
col.operator("ed.lib_id_generate_preview", icon='FILE_REFRESH', text="")
|
||||
col.menu("ASSETBROWSER_MT_metadata_preview_menu", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
|
||||
class ASSETBROWSER_MT_metadata_preview_menu(bpy.types.Menu):
|
||||
bl_label = "Preview"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator("ed.lib_id_generate_preview_from_object", text="Render Active Object")
|
||||
|
||||
|
||||
class ASSETBROWSER_PT_metadata_tags(asset_utils.AssetMetaDataPanel, Panel):
|
||||
@@ -840,6 +849,7 @@ classes = (
|
||||
ASSETBROWSER_MT_view,
|
||||
ASSETBROWSER_MT_select,
|
||||
ASSETBROWSER_MT_edit,
|
||||
ASSETBROWSER_MT_metadata_preview_menu,
|
||||
ASSETBROWSER_PT_metadata,
|
||||
ASSETBROWSER_PT_metadata_preview,
|
||||
ASSETBROWSER_PT_metadata_tags,
|
||||
|
@@ -54,7 +54,7 @@ set(LIB
|
||||
bf_gpu
|
||||
bf_intern_guardedalloc
|
||||
|
||||
${FREETYPE_LIBRARIES}
|
||||
${FREETYPE_LIBRARIES} ${BROTLI_LIBRARIES}
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
|
@@ -46,7 +46,7 @@ void *BKE_camera_add(struct Main *bmain, const char *name);
|
||||
/**
|
||||
* Get the camera's DOF value, takes the DOF object into account.
|
||||
*/
|
||||
float BKE_camera_object_dof_distance(struct Object *ob);
|
||||
float BKE_camera_object_dof_distance(const struct Object *ob);
|
||||
|
||||
int BKE_camera_sensor_fit(int sensor_fit, float sizex, float sizey);
|
||||
float BKE_camera_sensor_size(int sensor_fit, float sensor_x, float sensor_y);
|
||||
|
@@ -436,6 +436,12 @@ int CustomData_get_render_layer(const struct CustomData *data, int type);
|
||||
int CustomData_get_clone_layer(const struct CustomData *data, int type);
|
||||
int CustomData_get_stencil_layer(const struct CustomData *data, int type);
|
||||
|
||||
/**
|
||||
* Returns name of the active layer of the given type or NULL
|
||||
* if no such active layer is defined.
|
||||
*/
|
||||
const char *CustomData_get_active_layer_name(const struct CustomData *data, int type);
|
||||
|
||||
/**
|
||||
* Copies the data from source to the data element at index in the first layer of type
|
||||
* no effect if there is no layer of type.
|
||||
|
@@ -38,6 +38,9 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ID;
|
||||
struct IDRemapper;
|
||||
|
||||
/* BKE_libblock_free, delete are declared in BKE_lib_id.h for convenience. */
|
||||
|
||||
/* Also IDRemap->flag. */
|
||||
@@ -97,6 +100,19 @@ enum {
|
||||
ID_REMAP_FORCE_OBDATA_IN_EDITMODE = 1 << 9,
|
||||
};
|
||||
|
||||
/**
|
||||
* Replace all references in given Main using the given \a mappings
|
||||
*
|
||||
* \note Is preferred over BKE_libblock_remap_locked due to performance.
|
||||
*/
|
||||
void BKE_libblock_remap_multiple_locked(struct Main *bmain,
|
||||
const struct IDRemapper *mappings,
|
||||
const short remap_flags);
|
||||
|
||||
void BKE_libblock_remap_multiple(struct Main *bmain,
|
||||
const struct IDRemapper *mappings,
|
||||
const short remap_flags);
|
||||
|
||||
/**
|
||||
* Replace all references in given Main to \a old_id by \a new_id
|
||||
* (if \a new_id is NULL, it unlinks \a old_id).
|
||||
@@ -146,12 +162,61 @@ void BKE_libblock_relink_to_newid(struct Main *bmain, struct ID *id, int remap_f
|
||||
ATTR_NONNULL();
|
||||
|
||||
typedef void (*BKE_library_free_notifier_reference_cb)(const void *);
|
||||
typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *);
|
||||
typedef void (*BKE_library_remap_editor_id_reference_cb)(const struct IDRemapper *mappings);
|
||||
|
||||
void BKE_library_callback_free_notifier_reference_set(BKE_library_free_notifier_reference_cb func);
|
||||
void BKE_library_callback_remap_editor_id_reference_set(
|
||||
BKE_library_remap_editor_id_reference_cb func);
|
||||
|
||||
/* IDRemapper */
|
||||
struct IDRemapper;
|
||||
typedef enum IDRemapperApplyResult {
|
||||
/** No remapping rules available for the source. */
|
||||
ID_REMAP_RESULT_SOURCE_UNAVAILABLE,
|
||||
/** Source isn't mappable (e.g. NULL). */
|
||||
ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE,
|
||||
/** Source has been remapped to a new pointer. */
|
||||
ID_REMAP_RESULT_SOURCE_REMAPPED,
|
||||
/** Source has been set to NULL. */
|
||||
ID_REMAP_RESULT_SOURCE_UNASSIGNED,
|
||||
} IDRemapperApplyResult;
|
||||
|
||||
typedef enum IDRemapperApplyOptions {
|
||||
ID_REMAP_APPLY_UPDATE_REFCOUNT = (1 << 0),
|
||||
ID_REMAP_APPLY_ENSURE_REAL = (1 << 1),
|
||||
|
||||
ID_REMAP_APPLY_DEFAULT = 0,
|
||||
} IDRemapperApplyOptions;
|
||||
|
||||
typedef void (*IDRemapperIterFunction)(struct ID *old_id, struct ID *new_id, void *user_data);
|
||||
|
||||
/**
|
||||
* Create a new ID Remapper.
|
||||
*
|
||||
* An ID remapper stores multiple remapping rules.
|
||||
*/
|
||||
struct IDRemapper *BKE_id_remapper_create(void);
|
||||
|
||||
void BKE_id_remapper_clear(struct IDRemapper *id_remapper);
|
||||
bool BKE_id_remapper_is_empty(const struct IDRemapper *id_remapper);
|
||||
/** Free the given ID Remapper. */
|
||||
void BKE_id_remapper_free(struct IDRemapper *id_remapper);
|
||||
/** Add a new remapping. */
|
||||
void BKE_id_remapper_add(struct IDRemapper *id_remapper, struct ID *old_id, struct ID *new_id);
|
||||
|
||||
/**
|
||||
* Apply a remapping.
|
||||
*
|
||||
* Update the id pointer stored in the given r_id_ptr if a remapping rule exists.
|
||||
*/
|
||||
IDRemapperApplyResult BKE_id_remapper_apply(const struct IDRemapper *id_remapper,
|
||||
struct ID **r_id_ptr,
|
||||
IDRemapperApplyOptions options);
|
||||
bool BKE_id_remapper_has_mapping_for(const struct IDRemapper *id_remapper, uint64_t type_filter);
|
||||
void BKE_id_remapper_iter(const struct IDRemapper *id_remapper,
|
||||
IDRemapperIterFunction func,
|
||||
void *user_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -38,6 +38,7 @@ struct BlendLibReader;
|
||||
struct BlendWriter;
|
||||
struct Header;
|
||||
struct ID;
|
||||
struct IDRemapper;
|
||||
struct LibraryForeachIDData;
|
||||
struct ListBase;
|
||||
struct Menu;
|
||||
@@ -117,10 +118,7 @@ typedef struct SpaceType {
|
||||
bContextDataCallback context;
|
||||
|
||||
/* Used when we want to replace an ID by another (or NULL). */
|
||||
void (*id_remap)(struct ScrArea *area,
|
||||
struct SpaceLink *sl,
|
||||
struct ID *old_id,
|
||||
struct ID *new_id);
|
||||
void (*id_remap)(struct ScrArea *area, struct SpaceLink *sl, const struct IDRemapper *mappings);
|
||||
|
||||
int (*space_subtype_get)(struct ScrArea *area);
|
||||
void (*space_subtype_set)(struct ScrArea *area, int value);
|
||||
|
@@ -33,6 +33,15 @@ struct World;
|
||||
struct World *BKE_world_add(struct Main *bmain, const char *name);
|
||||
void BKE_world_eval(struct Depsgraph *depsgraph, struct World *world);
|
||||
|
||||
struct World *BKE_world_default(void);
|
||||
|
||||
void BKE_world_defaults_free_gpu(void);
|
||||
|
||||
/* Module */
|
||||
|
||||
void BKE_worlds_init(void);
|
||||
void BKE_worlds_exit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -179,6 +179,7 @@ set(SRC
|
||||
intern/lib_id.c
|
||||
intern/lib_id_delete.c
|
||||
intern/lib_id_eval.c
|
||||
intern/lib_id_remapper.cc
|
||||
intern/lib_override.c
|
||||
intern/lib_query.c
|
||||
intern/lib_remap.c
|
||||
@@ -522,7 +523,7 @@ set(LIB
|
||||
bf_simulation
|
||||
|
||||
# For `vfontdata_freetype.c`.
|
||||
${FREETYPE_LIBRARIES}
|
||||
${FREETYPE_LIBRARIES} ${BROTLI_LIBRARIES}
|
||||
)
|
||||
|
||||
if(WITH_BINRELOC)
|
||||
@@ -823,6 +824,7 @@ if(WITH_GTESTS)
|
||||
intern/idprop_serialize_test.cc
|
||||
intern/lattice_deform_test.cc
|
||||
intern/layer_test.cc
|
||||
intern/lib_id_remapper_test.cc
|
||||
intern/lib_id_test.cc
|
||||
intern/lib_remap_test.cc
|
||||
intern/tracking_test.cc
|
||||
|
@@ -218,7 +218,7 @@ void *BKE_camera_add(Main *bmain, const char *name)
|
||||
return cam;
|
||||
}
|
||||
|
||||
float BKE_camera_object_dof_distance(Object *ob)
|
||||
float BKE_camera_object_dof_distance(const Object *ob)
|
||||
{
|
||||
Camera *cam = (Camera *)ob->data;
|
||||
if (ob->type != OB_CAMERA) {
|
||||
|
@@ -66,8 +66,8 @@ static void vert_extrude_to_mesh_data(const Spline &spline,
|
||||
|
||||
if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) {
|
||||
MEdge &edge = r_edges[edge_offset + spline.evaluated_edges_size() - 1];
|
||||
edge.v1 = vert_offset;
|
||||
edge.v2 = vert_offset + eval_size - 1;
|
||||
edge.v1 = vert_offset + eval_size - 1;
|
||||
edge.v2 = vert_offset;
|
||||
edge.flag = ME_LOOSEEDGE;
|
||||
}
|
||||
|
||||
|
@@ -2427,6 +2427,13 @@ int CustomData_get_stencil_layer(const CustomData *data, int type)
|
||||
return (layer_index != -1) ? data->layers[layer_index].active_mask : -1;
|
||||
}
|
||||
|
||||
const char *CustomData_get_active_layer_name(const struct CustomData *data, const int type)
|
||||
{
|
||||
/* Get the layer index of the active layer of this type. */
|
||||
const int layer_index = CustomData_get_active_layer_index(data, type);
|
||||
return layer_index < 0 ? NULL : data->layers[layer_index].name;
|
||||
}
|
||||
|
||||
void CustomData_set_layer_active(CustomData *data, int type, int n)
|
||||
{
|
||||
for (int i = 0; i < data->totlayer; i++) {
|
||||
|
@@ -154,7 +154,10 @@ void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_i
|
||||
}
|
||||
|
||||
if (remap_editor_id_reference_cb) {
|
||||
remap_editor_id_reference_cb(id, NULL);
|
||||
struct IDRemapper *remapper = BKE_id_remapper_create();
|
||||
BKE_id_remapper_add(remapper, id, NULL);
|
||||
remap_editor_id_reference_cb(remapper);
|
||||
BKE_id_remapper_free(remapper);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,32 +295,40 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion)
|
||||
* Note that we go forward here, since we want to check dependencies before users
|
||||
* (e.g. meshes before objects).
|
||||
* Avoids to have to loop twice. */
|
||||
struct IDRemapper *remapper = BKE_id_remapper_create();
|
||||
for (i = 0; i < base_count; i++) {
|
||||
ListBase *lb = lbarray[i];
|
||||
ID *id, *id_next;
|
||||
BKE_id_remapper_clear(remapper);
|
||||
|
||||
for (id = lb->first; id; id = id_next) {
|
||||
id_next = id->next;
|
||||
/* NOTE: in case we delete a library, we also delete all its datablocks! */
|
||||
if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) {
|
||||
id->tag |= tag;
|
||||
|
||||
/* Will tag 'never NULL' users of this ID too.
|
||||
* Note that we cannot use BKE_libblock_unlink() here, since it would ignore indirect
|
||||
* (and proxy!) links, this can lead to nasty crashing here in second,
|
||||
* actual deleting loop.
|
||||
* Also, this will also flag users of deleted data that cannot be unlinked
|
||||
* (object using deleted obdata, etc.), so that they also get deleted. */
|
||||
BKE_libblock_remap_locked(bmain,
|
||||
id,
|
||||
NULL,
|
||||
(ID_REMAP_FLAG_NEVER_NULL_USAGE |
|
||||
ID_REMAP_FORCE_NEVER_NULL_USAGE |
|
||||
ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS));
|
||||
BKE_id_remapper_add(remapper, id, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (BKE_id_remapper_is_empty(remapper)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Will tag 'never NULL' users of this ID too.
|
||||
* Note that we cannot use BKE_libblock_unlink() here, since it would ignore indirect
|
||||
* (and proxy!) links, this can lead to nasty crashing here in second,
|
||||
* actual deleting loop.
|
||||
* Also, this will also flag users of deleted data that cannot be unlinked
|
||||
* (object using deleted obdata, etc.), so that they also get deleted. */
|
||||
BKE_libblock_remap_multiple_locked(bmain,
|
||||
remapper,
|
||||
(ID_REMAP_FLAG_NEVER_NULL_USAGE |
|
||||
ID_REMAP_FORCE_NEVER_NULL_USAGE |
|
||||
ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS));
|
||||
}
|
||||
BKE_id_remapper_free(remapper);
|
||||
}
|
||||
|
||||
BKE_main_unlock(bmain);
|
||||
|
||||
/* In usual reversed order, such that all usage of a given ID, even 'never NULL' ones,
|
||||
|
175
source/blender/blenkernel/intern/lib_id_remapper.cc
Normal file
175
source/blender/blenkernel/intern/lib_id_remapper.cc
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2022 by Blender Foundation.
|
||||
*/
|
||||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
#include "BKE_idtype.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_lib_remap.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_map.hh"
|
||||
|
||||
using IDTypeFilter = uint64_t;
|
||||
|
||||
namespace blender::bke::id::remapper {
|
||||
struct IDRemapper {
|
||||
private:
|
||||
Map<ID *, ID *> mappings;
|
||||
IDTypeFilter source_types = 0;
|
||||
|
||||
public:
|
||||
void clear()
|
||||
{
|
||||
mappings.clear();
|
||||
source_types = 0;
|
||||
}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return mappings.is_empty();
|
||||
}
|
||||
|
||||
void add(ID *old_id, ID *new_id)
|
||||
{
|
||||
BLI_assert(old_id != nullptr);
|
||||
BLI_assert(new_id == nullptr || (GS(old_id->name) == GS(new_id->name)));
|
||||
mappings.add(old_id, new_id);
|
||||
source_types |= BKE_idtype_idcode_to_idfilter(GS(old_id->name));
|
||||
}
|
||||
|
||||
bool contains_mappings_for_any(IDTypeFilter filter) const
|
||||
{
|
||||
return (source_types & filter) != 0;
|
||||
}
|
||||
|
||||
IDRemapperApplyResult apply(ID **r_id_ptr, IDRemapperApplyOptions options) const
|
||||
{
|
||||
BLI_assert(r_id_ptr != nullptr);
|
||||
if (*r_id_ptr == nullptr) {
|
||||
return ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE;
|
||||
}
|
||||
|
||||
if (!mappings.contains(*r_id_ptr)) {
|
||||
return ID_REMAP_RESULT_SOURCE_UNAVAILABLE;
|
||||
}
|
||||
|
||||
if (options & ID_REMAP_APPLY_UPDATE_REFCOUNT) {
|
||||
id_us_min(*r_id_ptr);
|
||||
}
|
||||
|
||||
*r_id_ptr = mappings.lookup(*r_id_ptr);
|
||||
if (*r_id_ptr == nullptr) {
|
||||
return ID_REMAP_RESULT_SOURCE_UNASSIGNED;
|
||||
}
|
||||
|
||||
if (options & ID_REMAP_APPLY_UPDATE_REFCOUNT) {
|
||||
id_us_plus(*r_id_ptr);
|
||||
}
|
||||
|
||||
if (options & ID_REMAP_APPLY_ENSURE_REAL) {
|
||||
id_us_ensure_real(*r_id_ptr);
|
||||
}
|
||||
return ID_REMAP_RESULT_SOURCE_REMAPPED;
|
||||
}
|
||||
|
||||
void iter(IDRemapperIterFunction func, void *user_data) const
|
||||
{
|
||||
for (auto item : mappings.items()) {
|
||||
func(item.key, item.value, user_data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::bke::id::remapper
|
||||
|
||||
/** \brief wrap CPP IDRemapper to a C handle. */
|
||||
static IDRemapper *wrap(blender::bke::id::remapper::IDRemapper *remapper)
|
||||
{
|
||||
return static_cast<IDRemapper *>(static_cast<void *>(remapper));
|
||||
}
|
||||
|
||||
/** \brief wrap C handle to a CPP IDRemapper. */
|
||||
static blender::bke::id::remapper::IDRemapper *unwrap(IDRemapper *remapper)
|
||||
{
|
||||
return static_cast<blender::bke::id::remapper::IDRemapper *>(static_cast<void *>(remapper));
|
||||
}
|
||||
|
||||
/** \brief wrap C handle to a CPP IDRemapper. */
|
||||
static const blender::bke::id::remapper::IDRemapper *unwrap(const IDRemapper *remapper)
|
||||
{
|
||||
return static_cast<const blender::bke::id::remapper::IDRemapper *>(
|
||||
static_cast<const void *>(remapper));
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
IDRemapper *BKE_id_remapper_create(void)
|
||||
{
|
||||
blender::bke::id::remapper::IDRemapper *remapper =
|
||||
MEM_new<blender::bke::id::remapper::IDRemapper>(__func__);
|
||||
return wrap(remapper);
|
||||
}
|
||||
|
||||
void BKE_id_remapper_free(IDRemapper *id_remapper)
|
||||
{
|
||||
blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
|
||||
MEM_delete<blender::bke::id::remapper::IDRemapper>(remapper);
|
||||
}
|
||||
|
||||
void BKE_id_remapper_clear(struct IDRemapper *id_remapper)
|
||||
{
|
||||
blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
|
||||
remapper->clear();
|
||||
}
|
||||
|
||||
bool BKE_id_remapper_is_empty(const struct IDRemapper *id_remapper)
|
||||
{
|
||||
const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
|
||||
return remapper->is_empty();
|
||||
}
|
||||
|
||||
void BKE_id_remapper_add(IDRemapper *id_remapper, ID *old_id, ID *new_id)
|
||||
{
|
||||
blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
|
||||
remapper->add(old_id, new_id);
|
||||
}
|
||||
|
||||
bool BKE_id_remapper_has_mapping_for(const struct IDRemapper *id_remapper, uint64_t type_filter)
|
||||
{
|
||||
const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
|
||||
return remapper->contains_mappings_for_any(type_filter);
|
||||
}
|
||||
|
||||
IDRemapperApplyResult BKE_id_remapper_apply(const IDRemapper *id_remapper,
|
||||
ID **r_id_ptr,
|
||||
const IDRemapperApplyOptions options)
|
||||
{
|
||||
const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
|
||||
return remapper->apply(r_id_ptr, options);
|
||||
}
|
||||
|
||||
void BKE_id_remapper_iter(const struct IDRemapper *id_remapper,
|
||||
IDRemapperIterFunction func,
|
||||
void *user_data)
|
||||
{
|
||||
const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
|
||||
remapper->iter(func, user_data);
|
||||
}
|
||||
}
|
83
source/blender/blenkernel/intern/lib_id_remapper_test.cc
Normal file
83
source/blender/blenkernel/intern/lib_id_remapper_test.cc
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2022 by Blender Foundation.
|
||||
*/
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "BKE_lib_remap.h"
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
namespace blender::bke::id::remapper::tests {
|
||||
|
||||
TEST(lib_id_remapper, unavailable)
|
||||
{
|
||||
ID id1;
|
||||
ID *idp = &id1;
|
||||
|
||||
IDRemapper *remapper = BKE_id_remapper_create();
|
||||
IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT);
|
||||
EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_UNAVAILABLE);
|
||||
|
||||
BKE_id_remapper_free(remapper);
|
||||
}
|
||||
|
||||
TEST(lib_id_remapper, not_mappable)
|
||||
{
|
||||
ID *idp = nullptr;
|
||||
|
||||
IDRemapper *remapper = BKE_id_remapper_create();
|
||||
IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT);
|
||||
EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE);
|
||||
|
||||
BKE_id_remapper_free(remapper);
|
||||
}
|
||||
|
||||
TEST(lib_id_remapper, mapped)
|
||||
{
|
||||
ID id1;
|
||||
ID id2;
|
||||
ID *idp = &id1;
|
||||
BLI_strncpy(id1.name, "OB1", sizeof(id1.name));
|
||||
BLI_strncpy(id2.name, "OB2", sizeof(id2.name));
|
||||
|
||||
IDRemapper *remapper = BKE_id_remapper_create();
|
||||
BKE_id_remapper_add(remapper, &id1, &id2);
|
||||
IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT);
|
||||
EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_REMAPPED);
|
||||
EXPECT_EQ(idp, &id2);
|
||||
|
||||
BKE_id_remapper_free(remapper);
|
||||
}
|
||||
|
||||
TEST(lib_id_remapper, unassigned)
|
||||
{
|
||||
ID id1;
|
||||
ID *idp = &id1;
|
||||
|
||||
IDRemapper *remapper = BKE_id_remapper_create();
|
||||
BKE_id_remapper_add(remapper, &id1, nullptr);
|
||||
IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT);
|
||||
EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_UNASSIGNED);
|
||||
EXPECT_EQ(idp, nullptr);
|
||||
|
||||
BKE_id_remapper_free(remapper);
|
||||
}
|
||||
|
||||
} // namespace blender::bke::id::remapper::tests
|
@@ -1121,6 +1121,45 @@ void BKE_lib_override_library_main_proxy_convert(Main *bmain, BlendFileReadRepor
|
||||
}
|
||||
}
|
||||
|
||||
static void lib_override_library_remap(Main *bmain,
|
||||
const ID *id_root_reference,
|
||||
GHash *linkedref_to_old_override)
|
||||
{
|
||||
ID *id;
|
||||
struct IDRemapper *remapper = BKE_id_remapper_create();
|
||||
FOREACH_MAIN_ID_BEGIN (bmain, id) {
|
||||
|
||||
if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
|
||||
ID *id_override_new = id->newid;
|
||||
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
|
||||
if (id_override_old == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BKE_id_remapper_add(remapper, id_override_old, id_override_new);
|
||||
/* Remap no-main override IDs we just created too. */
|
||||
GHashIterator linkedref_to_old_override_iter;
|
||||
GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) {
|
||||
ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter);
|
||||
if ((id_override_old_iter->tag & LIB_TAG_NO_MAIN) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BKE_libblock_relink_ex(bmain,
|
||||
id_override_old_iter,
|
||||
id_override_old,
|
||||
id_override_new,
|
||||
ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
/* Remap all IDs to use the new override. */
|
||||
BKE_libblock_remap_multiple(bmain, remapper, 0);
|
||||
BKE_id_remapper_free(remapper);
|
||||
}
|
||||
|
||||
static bool lib_override_library_resync(Main *bmain,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer,
|
||||
@@ -1312,32 +1351,9 @@ static bool lib_override_library_resync(Main *bmain,
|
||||
}
|
||||
FOREACH_MAIN_LISTBASE_END;
|
||||
|
||||
/* We need to remap old to new override usages in a separate loop, after all new overrides have
|
||||
/* We remap old to new override usages in a separate loop, after all new overrides have
|
||||
* been added to Main. */
|
||||
FOREACH_MAIN_ID_BEGIN (bmain, id) {
|
||||
if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
|
||||
ID *id_override_new = id->newid;
|
||||
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
|
||||
|
||||
if (id_override_old != NULL) {
|
||||
/* Remap all IDs to use the new override. */
|
||||
BKE_libblock_remap(bmain, id_override_old, id_override_new, 0);
|
||||
/* Remap no-main override IDs we just created too. */
|
||||
GHashIterator linkedref_to_old_override_iter;
|
||||
GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) {
|
||||
ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter);
|
||||
if (id_override_old_iter->tag & LIB_TAG_NO_MAIN) {
|
||||
BKE_libblock_relink_ex(bmain,
|
||||
id_override_old_iter,
|
||||
id_override_old,
|
||||
id_override_new,
|
||||
ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
lib_override_library_remap(bmain, id_root_reference, linkedref_to_old_override);
|
||||
|
||||
BKE_main_collection_sync(bmain);
|
||||
|
||||
@@ -1537,11 +1553,13 @@ static void lib_override_resync_tagging_finalize_recurse(Main *bmain,
|
||||
CLOG_ERROR(
|
||||
&LOG,
|
||||
"While processing indirect level %d, ID %s from lib %s of indirect level %d detected "
|
||||
"as needing resync.",
|
||||
"as needing resync, skipping.",
|
||||
library_indirect_level,
|
||||
id->name,
|
||||
id->lib->filepath,
|
||||
id->lib->temp_index);
|
||||
id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
|
||||
return;
|
||||
}
|
||||
|
||||
MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
|
||||
|
@@ -510,11 +510,18 @@ static void libblock_remap_data(
|
||||
#endif
|
||||
}
|
||||
|
||||
void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const short remap_flags)
|
||||
typedef struct LibblockRemapMultipleUserData {
|
||||
Main *bmain;
|
||||
short remap_flags;
|
||||
} LibBlockRemapMultipleUserData;
|
||||
|
||||
static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_data)
|
||||
{
|
||||
LibBlockRemapMultipleUserData *data = user_data;
|
||||
Main *bmain = data->bmain;
|
||||
const short remap_flags = data->remap_flags;
|
||||
|
||||
IDRemap id_remap_data;
|
||||
ID *old_id = old_idv;
|
||||
ID *new_id = new_idv;
|
||||
int skipped_direct, skipped_refcounted;
|
||||
|
||||
BLI_assert(old_id != NULL);
|
||||
@@ -527,13 +534,6 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
|
||||
free_notifier_reference_cb(old_id);
|
||||
}
|
||||
|
||||
/* We assume editors do not hold references to their IDs... This is false in some cases
|
||||
* (Image is especially tricky here),
|
||||
* editors' code is to handle refcount (id->us) itself then. */
|
||||
if (remap_editor_id_reference_cb) {
|
||||
remap_editor_id_reference_cb(old_id, new_id);
|
||||
}
|
||||
|
||||
skipped_direct = id_remap_data.skipped_direct;
|
||||
skipped_refcounted = id_remap_data.skipped_refcounted;
|
||||
|
||||
@@ -606,6 +606,41 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
|
||||
DEG_relations_tag_update(bmain);
|
||||
}
|
||||
|
||||
void BKE_libblock_remap_multiple_locked(Main *bmain,
|
||||
const struct IDRemapper *mappings,
|
||||
const short remap_flags)
|
||||
{
|
||||
if (BKE_id_remapper_is_empty(mappings)) {
|
||||
/* Early exit nothing to do. */
|
||||
return;
|
||||
}
|
||||
|
||||
LibBlockRemapMultipleUserData user_data;
|
||||
user_data.bmain = bmain;
|
||||
user_data.remap_flags = remap_flags;
|
||||
BKE_id_remapper_iter(mappings, libblock_remap_foreach_idpair_cb, &user_data);
|
||||
|
||||
/* We assume editors do not hold references to their IDs... This is false in some cases
|
||||
* (Image is especially tricky here),
|
||||
* editors' code is to handle refcount (id->us) itself then. */
|
||||
if (remap_editor_id_reference_cb) {
|
||||
remap_editor_id_reference_cb(mappings);
|
||||
}
|
||||
|
||||
/* Full rebuild of DEG! */
|
||||
DEG_relations_tag_update(bmain);
|
||||
}
|
||||
|
||||
void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const short remap_flags)
|
||||
{
|
||||
struct IDRemapper *remapper = BKE_id_remapper_create();
|
||||
ID *old_id = old_idv;
|
||||
ID *new_id = new_idv;
|
||||
BKE_id_remapper_add(remapper, old_id, new_id);
|
||||
BKE_libblock_remap_multiple_locked(bmain, remapper, remap_flags);
|
||||
BKE_id_remapper_free(remapper);
|
||||
}
|
||||
|
||||
void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short remap_flags)
|
||||
{
|
||||
BKE_main_lock(bmain);
|
||||
@@ -615,6 +650,17 @@ void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short r
|
||||
BKE_main_unlock(bmain);
|
||||
}
|
||||
|
||||
void BKE_libblock_remap_multiple(Main *bmain,
|
||||
const struct IDRemapper *mappings,
|
||||
const short remap_flags)
|
||||
{
|
||||
BKE_main_lock(bmain);
|
||||
|
||||
BKE_libblock_remap_multiple_locked(bmain, mappings, remap_flags);
|
||||
|
||||
BKE_main_unlock(bmain);
|
||||
}
|
||||
|
||||
void BKE_libblock_unlink(Main *bmain,
|
||||
void *idv,
|
||||
const bool do_flag_never_null,
|
||||
|
@@ -46,7 +46,7 @@ void BKE_mesh_foreach_mapped_vert(
|
||||
BMIter iter;
|
||||
BMVert *eve;
|
||||
int i;
|
||||
if (mesh->runtime.edit_data->vertexCos != NULL) {
|
||||
if (mesh->runtime.edit_data != NULL && mesh->runtime.edit_data->vertexCos != NULL) {
|
||||
const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos;
|
||||
const float(*vertexNos)[3];
|
||||
if (flag & MESH_FOREACH_USE_NORMAL) {
|
||||
@@ -106,7 +106,7 @@ void BKE_mesh_foreach_mapped_edge(
|
||||
BMIter iter;
|
||||
BMEdge *eed;
|
||||
int i;
|
||||
if (mesh->runtime.edit_data->vertexCos != NULL) {
|
||||
if (mesh->runtime.edit_data != NULL && mesh->runtime.edit_data->vertexCos != NULL) {
|
||||
const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos;
|
||||
BM_mesh_elem_index_ensure(bm, BM_VERT);
|
||||
|
||||
@@ -164,7 +164,8 @@ void BKE_mesh_foreach_mapped_loop(Mesh *mesh,
|
||||
BMIter iter;
|
||||
BMFace *efa;
|
||||
|
||||
const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos;
|
||||
const float(*vertexCos)[3] = mesh->runtime.edit_data ? mesh->runtime.edit_data->vertexCos :
|
||||
NULL;
|
||||
|
||||
/* XXX: investigate using EditMesh data. */
|
||||
const float(*lnors)[3] = (flag & MESH_FOREACH_USE_NORMAL) ?
|
||||
@@ -231,7 +232,7 @@ void BKE_mesh_foreach_mapped_face_center(
|
||||
void *userData,
|
||||
MeshForeachFlag flag)
|
||||
{
|
||||
if (mesh->edit_mesh != NULL) {
|
||||
if (mesh->edit_mesh != NULL && mesh->runtime.edit_data != NULL) {
|
||||
BMEditMesh *em = mesh->edit_mesh;
|
||||
BMesh *bm = em->bm;
|
||||
const float(*polyCos)[3];
|
||||
|
@@ -420,7 +420,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
|
||||
/* calculate custom normals into loop_normals, then mirror first half into second half */
|
||||
|
||||
BKE_mesh_normals_loop_split(result->mvert,
|
||||
BKE_mesh_vertex_normals_ensure(mesh),
|
||||
BKE_mesh_vertex_normals_ensure(result),
|
||||
result->totvert,
|
||||
result->medge,
|
||||
result->totedge,
|
||||
@@ -428,7 +428,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
|
||||
loop_normals,
|
||||
totloop,
|
||||
result->mpoly,
|
||||
BKE_mesh_poly_normals_ensure(mesh),
|
||||
BKE_mesh_poly_normals_ensure(result),
|
||||
totpoly,
|
||||
true,
|
||||
mesh->smoothresh,
|
||||
|
@@ -49,6 +49,8 @@
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "NOD_shader.h"
|
||||
|
||||
#include "DRW_engine.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
@@ -227,3 +229,57 @@ void BKE_world_eval(struct Depsgraph *depsgraph, World *world)
|
||||
DEG_debug_print_eval(depsgraph, __func__, world->id.name, world);
|
||||
GPU_material_free(&world->gpumaterial);
|
||||
}
|
||||
|
||||
/* Default World
|
||||
*
|
||||
* Used for rendering when a scene have no world assigned. */
|
||||
|
||||
static World default_world;
|
||||
|
||||
static void world_default_init(World *ma)
|
||||
{
|
||||
bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
|
||||
ma->nodetree = ntree;
|
||||
ma->use_nodes = true;
|
||||
|
||||
bNode *background = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND);
|
||||
bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD);
|
||||
bNodeSocket *background_out = nodeFindSocket(background, SOCK_OUT, "Background");
|
||||
bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface");
|
||||
nodeAddLink(ntree, background, background_out, output, output_in);
|
||||
nodeSetActive(ntree, output);
|
||||
|
||||
bNodeSocketValueRGBA *color_socket_ =
|
||||
(bNodeSocketValueRGBA *)nodeFindSocket(background, SOCK_IN, "Color")->default_value;
|
||||
|
||||
color_socket_->value[0] = 0.0f;
|
||||
color_socket_->value[1] = 0.0f;
|
||||
color_socket_->value[2] = 0.0f;
|
||||
color_socket_->value[3] = 1.0f;
|
||||
}
|
||||
|
||||
World *BKE_world_default(void)
|
||||
{
|
||||
return &default_world;
|
||||
}
|
||||
|
||||
void BKE_world_defaults_free_gpu(void)
|
||||
{
|
||||
if (default_world.gpumaterial.first) {
|
||||
GPU_material_free(&default_world.gpumaterial);
|
||||
}
|
||||
}
|
||||
|
||||
/* Module functions called on startup and exit. */
|
||||
|
||||
void BKE_worlds_init(void)
|
||||
{
|
||||
world_init_data(&default_world.id);
|
||||
|
||||
world_default_init(&default_world);
|
||||
}
|
||||
|
||||
void BKE_worlds_exit(void)
|
||||
{
|
||||
world_free_data(&default_world.id);
|
||||
}
|
||||
|
@@ -100,6 +100,9 @@ void _BLI_assert_unreachable_print(const char *file, int line, const char *funct
|
||||
#define BLI_STATIC_ASSERT_ALIGN(st, align) \
|
||||
BLI_STATIC_ASSERT((sizeof(st) % (align) == 0), "Structure must be strictly aligned")
|
||||
|
||||
#define BLI_STATIC_ASSERT_SIZE(st, max_size) \
|
||||
BLI_STATIC_ASSERT(sizeof(CullingData) <= max_size, "Structure is too big")
|
||||
|
||||
/**
|
||||
* Indicates that this line of code should never be executed. If it is reached, it will abort in
|
||||
* debug builds and print an error in release builds.
|
||||
|
@@ -107,6 +107,20 @@ struct float4x4 {
|
||||
return &values[0][0];
|
||||
}
|
||||
|
||||
float *operator[](const int64_t index)
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < 4);
|
||||
return &values[index][0];
|
||||
}
|
||||
|
||||
const float *operator[](const int64_t index) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < 4);
|
||||
return &values[index][0];
|
||||
}
|
||||
|
||||
using c_style_float4x4 = float[4][4];
|
||||
c_style_float4x4 &ptr()
|
||||
{
|
||||
|
170
source/blender/blenlib/BLI_int2.hh
Normal file
170
source/blender/blenlib/BLI_int2.hh
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_float2.hh"
|
||||
#include "BLI_int3.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct int2 {
|
||||
int32_t x, y;
|
||||
|
||||
int2() = default;
|
||||
|
||||
int2(const int32_t *ptr) : x{ptr[0]}, y{ptr[1]}
|
||||
{
|
||||
}
|
||||
|
||||
explicit int2(int32_t value) : x(value), y(value)
|
||||
{
|
||||
}
|
||||
|
||||
int2(int32_t x, int32_t y) : x(x), y(y)
|
||||
{
|
||||
}
|
||||
|
||||
explicit int2(const float2 &other) : x(other.x), y(other.y)
|
||||
{
|
||||
}
|
||||
|
||||
int2(const int3 &other) : x(other.x), y(other.y)
|
||||
{
|
||||
}
|
||||
|
||||
operator int32_t *()
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
operator float2() const
|
||||
{
|
||||
return float2(x, y);
|
||||
}
|
||||
|
||||
operator const int32_t *() const
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
bool is_zero() const
|
||||
{
|
||||
return this->x == 0 && this->y == 0;
|
||||
}
|
||||
|
||||
int2 &operator+=(const int2 &other)
|
||||
{
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int2 &operator-=(const int2 &other)
|
||||
{
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int2 &operator*=(int32_t factor)
|
||||
{
|
||||
x *= factor;
|
||||
y *= factor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int2 &operator/=(int32_t divisor)
|
||||
{
|
||||
x /= divisor;
|
||||
y /= divisor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend int2 operator+(const int2 &a, const int2 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y};
|
||||
}
|
||||
|
||||
friend int2 operator-(const int2 &a, const int2 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y};
|
||||
}
|
||||
|
||||
friend int2 operator*(const int2 &a, int32_t b)
|
||||
{
|
||||
return {a.x * b, a.y * b};
|
||||
}
|
||||
|
||||
friend int2 operator/(const int2 &a, int32_t b)
|
||||
{
|
||||
BLI_assert(b != 0);
|
||||
return {a.x / b, a.y / b};
|
||||
}
|
||||
|
||||
friend int2 operator*(int32_t a, const int2 &b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
friend float2 operator*(const int2 &a, float b)
|
||||
{
|
||||
return b * float2(a.x, a.y);
|
||||
}
|
||||
|
||||
friend float2 operator/(const int2 &a, float b)
|
||||
{
|
||||
return float2(a.x, a.y) / b;
|
||||
}
|
||||
|
||||
friend float2 operator*(float a, const int2 &b)
|
||||
{
|
||||
return a * float2(b.x, b.y);
|
||||
}
|
||||
|
||||
friend float2 operator/(float a, const int2 &b)
|
||||
{
|
||||
return a / float2(b.x, b.y);
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const int2 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
static int2 clamp(const int2 &a, const int2 &min, const int2 &max)
|
||||
{
|
||||
return int2(clamp_i(a.x, min.x, max.x), clamp_i(a.y, min.y, max.y));
|
||||
}
|
||||
|
||||
static int2 clamp(const int2 &a, const int32_t &min, const int32_t &max)
|
||||
{
|
||||
return int2(clamp_i(a.x, min, max), clamp_i(a.y, min, max));
|
||||
}
|
||||
|
||||
friend bool operator==(const int2 &a, const int2 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
||||
friend bool operator!=(const int2 &a, const int2 &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
156
source/blender/blenlib/BLI_int3.hh
Normal file
156
source/blender/blenlib/BLI_int3.hh
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "BLI_float3.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct int3 {
|
||||
int32_t x, y, z;
|
||||
|
||||
int3() = default;
|
||||
|
||||
int3(const int *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]}
|
||||
{
|
||||
}
|
||||
|
||||
explicit int3(int value) : x(value), y(value), z(value)
|
||||
{
|
||||
}
|
||||
|
||||
int3(int x, int y, int z) : x(x), y(y), z(z)
|
||||
{
|
||||
}
|
||||
|
||||
int3(const int3 &other) : x(other.x), y(other.y), z(other.z)
|
||||
{
|
||||
}
|
||||
|
||||
explicit int3(const float3 &other) : x(other.x), y(other.y), z(other.z)
|
||||
{
|
||||
}
|
||||
|
||||
operator int *()
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
operator float3() const
|
||||
{
|
||||
return float3(x, y, z);
|
||||
}
|
||||
|
||||
operator const int *() const
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
bool is_zero() const
|
||||
{
|
||||
return this->x == 0 && this->y == 0 && this->z == 0;
|
||||
}
|
||||
|
||||
int3 &operator+=(const int3 &other)
|
||||
{
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int3 &operator-=(const int3 &other)
|
||||
{
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
z -= other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int3 &operator*=(int factor)
|
||||
{
|
||||
x *= factor;
|
||||
y *= factor;
|
||||
z *= factor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int3 &operator/=(int divisor)
|
||||
{
|
||||
x /= divisor;
|
||||
y /= divisor;
|
||||
z /= divisor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend int3 operator+(const int3 &a, const int3 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y, a.z + b.z};
|
||||
}
|
||||
|
||||
friend int3 operator-(const int3 &a, const int3 &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y, a.z - b.z};
|
||||
}
|
||||
|
||||
friend int3 operator*(const int3 &a, int b)
|
||||
{
|
||||
return {a.x * b, a.y * b, a.z * b};
|
||||
}
|
||||
|
||||
friend int3 operator/(const int3 &a, int b)
|
||||
{
|
||||
BLI_assert(b != 0);
|
||||
return {a.x / b, a.y / b, a.z / b};
|
||||
}
|
||||
|
||||
friend int3 operator*(int a, const int3 &b)
|
||||
{
|
||||
return b * a;
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const int3 &v)
|
||||
{
|
||||
stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
static int3 clamp(const int3 &a, const int3 &min, const int3 &max)
|
||||
{
|
||||
return int3(
|
||||
clamp_i(a.x, min.x, max.x), clamp_i(a.y, min.y, max.y), clamp_i(a.z, min.z, max.z));
|
||||
}
|
||||
|
||||
static int3 clamp(const int3 &a, const int32_t &min, const int32_t &max)
|
||||
{
|
||||
return int3(clamp_i(a.x, min, max), clamp_i(a.y, min, max), clamp_i(a.z, min, max));
|
||||
}
|
||||
|
||||
friend bool operator==(const int3 &a, const int3 &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
||||
friend bool operator!=(const int3 &a, const int3 &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
@@ -380,6 +380,11 @@ void BLI_path_normalize_unc_16(wchar_t *path_16);
|
||||
void BLI_path_normalize_unc(char *path_16, int maxlen);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns true if the given paths are equal.
|
||||
*/
|
||||
bool BLI_paths_equal(const char *p1, const char *p2);
|
||||
|
||||
/**
|
||||
* Appends a suffix to the string, fitting it before the extension
|
||||
*
|
||||
|
@@ -337,6 +337,18 @@ constexpr int64_t StringRefBase::find(StringRef str, int64_t pos) const
|
||||
return index_or_npos_to_int64(std::string_view(*this).find(str, static_cast<size_t>(pos)));
|
||||
}
|
||||
|
||||
constexpr int64_t StringRefBase::rfind(char c, int64_t pos) const
|
||||
{
|
||||
BLI_assert(pos >= 0);
|
||||
return index_or_npos_to_int64(std::string_view(*this).rfind(c, static_cast<size_t>(pos)));
|
||||
}
|
||||
|
||||
constexpr int64_t StringRefBase::rfind(StringRef str, int64_t pos) const
|
||||
{
|
||||
BLI_assert(pos >= 0);
|
||||
return index_or_npos_to_int64(std::string_view(*this).rfind(str, static_cast<size_t>(pos)));
|
||||
}
|
||||
|
||||
constexpr int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) const
|
||||
{
|
||||
BLI_assert(pos >= 0);
|
||||
|
@@ -681,6 +681,12 @@ MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
|
||||
r[2] += a[2] * f;
|
||||
}
|
||||
|
||||
MINLINE void madd_v2_v2v2(float r[2], const float a[2], const float b[2])
|
||||
{
|
||||
r[0] += a[0] * b[0];
|
||||
r[1] += a[1] * b[1];
|
||||
}
|
||||
|
||||
MINLINE void madd_v3_v3v3(float r[3], const float a[3], const float b[3])
|
||||
{
|
||||
r[0] += a[0] * b[0];
|
||||
|
@@ -1829,3 +1829,21 @@ void BLI_path_slash_native(char *path)
|
||||
BLI_str_replace_char(path + BLI_path_unc_prefix_len(path), ALTSEP, SEP);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool BLI_paths_equal(const char *p1, const char *p2)
|
||||
{
|
||||
/* Normalize the paths so we can compare them. */
|
||||
char norm_p1[FILE_MAX];
|
||||
char norm_p2[FILE_MAX];
|
||||
|
||||
BLI_strncpy(norm_p1, p1, sizeof(norm_p1));
|
||||
BLI_strncpy(norm_p2, p2, sizeof(norm_p2));
|
||||
|
||||
BLI_path_slash_native(norm_p1);
|
||||
BLI_path_slash_native(norm_p2);
|
||||
|
||||
BLI_path_normalize(NULL, norm_p1);
|
||||
BLI_path_normalize(NULL, norm_p2);
|
||||
|
||||
return BLI_path_cmp(norm_p1, norm_p2) == 0;
|
||||
}
|
||||
|
@@ -2273,8 +2273,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
scene->eevee.shadow_cascade_size = 1024;
|
||||
|
||||
scene->eevee.flag = SCE_EEVEE_VOLUMETRIC_LIGHTS | SCE_EEVEE_GTAO_BENT_NORMALS |
|
||||
SCE_EEVEE_GTAO_BOUNCE | SCE_EEVEE_TAA_REPROJECTION |
|
||||
SCE_EEVEE_SSR_HALF_RESOLUTION;
|
||||
SCE_EEVEE_GTAO_BOUNCE | SCE_EEVEE_TAA_REPROJECTION;
|
||||
|
||||
/* If the file is pre-2.80 move on. */
|
||||
if (scene->layer_properties == NULL) {
|
||||
@@ -2342,9 +2341,9 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
EEVEE_GET_BOOL(props, taa_reprojection, SCE_EEVEE_TAA_REPROJECTION);
|
||||
// EEVEE_GET_BOOL(props, sss_enable, SCE_EEVEE_SSS_ENABLED);
|
||||
// EEVEE_GET_BOOL(props, sss_separate_albedo, SCE_EEVEE_SSS_SEPARATE_ALBEDO);
|
||||
EEVEE_GET_BOOL(props, ssr_enable, SCE_EEVEE_SSR_ENABLED);
|
||||
EEVEE_GET_BOOL(props, ssr_refraction, SCE_EEVEE_SSR_REFRACTION);
|
||||
EEVEE_GET_BOOL(props, ssr_halfres, SCE_EEVEE_SSR_HALF_RESOLUTION);
|
||||
EEVEE_GET_BOOL(props, ssr_enable, SCE_EEVEE_RAYTRACING_ENABLED);
|
||||
// EEVEE_GET_BOOL(props, ssr_refraction, SCE_EEVEE_SSR_REFRACTION);
|
||||
// EEVEE_GET_BOOL(props, ssr_halfres, SCE_EEVEE_SSR_HALF_RESOLUTION);
|
||||
|
||||
EEVEE_GET_INT(props, gi_diffuse_bounces);
|
||||
EEVEE_GET_INT(props, gi_diffuse_bounces);
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_cachefile_types.h"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_constraint_types.h"
|
||||
#include "DNA_fluid_types.h"
|
||||
@@ -2036,5 +2037,22 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
*/
|
||||
{
|
||||
/* Keep this block, even when empty. */
|
||||
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "Camera", "float", "fisheye_fov")) {
|
||||
/* EEVEE Panoramic Camera support. */
|
||||
LISTBASE_FOREACH (Camera *, camera, &bmain->cameras) {
|
||||
camera->panorama_type = CAM_PANO_FISHEYE_EQUISOLID;
|
||||
camera->fisheye_fov = DEG2RADF(180.0f);
|
||||
camera->fisheye_lens = 10.5f;
|
||||
camera->latitude_min = DEG2RADF(-90.0f);
|
||||
camera->latitude_max = DEG2RADF(90.0f);
|
||||
camera->longitude_min = DEG2RADF(-180.0f);
|
||||
camera->longitude_max = DEG2RADF(180.0f);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
scene->eevee.flag |= SCE_EEVEE_FILM_LOG_ENCODING;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -51,6 +51,25 @@ set(INC
|
||||
${OPENSUBDIV_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(EEVEE_BUILD_DIR
|
||||
${CMAKE_CURRENT_BINARY_DIR}/engines/eevee
|
||||
)
|
||||
|
||||
set(EEVEE_BUILD_SRC
|
||||
engines/eevee/eevee_build.cc
|
||||
engines/eevee/eevee_lut.c
|
||||
)
|
||||
add_executable(eevee_build ${EEVEE_BUILD_SRC})
|
||||
target_include_directories(eevee_build PRIVATE ${INC})
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl
|
||||
COMMAND
|
||||
"$<TARGET_FILE:eevee_build>" --resolve_sample_table ${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl
|
||||
DEPENDS eevee_build
|
||||
)
|
||||
|
||||
set(SRC
|
||||
intern/draw_cache.c
|
||||
intern/draw_cache_extract_mesh.cc
|
||||
@@ -118,33 +137,33 @@ set(SRC
|
||||
engines/basic/basic_shader.c
|
||||
engines/image/image_engine.cc
|
||||
engines/image/image_shader.cc
|
||||
engines/eevee/eevee_bloom.c
|
||||
engines/eevee/eevee_cryptomatte.c
|
||||
engines/eevee/eevee_data.c
|
||||
engines/eevee/eevee_depth_of_field.c
|
||||
engines/eevee/eevee_effects.c
|
||||
engines/eevee/eevee_camera.cc
|
||||
engines/eevee/eevee_depth_of_field.cc
|
||||
engines/eevee/eevee_engine.c
|
||||
engines/eevee/eevee_lightcache.c
|
||||
engines/eevee/eevee_lightprobes.c
|
||||
engines/eevee/eevee_lights.c
|
||||
engines/eevee/eevee_lookdev.c
|
||||
engines/eevee/eevee_engine.cc
|
||||
engines/eevee/eevee_film.cc
|
||||
engines/eevee/eevee_gpencil.cc
|
||||
engines/eevee/eevee_hair.cc
|
||||
engines/eevee/eevee_hizbuffer.cc
|
||||
engines/eevee/eevee_id_map.cc
|
||||
engines/eevee/eevee_instance.cc
|
||||
engines/eevee/eevee_light.cc
|
||||
engines/eevee/eevee_lightcache.cc
|
||||
engines/eevee/eevee_lightprobe.cc
|
||||
engines/eevee/eevee_lookdev.cc
|
||||
engines/eevee/eevee_lut.c
|
||||
engines/eevee/eevee_lut_gen.c
|
||||
engines/eevee/eevee_materials.c
|
||||
engines/eevee/eevee_mist.c
|
||||
engines/eevee/eevee_motion_blur.c
|
||||
engines/eevee/eevee_occlusion.c
|
||||
engines/eevee/eevee_render.c
|
||||
engines/eevee/eevee_renderpasses.c
|
||||
engines/eevee/eevee_sampling.c
|
||||
engines/eevee/eevee_screen_raytrace.c
|
||||
engines/eevee/eevee_shaders.c
|
||||
engines/eevee/eevee_shadows.c
|
||||
engines/eevee/eevee_shadows_cascade.c
|
||||
engines/eevee/eevee_shadows_cube.c
|
||||
engines/eevee/eevee_subsurface.c
|
||||
engines/eevee/eevee_temporal_sampling.c
|
||||
engines/eevee/eevee_volumes.c
|
||||
engines/eevee/eevee_material.cc
|
||||
engines/eevee/eevee_mesh.cc
|
||||
engines/eevee/eevee_motion_blur.cc
|
||||
engines/eevee/eevee_raytracing.cc
|
||||
engines/eevee/eevee_renderpasses.cc
|
||||
engines/eevee/eevee_shader.cc
|
||||
engines/eevee/eevee_shading.cc
|
||||
engines/eevee/eevee_shadow.cc
|
||||
engines/eevee/eevee_subsurface.cc
|
||||
engines/eevee/eevee_velocity.cc
|
||||
engines/eevee/eevee_view.cc
|
||||
engines/eevee/eevee_world.cc
|
||||
engines/workbench/workbench_data.c
|
||||
engines/workbench/workbench_effect_antialiasing.c
|
||||
engines/workbench/workbench_effect_cavity.c
|
||||
@@ -224,6 +243,10 @@ set(SRC
|
||||
engines/basic/basic_private.h
|
||||
engines/eevee/eevee_engine.h
|
||||
engines/eevee/eevee_lightcache.h
|
||||
engines/eevee/eevee_camera.hh
|
||||
engines/eevee/eevee_instance.hh
|
||||
engines/eevee/eevee_light.hh
|
||||
engines/eevee/eevee_depth_of_field.hh
|
||||
engines/eevee/eevee_lut.h
|
||||
engines/eevee/eevee_private.h
|
||||
engines/external/external_engine.h
|
||||
@@ -248,95 +271,117 @@ set(LIB
|
||||
)
|
||||
|
||||
set(GLSL_SRC
|
||||
engines/eevee/shaders/ambient_occlusion_lib.glsl
|
||||
engines/eevee/shaders/background_vert.glsl
|
||||
engines/eevee/shaders/common_uniforms_lib.glsl
|
||||
engines/eevee/shaders/common_utiltex_lib.glsl
|
||||
engines/eevee/shaders/lights_lib.glsl
|
||||
engines/eevee/shaders/lightprobe_lib.glsl
|
||||
engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_geom.glsl
|
||||
engines/eevee/shaders/lightprobe_vert.glsl
|
||||
engines/eevee/shaders/lightprobe_cube_display_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_cube_display_vert.glsl
|
||||
engines/eevee/shaders/lightprobe_grid_display_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_grid_display_vert.glsl
|
||||
engines/eevee/shaders/lightprobe_grid_fill_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_planar_display_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_planar_display_vert.glsl
|
||||
engines/eevee/shaders/lookdev_world_frag.glsl
|
||||
engines/eevee/shaders/closure_eval_lib.glsl
|
||||
engines/eevee/shaders/closure_eval_diffuse_lib.glsl
|
||||
engines/eevee/shaders/closure_eval_glossy_lib.glsl
|
||||
engines/eevee/shaders/closure_eval_refraction_lib.glsl
|
||||
engines/eevee/shaders/closure_eval_translucent_lib.glsl
|
||||
engines/eevee/shaders/closure_type_lib.glsl
|
||||
engines/eevee/shaders/effect_bloom_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_bokeh_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_downsample_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_filter_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_flatten_tiles_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_gather_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_lib.glsl
|
||||
engines/eevee/shaders/effect_dof_reduce_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_resolve_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_scatter_frag.glsl
|
||||
engines/eevee/shaders/effect_dof_scatter_vert.glsl
|
||||
engines/eevee/shaders/effect_dof_setup_frag.glsl
|
||||
engines/eevee/shaders/effect_reflection_lib.glsl
|
||||
engines/eevee/shaders/effect_reflection_resolve_frag.glsl
|
||||
engines/eevee/shaders/effect_reflection_trace_frag.glsl
|
||||
engines/eevee/shaders/effect_downsample_frag.glsl
|
||||
engines/eevee/shaders/effect_downsample_cube_frag.glsl
|
||||
engines/eevee/shaders/effect_gtao_frag.glsl
|
||||
engines/eevee/shaders/effect_velocity_resolve_frag.glsl
|
||||
engines/eevee/shaders/effect_velocity_tile_frag.glsl
|
||||
engines/eevee/shaders/effect_minmaxz_frag.glsl
|
||||
engines/eevee/shaders/effect_mist_frag.glsl
|
||||
engines/eevee/shaders/effect_motion_blur_frag.glsl
|
||||
engines/eevee/shaders/effect_subsurface_frag.glsl
|
||||
engines/eevee/shaders/effect_translucency_frag.glsl
|
||||
engines/eevee/shaders/effect_temporal_aa.glsl
|
||||
engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl
|
||||
engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl
|
||||
engines/eevee/shaders/lightprobe_planar_downsample_vert.glsl
|
||||
engines/eevee/shaders/object_motion_frag.glsl
|
||||
engines/eevee/shaders/object_motion_vert.glsl
|
||||
engines/eevee/shaders/prepass_frag.glsl
|
||||
engines/eevee/shaders/prepass_vert.glsl
|
||||
engines/eevee/shaders/shadow_accum_frag.glsl
|
||||
engines/eevee/shaders/shadow_frag.glsl
|
||||
engines/eevee/shaders/shadow_vert.glsl
|
||||
engines/eevee/shaders/bsdf_lut_frag.glsl
|
||||
engines/eevee/shaders/btdf_lut_frag.glsl
|
||||
engines/eevee/shaders/bsdf_common_lib.glsl
|
||||
engines/eevee/shaders/irradiance_lib.glsl
|
||||
engines/eevee/shaders/octahedron_lib.glsl
|
||||
engines/eevee/shaders/cubemap_lib.glsl
|
||||
engines/eevee/shaders/bsdf_sampling_lib.glsl
|
||||
engines/eevee/shaders/random_lib.glsl
|
||||
engines/eevee/shaders/raytrace_lib.glsl
|
||||
engines/eevee/shaders/renderpass_lib.glsl
|
||||
engines/eevee/shaders/renderpass_postprocess_frag.glsl
|
||||
engines/eevee/shaders/cryptomatte_frag.glsl
|
||||
engines/eevee/shaders/ltc_lib.glsl
|
||||
engines/eevee/shaders/ssr_lib.glsl
|
||||
engines/eevee/shaders/surface_frag.glsl
|
||||
engines/eevee/shaders/surface_geom.glsl
|
||||
engines/eevee/shaders/surface_lib.glsl
|
||||
engines/eevee/shaders/surface_vert.glsl
|
||||
engines/eevee/shaders/update_noise_frag.glsl
|
||||
engines/eevee/shaders/volumetric_accum_frag.glsl
|
||||
engines/eevee/shaders/volumetric_lib.glsl
|
||||
engines/eevee/shaders/volumetric_frag.glsl
|
||||
engines/eevee/shaders/volumetric_geom.glsl
|
||||
engines/eevee/shaders/volumetric_vert.glsl
|
||||
engines/eevee/shaders/volumetric_resolve_frag.glsl
|
||||
engines/eevee/shaders/volumetric_scatter_frag.glsl
|
||||
engines/eevee/shaders/volumetric_integration_frag.glsl
|
||||
engines/eevee/shaders/eevee_bsdf_lib.glsl
|
||||
engines/eevee/shaders/eevee_bsdf_microfacet_lib.glsl
|
||||
engines/eevee/shaders/eevee_bsdf_sampling_lib.glsl
|
||||
engines/eevee/shaders/eevee_bsdf_stubs_lib.glsl
|
||||
engines/eevee/shaders/eevee_camera_lib.glsl
|
||||
engines/eevee/shaders/eevee_camera_velocity_frag.glsl
|
||||
engines/eevee/shaders/eevee_closure_lib.glsl
|
||||
engines/eevee/shaders/eevee_cubemap_lib.glsl
|
||||
engines/eevee/shaders/eevee_culling_debug_frag.glsl
|
||||
engines/eevee/shaders/eevee_culling_iter_lib.glsl
|
||||
engines/eevee/shaders/eevee_culling_lib.glsl
|
||||
engines/eevee/shaders/eevee_culling_select_comp.glsl
|
||||
engines/eevee/shaders/eevee_culling_sort_comp.glsl
|
||||
engines/eevee/shaders/eevee_culling_tile_comp.glsl
|
||||
engines/eevee/shaders/eevee_deferred_direct_frag.glsl
|
||||
engines/eevee/shaders/eevee_deferred_holdout_frag.glsl
|
||||
engines/eevee/shaders/eevee_deferred_transparent_frag.glsl
|
||||
engines/eevee/shaders/eevee_deferred_volume_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_clear_frag.glsl
|
||||
engines/eevee/shaders/eevee_hiz_copy_frag.glsl
|
||||
engines/eevee/shaders/eevee_hiz_downsample_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_accumulator_lib.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_bokeh_lut_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_filter_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_gather_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_gather_holefill_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_lib.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_reduce_copy_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_reduce_downsample_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_reduce_recursive_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_resolve_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_scatter_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_scatter_lib.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_scatter_vert.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_setup_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_tiles_dilate_frag.glsl
|
||||
engines/eevee/shaders/eevee_depth_of_field_tiles_flatten_frag.glsl
|
||||
engines/eevee/shaders/eevee_film_filter_frag.glsl
|
||||
engines/eevee/shaders/eevee_film_lib.glsl
|
||||
engines/eevee/shaders/eevee_film_resolve_frag.glsl
|
||||
engines/eevee/shaders/eevee_film_resolve_depth_frag.glsl
|
||||
engines/eevee/shaders/eevee_gbuffer_lib.glsl
|
||||
engines/eevee/shaders/eevee_irradiance_lib.glsl
|
||||
engines/eevee/shaders/eevee_light_lib.glsl
|
||||
engines/eevee/shaders/eevee_light_eval_lib.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_display_cubemap_frag.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_display_cubemap_vert.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_display_grid_frag.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_display_grid_vert.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_display_lib.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_eval_cubemap_lib.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_eval_grid_lib.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_filter_diffuse_frag.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_filter_downsample_frag.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_filter_geom.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_filter_glossy_frag.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_filter_lib.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_filter_vert.glsl
|
||||
engines/eevee/shaders/eevee_lightprobe_filter_visibility_frag.glsl
|
||||
engines/eevee/shaders/eevee_lookdev_background_frag.glsl
|
||||
engines/eevee/shaders/eevee_ltc_lib.glsl
|
||||
engines/eevee/shaders/eevee_motion_blur_gather_frag.glsl
|
||||
engines/eevee/shaders/eevee_motion_blur_lib.glsl
|
||||
engines/eevee/shaders/eevee_motion_blur_tiles_dilate_frag.glsl
|
||||
engines/eevee/shaders/eevee_motion_blur_tiles_flatten_frag.glsl
|
||||
engines/eevee/shaders/eevee_nodetree_eval_lib.glsl
|
||||
engines/eevee/shaders/eevee_raytrace_denoise_comp.glsl
|
||||
engines/eevee/shaders/eevee_raytrace_raygen_frag.glsl
|
||||
engines/eevee/shaders/eevee_raytrace_raygen_lib.glsl
|
||||
engines/eevee/shaders/eevee_raytrace_resolve_frag.glsl
|
||||
engines/eevee/shaders/eevee_raytrace_trace_lib.glsl
|
||||
engines/eevee/shaders/eevee_sampling_lib.glsl
|
||||
engines/eevee/shaders/eevee_shadow_debug_frag.glsl
|
||||
engines/eevee/shaders/eevee_shadow_lib.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_alloc_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_copy_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_debug_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_defrag_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_free_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_init_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_lib.glsl
|
||||
engines/eevee/shaders/eevee_shadow_page_mark_vert.glsl
|
||||
engines/eevee/shaders/eevee_shadow_tilemap_depth_scan_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_tilemap_lod_mask_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl
|
||||
engines/eevee/shaders/eevee_shadow_tilemap_setup_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl
|
||||
engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl
|
||||
engines/eevee/shaders/eevee_subsurface_eval_frag.glsl
|
||||
engines/eevee/shaders/eevee_surface_background_frag.glsl
|
||||
engines/eevee/shaders/eevee_surface_deferred_frag.glsl
|
||||
engines/eevee/shaders/eevee_surface_depth_frag.glsl
|
||||
engines/eevee/shaders/eevee_surface_depth_simple_frag.glsl
|
||||
engines/eevee/shaders/eevee_surface_forward_frag.glsl
|
||||
engines/eevee/shaders/eevee_surface_gpencil_vert.glsl
|
||||
engines/eevee/shaders/eevee_surface_hair_vert.glsl
|
||||
engines/eevee/shaders/eevee_surface_lib.glsl
|
||||
engines/eevee/shaders/eevee_surface_lookdev_vert.glsl
|
||||
engines/eevee/shaders/eevee_surface_mesh_geom.glsl
|
||||
engines/eevee/shaders/eevee_surface_mesh_vert.glsl
|
||||
engines/eevee/shaders/eevee_surface_velocity_frag.glsl
|
||||
engines/eevee/shaders/eevee_surface_velocity_lib.glsl
|
||||
engines/eevee/shaders/eevee_surface_velocity_mesh_vert.glsl
|
||||
engines/eevee/shaders/eevee_surface_world_vert.glsl
|
||||
engines/eevee/shaders/eevee_velocity_lib.glsl
|
||||
engines/eevee/shaders/eevee_volume_deferred_frag.glsl
|
||||
engines/eevee/shaders/eevee_volume_eval_lib.glsl
|
||||
engines/eevee/shaders/eevee_volume_lib.glsl
|
||||
engines/eevee/shaders/eevee_volume_vert.glsl
|
||||
|
||||
engines/eevee/eevee_shader_shared.hh
|
||||
|
||||
engines/workbench/shaders/workbench_cavity_lib.glsl
|
||||
engines/workbench/shaders/workbench_common_lib.glsl
|
||||
@@ -368,19 +413,24 @@ set(GLSL_SRC
|
||||
|
||||
engines/workbench/workbench_shader_shared.h
|
||||
|
||||
intern/shaders/common_attribute_lib.glsl
|
||||
intern/shaders/common_colormanagement_lib.glsl
|
||||
intern/shaders/common_globals_lib.glsl
|
||||
intern/shaders/common_pointcloud_lib.glsl
|
||||
intern/shaders/common_hair_lib.glsl
|
||||
intern/shaders/common_hair_refine_vert.glsl
|
||||
intern/shaders/common_hair_refine_comp.glsl
|
||||
intern/shaders/common_math_lib.glsl
|
||||
intern/shaders/common_math_geom_lib.glsl
|
||||
intern/shaders/common_view_clipping_lib.glsl
|
||||
intern/shaders/common_view_lib.glsl
|
||||
intern/shaders/common_fxaa_lib.glsl
|
||||
intern/shaders/common_smaa_lib.glsl
|
||||
intern/shaders/common_debug_lib.glsl
|
||||
intern/shaders/common_fullscreen_vert.glsl
|
||||
intern/shaders/common_fxaa_lib.glsl
|
||||
intern/shaders/common_globals_lib.glsl
|
||||
intern/shaders/common_gpencil_lib.glsl
|
||||
intern/shaders/common_hair_lib.glsl
|
||||
intern/shaders/common_hair_refine_comp.glsl
|
||||
intern/shaders/common_hair_refine_vert.glsl
|
||||
intern/shaders/common_intersection_lib.glsl
|
||||
intern/shaders/common_math_geom_lib.glsl
|
||||
intern/shaders/common_math_lib.glsl
|
||||
intern/shaders/common_obinfos_lib.glsl
|
||||
intern/shaders/common_pointcloud_lib.glsl
|
||||
intern/shaders/common_smaa_lib.glsl
|
||||
intern/shaders/common_uniform_attribute_lib.glsl
|
||||
intern/shaders/common_view_lib.glsl
|
||||
|
||||
intern/shaders/common_subdiv_custom_data_interp_comp.glsl
|
||||
intern/shaders/common_subdiv_ibo_lines_comp.glsl
|
||||
@@ -531,7 +581,7 @@ set(GLSL_SOURCE_CONTENT "")
|
||||
foreach(GLSL_FILE ${GLSL_SRC})
|
||||
get_filename_component(GLSL_FILE_NAME ${GLSL_FILE} NAME)
|
||||
string(REPLACE "." "_" GLSL_FILE_NAME_UNDERSCORES ${GLSL_FILE_NAME})
|
||||
string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(datatoc_${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\"\)\n")
|
||||
string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(datatoc_${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\", \"${GLSL_FILE}\"\)\n")
|
||||
endforeach()
|
||||
|
||||
set(glsl_source_list_file "${CMAKE_CURRENT_BINARY_DIR}/glsl_draw_source_list.h")
|
||||
|
219
source/blender/draw/engines/eevee/eevee_allocator.hh
Normal file
219
source/blender/draw/engines/eevee/eevee_allocator.hh
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Allocators that may be moved to BLI at some point.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_bits.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/**
|
||||
* Allow allocation and deletion of elements without reordering.
|
||||
* Useful to keed valid indices to items inside this allocator.
|
||||
* Type T need to implement the free_resources() method.
|
||||
*/
|
||||
template<typename T> class IndexedAllocator {
|
||||
private:
|
||||
Vector<T> items_;
|
||||
/** Bitmap of used items. Make search of unused slot faster. */
|
||||
Vector<uint32_t> unused_;
|
||||
/** First unused batch of items in the vector for fast reallocating. */
|
||||
int64_t unused_first_ = LONG_MAX;
|
||||
/** Unused item count in the vector for fast reallocating. */
|
||||
int64_t unused_count_ = 0;
|
||||
|
||||
public:
|
||||
int64_t alloc(T &&value)
|
||||
{
|
||||
if (unused_count_ > 0) {
|
||||
/* Reclaim unused slot. */
|
||||
int64_t index = unused_first_;
|
||||
items_[index] = std::move(value);
|
||||
set_slot_used(index);
|
||||
|
||||
unused_count_ -= 1;
|
||||
unused_first_ = first_unused_slot_get();
|
||||
return index;
|
||||
}
|
||||
/* Not enough place, grow the vector. */
|
||||
int64_t index = items_.append_and_get_index(value);
|
||||
int64_t size_needed = (index + 32) / 32;
|
||||
int64_t size_old = unused_.size();
|
||||
unused_.resize(size_needed);
|
||||
if (size_old < size_needed) {
|
||||
for (auto i : IndexRange(size_old, size_needed - size_old)) {
|
||||
/* Used by default. */
|
||||
unused_[i] = 0;
|
||||
}
|
||||
}
|
||||
set_slot_used(index);
|
||||
return index;
|
||||
}
|
||||
|
||||
void free(int64_t index)
|
||||
{
|
||||
unused_count_ += 1;
|
||||
if (index < unused_first_) {
|
||||
unused_first_ = index;
|
||||
}
|
||||
set_slot_unused(index);
|
||||
is_slot_unused(index);
|
||||
items_[index].free_resources();
|
||||
}
|
||||
|
||||
/* Pruned unused shadows at the end of the vector. */
|
||||
void resize()
|
||||
{
|
||||
while (items_.size() > 0 && is_slot_unused(items_.size() - 1)) {
|
||||
set_slot_used(items_.size() - 1);
|
||||
items_.remove_last();
|
||||
unused_count_--;
|
||||
}
|
||||
if (unused_first_ >= items_.size()) {
|
||||
/* First unused has been pruned. */
|
||||
unused_first_ = first_unused_slot_get();
|
||||
}
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return items_.size() - unused_count_;
|
||||
}
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = T;
|
||||
using pointer = const T *;
|
||||
using reference = const T &;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
private:
|
||||
IndexedAllocator &allocator_;
|
||||
int64_t current_;
|
||||
|
||||
public:
|
||||
constexpr explicit Iterator(IndexedAllocator &allocator, int64_t current)
|
||||
: allocator_(allocator), current_(current)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr Iterator &operator++()
|
||||
{
|
||||
current_++;
|
||||
while ((current_ < allocator_.items_.size()) && allocator_.is_slot_unused(current_)) {
|
||||
current_++;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Iterator operator++(int) const
|
||||
{
|
||||
Iterator iterator = *this;
|
||||
++*this;
|
||||
return iterator;
|
||||
}
|
||||
|
||||
constexpr friend bool operator!=(const Iterator &a, const Iterator &b)
|
||||
{
|
||||
return a.current_ != b.current_;
|
||||
}
|
||||
|
||||
T &operator*()
|
||||
{
|
||||
BLI_assert(allocator_.is_slot_unused(current_) == false);
|
||||
return allocator_[current_];
|
||||
}
|
||||
};
|
||||
|
||||
constexpr Iterator begin()
|
||||
{
|
||||
int64_t first_used = first_used_slot_get();
|
||||
if (first_used == LONG_MAX) {
|
||||
/* Will produce no iteration. */
|
||||
first_used = items_.size();
|
||||
}
|
||||
return Iterator(*this, first_used);
|
||||
}
|
||||
|
||||
constexpr Iterator end()
|
||||
{
|
||||
return Iterator(*this, items_.size());
|
||||
}
|
||||
|
||||
const T &operator[](int64_t index) const
|
||||
{
|
||||
BLI_assert(is_slot_unused(index) == false);
|
||||
return items_[index];
|
||||
}
|
||||
|
||||
T &operator[](int64_t index)
|
||||
{
|
||||
BLI_assert(is_slot_unused(index) == false);
|
||||
return items_[index];
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t first_unused_slot_get(void) const
|
||||
{
|
||||
if (unused_count_ > 0) {
|
||||
for (auto i : IndexRange(unused_.size())) {
|
||||
if (unused_[i] != 0) {
|
||||
return i * 32 + bitscan_forward_uint(unused_[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return LONG_MAX;
|
||||
}
|
||||
|
||||
int64_t first_used_slot_get(void) const
|
||||
{
|
||||
if (unused_count_ < items_.size()) {
|
||||
for (auto i : IndexRange(unused_.size())) {
|
||||
if (~unused_[i] != 0) {
|
||||
return i * 32 + bitscan_forward_uint(~unused_[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return LONG_MAX;
|
||||
}
|
||||
|
||||
bool is_slot_unused(int64_t index) const
|
||||
{
|
||||
return (unused_[index / 32] & (1u << uint32_t(index % 32))) != 0;
|
||||
}
|
||||
|
||||
void set_slot_unused(int64_t index)
|
||||
{
|
||||
SET_FLAG_FROM_TEST(unused_[index / 32], true, (1u << uint32_t(index % 32)));
|
||||
}
|
||||
|
||||
void set_slot_used(int64_t index)
|
||||
{
|
||||
SET_FLAG_FROM_TEST(unused_[index / 32], false, (1u << uint32_t(index % 32)));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::eevee
|
@@ -1,344 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*
|
||||
* Eevee's bloom shader.
|
||||
*/
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "eevee_private.h"
|
||||
|
||||
static const bool use_highres = true;
|
||||
|
||||
int EEVEE_bloom_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
|
||||
|
||||
if (scene_eval->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) {
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
|
||||
/* Bloom */
|
||||
int blitsize[2], texsize[2];
|
||||
|
||||
/* Blit Buffer */
|
||||
effects->source_texel_size[0] = 1.0f / viewport_size[0];
|
||||
effects->source_texel_size[1] = 1.0f / viewport_size[1];
|
||||
|
||||
blitsize[0] = (int)viewport_size[0];
|
||||
blitsize[1] = (int)viewport_size[1];
|
||||
|
||||
effects->blit_texel_size[0] = 1.0f / (float)blitsize[0];
|
||||
effects->blit_texel_size[1] = 1.0f / (float)blitsize[1];
|
||||
|
||||
effects->bloom_blit = DRW_texture_pool_query_2d(
|
||||
blitsize[0], blitsize[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
|
||||
|
||||
GPU_framebuffer_ensure_config(
|
||||
&fbl->bloom_blit_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->bloom_blit)});
|
||||
|
||||
/* Parameters */
|
||||
const float threshold = scene_eval->eevee.bloom_threshold;
|
||||
const float knee = scene_eval->eevee.bloom_knee;
|
||||
const float intensity = scene_eval->eevee.bloom_intensity;
|
||||
const float *color = scene_eval->eevee.bloom_color;
|
||||
const float radius = scene_eval->eevee.bloom_radius;
|
||||
effects->bloom_clamp = scene_eval->eevee.bloom_clamp;
|
||||
|
||||
/* determine the iteration count */
|
||||
const float minDim = (float)MIN2(blitsize[0], blitsize[1]);
|
||||
const float maxIter = (radius - 8.0f) + log(minDim) / log(2);
|
||||
const int maxIterInt = effects->bloom_iteration_len = (int)maxIter;
|
||||
|
||||
CLAMP(effects->bloom_iteration_len, 1, MAX_BLOOM_STEP);
|
||||
|
||||
effects->bloom_sample_scale = 0.5f + maxIter - (float)maxIterInt;
|
||||
effects->bloom_curve_threshold[0] = threshold - knee;
|
||||
effects->bloom_curve_threshold[1] = knee * 2.0f;
|
||||
effects->bloom_curve_threshold[2] = 0.25f / max_ff(1e-5f, knee);
|
||||
effects->bloom_curve_threshold[3] = threshold;
|
||||
|
||||
mul_v3_v3fl(effects->bloom_color, color, intensity);
|
||||
|
||||
/* Downsample buffers */
|
||||
copy_v2_v2_int(texsize, blitsize);
|
||||
for (int i = 0; i < effects->bloom_iteration_len; i++) {
|
||||
texsize[0] /= 2;
|
||||
texsize[1] /= 2;
|
||||
|
||||
texsize[0] = MAX2(texsize[0], 2);
|
||||
texsize[1] = MAX2(texsize[1], 2);
|
||||
|
||||
effects->downsamp_texel_size[i][0] = 1.0f / (float)texsize[0];
|
||||
effects->downsamp_texel_size[i][1] = 1.0f / (float)texsize[1];
|
||||
|
||||
effects->bloom_downsample[i] = DRW_texture_pool_query_2d(
|
||||
texsize[0], texsize[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
|
||||
GPU_framebuffer_ensure_config(
|
||||
&fbl->bloom_down_fb[i],
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->bloom_downsample[i])});
|
||||
}
|
||||
|
||||
/* Upsample buffers */
|
||||
copy_v2_v2_int(texsize, blitsize);
|
||||
for (int i = 0; i < effects->bloom_iteration_len - 1; i++) {
|
||||
texsize[0] /= 2;
|
||||
texsize[1] /= 2;
|
||||
|
||||
texsize[0] = MAX2(texsize[0], 2);
|
||||
texsize[1] = MAX2(texsize[1], 2);
|
||||
|
||||
effects->bloom_upsample[i] = DRW_texture_pool_query_2d(
|
||||
texsize[0], texsize[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
|
||||
GPU_framebuffer_ensure_config(
|
||||
&fbl->bloom_accum_fb[i],
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->bloom_upsample[i])});
|
||||
}
|
||||
|
||||
return EFFECT_BLOOM | EFFECT_POST_BUFFER;
|
||||
}
|
||||
|
||||
/* Cleanup to release memory */
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_blit_fb);
|
||||
|
||||
for (int i = 0; i < MAX_BLOOM_STEP - 1; i++) {
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_down_fb[i]);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_accum_fb[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DRWShadingGroup *eevee_create_bloom_pass(const char *name,
|
||||
EEVEE_EffectsInfo *effects,
|
||||
struct GPUShader *sh,
|
||||
DRWPass **pass,
|
||||
bool upsample,
|
||||
bool resolve)
|
||||
{
|
||||
struct GPUBatch *quad = DRW_cache_fullscreen_quad_get();
|
||||
|
||||
*pass = DRW_pass_create(name, DRW_STATE_WRITE_COLOR);
|
||||
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, *pass);
|
||||
DRW_shgroup_call(grp, quad, NULL);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "sourceBuffer", &effects->unf_source_buffer);
|
||||
DRW_shgroup_uniform_vec2(grp, "sourceBufferTexelSize", effects->unf_source_texel_size, 1);
|
||||
if (upsample) {
|
||||
DRW_shgroup_uniform_texture_ref(grp, "baseBuffer", &effects->unf_base_buffer);
|
||||
DRW_shgroup_uniform_float(grp, "sampleScale", &effects->bloom_sample_scale, 1);
|
||||
}
|
||||
if (resolve) {
|
||||
DRW_shgroup_uniform_vec3(grp, "bloomColor", effects->bloom_color, 1);
|
||||
DRW_shgroup_uniform_bool_copy(grp, "bloomAddBase", true);
|
||||
}
|
||||
|
||||
return grp;
|
||||
}
|
||||
|
||||
void EEVEE_bloom_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
psl->bloom_accum_ps = NULL;
|
||||
|
||||
if ((effects->enabled_effects & EFFECT_BLOOM) != 0) {
|
||||
/**
|
||||
* Bloom Algorithm
|
||||
*
|
||||
* Overview:
|
||||
* - Down-sample the color buffer doing a small blur during each step.
|
||||
* - Accumulate bloom color using previously down-sampled color buffers
|
||||
* and do an up-sample blur for each new accumulated layer.
|
||||
* - Finally add accumulation buffer onto the source color buffer.
|
||||
*
|
||||
* [1/1] is original copy resolution (can be half or quarter res for performance)
|
||||
* <pre>
|
||||
* [DOWNSAMPLE CHAIN] [UPSAMPLE CHAIN]
|
||||
*
|
||||
* Source Color ─ [Blit] ─> Bright Color Extract [1/1] Final Color
|
||||
* | Λ
|
||||
* [Downsample First] Source Color ─> + [Resolve]
|
||||
* v |
|
||||
* Color Downsampled [1/2] ────────────> + Accumulation Buffer [1/2]
|
||||
* | Λ
|
||||
* ─── ───
|
||||
* Repeat Repeat
|
||||
* ─── ───
|
||||
* v |
|
||||
* Color Downsampled [1/N-1] ──────────> + Accumulation Buffer [1/N-1]
|
||||
* | Λ
|
||||
* [Downsample] [Upsample]
|
||||
* v |
|
||||
* Color Downsampled [1/N] ─────────────────────────┘
|
||||
* </pre>
|
||||
*/
|
||||
DRWShadingGroup *grp;
|
||||
const bool use_antiflicker = true;
|
||||
eevee_create_bloom_pass("Bloom Downsample First",
|
||||
effects,
|
||||
EEVEE_shaders_bloom_downsample_get(use_antiflicker),
|
||||
&psl->bloom_downsample_first,
|
||||
false,
|
||||
false);
|
||||
eevee_create_bloom_pass("Bloom Downsample",
|
||||
effects,
|
||||
EEVEE_shaders_bloom_downsample_get(false),
|
||||
&psl->bloom_downsample,
|
||||
false,
|
||||
false);
|
||||
eevee_create_bloom_pass("Bloom Upsample",
|
||||
effects,
|
||||
EEVEE_shaders_bloom_upsample_get(use_highres),
|
||||
&psl->bloom_upsample,
|
||||
true,
|
||||
false);
|
||||
|
||||
grp = eevee_create_bloom_pass("Bloom Blit",
|
||||
effects,
|
||||
EEVEE_shaders_bloom_blit_get(use_antiflicker),
|
||||
&psl->bloom_blit,
|
||||
false,
|
||||
false);
|
||||
DRW_shgroup_uniform_vec4(grp, "curveThreshold", effects->bloom_curve_threshold, 1);
|
||||
DRW_shgroup_uniform_float(grp, "clampIntensity", &effects->bloom_clamp, 1);
|
||||
|
||||
grp = eevee_create_bloom_pass("Bloom Resolve",
|
||||
effects,
|
||||
EEVEE_shaders_bloom_resolve_get(use_highres),
|
||||
&psl->bloom_resolve,
|
||||
true,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_bloom_draw(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
/* Bloom */
|
||||
if ((effects->enabled_effects & EFFECT_BLOOM) != 0) {
|
||||
struct GPUTexture *last;
|
||||
|
||||
/* Extract bright pixels */
|
||||
copy_v2_v2(effects->unf_source_texel_size, effects->source_texel_size);
|
||||
effects->unf_source_buffer = effects->source_buffer;
|
||||
|
||||
GPU_framebuffer_bind(fbl->bloom_blit_fb);
|
||||
DRW_draw_pass(psl->bloom_blit);
|
||||
|
||||
/* Downsample */
|
||||
copy_v2_v2(effects->unf_source_texel_size, effects->blit_texel_size);
|
||||
effects->unf_source_buffer = effects->bloom_blit;
|
||||
|
||||
GPU_framebuffer_bind(fbl->bloom_down_fb[0]);
|
||||
DRW_draw_pass(psl->bloom_downsample_first);
|
||||
|
||||
last = effects->bloom_downsample[0];
|
||||
|
||||
for (int i = 1; i < effects->bloom_iteration_len; i++) {
|
||||
copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[i - 1]);
|
||||
effects->unf_source_buffer = last;
|
||||
|
||||
GPU_framebuffer_bind(fbl->bloom_down_fb[i]);
|
||||
DRW_draw_pass(psl->bloom_downsample);
|
||||
|
||||
/* Used in next loop */
|
||||
last = effects->bloom_downsample[i];
|
||||
}
|
||||
|
||||
/* Upsample and accumulate */
|
||||
for (int i = effects->bloom_iteration_len - 2; i >= 0; i--) {
|
||||
copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[i]);
|
||||
effects->unf_source_buffer = last;
|
||||
effects->unf_base_buffer = effects->bloom_downsample[i];
|
||||
|
||||
GPU_framebuffer_bind(fbl->bloom_accum_fb[i]);
|
||||
DRW_draw_pass(psl->bloom_upsample);
|
||||
|
||||
last = effects->bloom_upsample[i];
|
||||
}
|
||||
|
||||
/* Resolve */
|
||||
copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[0]);
|
||||
effects->unf_source_buffer = last;
|
||||
effects->unf_base_buffer = effects->source_buffer;
|
||||
|
||||
GPU_framebuffer_bind(effects->target_buffer);
|
||||
DRW_draw_pass(psl->bloom_resolve);
|
||||
SWAP_BUFFERS();
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_bloom_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
|
||||
EEVEE_Data *vedata,
|
||||
uint UNUSED(tot_samples))
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
/* Create FrameBuffer. */
|
||||
DRW_texture_ensure_fullscreen_2d(&txl->bloom_accum, GPU_R11F_G11F_B10F, 0);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->bloom_pass_accum_fb,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->bloom_accum)});
|
||||
|
||||
/* Create Pass and shgroup. */
|
||||
DRWShadingGroup *grp = eevee_create_bloom_pass("Bloom Accumulate",
|
||||
effects,
|
||||
EEVEE_shaders_bloom_resolve_get(use_highres),
|
||||
&psl->bloom_accum_ps,
|
||||
true,
|
||||
true);
|
||||
DRW_shgroup_uniform_bool_copy(grp, "bloomAddBase", false);
|
||||
}
|
||||
|
||||
void EEVEE_bloom_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
|
||||
if (stl->g_data->render_passes & EEVEE_RENDER_PASS_BLOOM) {
|
||||
GPU_framebuffer_bind(fbl->bloom_pass_accum_fb);
|
||||
DRW_draw_pass(psl->bloom_accum_ps);
|
||||
|
||||
/* Restore */
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
}
|
||||
}
|
155
source/blender/draw/engines/eevee/eevee_build.cc
Normal file
155
source/blender/draw/engines/eevee/eevee_build.cc
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Compile time computation and code generation.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/* For blue_noise. */
|
||||
#include "eevee_lut.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef unsigned char uchar;
|
||||
typedef float float3[3];
|
||||
|
||||
const int samples_per_pool = 32;
|
||||
|
||||
static void raytrace_sample_reuse_table(string &output_name, bool debug)
|
||||
{
|
||||
/** Following "Stochastic All The Things: Raytracing in Hybrid Real-Time Rendering"
|
||||
* by Tomasz Stachowiak
|
||||
* https://www.ea.com/seed/news/seed-dd18-presentation-slides-raytracing
|
||||
*/
|
||||
|
||||
struct sample {
|
||||
int x, y;
|
||||
sample(int _x, int _y) : x(_x), y(_y){};
|
||||
};
|
||||
|
||||
array<vector<sample>, 4> pools;
|
||||
array<float3, 4> pools_color = {1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0};
|
||||
vector<float3> debug_image(64 * 64);
|
||||
|
||||
ofstream ppm;
|
||||
auto ppm_file_out = [&](const char *name, vector<float3> &debug_image) {
|
||||
ppm.open(name);
|
||||
ppm << "P3\n64 64\n255\n";
|
||||
for (auto &vec : debug_image) {
|
||||
uchar ucol[3] = {(uchar)(vec[0] * 255), (uchar)(vec[1] * 255), (uchar)(vec[2] * 255)};
|
||||
ppm << (int)ucol[0] << " " << (int)ucol[1] << " " << (int)ucol[2] << "\n";
|
||||
/* Clear the image. */
|
||||
vec[0] = vec[1] = vec[2] = 0.0f;
|
||||
}
|
||||
ppm.close();
|
||||
};
|
||||
|
||||
/* Using sample_reuse_1.ppm, set a center position where there is 4 different pool (color). */
|
||||
int center[4][2] = {{49, 47}, {48, 46}, {49, 46}, {48, 47}};
|
||||
/* Remapping of pool order since the center samples may not match the pool order from shader.
|
||||
* Order is (0,0), (1,0), (0,1), (1,1).
|
||||
* IMPORTANT: the actual PPM picture has Y flipped compared to OpenGL. */
|
||||
int poolmap[4] = {3, 0, 1, 2};
|
||||
|
||||
for (int x = 0; x < 64; x++) {
|
||||
for (int y = 0; y < 64; y++) {
|
||||
int px = y * 64 + x;
|
||||
float noise_value = blue_noise[px][0];
|
||||
int pool_id = floorf(noise_value * 4.0f);
|
||||
sample ofs(x - center[pool_id][0], y - center[pool_id][1]);
|
||||
pools[pool_id].push_back(ofs);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
debug_image[px][i] = pools_color[pool_id][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
ppm_file_out("sample_reuse_1.ppm", debug_image);
|
||||
}
|
||||
|
||||
for (int pool_id = 0; pool_id < 4; pool_id++) {
|
||||
auto &pool = pools[pool_id];
|
||||
auto sort = [](const sample &a, const sample &b) {
|
||||
/* Manhattan distance. */
|
||||
return abs(a.x) + abs(a.y) < abs(b.x) + abs(b.y);
|
||||
};
|
||||
std::sort(pool.begin(), pool.end(), sort);
|
||||
|
||||
for (int j = 0; j < samples_per_pool; j++) {
|
||||
int pos[2] = {pool[j].x + center[pool_id][0], pool[j].y + center[pool_id][1]};
|
||||
int px = pos[1] * 64 + pos[0];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
debug_image[px][i] = pools_color[pool_id][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
ppm_file_out("sample_reuse_2.ppm", debug_image);
|
||||
}
|
||||
|
||||
/* TODO(fclem): Order the samples to have better spatial coherence. */
|
||||
|
||||
ofstream table_out;
|
||||
int total = samples_per_pool * 4;
|
||||
table_out.open(output_name);
|
||||
|
||||
table_out << "\n/* Sample table generated at build time. */\n";
|
||||
table_out << "const int resolve_sample_max = " << samples_per_pool << ";\n";
|
||||
table_out << "const float2 resolve_sample_offsets[" << total << "] = float2[" << total << "](\n";
|
||||
for (int pool_id = 0; pool_id < 4; pool_id++) {
|
||||
auto &pool = pools[poolmap[pool_id]];
|
||||
for (int i = 0; i < samples_per_pool; i++) {
|
||||
table_out << " float2(" << pool[i].x << ", " << pool[i].y << ")";
|
||||
if (i < samples_per_pool - 1) {
|
||||
table_out << ",\n";
|
||||
}
|
||||
}
|
||||
if (pool_id < 3) {
|
||||
table_out << ",\n";
|
||||
}
|
||||
}
|
||||
table_out << ");\n\n";
|
||||
|
||||
table_out.close();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "usage: eevee_build [--resolve_sample_table] output_file\n");
|
||||
return -1;
|
||||
}
|
||||
if (string(argv[1]) == "--resolve_sample_table") {
|
||||
string output_name(argv[2]);
|
||||
raytrace_sample_reuse_table(output_name, false);
|
||||
}
|
||||
return 0;
|
||||
}
|
167
source/blender/draw/engines/eevee/eevee_camera.cc
Normal file
167
source/blender/draw/engines/eevee/eevee_camera.cc
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BKE_camera.h"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "eevee_camera.hh"
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name eCameraType
|
||||
* \{ */
|
||||
|
||||
static eCameraType from_camera(const ::Camera *camera)
|
||||
{
|
||||
switch (camera->type) {
|
||||
default:
|
||||
case CAM_PERSP:
|
||||
return CAMERA_PERSP;
|
||||
case CAM_ORTHO:
|
||||
return CAMERA_ORTHO;
|
||||
case CAM_PANO:
|
||||
switch (camera->panorama_type) {
|
||||
default:
|
||||
case CAM_PANO_EQUIRECTANGULAR:
|
||||
return CAMERA_PANO_EQUIRECT;
|
||||
case CAM_PANO_FISHEYE_EQUIDISTANT:
|
||||
return CAMERA_PANO_EQUIDISTANT;
|
||||
case CAM_PANO_FISHEYE_EQUISOLID:
|
||||
return CAMERA_PANO_EQUISOLID;
|
||||
case CAM_PANO_MIRRORBALL:
|
||||
return CAMERA_PANO_MIRROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Camera
|
||||
* \{ */
|
||||
|
||||
void Camera::init(void)
|
||||
{
|
||||
const Object *camera_eval = inst_.camera_eval_object;
|
||||
synced_ = false;
|
||||
/* Swap! */
|
||||
data_id_ = !data_id_;
|
||||
|
||||
CameraData &data = data_[data_id_];
|
||||
|
||||
if (camera_eval) {
|
||||
const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data);
|
||||
data.type = from_camera(cam);
|
||||
}
|
||||
else if (inst_.drw_view) {
|
||||
data.type = DRW_view_is_persp_get(inst_.drw_view) ? CAMERA_PERSP : CAMERA_ORTHO;
|
||||
}
|
||||
else {
|
||||
/* Lightprobe baking. */
|
||||
data.type = CAMERA_PERSP;
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::sync(void)
|
||||
{
|
||||
const Object *camera_eval = inst_.camera_eval_object;
|
||||
CameraData &data = data_[data_id_];
|
||||
|
||||
data.filter_size = inst_.scene->r.gauss;
|
||||
|
||||
if (inst_.drw_view) {
|
||||
DRW_view_viewmat_get(inst_.drw_view, data.viewmat.ptr(), false);
|
||||
DRW_view_viewmat_get(inst_.drw_view, data.viewinv.ptr(), true);
|
||||
DRW_view_winmat_get(inst_.drw_view, data.winmat.ptr(), false);
|
||||
DRW_view_winmat_get(inst_.drw_view, data.wininv.ptr(), true);
|
||||
DRW_view_persmat_get(inst_.drw_view, data.persmat.ptr(), false);
|
||||
DRW_view_persmat_get(inst_.drw_view, data.persinv.ptr(), true);
|
||||
DRW_view_camtexco_get(inst_.drw_view, data.uv_scale);
|
||||
}
|
||||
else if (inst_.render) {
|
||||
/* TODO(fclem) Overscan */
|
||||
// RE_GetCameraWindowWithOverscan(inst_.render->re, g_data->overscan, data.winmat);
|
||||
RE_GetCameraWindow(inst_.render->re, camera_eval, data.winmat.ptr());
|
||||
RE_GetCameraModelMatrix(inst_.render->re, camera_eval, data.viewinv.ptr());
|
||||
invert_m4_m4(data.viewmat.ptr(), data.viewinv.ptr());
|
||||
invert_m4_m4(data.wininv.ptr(), data.winmat.ptr());
|
||||
mul_m4_m4m4(data.persmat.ptr(), data.winmat.ptr(), data.viewmat.ptr());
|
||||
invert_m4_m4(data.persinv.ptr(), data.persmat.ptr());
|
||||
data.uv_scale = float2(1.0f);
|
||||
data.uv_bias = float2(0.0f);
|
||||
}
|
||||
else {
|
||||
data.viewmat.identity();
|
||||
data.viewinv.identity();
|
||||
perspective_m4(data.winmat.ptr(), -0.1f, 0.1f, -0.1f, 0.1f, 0.1f, 1.0f);
|
||||
data.wininv = data.winmat.inverted();
|
||||
data.persmat = data.winmat * data.viewmat;
|
||||
data.persinv = data.persmat.inverted();
|
||||
}
|
||||
|
||||
if (camera_eval) {
|
||||
const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data);
|
||||
data.clip_near = cam->clip_start;
|
||||
data.clip_far = cam->clip_end;
|
||||
data.fisheye_fov = cam->fisheye_fov;
|
||||
data.fisheye_lens = cam->fisheye_lens;
|
||||
data.equirect_bias.x = -cam->longitude_min + M_PI_2;
|
||||
data.equirect_bias.y = -cam->latitude_min + M_PI_2;
|
||||
data.equirect_scale.x = cam->longitude_min - cam->longitude_max;
|
||||
data.equirect_scale.y = cam->latitude_min - cam->latitude_max;
|
||||
/* Combine with uv_scale/bias to avoid doing extra computation. */
|
||||
data.equirect_bias += data.uv_bias * data.equirect_scale;
|
||||
data.equirect_scale *= data.uv_scale;
|
||||
|
||||
data.equirect_scale_inv = 1.0f / data.equirect_scale;
|
||||
}
|
||||
else if (inst_.drw_view) {
|
||||
data.clip_near = DRW_view_near_distance_get(inst_.drw_view);
|
||||
data.clip_far = DRW_view_far_distance_get(inst_.drw_view);
|
||||
data.fisheye_fov = data.fisheye_lens = -1.0f;
|
||||
data.equirect_bias = float2(0.0f);
|
||||
data.equirect_scale = float2(0.0f);
|
||||
}
|
||||
|
||||
data_[data_id_].push_update();
|
||||
|
||||
synced_ = true;
|
||||
|
||||
/* Detect changes in parameters. */
|
||||
if (data_[data_id_] != data_[!data_id_]) {
|
||||
inst_.sampling.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
149
source/blender/draw/engines/eevee/eevee_camera.hh
Normal file
149
source/blender/draw/engines/eevee/eevee_camera.hh
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "eevee_sampling.hh"
|
||||
#include "eevee_shader_shared.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* TODO(fclem) Might want to move to eevee_shader_shared.hh. */
|
||||
static const float cubeface_mat[6][4][4] = {
|
||||
/* Pos X */
|
||||
{{0.0f, 0.0f, -1.0f, 0.0f},
|
||||
{0.0f, -1.0f, 0.0f, 0.0f},
|
||||
{-1.0f, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f}},
|
||||
/* Neg X */
|
||||
{{0.0f, 0.0f, 1.0f, 0.0f},
|
||||
{0.0f, -1.0f, 0.0f, 0.0f},
|
||||
{1.0f, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f}},
|
||||
/* Pos Y */
|
||||
{{1.0f, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, -1.0f, 0.0f},
|
||||
{0.0f, 1.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f}},
|
||||
/* Neg Y */
|
||||
{{1.0f, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, 1.0f, 0.0f},
|
||||
{0.0f, -1.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f}},
|
||||
/* Pos Z */
|
||||
{{1.0f, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, -1.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, -1.0f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f}},
|
||||
/* Neg Z */
|
||||
{{-1.0f, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, -1.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, 1.0f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f}},
|
||||
};
|
||||
|
||||
inline void cubeface_winmat_get(float4x4 &winmat, float near, float far)
|
||||
{
|
||||
/* Simple 90° FOV projection. */
|
||||
perspective_m4(winmat.ptr(), -near, near, -near, near, near, far);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name CameraData operators
|
||||
* \{ */
|
||||
|
||||
inline bool operator==(const CameraData &a, const CameraData &b)
|
||||
{
|
||||
return compare_m4m4(a.persmat.ptr(), b.persmat.ptr(), FLT_MIN) && (a.uv_scale == b.uv_scale) &&
|
||||
(a.uv_bias == b.uv_bias) && (a.equirect_scale == b.equirect_scale) &&
|
||||
(a.equirect_bias == b.equirect_bias) && (a.fisheye_fov == b.fisheye_fov) &&
|
||||
(a.fisheye_lens == b.fisheye_lens) && (a.filter_size == b.filter_size) &&
|
||||
(a.type == b.type);
|
||||
}
|
||||
|
||||
inline bool operator!=(const CameraData &a, const CameraData &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Camera
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Point of view in the scene. Can be init from viewport or camera object.
|
||||
*/
|
||||
class Camera {
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
/** Double buffered to detect changes and have history for re-projection. */
|
||||
CameraDataBuf data_[2];
|
||||
/** Active data index in data_. */
|
||||
int data_id_ = 0;
|
||||
/** Detects wrong usage. */
|
||||
bool synced_ = false;
|
||||
|
||||
public:
|
||||
Camera(Instance &inst) : inst_(inst){};
|
||||
~Camera(){};
|
||||
|
||||
void init(void);
|
||||
void sync(void);
|
||||
|
||||
/**
|
||||
* Getters
|
||||
**/
|
||||
const CameraData &data_get(void) const
|
||||
{
|
||||
BLI_assert(synced_);
|
||||
return data_[data_id_];
|
||||
}
|
||||
const GPUUniformBuf *ubo_get(void) const
|
||||
{
|
||||
return data_[data_id_];
|
||||
}
|
||||
bool is_panoramic(void) const
|
||||
{
|
||||
return eevee::is_panoramic(data_[data_id_].type);
|
||||
}
|
||||
bool is_orthographic(void) const
|
||||
{
|
||||
return data_[data_id_].type == CAMERA_ORTHO;
|
||||
}
|
||||
float3 position(void) const
|
||||
{
|
||||
return float3(data_[data_id_].viewinv[3]);
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
@@ -1,721 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2020, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup EEVEE
|
||||
*
|
||||
* This file implements Cryptomatte for EEVEE. Cryptomatte is used to extract mattes using
|
||||
* information already available at render time. See
|
||||
* https://raw.githubusercontent.com/Psyop/Cryptomatte/master/specification/IDmattes_poster.pdf
|
||||
* for reference to the cryptomatte specification.
|
||||
*
|
||||
* The challenge with cryptomatte in EEVEE is the merging and sorting of the samples.
|
||||
* User can enable up to 3 cryptomatte layers (Object, Material and Asset).
|
||||
*
|
||||
* Process
|
||||
*
|
||||
* - Cryptomatte sample: Rendering of a cryptomatte sample is stored in a GPUBuffer. The buffer
|
||||
* holds a single float per pixel per number of active cryptomatte layers. The float is the
|
||||
* cryptomatte hash of each layer. After drawing the cryptomatte sample the intermediate result is
|
||||
* downloaded to a CPU buffer (`cryptomatte_download_buffer`).
|
||||
*
|
||||
* Accurate mode
|
||||
*
|
||||
* There are two accuracy modes. The difference between the two is the number of render samples
|
||||
* they take into account to create the render passes. When accurate mode is off the number of
|
||||
* levels is used as the number of cryptomatte samples to take. When accuracy mode is on the number
|
||||
* of render samples is used.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "DRW_engine.h"
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BKE_cryptomatte.h"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "BLI_alloca.h"
|
||||
#include "BLI_math_bits.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_particle_types.h"
|
||||
|
||||
#include "eevee_private.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Data Management cryptomatte accum buffer
|
||||
* \{ */
|
||||
|
||||
BLI_INLINE eViewLayerCryptomatteFlags eevee_cryptomatte_active_layers(const ViewLayer *view_layer)
|
||||
{
|
||||
const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag &
|
||||
VIEW_LAYER_CRYPTOMATTE_ALL;
|
||||
return cryptomatte_layers;
|
||||
}
|
||||
|
||||
/* The number of cryptomatte layers that are enabled */
|
||||
BLI_INLINE int eevee_cryptomatte_layers_count(const ViewLayer *view_layer)
|
||||
{
|
||||
const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
|
||||
view_layer);
|
||||
return count_bits_i(cryptomatte_layers);
|
||||
}
|
||||
|
||||
/* The number of render result passes are needed to store a single cryptomatte layer. Per
|
||||
* renderpass 2 cryptomatte samples can be stored. */
|
||||
BLI_INLINE int eevee_cryptomatte_passes_per_layer(const ViewLayer *view_layer)
|
||||
{
|
||||
const int num_cryptomatte_levels = view_layer->cryptomatte_levels;
|
||||
const int num_cryptomatte_passes = (num_cryptomatte_levels + 1) / 2;
|
||||
return num_cryptomatte_passes;
|
||||
}
|
||||
|
||||
BLI_INLINE int eevee_cryptomatte_layer_stride(const ViewLayer *view_layer)
|
||||
{
|
||||
return view_layer->cryptomatte_levels;
|
||||
}
|
||||
|
||||
BLI_INLINE int eevee_cryptomatte_layer_offset(const ViewLayer *view_layer, const int layer)
|
||||
{
|
||||
return view_layer->cryptomatte_levels * layer;
|
||||
}
|
||||
|
||||
BLI_INLINE int eevee_cryptomatte_pixel_stride(const ViewLayer *view_layer)
|
||||
{
|
||||
return eevee_cryptomatte_layer_stride(view_layer) * eevee_cryptomatte_layers_count(view_layer);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Init Renderpasses
|
||||
* \{ */
|
||||
|
||||
void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
|
||||
/* Cryptomatte is only rendered for final image renders */
|
||||
if (!DRW_state_is_scene_render()) {
|
||||
return;
|
||||
}
|
||||
const eViewLayerCryptomatteFlags active_layers = eevee_cryptomatte_active_layers(view_layer);
|
||||
if (active_layers) {
|
||||
struct CryptomatteSession *session = BKE_cryptomatte_init();
|
||||
if ((active_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
|
||||
BKE_cryptomatte_add_layer(session, "CryptoObject");
|
||||
}
|
||||
if ((active_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
|
||||
BKE_cryptomatte_add_layer(session, "CryptoMaterial");
|
||||
}
|
||||
if ((active_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
|
||||
BKE_cryptomatte_add_layer(session, "CryptoAsset");
|
||||
}
|
||||
g_data->cryptomatte_session = session;
|
||||
|
||||
g_data->render_passes |= EEVEE_RENDER_PASS_CRYPTOMATTE | EEVEE_RENDER_PASS_VOLUME_LIGHT;
|
||||
g_data->cryptomatte_accurate_mode = (view_layer->cryptomatte_flag &
|
||||
VIEW_LAYER_CRYPTOMATTE_ACCURATE) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
|
||||
EEVEE_Data *vedata,
|
||||
int UNUSED(tot_samples))
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
|
||||
const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
|
||||
eGPUTextureFormat format = (num_cryptomatte_layers == 1) ? GPU_R32F :
|
||||
(num_cryptomatte_layers == 2) ? GPU_RG32F :
|
||||
GPU_RGBA32F;
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
const int buffer_size = viewport_size[0] * viewport_size[1];
|
||||
|
||||
if (g_data->cryptomatte_accum_buffer == NULL) {
|
||||
g_data->cryptomatte_accum_buffer = MEM_calloc_arrayN(
|
||||
buffer_size * eevee_cryptomatte_pixel_stride(view_layer),
|
||||
sizeof(EEVEE_CryptomatteSample),
|
||||
__func__);
|
||||
/* Download buffer should store a float per active cryptomatte layer. */
|
||||
g_data->cryptomatte_download_buffer = MEM_malloc_arrayN(
|
||||
buffer_size * num_cryptomatte_layers, sizeof(float), __func__);
|
||||
}
|
||||
else {
|
||||
/* During multiview rendering the `cryptomatte_accum_buffer` is deallocated after all views
|
||||
* have been rendered. Clear it here to be reused by the next view. */
|
||||
memset(g_data->cryptomatte_accum_buffer,
|
||||
0,
|
||||
buffer_size * eevee_cryptomatte_pixel_stride(view_layer) *
|
||||
sizeof(EEVEE_CryptomatteSample));
|
||||
}
|
||||
|
||||
DRW_texture_ensure_fullscreen_2d(&txl->cryptomatte, format, 0);
|
||||
GPU_framebuffer_ensure_config(&fbl->cryptomatte_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_TEXTURE(dtxl->depth),
|
||||
GPU_ATTACHMENT_TEXTURE(txl->cryptomatte),
|
||||
});
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Populate Cache
|
||||
* \{ */
|
||||
|
||||
void EEVEE_cryptomatte_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) {
|
||||
DRW_PASS_CREATE(psl->cryptomatte_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
|
||||
}
|
||||
}
|
||||
|
||||
static DRWShadingGroup *eevee_cryptomatte_shading_group_create(EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *UNUSED(sldata),
|
||||
Object *ob,
|
||||
Material *material,
|
||||
bool is_hair)
|
||||
{
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
|
||||
view_layer);
|
||||
EEVEE_PrivateData *g_data = vedata->stl->g_data;
|
||||
float cryptohash[4] = {0.0f};
|
||||
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
int layer_offset = 0;
|
||||
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
|
||||
uint32_t cryptomatte_hash = BKE_cryptomatte_object_hash(
|
||||
g_data->cryptomatte_session, "CryptoObject", ob);
|
||||
float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
|
||||
cryptohash[layer_offset] = cryptomatte_color_value;
|
||||
layer_offset++;
|
||||
}
|
||||
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
|
||||
uint32_t cryptomatte_hash = BKE_cryptomatte_material_hash(
|
||||
g_data->cryptomatte_session, "CryptoMaterial", material);
|
||||
float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
|
||||
cryptohash[layer_offset] = cryptomatte_color_value;
|
||||
layer_offset++;
|
||||
}
|
||||
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
|
||||
uint32_t cryptomatte_hash = BKE_cryptomatte_asset_hash(
|
||||
g_data->cryptomatte_session, "CryptoAsset", ob);
|
||||
float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
|
||||
cryptohash[layer_offset] = cryptomatte_color_value;
|
||||
layer_offset++;
|
||||
}
|
||||
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_cryptomatte_sh_get(is_hair),
|
||||
psl->cryptomatte_ps);
|
||||
DRW_shgroup_uniform_vec4_copy(grp, "cryptohash", cryptohash);
|
||||
|
||||
return grp;
|
||||
}
|
||||
|
||||
static void eevee_cryptomatte_hair_cache_populate(EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata,
|
||||
Object *ob,
|
||||
ParticleSystem *psys,
|
||||
ModifierData *md,
|
||||
Material *material)
|
||||
{
|
||||
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
|
||||
vedata, sldata, ob, material, true);
|
||||
DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL);
|
||||
}
|
||||
|
||||
void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata,
|
||||
Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_HAIR);
|
||||
Material *material = BKE_object_material_get_eval(ob, HAIR_MATERIAL_NR);
|
||||
eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, NULL, NULL, material);
|
||||
}
|
||||
|
||||
void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata,
|
||||
Object *ob)
|
||||
{
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
|
||||
if (ob->type == OB_MESH) {
|
||||
if (ob != draw_ctx->object_edit) {
|
||||
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
||||
if (md->type != eModifierType_ParticleSystem) {
|
||||
continue;
|
||||
}
|
||||
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
|
||||
if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) {
|
||||
continue;
|
||||
}
|
||||
ParticleSettings *part = psys->part;
|
||||
const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
|
||||
if (draw_as != PART_DRAW_PATH) {
|
||||
continue;
|
||||
}
|
||||
Material *material = BKE_object_material_get_eval(ob, part->omat);
|
||||
eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, psys, md, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob)
|
||||
{
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
|
||||
view_layer);
|
||||
|
||||
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
|
||||
const int materials_len = DRW_cache_object_material_count_get(ob);
|
||||
struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len);
|
||||
memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len);
|
||||
struct GPUBatch **geoms = DRW_cache_object_surface_material_get(
|
||||
ob, gpumat_array, materials_len);
|
||||
if (geoms) {
|
||||
for (int i = 0; i < materials_len; i++) {
|
||||
struct GPUBatch *geom = geoms[i];
|
||||
if (geom == NULL) {
|
||||
continue;
|
||||
}
|
||||
Material *material = BKE_object_material_get_eval(ob, i + 1);
|
||||
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
|
||||
vedata, sldata, ob, material, false);
|
||||
DRW_shgroup_call(grp, geom, ob);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
GPUBatch *geom = DRW_cache_object_surface_get(ob);
|
||||
if (geom) {
|
||||
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
|
||||
vedata, sldata, ob, NULL, false);
|
||||
DRW_shgroup_call(grp, geom, ob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Accumulate Samples
|
||||
* \{ */
|
||||
|
||||
/* Downloads cryptomatte sample buffer from the GPU and integrate the samples with the accumulated
|
||||
* cryptomatte samples. */
|
||||
static void eevee_cryptomatte_download_buffer(EEVEE_Data *vedata, GPUFrameBuffer *framebuffer)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
|
||||
const int num_levels = view_layer->cryptomatte_levels;
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
const int buffer_size = viewport_size[0] * viewport_size[1];
|
||||
|
||||
EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
|
||||
float *download_buffer = g_data->cryptomatte_download_buffer;
|
||||
|
||||
BLI_assert(accum_buffer);
|
||||
BLI_assert(download_buffer);
|
||||
|
||||
GPU_framebuffer_read_color(framebuffer,
|
||||
0,
|
||||
0,
|
||||
viewport_size[0],
|
||||
viewport_size[1],
|
||||
num_cryptomatte_layers,
|
||||
0,
|
||||
GPU_DATA_FLOAT,
|
||||
download_buffer);
|
||||
|
||||
/* Integrate download buffer into the accum buffer.
|
||||
* The download buffer contains up to 3 floats per pixel (one float per cryptomatte layer.
|
||||
*
|
||||
* NOTE: here we deviate from the cryptomatte standard. During integration the standard always
|
||||
* sort the samples by its weight to make sure that samples with the lowest weight
|
||||
* are discarded first. In our case the weight of each sample is always 1 as we don't have
|
||||
* subsamples and apply the coverage during the post processing. When there is no room for new
|
||||
* samples the new samples has a weight of 1 and will always be discarded. */
|
||||
int download_pixel_index = 0;
|
||||
int accum_pixel_index = 0;
|
||||
int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);
|
||||
for (int pixel_index = 0; pixel_index < buffer_size; pixel_index++) {
|
||||
for (int layer = 0; layer < num_cryptomatte_layers; layer++) {
|
||||
const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer);
|
||||
float download_hash = download_buffer[download_pixel_index++];
|
||||
for (int level = 0; level < num_levels; level++) {
|
||||
EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level];
|
||||
if (sample->hash == download_hash) {
|
||||
sample->weight += 1.0f;
|
||||
break;
|
||||
}
|
||||
/* We test against weight as hash 0.0f is used for samples hitting the world background. */
|
||||
if (sample->weight == 0.0f) {
|
||||
sample->hash = download_hash;
|
||||
sample->weight = 1.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
accum_pixel_index += accum_pixel_stride;
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
const int cryptomatte_levels = view_layer->cryptomatte_levels;
|
||||
const int current_sample = effects->taa_current_sample;
|
||||
|
||||
/* In accurate mode all render samples are evaluated. In inaccurate mode this is limited to the
|
||||
* number of cryptomatte levels. This will reduce the overhead of downloading the GPU buffer and
|
||||
* integrating it into the accum buffer. */
|
||||
if (g_data->cryptomatte_accurate_mode || current_sample < cryptomatte_levels) {
|
||||
static float clear_color[4] = {0.0};
|
||||
GPU_framebuffer_bind(fbl->cryptomatte_fb);
|
||||
GPU_framebuffer_clear_color(fbl->cryptomatte_fb, clear_color);
|
||||
DRW_draw_pass(psl->cryptomatte_ps);
|
||||
|
||||
eevee_cryptomatte_download_buffer(vedata, fbl->cryptomatte_fb);
|
||||
|
||||
/* Restore */
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Update Render Passes
|
||||
* \{ */
|
||||
|
||||
void EEVEE_cryptomatte_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
|
||||
{
|
||||
char cryptomatte_pass_name[MAX_NAME];
|
||||
const short num_passes = eevee_cryptomatte_passes_per_layer(view_layer);
|
||||
if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
|
||||
for (short pass = 0; pass < num_passes; pass++) {
|
||||
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoObject%02d", pass);
|
||||
RE_engine_register_pass(
|
||||
engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
|
||||
}
|
||||
}
|
||||
if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
|
||||
for (short pass = 0; pass < num_passes; pass++) {
|
||||
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoMaterial%02d", pass);
|
||||
RE_engine_register_pass(
|
||||
engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
|
||||
}
|
||||
}
|
||||
if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
|
||||
for (short pass = 0; pass < num_passes; pass++) {
|
||||
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoAsset%02d", pass);
|
||||
RE_engine_register_pass(
|
||||
engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Construct Render Result
|
||||
* \{ */
|
||||
|
||||
/* Compare function for cryptomatte samples. Samples with the highest weight will be at the
|
||||
* beginning of the list. */
|
||||
static int eevee_cryptomatte_sample_cmp_reverse(const void *a_, const void *b_)
|
||||
{
|
||||
const EEVEE_CryptomatteSample *a = a_;
|
||||
const EEVEE_CryptomatteSample *b = b_;
|
||||
if (a->weight < b->weight) {
|
||||
return 1;
|
||||
}
|
||||
if (a->weight > b->weight) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Post process the weights. The accumulated weights buffer adds one to each weight per sample.
|
||||
* During post processing ensure that the total of weights per sample is between 0 and 1. */
|
||||
static void eevee_cryptomatte_postprocess_weights(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
|
||||
const int num_levels = view_layer->cryptomatte_levels;
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
const int buffer_size = viewport_size[0] * viewport_size[1];
|
||||
|
||||
EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
|
||||
BLI_assert(accum_buffer);
|
||||
float *volumetric_transmittance_buffer = NULL;
|
||||
if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
|
||||
volumetric_transmittance_buffer = GPU_texture_read(
|
||||
txl->volume_transmittance_accum, GPU_DATA_FLOAT, 0);
|
||||
}
|
||||
const int num_samples = effects->taa_current_sample - 1;
|
||||
|
||||
int accum_pixel_index = 0;
|
||||
int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);
|
||||
|
||||
for (int pixel_index = 0; pixel_index < buffer_size;
|
||||
pixel_index++, accum_pixel_index += accum_pixel_stride) {
|
||||
float coverage = 1.0f;
|
||||
if (volumetric_transmittance_buffer != NULL) {
|
||||
coverage = (volumetric_transmittance_buffer[pixel_index * 4] +
|
||||
volumetric_transmittance_buffer[pixel_index * 4 + 1] +
|
||||
volumetric_transmittance_buffer[pixel_index * 4 + 2]) /
|
||||
(3.0f * num_samples);
|
||||
}
|
||||
for (int layer = 0; layer < num_cryptomatte_layers; layer++) {
|
||||
const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer);
|
||||
/* Calculate the total weight of the sample. */
|
||||
float total_weight = 0.0f;
|
||||
for (int level = 0; level < num_levels; level++) {
|
||||
EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level];
|
||||
total_weight += sample->weight;
|
||||
}
|
||||
BLI_assert(total_weight > 0.0f);
|
||||
|
||||
float total_weight_inv = coverage / total_weight;
|
||||
if (total_weight_inv > 0.0f) {
|
||||
for (int level = 0; level < num_levels; level++) {
|
||||
EEVEE_CryptomatteSample *sample =
|
||||
&accum_buffer[accum_pixel_index + layer_offset + level];
|
||||
/* Remove background samples. These samples were used to determine the correct weight
|
||||
* but won't be part of the final result. */
|
||||
if (sample->hash == 0.0f) {
|
||||
sample->weight = 0.0f;
|
||||
}
|
||||
sample->weight *= total_weight_inv;
|
||||
}
|
||||
|
||||
/* Sort accum buffer by coverage of each sample. */
|
||||
qsort(&accum_buffer[accum_pixel_index + layer_offset],
|
||||
num_levels,
|
||||
sizeof(EEVEE_CryptomatteSample),
|
||||
eevee_cryptomatte_sample_cmp_reverse);
|
||||
}
|
||||
else {
|
||||
/* This pixel doesn't have any weight, so clear it fully. */
|
||||
for (int level = 0; level < num_levels; level++) {
|
||||
EEVEE_CryptomatteSample *sample =
|
||||
&accum_buffer[accum_pixel_index + layer_offset + level];
|
||||
sample->weight = 0.0f;
|
||||
sample->hash = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (volumetric_transmittance_buffer) {
|
||||
MEM_freeN(volumetric_transmittance_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract cryptomatte layer from the cryptomatte_accum_buffer to render passes. */
|
||||
static void eevee_cryptomatte_extract_render_passes(
|
||||
RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const char *render_pass_name_format,
|
||||
EEVEE_CryptomatteSample *accum_buffer,
|
||||
/* number of render passes per cryptomatte layer. */
|
||||
const int num_cryptomatte_passes,
|
||||
const int num_cryptomatte_levels,
|
||||
const int accum_pixel_stride,
|
||||
const int layer_stride,
|
||||
const int layer_index,
|
||||
const int rect_width,
|
||||
const int rect_height,
|
||||
const int rect_offset_x,
|
||||
const int rect_offset_y,
|
||||
const int viewport_width)
|
||||
{
|
||||
char cryptomatte_pass_name[MAX_NAME];
|
||||
/* A pass can store 2 levels. Technically the last pass can have a single level if the number of
|
||||
* levels is an odd number. This parameter counts the number of levels it has processed. */
|
||||
int levels_done = 0;
|
||||
for (int pass = 0; pass < num_cryptomatte_passes; pass++) {
|
||||
/* Each pass holds 2 cryptomatte samples. */
|
||||
const int pass_offset = pass * 2;
|
||||
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, render_pass_name_format, pass);
|
||||
RenderPass *rp_object = RE_pass_find_by_name(rl, cryptomatte_pass_name, viewname);
|
||||
for (int y = 0; y < rect_height; y++) {
|
||||
for (int x = 0; x < rect_width; x++) {
|
||||
const int accum_buffer_offset = (rect_offset_x + x +
|
||||
(rect_offset_y + y) * viewport_width) *
|
||||
accum_pixel_stride +
|
||||
layer_index * layer_stride + pass_offset;
|
||||
const int render_pass_offset = (y * rect_width + x) * 4;
|
||||
rp_object->rect[render_pass_offset] = accum_buffer[accum_buffer_offset].hash;
|
||||
rp_object->rect[render_pass_offset + 1] = accum_buffer[accum_buffer_offset].weight;
|
||||
if (levels_done + 1 < num_cryptomatte_levels) {
|
||||
rp_object->rect[render_pass_offset + 2] = accum_buffer[accum_buffer_offset + 1].hash;
|
||||
rp_object->rect[render_pass_offset + 3] = accum_buffer[accum_buffer_offset + 1].weight;
|
||||
}
|
||||
else {
|
||||
rp_object->rect[render_pass_offset + 2] = 0.0f;
|
||||
rp_object->rect[render_pass_offset + 3] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
levels_done++;
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_cryptomatte_render_result(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
const rcti *rect,
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *UNUSED(sldata))
|
||||
{
|
||||
EEVEE_PrivateData *g_data = vedata->stl->g_data;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag &
|
||||
VIEW_LAYER_CRYPTOMATTE_ALL;
|
||||
|
||||
eevee_cryptomatte_postprocess_weights(vedata);
|
||||
|
||||
const int rect_width = BLI_rcti_size_x(rect);
|
||||
const int rect_height = BLI_rcti_size_y(rect);
|
||||
const int rect_offset_x = vedata->stl->g_data->overscan_pixels + rect->xmin;
|
||||
const int rect_offset_y = vedata->stl->g_data->overscan_pixels + rect->ymin;
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
const int viewport_width = viewport_size[0];
|
||||
EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
|
||||
BLI_assert(accum_buffer);
|
||||
const int num_cryptomatte_levels = view_layer->cryptomatte_levels;
|
||||
const int num_cryptomatte_passes = eevee_cryptomatte_passes_per_layer(view_layer);
|
||||
const int layer_stride = eevee_cryptomatte_layer_stride(view_layer);
|
||||
const int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);
|
||||
|
||||
int layer_index = 0;
|
||||
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
|
||||
eevee_cryptomatte_extract_render_passes(rl,
|
||||
viewname,
|
||||
"CryptoObject%02d",
|
||||
accum_buffer,
|
||||
num_cryptomatte_passes,
|
||||
num_cryptomatte_levels,
|
||||
accum_pixel_stride,
|
||||
layer_stride,
|
||||
layer_index,
|
||||
rect_width,
|
||||
rect_height,
|
||||
rect_offset_x,
|
||||
rect_offset_y,
|
||||
viewport_width);
|
||||
layer_index++;
|
||||
}
|
||||
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
|
||||
eevee_cryptomatte_extract_render_passes(rl,
|
||||
viewname,
|
||||
"CryptoMaterial%02d",
|
||||
accum_buffer,
|
||||
num_cryptomatte_passes,
|
||||
num_cryptomatte_levels,
|
||||
accum_pixel_stride,
|
||||
layer_stride,
|
||||
layer_index,
|
||||
rect_width,
|
||||
rect_height,
|
||||
rect_offset_x,
|
||||
rect_offset_y,
|
||||
viewport_width);
|
||||
layer_index++;
|
||||
}
|
||||
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
|
||||
eevee_cryptomatte_extract_render_passes(rl,
|
||||
viewname,
|
||||
"CryptoAsset%02d",
|
||||
accum_buffer,
|
||||
num_cryptomatte_passes,
|
||||
num_cryptomatte_levels,
|
||||
accum_pixel_stride,
|
||||
layer_stride,
|
||||
layer_index,
|
||||
rect_width,
|
||||
rect_height,
|
||||
rect_offset_x,
|
||||
rect_offset_y,
|
||||
viewport_width);
|
||||
layer_index++;
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_cryptomatte_store_metadata(EEVEE_Data *vedata, RenderResult *render_result)
|
||||
{
|
||||
EEVEE_PrivateData *g_data = vedata->stl->g_data;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
BLI_assert(g_data->cryptomatte_session);
|
||||
|
||||
BKE_cryptomatte_store_metadata(g_data->cryptomatte_session, render_result, view_layer);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void EEVEE_cryptomatte_free(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PrivateData *g_data = vedata->stl->g_data;
|
||||
MEM_SAFE_FREE(g_data->cryptomatte_accum_buffer);
|
||||
MEM_SAFE_FREE(g_data->cryptomatte_download_buffer);
|
||||
if (g_data->cryptomatte_session) {
|
||||
BKE_cryptomatte_free(g_data->cryptomatte_session);
|
||||
g_data->cryptomatte_session = NULL;
|
||||
}
|
||||
}
|
@@ -1,389 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*
|
||||
* All specific data handler for Objects, Lights, ViewLayers, ...
|
||||
*/
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_memblock.h"
|
||||
|
||||
#include "BKE_duplilist.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "GPU_vertex_buffer.h"
|
||||
|
||||
#include "eevee_lightcache.h"
|
||||
#include "eevee_private.h"
|
||||
|
||||
/* Motion Blur data. */
|
||||
|
||||
static void eevee_motion_blur_mesh_data_free(void *val)
|
||||
{
|
||||
EEVEE_GeometryMotionData *geom_mb = (EEVEE_GeometryMotionData *)val;
|
||||
EEVEE_HairMotionData *hair_mb = (EEVEE_HairMotionData *)val;
|
||||
switch (geom_mb->type) {
|
||||
case EEVEE_MOTION_DATA_HAIR:
|
||||
for (int j = 0; j < hair_mb->psys_len; j++) {
|
||||
for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) {
|
||||
GPU_VERTBUF_DISCARD_SAFE(hair_mb->psys[j].hair_pos[i]);
|
||||
}
|
||||
for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) {
|
||||
DRW_TEXTURE_FREE_SAFE(hair_mb->psys[j].hair_pos_tx[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EEVEE_MOTION_DATA_MESH:
|
||||
for (int i = 0; i < ARRAY_SIZE(geom_mb->vbo); i++) {
|
||||
GPU_VERTBUF_DISCARD_SAFE(geom_mb->vbo[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
MEM_freeN(val);
|
||||
}
|
||||
|
||||
static uint eevee_object_key_hash(const void *key)
|
||||
{
|
||||
EEVEE_ObjectKey *ob_key = (EEVEE_ObjectKey *)key;
|
||||
uint hash = BLI_ghashutil_ptrhash(ob_key->ob);
|
||||
hash = BLI_ghashutil_combine_hash(hash, BLI_ghashutil_ptrhash(ob_key->parent));
|
||||
for (int i = 0; i < MAX_DUPLI_RECUR; i++) {
|
||||
if (ob_key->id[i] != 0) {
|
||||
hash = BLI_ghashutil_combine_hash(hash, BLI_ghashutil_inthash(ob_key->id[i]));
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Return false if equal. */
|
||||
static bool eevee_object_key_cmp(const void *a, const void *b)
|
||||
{
|
||||
EEVEE_ObjectKey *key_a = (EEVEE_ObjectKey *)a;
|
||||
EEVEE_ObjectKey *key_b = (EEVEE_ObjectKey *)b;
|
||||
|
||||
if (key_a->ob != key_b->ob) {
|
||||
return true;
|
||||
}
|
||||
if (key_a->parent != key_b->parent) {
|
||||
return true;
|
||||
}
|
||||
if (memcmp(key_a->id, key_b->id, sizeof(key_a->id)) != 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb)
|
||||
{
|
||||
if (mb->object == NULL) {
|
||||
mb->object = BLI_ghash_new(eevee_object_key_hash, eevee_object_key_cmp, "EEVEE Object Motion");
|
||||
}
|
||||
if (mb->geom == NULL) {
|
||||
mb->geom = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE Mesh Motion");
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb)
|
||||
{
|
||||
if (mb->object) {
|
||||
BLI_ghash_free(mb->object, MEM_freeN, MEM_freeN);
|
||||
mb->object = NULL;
|
||||
}
|
||||
if (mb->geom) {
|
||||
BLI_ghash_free(mb->geom, NULL, eevee_motion_blur_mesh_data_free);
|
||||
mb->geom = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb,
|
||||
Object *ob,
|
||||
bool hair)
|
||||
{
|
||||
if (mb->object == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EEVEE_ObjectKey key, *key_p;
|
||||
/* Small hack to avoid another comparison. */
|
||||
key.ob = (Object *)((char *)ob + hair);
|
||||
DupliObject *dup = DRW_object_get_dupli(ob);
|
||||
if (dup) {
|
||||
key.parent = DRW_object_get_dupli_parent(ob);
|
||||
memcpy(key.id, dup->persistent_id, sizeof(key.id));
|
||||
}
|
||||
else {
|
||||
key.parent = key.ob;
|
||||
memset(key.id, 0, sizeof(key.id));
|
||||
}
|
||||
|
||||
EEVEE_ObjectMotionData *ob_step = BLI_ghash_lookup(mb->object, &key);
|
||||
if (ob_step == NULL) {
|
||||
key_p = MEM_mallocN(sizeof(*key_p), __func__);
|
||||
memcpy(key_p, &key, sizeof(*key_p));
|
||||
|
||||
ob_step = MEM_callocN(sizeof(EEVEE_ObjectMotionData), __func__);
|
||||
|
||||
BLI_ghash_insert(mb->object, key_p, ob_step);
|
||||
}
|
||||
return ob_step;
|
||||
}
|
||||
|
||||
static void *motion_blur_deform_data_get(EEVEE_MotionBlurData *mb, Object *ob, bool hair)
|
||||
{
|
||||
if (mb->geom == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
DupliObject *dup = DRW_object_get_dupli(ob);
|
||||
void *key;
|
||||
if (dup) {
|
||||
key = dup->ob;
|
||||
}
|
||||
else {
|
||||
key = ob;
|
||||
}
|
||||
/* Only use data for object that have no modifiers. */
|
||||
if (!BKE_object_is_modified(DRW_context_state_get()->scene, ob)) {
|
||||
key = ob->data;
|
||||
}
|
||||
key = (char *)key + (int)hair;
|
||||
EEVEE_GeometryMotionData *geom_step = BLI_ghash_lookup(mb->geom, key);
|
||||
if (geom_step == NULL) {
|
||||
if (hair) {
|
||||
EEVEE_HairMotionData *hair_step;
|
||||
/* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */
|
||||
int psys_len = (ob->type != OB_HAIR) ? BLI_listbase_count(&ob->modifiers) : 1;
|
||||
hair_step = MEM_callocN(sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len,
|
||||
__func__);
|
||||
hair_step->psys_len = psys_len;
|
||||
geom_step = (EEVEE_GeometryMotionData *)hair_step;
|
||||
geom_step->type = EEVEE_MOTION_DATA_HAIR;
|
||||
}
|
||||
else {
|
||||
geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__);
|
||||
geom_step->type = EEVEE_MOTION_DATA_MESH;
|
||||
}
|
||||
BLI_ghash_insert(mb->geom, key, geom_step);
|
||||
}
|
||||
return geom_step;
|
||||
}
|
||||
|
||||
EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb, Object *ob)
|
||||
{
|
||||
return motion_blur_deform_data_get(mb, ob, false);
|
||||
}
|
||||
|
||||
EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_MotionBlurData *mb, Object *ob)
|
||||
{
|
||||
return motion_blur_deform_data_get(mb, ob, true);
|
||||
}
|
||||
|
||||
/* View Layer data. */
|
||||
|
||||
void EEVEE_view_layer_data_free(void *storage)
|
||||
{
|
||||
EEVEE_ViewLayerData *sldata = (EEVEE_ViewLayerData *)storage;
|
||||
|
||||
/* Lights */
|
||||
MEM_SAFE_FREE(sldata->lights);
|
||||
DRW_UBO_FREE_SAFE(sldata->light_ubo);
|
||||
DRW_UBO_FREE_SAFE(sldata->shadow_ubo);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(sldata->shadow_fb);
|
||||
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_pool);
|
||||
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_pool);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
MEM_SAFE_FREE(sldata->shcasters_buffers[i].bbox);
|
||||
MEM_SAFE_FREE(sldata->shcasters_buffers[i].update);
|
||||
}
|
||||
|
||||
if (sldata->fallback_lightcache) {
|
||||
EEVEE_lightcache_free(sldata->fallback_lightcache);
|
||||
sldata->fallback_lightcache = NULL;
|
||||
}
|
||||
|
||||
/* Probes */
|
||||
MEM_SAFE_FREE(sldata->probes);
|
||||
DRW_UBO_FREE_SAFE(sldata->probe_ubo);
|
||||
DRW_UBO_FREE_SAFE(sldata->grid_ubo);
|
||||
DRW_UBO_FREE_SAFE(sldata->planar_ubo);
|
||||
DRW_UBO_FREE_SAFE(sldata->common_ubo);
|
||||
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.combined);
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.diff_color);
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.diff_light);
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.spec_color);
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.spec_light);
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.emit);
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.environment);
|
||||
for (int aov_index = 0; aov_index < MAX_AOVS; aov_index++) {
|
||||
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.aovs[aov_index]);
|
||||
}
|
||||
|
||||
if (sldata->material_cache) {
|
||||
BLI_memblock_destroy(sldata->material_cache, NULL);
|
||||
sldata->material_cache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void)
|
||||
{
|
||||
return (EEVEE_ViewLayerData *)DRW_view_layer_engine_data_get(&draw_engine_eevee_type);
|
||||
}
|
||||
|
||||
static void eevee_view_layer_init(EEVEE_ViewLayerData *sldata)
|
||||
{
|
||||
sldata->common_ubo = GPU_uniformbuf_create(sizeof(sldata->common_data));
|
||||
}
|
||||
|
||||
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer)
|
||||
{
|
||||
EEVEE_ViewLayerData **sldata = (EEVEE_ViewLayerData **)DRW_view_layer_engine_data_ensure_ex(
|
||||
view_layer, &draw_engine_eevee_type, &EEVEE_view_layer_data_free);
|
||||
|
||||
if (*sldata == NULL) {
|
||||
*sldata = MEM_callocN(sizeof(**sldata), "EEVEE_ViewLayerData");
|
||||
eevee_view_layer_init(*sldata);
|
||||
}
|
||||
|
||||
return *sldata;
|
||||
}
|
||||
|
||||
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void)
|
||||
{
|
||||
EEVEE_ViewLayerData **sldata = (EEVEE_ViewLayerData **)DRW_view_layer_engine_data_ensure(
|
||||
&draw_engine_eevee_type, &EEVEE_view_layer_data_free);
|
||||
|
||||
if (*sldata == NULL) {
|
||||
*sldata = MEM_callocN(sizeof(**sldata), "EEVEE_ViewLayerData");
|
||||
eevee_view_layer_init(*sldata);
|
||||
}
|
||||
|
||||
return *sldata;
|
||||
}
|
||||
|
||||
/* Object data. */
|
||||
|
||||
static void eevee_object_data_init(DrawData *dd)
|
||||
{
|
||||
EEVEE_ObjectEngineData *eevee_data = (EEVEE_ObjectEngineData *)dd;
|
||||
eevee_data->shadow_caster_id = -1;
|
||||
eevee_data->need_update = false;
|
||||
eevee_data->geom_update = false;
|
||||
}
|
||||
|
||||
EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob)
|
||||
{
|
||||
if (ELEM(ob->type, OB_LIGHTPROBE, OB_LAMP)) {
|
||||
return NULL;
|
||||
}
|
||||
return (EEVEE_ObjectEngineData *)DRW_drawdata_get(&ob->id, &draw_engine_eevee_type);
|
||||
}
|
||||
|
||||
EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob)
|
||||
{
|
||||
BLI_assert(!ELEM(ob->type, OB_LIGHTPROBE, OB_LAMP));
|
||||
return (EEVEE_ObjectEngineData *)DRW_drawdata_ensure(&ob->id,
|
||||
&draw_engine_eevee_type,
|
||||
sizeof(EEVEE_ObjectEngineData),
|
||||
eevee_object_data_init,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* Light probe data. */
|
||||
|
||||
static void eevee_lightprobe_data_init(DrawData *dd)
|
||||
{
|
||||
EEVEE_LightProbeEngineData *ped = (EEVEE_LightProbeEngineData *)dd;
|
||||
ped->need_update = false;
|
||||
}
|
||||
|
||||
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob)
|
||||
{
|
||||
if (ob->type != OB_LIGHTPROBE) {
|
||||
return NULL;
|
||||
}
|
||||
return (EEVEE_LightProbeEngineData *)DRW_drawdata_get(&ob->id, &draw_engine_eevee_type);
|
||||
}
|
||||
|
||||
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_LIGHTPROBE);
|
||||
return (EEVEE_LightProbeEngineData *)DRW_drawdata_ensure(&ob->id,
|
||||
&draw_engine_eevee_type,
|
||||
sizeof(EEVEE_LightProbeEngineData),
|
||||
eevee_lightprobe_data_init,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* Light data. */
|
||||
|
||||
static void eevee_light_data_init(DrawData *dd)
|
||||
{
|
||||
EEVEE_LightEngineData *led = (EEVEE_LightEngineData *)dd;
|
||||
led->need_update = true;
|
||||
}
|
||||
|
||||
EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob)
|
||||
{
|
||||
if (ob->type != OB_LAMP) {
|
||||
return NULL;
|
||||
}
|
||||
return (EEVEE_LightEngineData *)DRW_drawdata_get(&ob->id, &draw_engine_eevee_type);
|
||||
}
|
||||
|
||||
EEVEE_LightEngineData *EEVEE_light_data_ensure(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_LAMP);
|
||||
return (EEVEE_LightEngineData *)DRW_drawdata_ensure(&ob->id,
|
||||
&draw_engine_eevee_type,
|
||||
sizeof(EEVEE_LightEngineData),
|
||||
eevee_light_data_init,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* World data. */
|
||||
|
||||
static void eevee_world_data_init(DrawData *dd)
|
||||
{
|
||||
EEVEE_WorldEngineData *wed = (EEVEE_WorldEngineData *)dd;
|
||||
wed->dd.recalc |= 1;
|
||||
}
|
||||
|
||||
EEVEE_WorldEngineData *EEVEE_world_data_get(World *wo)
|
||||
{
|
||||
return (EEVEE_WorldEngineData *)DRW_drawdata_get(&wo->id, &draw_engine_eevee_type);
|
||||
}
|
||||
|
||||
EEVEE_WorldEngineData *EEVEE_world_data_ensure(World *wo)
|
||||
{
|
||||
return (EEVEE_WorldEngineData *)DRW_drawdata_ensure(&wo->id,
|
||||
&draw_engine_eevee_type,
|
||||
sizeof(EEVEE_WorldEngineData),
|
||||
eevee_world_data_init,
|
||||
NULL);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
732
source/blender/draw/engines/eevee/eevee_depth_of_field.cc
Normal file
732
source/blender/draw/engines/eevee/eevee_depth_of_field.cc
Normal file
@@ -0,0 +1,732 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Depth of field post process effect.
|
||||
*
|
||||
* There are 2 methods to achieve this effect.
|
||||
* - The first uses projection matrix offsetting and sample accumulation to give
|
||||
* reference quality depth of field. But this needs many samples to hide the
|
||||
* under-sampling.
|
||||
* - The second one is a post-processing based one. It follows the
|
||||
* implementation described in the presentation "Life of a Bokeh - Siggraph
|
||||
* 2018" from Guillaume Abadie. There are some difference with our actual
|
||||
* implementation that prioritize quality.
|
||||
*/
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BKE_camera.h"
|
||||
#include "DNA_camera_types.h"
|
||||
|
||||
#include "GPU_texture.h"
|
||||
#include "GPU_uniform_buffer.h"
|
||||
|
||||
#include "eevee_camera.hh"
|
||||
#include "eevee_instance.hh"
|
||||
#include "eevee_sampling.hh"
|
||||
#include "eevee_shader.hh"
|
||||
#include "eevee_shader_shared.hh"
|
||||
|
||||
#include "eevee_depth_of_field.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Depth of field
|
||||
* \{ */
|
||||
|
||||
void DepthOfField::init(void)
|
||||
{
|
||||
const Instance &inst = inst_;
|
||||
const SceneEEVEE &sce_eevee = inst.scene->eevee;
|
||||
do_hq_slight_focus_ = (sce_eevee.flag & SCE_EEVEE_DOF_HQ_SLIGHT_FOCUS) != 0;
|
||||
do_jitter_ = (sce_eevee.flag & SCE_EEVEE_DOF_JITTER) != 0;
|
||||
user_overblur_ = sce_eevee.bokeh_overblur / 100.0f;
|
||||
fx_max_coc_ = sce_eevee.bokeh_max_size;
|
||||
data_.scatter_color_threshold = sce_eevee.bokeh_threshold;
|
||||
data_.scatter_neighbor_max_color = sce_eevee.bokeh_neighbor_max;
|
||||
data_.denoise_factor = sce_eevee.bokeh_denoise_fac;
|
||||
/* Default to no depth of field. */
|
||||
fx_radius_ = 0.0f;
|
||||
jitter_radius_ = 0.0f;
|
||||
}
|
||||
|
||||
void DepthOfField::sync(const float4x4 winmat, int2 input_extent)
|
||||
{
|
||||
const Object *camera_object_eval = inst_.camera_eval_object;
|
||||
const ::Camera *cam = (camera_object_eval) ?
|
||||
reinterpret_cast<const ::Camera *>(camera_object_eval->data) :
|
||||
nullptr;
|
||||
|
||||
if (cam == nullptr || (cam->dof.flag & CAM_DOF_ENABLED) == 0) {
|
||||
fx_radius_ = 0.0f;
|
||||
jitter_radius_ = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
extent_ = input_extent;
|
||||
|
||||
data_.bokeh_blades = cam->dof.aperture_blades;
|
||||
data_.bokeh_rotation = cam->dof.aperture_rotation;
|
||||
data_.bokeh_anisotropic_scale.x = clamp_f(1.0f / cam->dof.aperture_ratio, 0.00001f, 1.0f);
|
||||
data_.bokeh_anisotropic_scale.y = clamp_f(cam->dof.aperture_ratio, 0.00001f, 1.0f);
|
||||
data_.bokeh_anisotropic_scale_inv = 1.0f / data_.bokeh_anisotropic_scale;
|
||||
|
||||
focus_distance_ = BKE_camera_object_dof_distance(camera_object_eval);
|
||||
float fstop = max_ff(cam->dof.aperture_fstop, 1e-5f);
|
||||
float aperture = 1.0f / (2.0f * fstop);
|
||||
if (cam->type == CAM_PERSP) {
|
||||
aperture *= cam->lens * 1e-3f;
|
||||
}
|
||||
|
||||
if (cam->type == CAM_ORTHO) {
|
||||
/* FIXME: Why is this needed? Some kind of implicit unit conversion? */
|
||||
aperture *= 0.04f;
|
||||
/* Really strange behavior from Cycles but replicating. */
|
||||
focus_distance_ += cam->clip_start;
|
||||
}
|
||||
|
||||
if (cam->type == CAM_PANO) {
|
||||
/* FIXME: Eyeballed. */
|
||||
aperture *= 0.185f;
|
||||
}
|
||||
|
||||
if (cam->dof.aperture_ratio < 1.0) {
|
||||
/* If ratio is scaling the bokeh outwards, we scale the aperture so that
|
||||
* the gather kernel size will encompass the maximum axis. */
|
||||
aperture /= max_ff(cam->dof.aperture_ratio, 1e-5f);
|
||||
}
|
||||
|
||||
/* Balance blur radius between fx dof and jitter dof. */
|
||||
if (do_jitter_ && (inst_.sampling.dof_ring_count_get() > 0) && (cam->type != CAM_PANO)) {
|
||||
/* Compute a minimal overblur radius to fill the gaps between the samples.
|
||||
* This is just the simplified form of dividing the area of the bokeh by
|
||||
* the number of samples. */
|
||||
float minimal_overblur = 1.0f / sqrtf(inst_.sampling.dof_sample_count_get());
|
||||
|
||||
fx_radius_ = (minimal_overblur + user_overblur_) * aperture;
|
||||
/* Avoid dilating the shape. Over-blur only soften. */
|
||||
jitter_radius_ = max_ff(0.0f, aperture - fx_radius_);
|
||||
}
|
||||
else {
|
||||
jitter_radius_ = 0.0f;
|
||||
fx_radius_ = aperture;
|
||||
}
|
||||
|
||||
if (fx_max_coc_ > 0.0f && fx_radius_ > 0.0f) {
|
||||
data_.camera_type = inst_.camera.data_get().type;
|
||||
/* OPTI(fclem) Could be optimized. */
|
||||
float jitter[3] = {fx_radius_, 0.0f, -focus_distance_};
|
||||
float center[3] = {0.0f, 0.0f, -focus_distance_};
|
||||
mul_project_m4_v3(winmat.ptr(), jitter);
|
||||
mul_project_m4_v3(winmat.ptr(), center);
|
||||
/* Simplify CoC calculation to a simple MADD. */
|
||||
if (data_.camera_type != CAMERA_ORTHO) {
|
||||
data_.coc_bias = -(center[0] - jitter[0]) * 0.5f * extent_[0];
|
||||
data_.coc_mul = focus_distance_ * data_.coc_bias;
|
||||
}
|
||||
else {
|
||||
data_.coc_mul = (center[0] - jitter[0]) * 0.5f * extent_[0];
|
||||
data_.coc_bias = focus_distance_ * data_.coc_mul;
|
||||
}
|
||||
|
||||
float min_fg_coc = coc_radius_from_camera_depth(data_, -cam->clip_start);
|
||||
float max_bg_coc = coc_radius_from_camera_depth(data_, -cam->clip_end);
|
||||
if (data_.camera_type != CAMERA_ORTHO) {
|
||||
/* Background is at infinity so maximum CoC is the limit of the function at -inf. */
|
||||
/* NOTE: we only do this for perspective camera since orthographic coc limit is inf. */
|
||||
max_bg_coc = data_.coc_bias;
|
||||
}
|
||||
/* Clamp with user defined max. */
|
||||
data_.coc_abs_max = min_ff(max_ff(fabsf(min_fg_coc), fabsf(max_bg_coc)), fx_max_coc_);
|
||||
|
||||
bokeh_lut_pass_sync();
|
||||
setup_pass_sync();
|
||||
tiles_prepare_pass_sync();
|
||||
reduce_pass_sync();
|
||||
convolve_pass_sync();
|
||||
resolve_pass_sync();
|
||||
|
||||
data_.push_update();
|
||||
}
|
||||
}
|
||||
|
||||
void DepthOfField::jitter_apply(float4x4 winmat, float4x4 viewmat)
|
||||
{
|
||||
if (jitter_radius_ == 0.0f) {
|
||||
return;
|
||||
}
|
||||
float radius, theta;
|
||||
inst_.sampling.dof_disk_sample_get(&radius, &theta);
|
||||
|
||||
if (data_.bokeh_blades >= 3.0f) {
|
||||
theta = circle_to_polygon_angle(data_.bokeh_blades, theta);
|
||||
radius *= circle_to_polygon_radius(data_.bokeh_blades, theta);
|
||||
}
|
||||
radius *= jitter_radius_;
|
||||
theta += data_.bokeh_rotation;
|
||||
|
||||
/* Sample in View Space. */
|
||||
float2 sample = float2(radius * cosf(theta), radius * sinf(theta));
|
||||
sample *= data_.bokeh_anisotropic_scale;
|
||||
/* Convert to NDC Space. */
|
||||
float3 jitter = float3(UNPACK2(sample), -focus_distance_);
|
||||
float3 center = float3(0.0f, 0.0f, -focus_distance_);
|
||||
mul_project_m4_v3(winmat.ptr(), jitter);
|
||||
mul_project_m4_v3(winmat.ptr(), center);
|
||||
|
||||
const bool is_ortho = (winmat[2][3] != -1.0f);
|
||||
if (is_ortho) {
|
||||
sample *= focus_distance_;
|
||||
}
|
||||
/* Translate origin. */
|
||||
sub_v2_v2(viewmat[3], sample);
|
||||
/* Skew winmat Z axis. */
|
||||
add_v2_v2(winmat[2], center - jitter);
|
||||
}
|
||||
|
||||
/** Will swap input and output texture if rendering happens. The actual output of this function
|
||||
* is in intput_tx. */
|
||||
void DepthOfField::render(GPUTexture *depth_tx, GPUTexture **input_tx, GPUTexture **output_tx)
|
||||
{
|
||||
if (fx_radius_ == 0.0f || fx_max_coc_ < 0.5f) {
|
||||
return;
|
||||
}
|
||||
|
||||
input_color_tx_ = *input_tx;
|
||||
input_depth_tx_ = depth_tx;
|
||||
|
||||
DRW_stats_group_start("Depth of Field");
|
||||
|
||||
bokeh_lut_pass_render();
|
||||
|
||||
setup_pass_render();
|
||||
tiles_prepare_pass_render();
|
||||
reduce_pass_render();
|
||||
convolve_pass_render();
|
||||
resolve_pass_render(*output_tx);
|
||||
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* Swap buffers so that next effect has the right input. */
|
||||
*input_tx = *output_tx;
|
||||
*output_tx = input_color_tx_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates bokeh texture.
|
||||
**/
|
||||
void DepthOfField::bokeh_lut_pass_sync(void)
|
||||
{
|
||||
const bool has_anisotropy = data_.bokeh_anisotropic_scale != float2(1.0f);
|
||||
if (has_anisotropy && (data_.bokeh_blades == 0.0)) {
|
||||
bokeh_gather_lut_tx_ = nullptr;
|
||||
bokeh_scatter_lut_tx_ = nullptr;
|
||||
bokeh_resolve_lut_tx_ = nullptr;
|
||||
bokeh_lut_ps_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
|
||||
int res[2] = {DOF_BOKEH_LUT_SIZE, DOF_BOKEH_LUT_SIZE};
|
||||
|
||||
DRW_PASS_CREATE(bokeh_lut_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_BOKEH_LUT);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, bokeh_lut_ps_);
|
||||
DRW_shgroup_uniform_block(grp, "dof_block", data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
|
||||
bokeh_gather_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RG16F, owner);
|
||||
bokeh_scatter_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
|
||||
bokeh_resolve_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
|
||||
|
||||
bokeh_lut_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(bokeh_gather_lut_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(bokeh_scatter_lut_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(bokeh_resolve_lut_tx_));
|
||||
}
|
||||
|
||||
void DepthOfField::bokeh_lut_pass_render(void)
|
||||
{
|
||||
if (bokeh_lut_ps_) {
|
||||
GPU_framebuffer_bind(bokeh_lut_fb_);
|
||||
DRW_draw_pass(bokeh_lut_ps_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs halfResColorBuffer and halfResCocBuffer.
|
||||
**/
|
||||
void DepthOfField::setup_pass_sync(void)
|
||||
{
|
||||
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
|
||||
uint res[2] = {divide_ceil_u(extent_[0], 2), divide_ceil_u(extent_[1], 2)};
|
||||
|
||||
DRW_PASS_CREATE(setup_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_SETUP);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, setup_ps_);
|
||||
|
||||
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &input_depth_tx_, no_filter);
|
||||
DRW_shgroup_uniform_block(grp, "dof_block", data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
|
||||
setup_color_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
|
||||
setup_coc_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RG16F, owner);
|
||||
|
||||
setup_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(setup_color_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(setup_coc_tx_));
|
||||
}
|
||||
|
||||
void DepthOfField::setup_pass_render(void)
|
||||
{
|
||||
GPU_framebuffer_bind(setup_fb_);
|
||||
DRW_draw_pass(setup_ps_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs min & max COC in each 8x8 half res pixel tiles (so 1/16th of full resolution).
|
||||
* Then dilates the min & max CoCs to cover maximum COC values.
|
||||
**/
|
||||
void DepthOfField::tiles_prepare_pass_sync(void)
|
||||
{
|
||||
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
|
||||
uint res[2] = {divide_ceil_u(extent_[0], DOF_TILE_DIVISOR),
|
||||
divide_ceil_u(extent_[1], DOF_TILE_DIVISOR)};
|
||||
/* WARNING: If you change this, make sure dof_tile_* GLSL constants can be properly encoded. */
|
||||
eGPUTextureFormat fg_tile_format = GPU_RGBA16F;
|
||||
eGPUTextureFormat bg_tile_format = GPU_R11F_G11F_B10F;
|
||||
|
||||
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
{
|
||||
DRW_PASS_CREATE(tiles_flatten_ps_, DRW_STATE_WRITE_COLOR);
|
||||
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_TILES_FLATTEN);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_flatten_ps_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
|
||||
tiles_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), fg_tile_format, owner);
|
||||
tiles_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), bg_tile_format, owner);
|
||||
|
||||
tiles_flatten_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(tiles_fg_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(tiles_bg_tx_));
|
||||
}
|
||||
{
|
||||
DRW_PASS_CREATE(tiles_dilate_minmax_ps_, DRW_STATE_WRITE_COLOR);
|
||||
DRW_PASS_CREATE(tiles_dilate_minabs_ps_, DRW_STATE_WRITE_COLOR);
|
||||
|
||||
for (int pass = 0; pass < 2; pass++) {
|
||||
DRWPass *drw_pass = (pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_;
|
||||
GPUShader *sh = inst_.shaders.static_shader_get((pass == 0) ? DOF_TILES_DILATE_MINMAX :
|
||||
DOF_TILES_DILATE_MINABS);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, drw_pass);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_fg_tx", &tiles_fg_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_bg_tx", &tiles_bg_tx_, no_filter);
|
||||
DRW_shgroup_uniform_bool(grp, "dilate_slight_focus", &tiles_dilate_slight_focus_, 1);
|
||||
DRW_shgroup_uniform_int(grp, "ring_count", &tiles_dilate_ring_count_, 1);
|
||||
DRW_shgroup_uniform_int(
|
||||
grp, "ring_width_multiplier", &tiles_dilate_ring_width_multiplier_, 1);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
|
||||
tiles_dilated_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), fg_tile_format, owner);
|
||||
tiles_dilated_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), bg_tile_format, owner);
|
||||
|
||||
tiles_dilate_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(tiles_dilated_fg_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(tiles_dilated_bg_tx_));
|
||||
}
|
||||
}
|
||||
|
||||
void DepthOfField::tiles_prepare_pass_render(void)
|
||||
{
|
||||
GPU_framebuffer_bind(tiles_flatten_fb_);
|
||||
DRW_draw_pass(tiles_flatten_ps_);
|
||||
|
||||
/* Run dilation twice. One for minmax and one for minabs. */
|
||||
for (int pass = 0; pass < 2; pass++) {
|
||||
/* Error introduced by gather center jittering. */
|
||||
const float error_multiplier = 1.0f + 1.0f / (DOF_GATHER_RING_COUNT + 0.5f);
|
||||
int dilation_end_radius = ceilf((fx_max_coc_ * error_multiplier) / DOF_TILE_DIVISOR);
|
||||
|
||||
/* This algorithm produce the exact dilation radius by dividing it in multiple passes. */
|
||||
int dilation_radius = 0;
|
||||
while (dilation_radius < dilation_end_radius) {
|
||||
/* Dilate slight focus only on first iteration. */
|
||||
tiles_dilate_slight_focus_ = (dilation_radius == 0) ? 1 : 0;
|
||||
|
||||
int remainder = dilation_end_radius - dilation_radius;
|
||||
/* Do not step over any unvisited tile. */
|
||||
int max_multiplier = dilation_radius + 1;
|
||||
|
||||
int ring_count = min_ii(DOF_DILATE_RING_COUNT, ceilf(remainder / (float)max_multiplier));
|
||||
int multiplier = min_ii(max_multiplier, floor(remainder / (float)ring_count));
|
||||
|
||||
dilation_radius += ring_count * multiplier;
|
||||
|
||||
tiles_dilate_ring_count_ = ring_count;
|
||||
tiles_dilate_ring_width_multiplier_ = multiplier;
|
||||
|
||||
GPU_framebuffer_bind(tiles_dilate_fb_);
|
||||
DRW_draw_pass((pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_);
|
||||
|
||||
Framebuffer::swap(tiles_dilate_fb_, tiles_flatten_fb_);
|
||||
SWAP(GPUTexture *, tiles_dilated_bg_tx_, tiles_bg_tx_);
|
||||
SWAP(GPUTexture *, tiles_dilated_fg_tx_, tiles_fg_tx_);
|
||||
}
|
||||
}
|
||||
/* Swap again so that final textures are tiles_dilated_*_tx_. */
|
||||
Framebuffer::swap(tiles_dilate_fb_, tiles_flatten_fb_);
|
||||
SWAP(GPUTexture *, tiles_dilated_bg_tx_, tiles_bg_tx_);
|
||||
SWAP(GPUTexture *, tiles_dilated_fg_tx_, tiles_fg_tx_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create mipmapped color & COC textures for gather passes.
|
||||
**/
|
||||
void DepthOfField::reduce_pass_sync(void)
|
||||
{
|
||||
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
|
||||
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
/* Divide by 2 because dof_fx_max_coc is in fullres CoC radius and the reduce
|
||||
* texture begins at half resolution. */
|
||||
float max_space_between_sample = fx_max_coc_ * 0.5f / DOF_GATHER_RING_COUNT;
|
||||
|
||||
int mip_count = max_ii(1, log2_ceil_u(max_space_between_sample));
|
||||
|
||||
reduce_steps_ = mip_count - 1;
|
||||
/* This ensure the mipmaps are aligned for the needed 4 mip levels.
|
||||
* Starts at 2 because already at half resolution. */
|
||||
int multiple = 2 << (mip_count - 1);
|
||||
int2 res = (int2(divide_ceil_u(extent_[0], multiple), divide_ceil_u(extent_[1], multiple)) *
|
||||
multiple) /
|
||||
2;
|
||||
|
||||
int2 quater_res = int2(divide_ceil_u(extent_[0], 4), divide_ceil_u(extent_[1], 4));
|
||||
|
||||
/* TODO(fclem): Make this dependent of the quality of the gather pass. */
|
||||
data_.scatter_coc_threshold = 4.0f;
|
||||
|
||||
/* Color needs to be signed format here. See note in shader for explanation. */
|
||||
/* Do not use texture pool because of needs mipmaps. */
|
||||
reduced_color_tx_.ensure_2d(GPU_RGBA16F, res, nullptr, mip_count);
|
||||
reduced_coc_tx_.ensure_2d(GPU_R16F, res, nullptr, mip_count);
|
||||
|
||||
{
|
||||
DRW_PASS_CREATE(reduce_downsample_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_DOWNSAMPLE);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_downsample_ps_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &setup_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
|
||||
reduce_downsample_tx_ = DRW_texture_pool_query_2d(UNPACK2(quater_res), GPU_RGBA16F, owner);
|
||||
|
||||
reduce_downsample_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(reduce_downsample_tx_));
|
||||
}
|
||||
{
|
||||
DRW_PASS_CREATE(reduce_copy_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_COPY);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_copy_ps_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &setup_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "downsampled_tx", reduce_downsample_tx_, no_filter);
|
||||
DRW_shgroup_uniform_block(grp, "dof_block", data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
|
||||
scatter_src_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R11F_G11F_B10F, owner);
|
||||
}
|
||||
{
|
||||
DRW_PASS_CREATE(reduce_recursive_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_RECURSIVE);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_recursive_ps_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
|
||||
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
|
||||
}
|
||||
|
||||
reduce_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(reduced_color_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(reduced_coc_tx_));
|
||||
|
||||
reduce_copy_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(reduced_color_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(reduced_coc_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(scatter_src_tx_));
|
||||
}
|
||||
|
||||
void DepthOfField::reduce_recusive(void *thunk, int UNUSED(level))
|
||||
{
|
||||
DepthOfField *dof = reinterpret_cast<DepthOfField *>(thunk);
|
||||
DRW_draw_pass(dof->reduce_recursive_ps_);
|
||||
}
|
||||
|
||||
void DepthOfField::reduce_pass_render(void)
|
||||
{
|
||||
GPU_framebuffer_bind(reduce_downsample_fb_);
|
||||
DRW_draw_pass(reduce_downsample_ps_);
|
||||
|
||||
/* First step is just a copy. */
|
||||
GPU_framebuffer_bind(reduce_copy_fb_);
|
||||
DRW_draw_pass(reduce_copy_ps_);
|
||||
|
||||
GPU_framebuffer_recursive_downsample(reduce_fb_, reduce_steps_, &reduce_recusive, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the gather & scatter convolution. For each pixels we gather multiple pixels in its
|
||||
* neighborhood depending on the min & max CoC tiles. We apply a median filter on the output.
|
||||
* We also scatter a sprite for very bright pixels for high quality bokeh.
|
||||
**/
|
||||
void DepthOfField::convolve_pass_sync(void)
|
||||
{
|
||||
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
eGPUSamplerState with_filter = (GPU_SAMPLER_MIPMAP | GPU_SAMPLER_FILTER);
|
||||
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
|
||||
uint res[2] = {divide_ceil_u(extent_[0], 2), divide_ceil_u(extent_[1], 2)};
|
||||
int input_size[2];
|
||||
GPU_texture_get_mipmap_size(reduced_color_tx_, 0, input_size);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
data_.gather_uv_fac[i] = 1.0f / (float)input_size[i];
|
||||
data_.texel_size[i] = 1.0f / res[i];
|
||||
}
|
||||
|
||||
/* Reuse textures from the setup pass. */
|
||||
/* NOTE: We could use the texture pool do that for us but it does not track
|
||||
* usage and it might backfire (it does in practice). */
|
||||
/* Since it is only used for scatter, and foreground is processed before background, we can
|
||||
* reuse the occlusion_tx for both field. */
|
||||
occlusion_tx_ = setup_coc_tx_;
|
||||
|
||||
{
|
||||
DRW_PASS_CREATE(gather_holefill_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_GATHER_HOLEFILL);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_holefill_ps_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
|
||||
DRW_shgroup_uniform_block(grp, "dof_block", data_);
|
||||
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
/* Reuse textures from the setup pass. */
|
||||
/* NOTE: We could use the texture pool do that for us but it does not track
|
||||
* usage and it might backfire (it does in practice). */
|
||||
color_holefill_tx_ = setup_color_tx_;
|
||||
weight_holefill_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
|
||||
|
||||
gather_holefill_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(color_holefill_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(weight_holefill_tx_));
|
||||
}
|
||||
{
|
||||
DRW_PASS_CREATE(gather_fg_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(
|
||||
bokeh_gather_lut_tx_ ? DOF_GATHER_FOREGROUND_LUT : DOF_GATHER_FOREGROUND);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_fg_ps_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
|
||||
if (bokeh_gather_lut_tx_) {
|
||||
DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_gather_lut_tx_);
|
||||
}
|
||||
DRW_shgroup_uniform_block(grp, "dof_block", data_);
|
||||
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
color_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
|
||||
weight_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
|
||||
}
|
||||
{
|
||||
DRW_PASS_CREATE(gather_bg_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(
|
||||
bokeh_gather_lut_tx_ ? DOF_GATHER_BACKGROUND_LUT : DOF_GATHER_BACKGROUND);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_bg_ps_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
|
||||
if (bokeh_gather_lut_tx_) {
|
||||
DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_gather_lut_tx_);
|
||||
}
|
||||
DRW_shgroup_uniform_block(grp, "dof_block", data_);
|
||||
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
color_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
|
||||
weight_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
|
||||
}
|
||||
|
||||
/* NOTE: First target is holefill texture so we can use the median filter on it and save some
|
||||
* texture memory. Both field use the same framebuffer. */
|
||||
gather_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(color_holefill_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(weight_holefill_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(occlusion_tx_));
|
||||
{
|
||||
/**
|
||||
* Filter an input buffer using a median filter to reduce noise.
|
||||
* NOTE: We use the holefill texture as our input to reduce memory usage.
|
||||
* Thus, the holefill pass cannot be filtered.
|
||||
**/
|
||||
DRW_PASS_CREATE(filter_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(DOF_FILTER);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_ps_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_tx", color_holefill_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "weight_tx", weight_holefill_tx_, no_filter);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
filter_fg_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(color_fg_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(weight_fg_tx_));
|
||||
|
||||
filter_bg_fb_.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(color_bg_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(weight_bg_tx_));
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the Scatter convolution. A sprite is emitted for every 4 pixels but is only expanded
|
||||
* if the pixels are bright enough to be scattered.
|
||||
**/
|
||||
data_.scatter_sprite_per_row = input_size[0] / 2;
|
||||
int sprite_count = data_.scatter_sprite_per_row * (input_size[1] / 2);
|
||||
{
|
||||
DRW_PASS_CREATE(scatter_fg_ps_, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(
|
||||
bokeh_gather_lut_tx_ ? DOF_SCATTER_FOREGROUND_LUT : DOF_SCATTER_FOREGROUND);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, scatter_fg_ps_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_tx", scatter_src_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture(grp, "occlusion_tx", occlusion_tx_);
|
||||
if (bokeh_scatter_lut_tx_) {
|
||||
DRW_shgroup_uniform_texture(grp, "bokehLut", bokeh_scatter_lut_tx_);
|
||||
}
|
||||
DRW_shgroup_uniform_block(grp, "dof_block", data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, sprite_count);
|
||||
|
||||
scatter_fg_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_fg_tx_));
|
||||
}
|
||||
{
|
||||
DRW_PASS_CREATE(scatter_bg_ps_, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(
|
||||
bokeh_gather_lut_tx_ ? DOF_SCATTER_BACKGROUND_LUT : DOF_SCATTER_BACKGROUND);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, scatter_bg_ps_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_tx", scatter_src_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture(grp, "occlusion_tx", occlusion_tx_);
|
||||
if (bokeh_scatter_lut_tx_) {
|
||||
DRW_shgroup_uniform_texture(grp, "bokehLut", bokeh_scatter_lut_tx_);
|
||||
}
|
||||
DRW_shgroup_uniform_block(grp, "dof_block", data_);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, sprite_count);
|
||||
|
||||
scatter_bg_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_bg_tx_));
|
||||
}
|
||||
}
|
||||
|
||||
void DepthOfField::convolve_pass_render(void)
|
||||
{
|
||||
DRW_stats_group_start("Foreground convolution");
|
||||
GPU_framebuffer_bind(gather_fb_);
|
||||
DRW_draw_pass(gather_fg_ps_);
|
||||
|
||||
GPU_framebuffer_bind(filter_fg_fb_);
|
||||
DRW_draw_pass(filter_ps_);
|
||||
|
||||
GPU_framebuffer_bind(scatter_fg_fb_);
|
||||
DRW_draw_pass(scatter_fg_ps_);
|
||||
DRW_stats_group_end();
|
||||
|
||||
DRW_stats_group_start("Background convolution");
|
||||
GPU_framebuffer_bind(gather_fb_);
|
||||
DRW_draw_pass(gather_bg_ps_);
|
||||
|
||||
GPU_framebuffer_bind(filter_bg_fb_);
|
||||
DRW_draw_pass(filter_ps_);
|
||||
|
||||
GPU_framebuffer_bind(scatter_bg_fb_);
|
||||
DRW_draw_pass(scatter_bg_ps_);
|
||||
DRW_stats_group_end();
|
||||
|
||||
DRW_stats_group_start("Background convolution");
|
||||
/* Hole-fill convolution. */
|
||||
GPU_framebuffer_bind(gather_holefill_fb_);
|
||||
DRW_draw_pass(gather_holefill_ps_);
|
||||
/* NOTE: We do not filter the hole-fill pass as we use it as out filter input
|
||||
* buffer. Also effect is likely to not be noticeable. */
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recombine the result of the foreground and background processing.
|
||||
* Also perform a slight out of focus gather to improve geometric continuity.
|
||||
**/
|
||||
void DepthOfField::resolve_pass_sync(void)
|
||||
{
|
||||
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
eGPUSamplerState with_filter = GPU_SAMPLER_FILTER;
|
||||
eShaderType sh_type = (bokeh_resolve_lut_tx_) ?
|
||||
(do_hq_slight_focus_ ? DOF_RESOLVE_LUT_HQ : DOF_RESOLVE_LUT) :
|
||||
(do_hq_slight_focus_ ? DOF_RESOLVE_HQ : DOF_RESOLVE);
|
||||
|
||||
DRW_PASS_CREATE(resolve_ps_, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(sh_type);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &input_depth_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_bg_tx", color_bg_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_fg_tx", color_fg_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "color_holefill_tx", color_holefill_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
|
||||
DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "weight_bg_tx", weight_bg_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "weight_fg_tx", weight_fg_tx_, with_filter);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "weight_holefill_tx", weight_holefill_tx_, with_filter);
|
||||
DRW_shgroup_uniform_block(grp, "dof_block", data_);
|
||||
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
|
||||
if (bokeh_resolve_lut_tx_) {
|
||||
DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_resolve_lut_tx_);
|
||||
}
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
}
|
||||
|
||||
void DepthOfField::resolve_pass_render(GPUTexture *output_tx)
|
||||
{
|
||||
resolve_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(output_tx));
|
||||
|
||||
GPU_framebuffer_bind(resolve_fb_);
|
||||
DRW_draw_pass(resolve_ps_);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
176
source/blender/draw/engines/eevee/eevee_depth_of_field.hh
Normal file
176
source/blender/draw/engines/eevee/eevee_depth_of_field.hh
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Depth of field post process effect.
|
||||
*
|
||||
* There are 2 methods to achieve this effect.
|
||||
* - The first uses projection matrix offsetting and sample accumulation to give
|
||||
* reference quality depth of field. But this needs many samples to hide the
|
||||
* under-sampling.
|
||||
* - The second one is a post-processing based one. It follows the
|
||||
* implementation described in the presentation "Life of a Bokeh - Siggraph
|
||||
* 2018" from Guillaume Abadie. There are some difference with our actual
|
||||
* implementation that prioritize quality.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "eevee_shader_shared.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Depth of field
|
||||
* \{ */
|
||||
|
||||
class DepthOfField {
|
||||
private:
|
||||
class Instance &inst_;
|
||||
|
||||
DepthOfFieldDataBuf data_;
|
||||
|
||||
/** Textures from pool. Not owned. */
|
||||
GPUTexture *bokeh_gather_lut_tx_ = nullptr;
|
||||
GPUTexture *bokeh_resolve_lut_tx_ = nullptr;
|
||||
GPUTexture *bokeh_scatter_lut_tx_ = nullptr;
|
||||
GPUTexture *color_bg_tx_ = nullptr;
|
||||
GPUTexture *color_fg_tx_ = nullptr;
|
||||
GPUTexture *color_holefill_tx_ = nullptr;
|
||||
GPUTexture *occlusion_tx_ = nullptr;
|
||||
GPUTexture *reduce_downsample_tx_ = nullptr;
|
||||
GPUTexture *scatter_src_tx_ = nullptr;
|
||||
GPUTexture *setup_coc_tx_ = nullptr;
|
||||
GPUTexture *setup_color_tx_ = nullptr;
|
||||
GPUTexture *tiles_bg_tx_ = nullptr;
|
||||
GPUTexture *tiles_fg_tx_ = nullptr;
|
||||
GPUTexture *tiles_dilated_bg_tx_ = nullptr;
|
||||
GPUTexture *tiles_dilated_fg_tx_ = nullptr;
|
||||
GPUTexture *weight_bg_tx_ = nullptr;
|
||||
GPUTexture *weight_fg_tx_ = nullptr;
|
||||
GPUTexture *weight_holefill_tx_ = nullptr;
|
||||
/** Allocated textures. Owned. */
|
||||
Texture reduced_coc_tx_ = {"dof_reduced_coc"};
|
||||
Texture reduced_color_tx_ = {"dof_reduced_color"};
|
||||
/** Input texture. Not owned. */
|
||||
GPUTexture *input_color_tx_;
|
||||
GPUTexture *input_depth_tx_;
|
||||
/** Passes. Not owned. */
|
||||
DRWPass *bokeh_lut_ps_ = nullptr;
|
||||
DRWPass *gather_bg_ps_ = nullptr;
|
||||
DRWPass *gather_fg_ps_ = nullptr;
|
||||
DRWPass *filter_ps_ = nullptr;
|
||||
DRWPass *gather_holefill_ps_ = nullptr;
|
||||
DRWPass *reduce_copy_ps_ = nullptr;
|
||||
DRWPass *reduce_downsample_ps_ = nullptr;
|
||||
DRWPass *reduce_recursive_ps_ = nullptr;
|
||||
DRWPass *resolve_ps_ = nullptr;
|
||||
DRWPass *scatter_bg_ps_ = nullptr;
|
||||
DRWPass *scatter_fg_ps_ = nullptr;
|
||||
DRWPass *setup_ps_ = nullptr;
|
||||
DRWPass *tiles_dilate_minabs_ps_ = nullptr;
|
||||
DRWPass *tiles_dilate_minmax_ps_ = nullptr;
|
||||
DRWPass *tiles_flatten_ps_ = nullptr;
|
||||
/** Framebuffers. Owned. */
|
||||
Framebuffer bokeh_lut_fb_ = {"bokeh_lut_fb_"};
|
||||
Framebuffer filter_bg_fb_ = {"filter_bg_fb_"};
|
||||
Framebuffer filter_fg_fb_ = {"filter_fg_fb_"};
|
||||
Framebuffer gather_fb_ = {"gather_fb_"};
|
||||
Framebuffer gather_filter_bg_fb_ = {"gather_filter_bg_fb_"};
|
||||
Framebuffer gather_holefill_fb_ = {"gather_holefill_fb_"};
|
||||
Framebuffer reduce_copy_fb_ = {"reduce_copy_fb_"};
|
||||
Framebuffer reduce_downsample_fb_ = {"reduce_downsample_fb_"};
|
||||
Framebuffer reduce_fb_ = {"reduce_fb_"};
|
||||
Framebuffer resolve_fb_ = {"resolve_fb_"};
|
||||
Framebuffer scatter_bg_fb_ = {"scatter_bg_fb_"};
|
||||
Framebuffer scatter_fg_fb_ = {"scatter_fg_fb_"};
|
||||
Framebuffer setup_fb_ = {"setup_fb_"};
|
||||
Framebuffer tiles_dilate_fb_ = {"tiles_dilate_fb_"};
|
||||
Framebuffer tiles_flatten_fb_ = {"tiles_flatten_fb_"};
|
||||
|
||||
/** Scene settings that are immutable. */
|
||||
float user_overblur_;
|
||||
float fx_max_coc_;
|
||||
/** Use Hiqh Quality (expensive) in-focus gather pass. */
|
||||
bool do_hq_slight_focus_;
|
||||
/** Use jittered depth of field where we randomize camera location. */
|
||||
bool do_jitter_;
|
||||
|
||||
/** Circle of Confusion radius for FX DoF passes. Is in view X direction in [0..1] range. */
|
||||
float fx_radius_;
|
||||
/** Circle of Confusion radius for jittered DoF. Is in view X direction in [0..1] range. */
|
||||
float jitter_radius_;
|
||||
/** Focus distance in view space. */
|
||||
float focus_distance_;
|
||||
/** Extent of the input buffer. */
|
||||
int2 extent_;
|
||||
|
||||
/** Tile dilation uniforms. */
|
||||
int tiles_dilate_slight_focus_;
|
||||
int tiles_dilate_ring_count_;
|
||||
int tiles_dilate_ring_width_multiplier_;
|
||||
|
||||
/** Reduce pass info. */
|
||||
int reduce_steps_;
|
||||
|
||||
/** Static string pointer. Used as debug name and as UUID for texture pool. */
|
||||
StringRefNull view_name_;
|
||||
|
||||
public:
|
||||
DepthOfField(Instance &inst, StringRefNull view_name) : inst_(inst), view_name_(view_name){};
|
||||
~DepthOfField(){};
|
||||
|
||||
void init();
|
||||
|
||||
void sync(const float4x4 winmat, int2 input_extent);
|
||||
|
||||
/** Apply Depth Of Field jittering to the view and projection matrices.. */
|
||||
void jitter_apply(float4x4 winmat, float4x4 viewmat);
|
||||
|
||||
/** Will swap input and output texture if rendering happens. The actual output of this function
|
||||
* is in intput_tx. */
|
||||
void render(GPUTexture *depth_tx, GPUTexture **input_tx, GPUTexture **output_tx);
|
||||
|
||||
private:
|
||||
void bokeh_lut_pass_sync(void);
|
||||
void bokeh_lut_pass_render(void);
|
||||
|
||||
void setup_pass_sync(void);
|
||||
void setup_pass_render(void);
|
||||
|
||||
void tiles_prepare_pass_sync(void);
|
||||
void tiles_prepare_pass_render(void);
|
||||
|
||||
static void reduce_recusive(void *thunk, int level);
|
||||
void reduce_pass_sync(void);
|
||||
void reduce_pass_render(void);
|
||||
|
||||
void convolve_pass_sync(void);
|
||||
void convolve_pass_render(void);
|
||||
|
||||
void resolve_pass_sync(void);
|
||||
void resolve_pass_render(GPUTexture *output_tx);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
@@ -1,524 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*
|
||||
* Gather all screen space effects technique such as Bloom, Motion Blur, DoF, SSAO, SSR, ...
|
||||
*/
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BKE_global.h" /* for G.debug_value */
|
||||
|
||||
#include "GPU_capabilities.h"
|
||||
#include "GPU_platform.h"
|
||||
#include "GPU_state.h"
|
||||
#include "GPU_texture.h"
|
||||
#include "eevee_private.h"
|
||||
|
||||
static struct {
|
||||
/* These are just references, not actually allocated */
|
||||
struct GPUTexture *depth_src;
|
||||
struct GPUTexture *color_src;
|
||||
|
||||
int depth_src_layer;
|
||||
/* Size can be vec3. But we only use 2 components in the shader. */
|
||||
float texel_size[2];
|
||||
} e_data = {NULL}; /* Engine data */
|
||||
|
||||
#define SETUP_BUFFER(tex, fb, fb_color) \
|
||||
{ \
|
||||
eGPUTextureFormat format = (DRW_state_is_scene_render()) ? GPU_RGBA32F : GPU_RGBA16F; \
|
||||
DRW_texture_ensure_fullscreen_2d(&tex, format, DRW_TEX_FILTER); \
|
||||
GPU_framebuffer_ensure_config(&fb, \
|
||||
{ \
|
||||
GPU_ATTACHMENT_TEXTURE(dtxl->depth), \
|
||||
GPU_ATTACHMENT_TEXTURE(tex), \
|
||||
}); \
|
||||
GPU_framebuffer_ensure_config(&fb_color, \
|
||||
{ \
|
||||
GPU_ATTACHMENT_NONE, \
|
||||
GPU_ATTACHMENT_TEXTURE(tex), \
|
||||
}); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
#define CLEANUP_BUFFER(tex, fb, fb_color) \
|
||||
{ \
|
||||
/* Cleanup to release memory */ \
|
||||
DRW_TEXTURE_FREE_SAFE(tex); \
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fb); \
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fb_color); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
void EEVEE_effects_init(EEVEE_ViewLayerData *sldata,
|
||||
EEVEE_Data *vedata,
|
||||
Object *camera,
|
||||
const bool minimal)
|
||||
{
|
||||
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_EffectsInfo *effects;
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
const int size_fs[2] = {(int)viewport_size[0], (int)viewport_size[1]};
|
||||
|
||||
if (!stl->effects) {
|
||||
stl->effects = MEM_callocN(sizeof(EEVEE_EffectsInfo), "EEVEE_EffectsInfo");
|
||||
stl->effects->taa_render_sample = 1;
|
||||
}
|
||||
|
||||
/* WORKAROUND: EEVEE_lookdev_init can reset TAA and needs a stl->effect.
|
||||
* So putting this before EEVEE_temporal_sampling_init for now. */
|
||||
EEVEE_lookdev_init(vedata);
|
||||
|
||||
effects = stl->effects;
|
||||
|
||||
int div = 1 << MAX_SCREEN_BUFFERS_LOD_LEVEL;
|
||||
effects->hiz_size[0] = divide_ceil_u(size_fs[0], div) * div;
|
||||
effects->hiz_size[1] = divide_ceil_u(size_fs[1], div) * div;
|
||||
|
||||
effects->enabled_effects = 0;
|
||||
effects->enabled_effects |= (G.debug_value == 9) ? EFFECT_VELOCITY_BUFFER : 0;
|
||||
effects->enabled_effects |= EEVEE_motion_blur_init(sldata, vedata);
|
||||
effects->enabled_effects |= EEVEE_bloom_init(sldata, vedata);
|
||||
effects->enabled_effects |= EEVEE_depth_of_field_init(sldata, vedata, camera);
|
||||
effects->enabled_effects |= EEVEE_temporal_sampling_init(sldata, vedata);
|
||||
effects->enabled_effects |= EEVEE_occlusion_init(sldata, vedata);
|
||||
effects->enabled_effects |= EEVEE_screen_raytrace_init(sldata, vedata);
|
||||
|
||||
/* Update matrices here because EEVEE_screen_raytrace_init can have reset the
|
||||
* taa_current_sample. (See T66811) */
|
||||
EEVEE_temporal_sampling_update_matrices(vedata);
|
||||
|
||||
EEVEE_volumes_init(sldata, vedata);
|
||||
EEVEE_subsurface_init(sldata, vedata);
|
||||
|
||||
/* Force normal buffer creation. */
|
||||
if (!minimal && (stl->g_data->render_passes & EEVEE_RENDER_PASS_NORMAL) != 0) {
|
||||
effects->enabled_effects |= EFFECT_NORMAL_BUFFER;
|
||||
}
|
||||
|
||||
/**
|
||||
* MinMax Pyramid
|
||||
*/
|
||||
|
||||
if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
/* Intel gpu seems to have problem rendering to only depth hiz_format */
|
||||
DRW_texture_ensure_2d(&txl->maxzbuffer, UNPACK2(effects->hiz_size), GPU_R32F, DRW_TEX_MIPMAP);
|
||||
GPU_framebuffer_ensure_config(&fbl->maxzbuffer_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(txl->maxzbuffer),
|
||||
});
|
||||
}
|
||||
else {
|
||||
DRW_texture_ensure_2d(
|
||||
&txl->maxzbuffer, UNPACK2(effects->hiz_size), GPU_DEPTH_COMPONENT24, DRW_TEX_MIPMAP);
|
||||
GPU_framebuffer_ensure_config(&fbl->maxzbuffer_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_TEXTURE(txl->maxzbuffer),
|
||||
GPU_ATTACHMENT_NONE,
|
||||
});
|
||||
}
|
||||
|
||||
if (fbl->downsample_fb == NULL) {
|
||||
fbl->downsample_fb = GPU_framebuffer_create("downsample_fb");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute hiZ texel alignment.
|
||||
*/
|
||||
common_data->hiz_uv_scale[0] = viewport_size[0] / effects->hiz_size[0];
|
||||
common_data->hiz_uv_scale[1] = viewport_size[1] / effects->hiz_size[1];
|
||||
|
||||
/* Compute pixel size. Size is multiplied by 2 because it is applied in NDC [-1..1] range. */
|
||||
sldata->common_data.ssr_pixelsize[0] = 2.0f / size_fs[0];
|
||||
sldata->common_data.ssr_pixelsize[1] = 2.0f / size_fs[1];
|
||||
|
||||
/**
|
||||
* Color buffer with correct down-sampling alignment.
|
||||
* Used for SSReflections & SSRefractions.
|
||||
*/
|
||||
if ((effects->enabled_effects & EFFECT_RADIANCE_BUFFER) != 0) {
|
||||
DRW_texture_ensure_2d(&txl->filtered_radiance,
|
||||
UNPACK2(effects->hiz_size),
|
||||
GPU_R11F_G11F_B10F,
|
||||
DRW_TEX_FILTER | DRW_TEX_MIPMAP);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->radiance_filtered_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(txl->filtered_radiance),
|
||||
});
|
||||
}
|
||||
else {
|
||||
DRW_TEXTURE_FREE_SAFE(txl->filtered_radiance);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fbl->radiance_filtered_fb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal buffer for deferred passes.
|
||||
*/
|
||||
if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0) {
|
||||
effects->ssr_normal_input = DRW_texture_pool_query_2d(
|
||||
size_fs[0], size_fs[1], GPU_RG16, &draw_engine_eevee_type);
|
||||
|
||||
GPU_framebuffer_texture_attach(fbl->main_fb, effects->ssr_normal_input, 1, 0);
|
||||
}
|
||||
else {
|
||||
effects->ssr_normal_input = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Motion vector buffer for correct TAA / motion blur.
|
||||
*/
|
||||
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
|
||||
effects->velocity_tx = DRW_texture_pool_query_2d(
|
||||
size_fs[0], size_fs[1], GPU_RGBA16, &draw_engine_eevee_type);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->velocity_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_TEXTURE(dtxl->depth),
|
||||
GPU_ATTACHMENT_TEXTURE(effects->velocity_tx),
|
||||
});
|
||||
|
||||
GPU_framebuffer_ensure_config(
|
||||
&fbl->velocity_resolve_fb,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->velocity_tx)});
|
||||
}
|
||||
else {
|
||||
effects->velocity_tx = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup depth double buffer.
|
||||
*/
|
||||
if ((effects->enabled_effects & EFFECT_DEPTH_DOUBLE_BUFFER) != 0) {
|
||||
DRW_texture_ensure_fullscreen_2d(&txl->depth_double_buffer, GPU_DEPTH24_STENCIL8, 0);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->double_buffer_depth_fb,
|
||||
{GPU_ATTACHMENT_TEXTURE(txl->depth_double_buffer)});
|
||||
}
|
||||
else {
|
||||
/* Cleanup to release memory */
|
||||
DRW_TEXTURE_FREE_SAFE(txl->depth_double_buffer);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(fbl->double_buffer_depth_fb);
|
||||
}
|
||||
|
||||
if ((effects->enabled_effects & (EFFECT_TAA | EFFECT_TAA_REPROJECT)) != 0) {
|
||||
SETUP_BUFFER(txl->taa_history, fbl->taa_history_fb, fbl->taa_history_color_fb);
|
||||
}
|
||||
else {
|
||||
CLEANUP_BUFFER(txl->taa_history, fbl->taa_history_fb, fbl->taa_history_color_fb);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
DRWState downsample_write = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS;
|
||||
DRWShadingGroup *grp;
|
||||
|
||||
/* Intel gpu seems to have problem rendering to only depth format.
|
||||
* Use color texture instead. */
|
||||
if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
downsample_write = DRW_STATE_WRITE_COLOR;
|
||||
}
|
||||
|
||||
struct GPUBatch *quad = DRW_cache_fullscreen_quad_get();
|
||||
|
||||
if (effects->enabled_effects & EFFECT_RADIANCE_BUFFER) {
|
||||
DRW_PASS_CREATE(psl->color_copy_ps, DRW_STATE_WRITE_COLOR);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_color_copy_sh_get(), psl->color_copy_ps);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "source", &e_data.color_src, GPU_SAMPLER_DEFAULT);
|
||||
DRW_shgroup_uniform_float(grp, "fireflyFactor", &sldata->common_data.ssr_firefly_fac, 1);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
|
||||
DRW_PASS_CREATE(psl->color_downsample_ps, DRW_STATE_WRITE_COLOR);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_downsample_sh_get(), psl->color_downsample_ps);
|
||||
DRW_shgroup_uniform_texture_ex(grp, "source", txl->filtered_radiance, GPU_SAMPLER_FILTER);
|
||||
DRW_shgroup_uniform_vec2(grp, "texelSize", e_data.texel_size, 1);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
}
|
||||
|
||||
{
|
||||
DRW_PASS_CREATE(psl->color_downsample_cube_ps, DRW_STATE_WRITE_COLOR);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_downsample_cube_sh_get(),
|
||||
psl->color_downsample_cube_ps);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "source", &e_data.color_src);
|
||||
DRW_shgroup_uniform_float(grp, "texelSize", e_data.texel_size, 1);
|
||||
DRW_shgroup_uniform_int_copy(grp, "Layer", 0);
|
||||
DRW_shgroup_call_instances(grp, NULL, quad, 6);
|
||||
}
|
||||
|
||||
{
|
||||
/* Perform min/max down-sample. */
|
||||
DRW_PASS_CREATE(psl->maxz_downlevel_ps, downsample_write);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_maxz_downlevel_sh_get(), psl->maxz_downlevel_ps);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &txl->maxzbuffer, GPU_SAMPLER_DEFAULT);
|
||||
DRW_shgroup_uniform_vec2(grp, "texelSize", e_data.texel_size, 1);
|
||||
DRW_shgroup_call(grp, quad, NULL);
|
||||
|
||||
/* Copy depth buffer to top level of HiZ */
|
||||
DRW_PASS_CREATE(psl->maxz_copydepth_ps, downsample_write);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_maxz_copydepth_sh_get(), psl->maxz_copydepth_ps);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &e_data.depth_src, GPU_SAMPLER_DEFAULT);
|
||||
DRW_shgroup_call(grp, quad, NULL);
|
||||
|
||||
DRW_PASS_CREATE(psl->maxz_copydepth_layer_ps, downsample_write);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_effect_maxz_copydepth_layer_sh_get(),
|
||||
psl->maxz_copydepth_layer_ps);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &e_data.depth_src, GPU_SAMPLER_DEFAULT);
|
||||
DRW_shgroup_uniform_int(grp, "depthLayer", &e_data.depth_src_layer, 1);
|
||||
DRW_shgroup_call(grp, quad, NULL);
|
||||
}
|
||||
|
||||
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
|
||||
EEVEE_MotionBlurData *mb_data = &effects->motion_blur;
|
||||
|
||||
/* This pass compute camera motions to the non moving objects. */
|
||||
DRW_PASS_CREATE(psl->velocity_resolve, DRW_STATE_WRITE_COLOR);
|
||||
grp = DRW_shgroup_create(EEVEE_shaders_velocity_resolve_sh_get(), psl->velocity_resolve);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
|
||||
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
|
||||
|
||||
DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat);
|
||||
DRW_shgroup_uniform_mat4(grp, "currViewProjMatrixInv", mb_data->camera[MB_CURR].persinv);
|
||||
DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat);
|
||||
DRW_shgroup_call(grp, quad, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_effects_draw_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_EffectsInfo *effects = vedata->stl->effects;
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
/**
|
||||
* Setup double buffer so we can access last frame as it was before post processes.
|
||||
*/
|
||||
if ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0) {
|
||||
SETUP_BUFFER(txl->color_double_buffer, fbl->double_buffer_fb, fbl->double_buffer_color_fb);
|
||||
}
|
||||
else {
|
||||
CLEANUP_BUFFER(txl->color_double_buffer, fbl->double_buffer_fb, fbl->double_buffer_color_fb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ping Pong buffer
|
||||
*/
|
||||
if ((effects->enabled_effects & EFFECT_POST_BUFFER) != 0) {
|
||||
SETUP_BUFFER(txl->color_post, fbl->effect_fb, fbl->effect_color_fb);
|
||||
}
|
||||
else {
|
||||
CLEANUP_BUFFER(txl->color_post, fbl->effect_fb, fbl->effect_color_fb);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 /* Not required for now */
|
||||
static void min_downsample_cb(void *vedata, int UNUSED(level))
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
DRW_draw_pass(psl->minz_downlevel_ps);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void max_downsample_cb(void *vedata, int level)
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl;
|
||||
int texture_size[3];
|
||||
GPU_texture_get_mipmap_size(txl->maxzbuffer, level - 1, texture_size);
|
||||
e_data.texel_size[0] = 1.0f / texture_size[0];
|
||||
e_data.texel_size[1] = 1.0f / texture_size[1];
|
||||
DRW_draw_pass(psl->maxz_downlevel_ps);
|
||||
}
|
||||
|
||||
static void simple_downsample_cube_cb(void *vedata, int level)
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
e_data.texel_size[0] = (float)(1 << level) / (float)GPU_texture_width(e_data.color_src);
|
||||
e_data.texel_size[1] = e_data.texel_size[0];
|
||||
DRW_draw_pass(psl->color_downsample_cube_ps);
|
||||
}
|
||||
|
||||
void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, GPUTexture *depth_src, int layer)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
|
||||
e_data.depth_src = depth_src;
|
||||
e_data.depth_src_layer = layer;
|
||||
|
||||
DRW_stats_group_start("Max buffer");
|
||||
/* Copy depth buffer to max texture top level */
|
||||
GPU_framebuffer_bind(fbl->maxzbuffer_fb);
|
||||
if (layer >= 0) {
|
||||
DRW_draw_pass(psl->maxz_copydepth_layer_ps);
|
||||
}
|
||||
else {
|
||||
DRW_draw_pass(psl->maxz_copydepth_ps);
|
||||
}
|
||||
/* Create lower levels */
|
||||
GPU_framebuffer_recursive_downsample(
|
||||
fbl->maxzbuffer_fb, MAX_SCREEN_BUFFERS_LOD_LEVEL, &max_downsample_cb, vedata);
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* Restore */
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
|
||||
if (GPU_mip_render_workaround() ||
|
||||
GPU_type_matches(GPU_DEVICE_INTEL_UHD, GPU_OS_WIN, GPU_DRIVER_ANY)) {
|
||||
/* Fix dot corruption on intel HD5XX/HD6XX series. */
|
||||
GPU_flush();
|
||||
}
|
||||
}
|
||||
|
||||
static void downsample_radiance_cb(void *vedata, int level)
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl;
|
||||
int texture_size[3];
|
||||
GPU_texture_get_mipmap_size(txl->filtered_radiance, level - 1, texture_size);
|
||||
e_data.texel_size[0] = 1.0f / texture_size[0];
|
||||
e_data.texel_size[1] = 1.0f / texture_size[1];
|
||||
DRW_draw_pass(psl->color_downsample_ps);
|
||||
}
|
||||
|
||||
void EEVEE_effects_downsample_radiance_buffer(EEVEE_Data *vedata, GPUTexture *texture_src)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
|
||||
e_data.color_src = texture_src;
|
||||
DRW_stats_group_start("Downsample Radiance");
|
||||
|
||||
GPU_framebuffer_bind(fbl->radiance_filtered_fb);
|
||||
DRW_draw_pass(psl->color_copy_ps);
|
||||
|
||||
GPU_framebuffer_recursive_downsample(
|
||||
fbl->radiance_filtered_fb, MAX_SCREEN_BUFFERS_LOD_LEVEL, &downsample_radiance_cb, vedata);
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, GPUTexture *texture_src, int level)
|
||||
{
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
e_data.color_src = texture_src;
|
||||
|
||||
/* Create lower levels */
|
||||
DRW_stats_group_start("Downsample Cube buffer");
|
||||
GPU_framebuffer_texture_attach(fbl->downsample_fb, texture_src, 0, 0);
|
||||
GPU_framebuffer_recursive_downsample(
|
||||
fbl->downsample_fb, level, &simple_downsample_cube_cb, vedata);
|
||||
GPU_framebuffer_texture_detach(fbl->downsample_fb, texture_src);
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
|
||||
static void EEVEE_velocity_resolve(EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
e_data.depth_src = dtxl->depth;
|
||||
|
||||
GPU_framebuffer_bind(fbl->velocity_resolve_fb);
|
||||
DRW_draw_pass(psl->velocity_resolve);
|
||||
|
||||
if (psl->velocity_object) {
|
||||
GPU_framebuffer_bind(fbl->velocity_fb);
|
||||
DRW_draw_pass(psl->velocity_object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
|
||||
/* only once per frame after the first post process */
|
||||
effects->swap_double_buffer = ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0);
|
||||
|
||||
/* Init pointers */
|
||||
effects->source_buffer = txl->color; /* latest updated texture */
|
||||
effects->target_buffer = fbl->effect_color_fb; /* next target to render to */
|
||||
|
||||
/* Post process stack (order matters) */
|
||||
EEVEE_velocity_resolve(vedata);
|
||||
EEVEE_motion_blur_draw(vedata);
|
||||
EEVEE_depth_of_field_draw(vedata);
|
||||
|
||||
/* NOTE: Lookdev drawing happens before TAA but after
|
||||
* motion blur and DOF to avoid distortions.
|
||||
* Velocity resolve use a hack to exclude lookdev
|
||||
* spheres from creating shimmering re-projection vectors. */
|
||||
EEVEE_lookdev_draw(vedata);
|
||||
|
||||
EEVEE_temporal_sampling_draw(vedata);
|
||||
EEVEE_bloom_draw(vedata);
|
||||
|
||||
/* Post effect render passes are done here just after the drawing of the effects and just before
|
||||
* the swapping of the buffers. */
|
||||
EEVEE_renderpasses_output_accumulate(sldata, vedata, true);
|
||||
|
||||
/* Save the final texture and frame-buffer for final transformation or read. */
|
||||
effects->final_tx = effects->source_buffer;
|
||||
effects->final_fb = (effects->target_buffer != fbl->main_color_fb) ? fbl->main_fb :
|
||||
fbl->effect_fb;
|
||||
if ((effects->enabled_effects & EFFECT_TAA) && (effects->source_buffer == txl->taa_history)) {
|
||||
effects->final_fb = fbl->taa_history_fb;
|
||||
}
|
||||
|
||||
/* If no post processes is enabled, buffers are still not swapped, do it now. */
|
||||
SWAP_DOUBLE_BUFFERS();
|
||||
|
||||
if (!stl->g_data->valid_double_buffer &&
|
||||
((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0) &&
|
||||
(DRW_state_is_image_render() == false)) {
|
||||
/* If history buffer is not valid request another frame.
|
||||
* This fix black reflections on area resize. */
|
||||
DRW_viewport_request_redraw();
|
||||
}
|
||||
|
||||
/* Record perspective matrix for the next frame. */
|
||||
DRW_view_persmat_get(effects->taa_view, effects->prev_persmat, false);
|
||||
|
||||
/* Update double buffer status if render mode. */
|
||||
if (DRW_state_is_image_render()) {
|
||||
stl->g_data->valid_double_buffer = (txl->color_double_buffer != NULL);
|
||||
stl->g_data->valid_taa_history = (txl->taa_history != NULL);
|
||||
}
|
||||
}
|
@@ -22,602 +22,123 @@
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "draw_color_management.h" /* TODO: remove dependency. */
|
||||
|
||||
#include "BLI_rand.h"
|
||||
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
#include "DRW_engine.h"
|
||||
#include "GPU_framebuffer.h"
|
||||
|
||||
#include "eevee_engine.h"
|
||||
#include "eevee_private.h"
|
||||
|
||||
#include "eevee_engine.h" /* own include */
|
||||
typedef struct EEVEE_Data {
|
||||
DrawEngineType *engine_type;
|
||||
DRWViewportEmptyList *fbl;
|
||||
DRWViewportEmptyList *txl;
|
||||
DRWViewportEmptyList *psl;
|
||||
DRWViewportEmptyList *stl;
|
||||
struct EEVEE_Instance *instance_data;
|
||||
} EEVEE_Data;
|
||||
|
||||
#define EEVEE_ENGINE "BLENDER_EEVEE"
|
||||
|
||||
/* *********** FUNCTIONS *********** */
|
||||
|
||||
static void eevee_engine_init(void *ved)
|
||||
static void eevee_engine_init(void *vedata)
|
||||
{
|
||||
EEVEE_Data *vedata = (EEVEE_Data *)ved;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
EEVEE_Data *ved = (EEVEE_Data *)vedata;
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
View3D *v3d = draw_ctx->v3d;
|
||||
RegionView3D *rv3d = draw_ctx->rv3d;
|
||||
Object *camera = (rv3d->persp == RV3D_CAMOB) ? v3d->camera : NULL;
|
||||
|
||||
if (!stl->g_data) {
|
||||
/* Alloc transient pointers */
|
||||
stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__);
|
||||
if (ved->instance_data == NULL) {
|
||||
ved->instance_data = EEVEE_instance_alloc();
|
||||
}
|
||||
stl->g_data->use_color_render_settings = USE_SCENE_LIGHT(v3d) ||
|
||||
!LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d);
|
||||
stl->g_data->background_alpha = DRW_state_draw_background() ? 1.0f : 0.0f;
|
||||
stl->g_data->valid_double_buffer = (txl->color_double_buffer != NULL);
|
||||
stl->g_data->valid_taa_history = (txl->taa_history != NULL);
|
||||
stl->g_data->queued_shaders_count = 0;
|
||||
stl->g_data->render_timesteps = 1;
|
||||
|
||||
/* Main Buffer */
|
||||
DRW_texture_ensure_fullscreen_2d(&txl->color, GPU_RGBA16F, DRW_TEX_FILTER);
|
||||
EEVEE_instance_init(ved->instance_data);
|
||||
}
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->main_fb,
|
||||
{GPU_ATTACHMENT_TEXTURE(dtxl->depth),
|
||||
GPU_ATTACHMENT_TEXTURE(txl->color),
|
||||
GPU_ATTACHMENT_LEAVE,
|
||||
GPU_ATTACHMENT_LEAVE,
|
||||
GPU_ATTACHMENT_LEAVE,
|
||||
GPU_ATTACHMENT_LEAVE});
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->main_color_fb,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->color)});
|
||||
|
||||
/* `EEVEE_renderpasses_init` will set the active render passes used by `EEVEE_effects_init`.
|
||||
* `EEVEE_effects_init` needs to go second for TAA. */
|
||||
EEVEE_renderpasses_init(vedata);
|
||||
EEVEE_effects_init(sldata, vedata, camera, false);
|
||||
EEVEE_materials_init(sldata, vedata, stl, fbl);
|
||||
EEVEE_shadows_init(sldata);
|
||||
EEVEE_lightprobes_init(sldata, vedata);
|
||||
static void eevee_draw_scene(void *vedata)
|
||||
{
|
||||
EEVEE_instance_draw_viewport(((EEVEE_Data *)vedata)->instance_data);
|
||||
}
|
||||
|
||||
static void eevee_cache_init(void *vedata)
|
||||
{
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
|
||||
EEVEE_bloom_cache_init(sldata, vedata);
|
||||
EEVEE_depth_of_field_cache_init(sldata, vedata);
|
||||
EEVEE_effects_cache_init(sldata, vedata);
|
||||
EEVEE_lightprobes_cache_init(sldata, vedata);
|
||||
EEVEE_lights_cache_init(sldata, vedata);
|
||||
EEVEE_materials_cache_init(sldata, vedata);
|
||||
EEVEE_motion_blur_cache_init(sldata, vedata);
|
||||
EEVEE_occlusion_cache_init(sldata, vedata);
|
||||
EEVEE_screen_raytrace_cache_init(sldata, vedata);
|
||||
EEVEE_subsurface_cache_init(sldata, vedata);
|
||||
EEVEE_temporal_sampling_cache_init(sldata, vedata);
|
||||
EEVEE_volumes_cache_init(sldata, vedata);
|
||||
EEVEE_instance_cache_init(((EEVEE_Data *)vedata)->instance_data);
|
||||
}
|
||||
|
||||
void EEVEE_cache_populate(void *vedata, Object *ob)
|
||||
static void eevee_cache_populate(void *vedata, Object *object)
|
||||
{
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const int ob_visibility = DRW_object_visibility_in_active_context(ob);
|
||||
bool cast_shadow = false;
|
||||
|
||||
if (ob_visibility & OB_VISIBLE_PARTICLES) {
|
||||
EEVEE_particle_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
|
||||
}
|
||||
|
||||
if (DRW_object_is_renderable(ob) && (ob_visibility & OB_VISIBLE_SELF)) {
|
||||
if (ELEM(ob->type, OB_MESH, OB_SURF, OB_MBALL)) {
|
||||
EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
|
||||
}
|
||||
else if (ob->type == OB_HAIR) {
|
||||
EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
|
||||
}
|
||||
else if (ob->type == OB_VOLUME) {
|
||||
EEVEE_volumes_cache_object_add(sldata, vedata, draw_ctx->scene, ob);
|
||||
}
|
||||
else if (!USE_SCENE_LIGHT(draw_ctx->v3d)) {
|
||||
/* do not add any scene light sources to the cache */
|
||||
}
|
||||
else if (ob->type == OB_LIGHTPROBE) {
|
||||
if ((ob->base_flag & BASE_FROM_DUPLI) != 0) {
|
||||
/* TODO: Special case for dupli objects because we cannot save the object pointer. */
|
||||
}
|
||||
else {
|
||||
EEVEE_lightprobes_cache_add(sldata, vedata, ob);
|
||||
}
|
||||
}
|
||||
else if (ob->type == OB_LAMP) {
|
||||
EEVEE_lights_cache_add(sldata, ob);
|
||||
}
|
||||
}
|
||||
|
||||
if (cast_shadow) {
|
||||
EEVEE_shadows_caster_register(sldata, ob);
|
||||
}
|
||||
EEVEE_instance_cache_populate(((EEVEE_Data *)vedata)->instance_data, object);
|
||||
}
|
||||
|
||||
static void eevee_cache_finish(void *vedata)
|
||||
{
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
EEVEE_PrivateData *g_data = stl->g_data;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
|
||||
|
||||
EEVEE_volumes_cache_finish(sldata, vedata);
|
||||
EEVEE_materials_cache_finish(sldata, vedata);
|
||||
EEVEE_lights_cache_finish(sldata, vedata);
|
||||
EEVEE_lightprobes_cache_finish(sldata, vedata);
|
||||
EEVEE_renderpasses_cache_finish(sldata, vedata);
|
||||
|
||||
EEVEE_subsurface_draw_init(sldata, vedata);
|
||||
EEVEE_effects_draw_init(sldata, vedata);
|
||||
EEVEE_volumes_draw_init(sldata, vedata);
|
||||
|
||||
uint tot_samples = scene_eval->eevee.taa_render_samples;
|
||||
if (tot_samples == 0) {
|
||||
/* Use a high number of samples so the outputs accumulation buffers
|
||||
* will have the highest possible precision. */
|
||||
tot_samples = 1024;
|
||||
}
|
||||
EEVEE_renderpasses_output_init(sldata, vedata, tot_samples);
|
||||
|
||||
/* Restart TAA if a shader has finish compiling. */
|
||||
/* HACK: We should use notification of some sort from the compilation job instead. */
|
||||
if (g_data->queued_shaders_count != g_data->queued_shaders_count_prev) {
|
||||
g_data->queued_shaders_count_prev = g_data->queued_shaders_count;
|
||||
EEVEE_temporal_sampling_reset(vedata);
|
||||
}
|
||||
}
|
||||
|
||||
/* As renders in an HDR off-screen buffer, we need draw everything once
|
||||
* during the background pass. This way the other drawing callback between
|
||||
* the background and the scene pass are visible.
|
||||
* NOTE: we could break it up in two passes using some depth test
|
||||
* to reduce the fill-rate. */
|
||||
static void eevee_draw_scene(void *vedata)
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
EEVEE_FramebufferList *fbl = ((EEVEE_Data *)vedata)->fbl;
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
|
||||
/* Default framebuffer and texture */
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
|
||||
|
||||
/* Sort transparents before the loop. */
|
||||
DRW_pass_sort_shgroup_z(psl->transparent_pass);
|
||||
|
||||
/* Number of iteration: Use viewport taa_samples when using viewport rendering */
|
||||
int loop_len = 1;
|
||||
if (DRW_state_is_image_render()) {
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const Scene *scene = draw_ctx->scene;
|
||||
loop_len = MAX2(1, scene->eevee.taa_samples);
|
||||
}
|
||||
|
||||
if (stl->effects->bypass_drawing) {
|
||||
loop_len = 0;
|
||||
}
|
||||
|
||||
while (loop_len--) {
|
||||
const float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
float clear_depth = 1.0f;
|
||||
uint clear_stencil = 0x0;
|
||||
const uint primes[3] = {2, 3, 7};
|
||||
double offset[3] = {0.0, 0.0, 0.0};
|
||||
double r[3];
|
||||
|
||||
bool taa_use_reprojection = (stl->effects->enabled_effects & EFFECT_TAA_REPROJECT) != 0;
|
||||
|
||||
if (DRW_state_is_image_render() || taa_use_reprojection ||
|
||||
((stl->effects->enabled_effects & EFFECT_TAA) != 0)) {
|
||||
int samp = taa_use_reprojection ? stl->effects->taa_reproject_sample + 1 :
|
||||
stl->effects->taa_current_sample;
|
||||
BLI_halton_3d(primes, offset, samp, r);
|
||||
EEVEE_update_noise(psl, fbl, r);
|
||||
EEVEE_volumes_set_jitter(sldata, samp - 1);
|
||||
EEVEE_materials_init(sldata, vedata, stl, fbl);
|
||||
}
|
||||
/* Copy previous persmat to UBO data */
|
||||
copy_m4_m4(sldata->common_data.prev_persmat, stl->effects->prev_persmat);
|
||||
|
||||
/* Refresh Probes
|
||||
* Shadows needs to be updated for correct probes */
|
||||
DRW_stats_group_start("Probes Refresh");
|
||||
EEVEE_shadows_update(sldata, vedata);
|
||||
EEVEE_lightprobes_refresh(sldata, vedata);
|
||||
EEVEE_lightprobes_refresh_planar(sldata, vedata);
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* Refresh shadows */
|
||||
DRW_stats_group_start("Shadows");
|
||||
EEVEE_shadows_draw(sldata, vedata, stl->effects->taa_view);
|
||||
DRW_stats_group_end();
|
||||
|
||||
if (((stl->effects->enabled_effects & EFFECT_TAA) != 0) &&
|
||||
(stl->effects->taa_current_sample > 1) && !DRW_state_is_image_render() &&
|
||||
!taa_use_reprojection) {
|
||||
DRW_view_set_active(stl->effects->taa_view);
|
||||
}
|
||||
/* when doing viewport rendering the overrides needs to be recalculated for
|
||||
* every loop as this normally happens once inside
|
||||
* `EEVEE_temporal_sampling_init` */
|
||||
else if (((stl->effects->enabled_effects & EFFECT_TAA) != 0) &&
|
||||
(stl->effects->taa_current_sample > 1) && DRW_state_is_image_render()) {
|
||||
EEVEE_temporal_sampling_update_matrices(vedata);
|
||||
}
|
||||
|
||||
/* Set ray type. */
|
||||
sldata->common_data.ray_type = EEVEE_RAY_CAMERA;
|
||||
sldata->common_data.ray_depth = 0.0f;
|
||||
GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
|
||||
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
eGPUFrameBufferBits clear_bits = GPU_DEPTH_BIT;
|
||||
SET_FLAG_FROM_TEST(clear_bits, !DRW_state_draw_background(), GPU_COLOR_BIT);
|
||||
SET_FLAG_FROM_TEST(clear_bits, (stl->effects->enabled_effects & EFFECT_SSS), GPU_STENCIL_BIT);
|
||||
GPU_framebuffer_clear(fbl->main_fb, clear_bits, clear_col, clear_depth, clear_stencil);
|
||||
|
||||
/* Depth prepass */
|
||||
DRW_stats_group_start("Prepass");
|
||||
DRW_draw_pass(psl->depth_ps);
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* Create minmax texture */
|
||||
DRW_stats_group_start("Main MinMax buffer");
|
||||
EEVEE_create_minmax_buffer(vedata, dtxl->depth, -1);
|
||||
DRW_stats_group_end();
|
||||
|
||||
EEVEE_occlusion_compute(sldata, vedata);
|
||||
EEVEE_volumes_compute(sldata, vedata);
|
||||
|
||||
/* Shading pass */
|
||||
DRW_stats_group_start("Shading");
|
||||
if (DRW_state_draw_background()) {
|
||||
DRW_draw_pass(psl->background_ps);
|
||||
}
|
||||
DRW_draw_pass(psl->material_ps);
|
||||
EEVEE_subsurface_data_render(sldata, vedata);
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* Effects pre-transparency */
|
||||
EEVEE_subsurface_compute(sldata, vedata);
|
||||
EEVEE_reflection_compute(sldata, vedata);
|
||||
EEVEE_occlusion_draw_debug(sldata, vedata);
|
||||
if (psl->probe_display) {
|
||||
DRW_draw_pass(psl->probe_display);
|
||||
}
|
||||
EEVEE_refraction_compute(sldata, vedata);
|
||||
|
||||
/* Opaque refraction */
|
||||
DRW_stats_group_start("Opaque Refraction");
|
||||
DRW_draw_pass(psl->depth_refract_ps);
|
||||
DRW_draw_pass(psl->material_refract_ps);
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* Volumetrics Resolve Opaque */
|
||||
EEVEE_volumes_resolve(sldata, vedata);
|
||||
|
||||
/* Renderpasses */
|
||||
EEVEE_renderpasses_output_accumulate(sldata, vedata, false);
|
||||
|
||||
/* Transparent */
|
||||
/* TODO(fclem): should be its own Frame-buffer.
|
||||
* This is needed because dualsource blending only works with 1 color buffer. */
|
||||
GPU_framebuffer_texture_attach(fbl->main_color_fb, dtxl->depth, 0, 0);
|
||||
GPU_framebuffer_bind(fbl->main_color_fb);
|
||||
DRW_draw_pass(psl->transparent_pass);
|
||||
GPU_framebuffer_bind(fbl->main_fb);
|
||||
GPU_framebuffer_texture_detach(fbl->main_color_fb, dtxl->depth);
|
||||
|
||||
/* Post Process */
|
||||
DRW_stats_group_start("Post FX");
|
||||
EEVEE_draw_effects(sldata, vedata);
|
||||
DRW_stats_group_end();
|
||||
|
||||
DRW_view_set_active(NULL);
|
||||
|
||||
if (DRW_state_is_image_render() && (stl->effects->enabled_effects & EFFECT_SSR) &&
|
||||
!stl->effects->ssr_was_valid_double_buffer) {
|
||||
/* SSR needs one iteration to start properly. */
|
||||
loop_len++;
|
||||
/* Reset sampling (and accumulation) after the first sample to avoid
|
||||
* washed out first bounce for SSR. */
|
||||
EEVEE_temporal_sampling_reset(vedata);
|
||||
stl->effects->ssr_was_valid_double_buffer = stl->g_data->valid_double_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
if ((stl->g_data->render_passes & EEVEE_RENDER_PASS_COMBINED) != 0) {
|
||||
/* Transfer result to default framebuffer. */
|
||||
GPU_framebuffer_bind(dfbl->default_fb);
|
||||
DRW_transform_none(stl->effects->final_tx);
|
||||
}
|
||||
else {
|
||||
EEVEE_renderpasses_draw(sldata, vedata);
|
||||
}
|
||||
|
||||
if (stl->effects->bypass_drawing) {
|
||||
/* Restore the depth from sample 1. */
|
||||
GPU_framebuffer_blit(fbl->double_buffer_depth_fb, 0, dfbl->default_fb, 0, GPU_DEPTH_BIT);
|
||||
}
|
||||
|
||||
EEVEE_renderpasses_draw_debug(vedata);
|
||||
|
||||
EEVEE_volumes_free_smoke_textures();
|
||||
|
||||
stl->g_data->view_updated = false;
|
||||
|
||||
DRW_view_set_active(NULL);
|
||||
}
|
||||
|
||||
static void eevee_view_update(void *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
if (stl->g_data) {
|
||||
stl->g_data->view_updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_id_object_update(void *UNUSED(vedata), Object *object)
|
||||
{
|
||||
EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(object);
|
||||
if (ped != NULL && ped->dd.recalc != 0) {
|
||||
ped->need_update = (ped->dd.recalc & ID_RECALC_TRANSFORM) != 0;
|
||||
ped->dd.recalc = 0;
|
||||
}
|
||||
EEVEE_LightEngineData *led = EEVEE_light_data_get(object);
|
||||
if (led != NULL && led->dd.recalc != 0) {
|
||||
led->need_update = true;
|
||||
led->dd.recalc = 0;
|
||||
}
|
||||
EEVEE_ObjectEngineData *oedata = EEVEE_object_data_get(object);
|
||||
if (oedata != NULL && oedata->dd.recalc != 0) {
|
||||
oedata->need_update = true;
|
||||
oedata->geom_update = (oedata->dd.recalc & (ID_RECALC_GEOMETRY)) != 0;
|
||||
oedata->dd.recalc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_id_world_update(void *vedata, World *wo)
|
||||
{
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
LightCache *lcache = stl->g_data->light_cache;
|
||||
|
||||
if (ELEM(lcache, NULL, stl->lookdev_lightcache)) {
|
||||
/* Avoid Lookdev viewport clearing the update flag (see T67741). */
|
||||
return;
|
||||
}
|
||||
|
||||
EEVEE_WorldEngineData *wedata = EEVEE_world_data_ensure(wo);
|
||||
|
||||
if (wedata != NULL && wedata->dd.recalc != 0) {
|
||||
if ((lcache->flag & LIGHTCACHE_BAKING) == 0) {
|
||||
lcache->flag |= LIGHTCACHE_UPDATE_WORLD;
|
||||
}
|
||||
wedata->dd.recalc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void eevee_id_update(void *vedata, ID *id)
|
||||
{
|
||||
/* Handle updates based on ID type. */
|
||||
switch (GS(id->name)) {
|
||||
case ID_WO:
|
||||
eevee_id_world_update(vedata, (World *)id);
|
||||
break;
|
||||
case ID_OB:
|
||||
eevee_id_object_update(vedata, (Object *)id);
|
||||
break;
|
||||
default:
|
||||
/* pass */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_render_reset_passes(EEVEE_Data *vedata)
|
||||
{
|
||||
/* Reset passlist. This is safe as they are stored into managed memory chunks. */
|
||||
memset(vedata->psl, 0, sizeof(*vedata->psl));
|
||||
}
|
||||
|
||||
static void eevee_render_to_image(void *vedata,
|
||||
RenderEngine *engine,
|
||||
struct RenderLayer *render_layer,
|
||||
const rcti *rect)
|
||||
{
|
||||
EEVEE_Data *ved = (EEVEE_Data *)vedata;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
Depsgraph *depsgraph = draw_ctx->depsgraph;
|
||||
Scene *scene = DEG_get_evaluated_scene(depsgraph);
|
||||
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
|
||||
const bool do_motion_blur = (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0;
|
||||
const bool do_motion_blur_fx = do_motion_blur && (scene->eevee.motion_blur_max > 0);
|
||||
|
||||
if (!EEVEE_render_init(vedata, engine, depsgraph)) {
|
||||
return;
|
||||
}
|
||||
EEVEE_PrivateData *g_data = ved->stl->g_data;
|
||||
|
||||
int initial_frame = CFRA;
|
||||
float initial_subframe = SUBFRA;
|
||||
float shuttertime = (do_motion_blur) ? scene->eevee.motion_blur_shutter : 0.0f;
|
||||
int time_steps_tot = (do_motion_blur) ? max_ii(1, scene->eevee.motion_blur_steps) : 1;
|
||||
g_data->render_timesteps = time_steps_tot;
|
||||
|
||||
EEVEE_render_modules_init(vedata, engine, depsgraph);
|
||||
|
||||
g_data->render_sample_count_per_timestep = EEVEE_temporal_sampling_sample_count_get(scene,
|
||||
ved->stl);
|
||||
|
||||
/* Reset in case the same engine is used on multiple views. */
|
||||
EEVEE_temporal_sampling_reset(vedata);
|
||||
|
||||
/* Compute start time. The motion blur will cover `[time ...time + shuttertime]`. */
|
||||
float time = initial_frame + initial_subframe;
|
||||
switch (scene->eevee.motion_blur_position) {
|
||||
case SCE_EEVEE_MB_START:
|
||||
/* No offset. */
|
||||
break;
|
||||
case SCE_EEVEE_MB_CENTER:
|
||||
time -= shuttertime * 0.5f;
|
||||
break;
|
||||
case SCE_EEVEE_MB_END:
|
||||
time -= shuttertime;
|
||||
break;
|
||||
default:
|
||||
BLI_assert_msg(0, "Invalid motion blur position enum!");
|
||||
break;
|
||||
}
|
||||
|
||||
float time_step = shuttertime / time_steps_tot;
|
||||
for (int i = 0; i < time_steps_tot && !RE_engine_test_break(engine); i++) {
|
||||
float time_prev = time;
|
||||
float time_curr = time + time_step * 0.5f;
|
||||
float time_next = time + time_step;
|
||||
time += time_step;
|
||||
|
||||
/* Previous motion step. */
|
||||
if (do_motion_blur_fx) {
|
||||
if (i == 0) {
|
||||
EEVEE_motion_blur_step_set(ved, MB_PREV);
|
||||
DRW_render_set_time(engine, depsgraph, floorf(time_prev), fractf(time_prev));
|
||||
EEVEE_render_modules_init(vedata, engine, depsgraph);
|
||||
sldata = EEVEE_view_layer_data_ensure();
|
||||
|
||||
EEVEE_render_cache_init(sldata, vedata);
|
||||
|
||||
DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
|
||||
|
||||
EEVEE_motion_blur_cache_finish(vedata);
|
||||
EEVEE_materials_cache_finish(sldata, vedata);
|
||||
eevee_render_reset_passes(vedata);
|
||||
}
|
||||
}
|
||||
|
||||
/* Next motion step. */
|
||||
if (do_motion_blur_fx) {
|
||||
EEVEE_motion_blur_step_set(ved, MB_NEXT);
|
||||
DRW_render_set_time(engine, depsgraph, floorf(time_next), fractf(time_next));
|
||||
EEVEE_render_modules_init(vedata, engine, depsgraph);
|
||||
sldata = EEVEE_view_layer_data_ensure();
|
||||
|
||||
EEVEE_render_cache_init(sldata, vedata);
|
||||
|
||||
DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
|
||||
|
||||
EEVEE_motion_blur_cache_finish(vedata);
|
||||
EEVEE_materials_cache_finish(sldata, vedata);
|
||||
eevee_render_reset_passes(vedata);
|
||||
}
|
||||
|
||||
/* Current motion step. */
|
||||
{
|
||||
if (do_motion_blur) {
|
||||
EEVEE_motion_blur_step_set(ved, MB_CURR);
|
||||
DRW_render_set_time(engine, depsgraph, floorf(time_curr), fractf(time_curr));
|
||||
EEVEE_render_modules_init(vedata, engine, depsgraph);
|
||||
sldata = EEVEE_view_layer_data_ensure();
|
||||
}
|
||||
|
||||
EEVEE_render_cache_init(sldata, vedata);
|
||||
|
||||
DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
|
||||
|
||||
EEVEE_motion_blur_cache_finish(vedata);
|
||||
EEVEE_volumes_cache_finish(sldata, vedata);
|
||||
EEVEE_materials_cache_finish(sldata, vedata);
|
||||
EEVEE_lights_cache_finish(sldata, vedata);
|
||||
EEVEE_lightprobes_cache_finish(sldata, vedata);
|
||||
EEVEE_renderpasses_cache_finish(sldata, vedata);
|
||||
|
||||
EEVEE_subsurface_draw_init(sldata, vedata);
|
||||
EEVEE_effects_draw_init(sldata, vedata);
|
||||
EEVEE_volumes_draw_init(sldata, vedata);
|
||||
}
|
||||
|
||||
/* Actual drawing. */
|
||||
{
|
||||
EEVEE_renderpasses_output_init(
|
||||
sldata, vedata, g_data->render_sample_count_per_timestep * time_steps_tot);
|
||||
|
||||
if (scene->world) {
|
||||
/* Update world in case of animated world material. */
|
||||
eevee_id_world_update(vedata, scene->world);
|
||||
}
|
||||
|
||||
EEVEE_temporal_sampling_create_view(vedata);
|
||||
EEVEE_render_draw(vedata, engine, render_layer, rect);
|
||||
|
||||
if (i < time_steps_tot - 1) {
|
||||
/* Don't reset after the last loop. Since EEVEE_render_read_result
|
||||
* might need some DRWPasses. */
|
||||
DRW_cache_restart();
|
||||
}
|
||||
}
|
||||
|
||||
if (do_motion_blur_fx) {
|
||||
/* The previous step of next iteration N is exactly the next step of this iteration N - 1.
|
||||
* So we just swap the resources to avoid too much re-evaluation.
|
||||
* Note that this also clears the VBO references from the GPUBatches of deformed
|
||||
* geometries. */
|
||||
EEVEE_motion_blur_swap_data(vedata);
|
||||
}
|
||||
}
|
||||
|
||||
EEVEE_volumes_free_smoke_textures();
|
||||
EEVEE_motion_blur_data_free(&ved->stl->effects->motion_blur);
|
||||
|
||||
if (RE_engine_test_break(engine)) {
|
||||
return;
|
||||
}
|
||||
|
||||
EEVEE_render_read_result(vedata, engine, render_layer, rect);
|
||||
|
||||
/* Restore original viewport size. */
|
||||
DRW_render_viewport_size_set((int[2]){g_data->size_orig[0], g_data->size_orig[1]});
|
||||
|
||||
if (CFRA != initial_frame || SUBFRA != initial_subframe) {
|
||||
/* Restore original frame number. This is because the render pipeline expects it. */
|
||||
RE_engine_frame_set(engine, initial_frame, initial_subframe);
|
||||
}
|
||||
}
|
||||
|
||||
static void eevee_store_metadata(void *vedata, struct RenderResult *render_result)
|
||||
{
|
||||
EEVEE_Data *ved = (EEVEE_Data *)vedata;
|
||||
EEVEE_PrivateData *g_data = ved->stl->g_data;
|
||||
if (g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) {
|
||||
EEVEE_cryptomatte_store_metadata(ved, render_result);
|
||||
EEVEE_cryptomatte_free(ved);
|
||||
}
|
||||
EEVEE_instance_cache_finish(((EEVEE_Data *)vedata)->instance_data);
|
||||
}
|
||||
|
||||
static void eevee_engine_free(void)
|
||||
{
|
||||
EEVEE_shaders_free();
|
||||
EEVEE_lightprobes_free();
|
||||
EEVEE_materials_free();
|
||||
EEVEE_occlusion_free();
|
||||
EEVEE_volumes_free();
|
||||
EEVEE_shared_data_free();
|
||||
}
|
||||
|
||||
static void eevee_instance_free(void *instance_data)
|
||||
{
|
||||
EEVEE_instance_free((struct EEVEE_Instance *)instance_data);
|
||||
}
|
||||
|
||||
static void eevee_render_to_image(void *UNUSED(vedata),
|
||||
struct RenderEngine *engine,
|
||||
struct RenderLayer *layer,
|
||||
const struct rcti *UNUSED(rect))
|
||||
{
|
||||
struct EEVEE_Instance *instance = EEVEE_instance_alloc();
|
||||
EEVEE_instance_render_frame(instance, engine, layer);
|
||||
EEVEE_instance_free(instance);
|
||||
}
|
||||
|
||||
static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
|
||||
{
|
||||
RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
|
||||
|
||||
#define CHECK_PASS_LEGACY(name, type, channels, chanid) \
|
||||
if (view_layer->passflag & (SCE_PASS_##name)) { \
|
||||
RE_engine_register_pass( \
|
||||
engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
|
||||
} \
|
||||
((void)0)
|
||||
#define CHECK_PASS_EEVEE(name, type, channels, chanid) \
|
||||
if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \
|
||||
RE_engine_register_pass( \
|
||||
engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z");
|
||||
CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z");
|
||||
CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ");
|
||||
CHECK_PASS_LEGACY(VECTOR, SOCK_RGBA, 4, "RGBA");
|
||||
CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB");
|
||||
CHECK_PASS_EEVEE(BLOOM, SOCK_RGBA, 3, "RGB");
|
||||
|
||||
LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
|
||||
if ((aov->flag & AOV_CONFLICT) != 0) {
|
||||
continue;
|
||||
}
|
||||
switch (aov->type) {
|
||||
case AOV_TYPE_COLOR:
|
||||
RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA);
|
||||
break;
|
||||
case AOV_TYPE_VALUE:
|
||||
RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// EEVEE_cryptomatte_update_passes(engine, scene, view_layer);
|
||||
|
||||
#undef CHECK_PASS_LEGACY
|
||||
#undef CHECK_PASS_EEVEE
|
||||
}
|
||||
|
||||
static const DrawEngineDataSize eevee_data_size = DRW_VIEWPORT_DATA_SIZE(EEVEE_Data);
|
||||
@@ -629,17 +150,19 @@ DrawEngineType draw_engine_eevee_type = {
|
||||
&eevee_data_size,
|
||||
&eevee_engine_init,
|
||||
&eevee_engine_free,
|
||||
NULL, /* instance_free */
|
||||
&eevee_instance_free,
|
||||
&eevee_cache_init,
|
||||
&EEVEE_cache_populate,
|
||||
&eevee_cache_populate,
|
||||
&eevee_cache_finish,
|
||||
&eevee_draw_scene,
|
||||
&eevee_view_update,
|
||||
&eevee_id_update,
|
||||
NULL,
|
||||
NULL,
|
||||
&eevee_render_to_image,
|
||||
&eevee_store_metadata,
|
||||
NULL,
|
||||
};
|
||||
|
||||
#define EEVEE_ENGINE "BLENDER_EEVEE"
|
||||
|
||||
RenderEngineType DRW_engine_viewport_eevee_type = {
|
||||
NULL,
|
||||
NULL,
|
||||
@@ -654,7 +177,7 @@ RenderEngineType DRW_engine_viewport_eevee_type = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&EEVEE_render_update_passes,
|
||||
&eevee_render_update_passes,
|
||||
&draw_engine_eevee_type,
|
||||
{NULL, NULL, NULL},
|
||||
};
|
||||
|
167
source/blender/draw/engines/eevee/eevee_engine.cc
Normal file
167
source/blender/draw/engines/eevee/eevee_engine.cc
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
#include "BKE_global.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "GPU_framebuffer.h"
|
||||
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "eevee_private.h"
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
using namespace blender::eevee;
|
||||
|
||||
static ShaderModule *g_shader_module = nullptr;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name EEVEE Instance C interface
|
||||
* \{ */
|
||||
|
||||
EEVEE_Instance *EEVEE_instance_alloc(void)
|
||||
{
|
||||
if (g_shader_module == nullptr) {
|
||||
/* TODO(fclem) threadsafety. */
|
||||
g_shader_module = new ShaderModule();
|
||||
}
|
||||
return reinterpret_cast<EEVEE_Instance *>(new Instance(*g_shader_module));
|
||||
}
|
||||
|
||||
void EEVEE_instance_free(EEVEE_Instance *instance_)
|
||||
{
|
||||
Instance *instance = reinterpret_cast<Instance *>(instance_);
|
||||
delete instance;
|
||||
}
|
||||
|
||||
void EEVEE_instance_init(EEVEE_Instance *instance_)
|
||||
{
|
||||
Instance *instance = reinterpret_cast<Instance *>(instance_);
|
||||
|
||||
const DRWContextState *ctx_state = DRW_context_state_get();
|
||||
Depsgraph *depsgraph = ctx_state->depsgraph;
|
||||
Scene *scene = ctx_state->scene;
|
||||
View3D *v3d = ctx_state->v3d;
|
||||
const ARegion *region = ctx_state->region;
|
||||
RegionView3D *rv3d = ctx_state->rv3d;
|
||||
|
||||
/* Scaling output to better see what happens with accumulation. */
|
||||
int resolution_divider = (ELEM(G.debug_value, 1, 2)) ? 16 : 1;
|
||||
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
int size[2];
|
||||
size[0] = divide_ceil_u(GPU_texture_width(dtxl->color), resolution_divider);
|
||||
size[1] = divide_ceil_u(GPU_texture_height(dtxl->color), resolution_divider);
|
||||
|
||||
const DRWView *default_view = DRW_view_default_get();
|
||||
|
||||
Object *camera = nullptr;
|
||||
/* Get render borders. */
|
||||
rcti rect;
|
||||
BLI_rcti_init(&rect, 0, size[0], 0, size[1]);
|
||||
if (v3d) {
|
||||
if (rv3d && (rv3d->persp == RV3D_CAMOB)) {
|
||||
camera = v3d->camera;
|
||||
}
|
||||
|
||||
if (v3d->flag2 & V3D_RENDER_BORDER) {
|
||||
if (camera) {
|
||||
rctf viewborder;
|
||||
/* TODO(fclem) Might be better to get it from DRW. */
|
||||
ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &viewborder, false);
|
||||
float viewborder_sizex = BLI_rctf_size_x(&viewborder);
|
||||
float viewborder_sizey = BLI_rctf_size_y(&viewborder);
|
||||
rect.xmin = floorf(viewborder.xmin + (scene->r.border.xmin * viewborder_sizex));
|
||||
rect.ymin = floorf(viewborder.ymin + (scene->r.border.ymin * viewborder_sizey));
|
||||
rect.xmax = floorf(viewborder.xmin + (scene->r.border.xmax * viewborder_sizex));
|
||||
rect.ymax = floorf(viewborder.ymin + (scene->r.border.ymax * viewborder_sizey));
|
||||
}
|
||||
else {
|
||||
rect.xmin = v3d->render_border.xmin * size[0];
|
||||
rect.ymin = v3d->render_border.ymin * size[1];
|
||||
rect.xmax = v3d->render_border.xmax * size[0];
|
||||
rect.ymax = v3d->render_border.ymax * size[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instance->init(
|
||||
size, &rect, nullptr, depsgraph, nullptr, camera, nullptr, default_view, v3d, rv3d);
|
||||
}
|
||||
|
||||
void EEVEE_instance_cache_init(EEVEE_Instance *instance_)
|
||||
{
|
||||
Instance *instance = reinterpret_cast<Instance *>(instance_);
|
||||
instance->begin_sync();
|
||||
}
|
||||
|
||||
void EEVEE_instance_cache_populate(EEVEE_Instance *instance_, Object *object)
|
||||
{
|
||||
Instance *instance = reinterpret_cast<Instance *>(instance_);
|
||||
instance->object_sync(object);
|
||||
}
|
||||
|
||||
void EEVEE_instance_cache_finish(EEVEE_Instance *instance_)
|
||||
{
|
||||
Instance *instance = reinterpret_cast<Instance *>(instance_);
|
||||
instance->end_sync();
|
||||
}
|
||||
|
||||
void EEVEE_instance_draw_viewport(EEVEE_Instance *instance_)
|
||||
{
|
||||
Instance *instance = reinterpret_cast<Instance *>(instance_);
|
||||
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
|
||||
|
||||
instance->draw_viewport(dfbl);
|
||||
}
|
||||
|
||||
void EEVEE_instance_render_frame(EEVEE_Instance *instance_,
|
||||
struct RenderEngine *engine,
|
||||
struct RenderLayer *render_layer)
|
||||
{
|
||||
Instance *instance = reinterpret_cast<Instance *>(instance_);
|
||||
Render *render = engine->re;
|
||||
Depsgraph *depsgraph = DRW_context_state_get()->depsgraph;
|
||||
Object *camera_original_ob = RE_GetCamera(engine->re);
|
||||
const char *viewname = RE_GetActiveRenderView(engine->re);
|
||||
int size[2] = {engine->resolution_x, engine->resolution_y};
|
||||
|
||||
rctf view_rect;
|
||||
rcti rect;
|
||||
RE_GetViewPlane(render, &view_rect, &rect);
|
||||
|
||||
instance->init(size, &rect, engine, depsgraph, nullptr, camera_original_ob, render_layer);
|
||||
instance->render_frame(render_layer, viewname);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name EEVEE Shaders C interface
|
||||
* \{ */
|
||||
|
||||
void EEVEE_shared_data_free(void)
|
||||
{
|
||||
delete g_shader_module;
|
||||
g_shader_module = nullptr;
|
||||
}
|
||||
|
||||
/** \} */
|
@@ -22,4 +22,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
extern RenderEngineType DRW_engine_viewport_eevee_type;
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern RenderEngineType DRW_engine_viewport_eevee_type;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
253
source/blender/draw/engines/eevee/eevee_film.cc
Normal file
253
source/blender/draw/engines/eevee/eevee_film.cc
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* A film is a fullscreen buffer (usually at output extent)
|
||||
* that will be able to accumulate sample in any distorted camera_type
|
||||
* using a pixel filter.
|
||||
*
|
||||
* Input needs to be jittered so that the filter converges to the right result.
|
||||
*/
|
||||
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "GPU_framebuffer.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "eevee_film.hh"
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name FilmData
|
||||
* \{ */
|
||||
|
||||
static eGPUTextureFormat to_gpu_texture_format(eFilmDataType film_type)
|
||||
{
|
||||
switch (film_type) {
|
||||
default:
|
||||
case FILM_DATA_COLOR_LOG:
|
||||
case FILM_DATA_COLOR:
|
||||
case FILM_DATA_MOTION:
|
||||
case FILM_DATA_VEC4:
|
||||
return GPU_RGBA16F;
|
||||
case FILM_DATA_FLOAT:
|
||||
return GPU_R16F;
|
||||
case FILM_DATA_VEC2:
|
||||
return GPU_RG16F;
|
||||
case FILM_DATA_NORMAL:
|
||||
return GPU_RGB10_A2;
|
||||
case FILM_DATA_DEPTH:
|
||||
return GPU_R32F;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool operator==(const FilmData &a, const FilmData &b)
|
||||
{
|
||||
return (a.extent == b.extent) && (a.offset == b.offset);
|
||||
}
|
||||
|
||||
inline bool operator!=(const FilmData &a, const FilmData &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Film
|
||||
* \{ */
|
||||
|
||||
void Film::init(const int2 &full_extent, const rcti *output_rect)
|
||||
{
|
||||
FilmData data = data_;
|
||||
data.extent = int2(BLI_rcti_size_x(output_rect), BLI_rcti_size_y(output_rect));
|
||||
data.offset = int2(output_rect->xmin, output_rect->ymin);
|
||||
|
||||
has_changed_ = data_ != data;
|
||||
|
||||
if (has_changed_) {
|
||||
data_ = data;
|
||||
inst_.sampling.reset();
|
||||
}
|
||||
|
||||
data_.opacity = 1.0f;
|
||||
data_.uv_scale_inv = float2(full_extent);
|
||||
data_.uv_scale = 1.0f / data_.uv_scale_inv;
|
||||
data_.uv_bias = float2(data_.offset) * data_.uv_scale;
|
||||
}
|
||||
|
||||
void Film::sync(void)
|
||||
{
|
||||
char full_name[32];
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (data_tx_[i] == nullptr) {
|
||||
eGPUTextureFormat tex_format = to_gpu_texture_format(data_.data_type);
|
||||
data_tx_[i].ensure_2d(tex_format, data_.extent);
|
||||
/* TODO(fclem) The weight texture could be shared between all similar accumulators. */
|
||||
weight_tx_[i].ensure_2d(GPU_R16F, data_.extent);
|
||||
|
||||
accumulation_fb_[i].ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(data_tx_[i]),
|
||||
GPU_ATTACHMENT_TEXTURE(weight_tx_[i]));
|
||||
}
|
||||
}
|
||||
|
||||
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
||||
{
|
||||
SNPRINTF(full_name, "Film.%s.Accumulate", name_.c_str());
|
||||
accumulate_ps_ = DRW_pass_create(full_name, DRW_STATE_WRITE_COLOR);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(FILM_FILTER);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, accumulate_ps_);
|
||||
DRW_shgroup_uniform_block(grp, "film_block", data_);
|
||||
DRW_shgroup_uniform_block(grp, "camera_block", inst_.camera.ubo_get());
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "input_tx", &input_tx_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "data_tx", &data_tx_[0], no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "weight_tx", &weight_tx_[0], no_filter);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
}
|
||||
{
|
||||
SNPRINTF(full_name, "Film.%s.Resolve", name_.c_str());
|
||||
DRWState state = DRW_STATE_WRITE_COLOR;
|
||||
eShaderType sh_type = FILM_RESOLVE;
|
||||
if (data_.data_type == FILM_DATA_DEPTH) {
|
||||
state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS;
|
||||
sh_type = FILM_RESOLVE_DEPTH;
|
||||
}
|
||||
resolve_ps_ = DRW_pass_create(full_name, state);
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(sh_type);
|
||||
DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_);
|
||||
DRW_shgroup_uniform_block(grp, "film_block", data_);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "first_sample_tx", &first_sample_ref_, no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "data_tx", &data_tx_[0], no_filter);
|
||||
DRW_shgroup_uniform_texture_ref_ex(grp, "weight_tx", &weight_tx_[0], no_filter);
|
||||
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Film::end_sync()
|
||||
{
|
||||
/* TODO reprojection. */
|
||||
if (inst_.sampling.is_reset()) {
|
||||
data_.use_history = 0;
|
||||
}
|
||||
|
||||
if (inst_.is_viewport()) {
|
||||
data_.opacity = inst_.sampling.viewport_smoothing_opacity_factor_get();
|
||||
}
|
||||
|
||||
if (data_.use_history == 0 || inst_.is_viewport()) {
|
||||
data_.push_update();
|
||||
}
|
||||
|
||||
const bool is_first_sample = (inst_.sampling.sample_get() == 1);
|
||||
if (do_smooth_viewport_smooth_transition() && (data_.opacity < 1.0f || is_first_sample)) {
|
||||
GPUTexture *dtxl_color = DRW_viewport_texture_list_get()->color;
|
||||
eGPUTextureFormat tex_format = GPU_texture_format(dtxl_color);
|
||||
int extent[2] = {GPU_texture_width(dtxl_color), GPU_texture_height(dtxl_color)};
|
||||
first_sample_tx_.ensure_2d(tex_format, extent);
|
||||
first_sample_ref_ = first_sample_tx_;
|
||||
}
|
||||
else {
|
||||
/* Reuse the data_tx since there is no need to blend. */
|
||||
first_sample_tx_.free();
|
||||
first_sample_ref_ = data_tx_[0];
|
||||
}
|
||||
}
|
||||
|
||||
void Film::accumulate(GPUTexture *input, const DRWView *view)
|
||||
{
|
||||
input_tx_ = input;
|
||||
|
||||
DRW_view_set_active(view);
|
||||
|
||||
GPU_framebuffer_bind(accumulation_fb_[1]);
|
||||
DRW_draw_pass(accumulate_ps_);
|
||||
|
||||
Framebuffer::swap(accumulation_fb_[0], accumulation_fb_[1]);
|
||||
Texture::swap(data_tx_[0], data_tx_[1]);
|
||||
Texture::swap(weight_tx_[0], weight_tx_[1]);
|
||||
|
||||
/* Use history after first sample. */
|
||||
if (data_.use_history == 0) {
|
||||
data_.use_history = 1;
|
||||
data_.push_update();
|
||||
}
|
||||
}
|
||||
|
||||
void Film::resolve_viewport(GPUFrameBuffer *target)
|
||||
{
|
||||
int viewport[4];
|
||||
|
||||
GPU_framebuffer_bind(target);
|
||||
GPU_framebuffer_viewport_get(target, viewport);
|
||||
|
||||
const bool use_render_border = (data_.offset[0] > 0) || (data_.offset[1] > 0) ||
|
||||
(data_.extent[0] < viewport[2]) ||
|
||||
(data_.extent[1] < viewport[3]);
|
||||
if (use_render_border) {
|
||||
if (has_changed_) {
|
||||
/* Film is cropped and does not fill the view completely. Clear the background. */
|
||||
if (data_.data_type == FILM_DATA_DEPTH) {
|
||||
GPU_framebuffer_clear_depth(target, 1.0f);
|
||||
}
|
||||
else {
|
||||
float color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
GPU_framebuffer_clear_color(target, color);
|
||||
}
|
||||
}
|
||||
GPU_framebuffer_viewport_set(target, UNPACK2(data_.offset), UNPACK2(data_.extent));
|
||||
}
|
||||
|
||||
DRW_draw_pass(resolve_ps_);
|
||||
|
||||
/* Minus one because we already incremented it in step() which is the first
|
||||
* thing to happen in the sample loop. */
|
||||
const bool is_first_sample = (inst_.sampling.sample_get() - 1 == 1);
|
||||
const bool is_only_one_sample = is_first_sample && inst_.sampling.finished();
|
||||
if (is_first_sample && !is_only_one_sample && do_smooth_viewport_smooth_transition()) {
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
GPU_texture_copy(first_sample_tx_, dtxl->color);
|
||||
}
|
||||
|
||||
if (use_render_border) {
|
||||
GPU_framebuffer_viewport_reset(target);
|
||||
}
|
||||
}
|
||||
|
||||
void Film::read_result(float *data)
|
||||
{
|
||||
/* Resolve onto the next data texture. */
|
||||
read_result_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(data_tx_[1]));
|
||||
GPU_framebuffer_bind(read_result_fb_);
|
||||
DRW_draw_pass(resolve_ps_);
|
||||
|
||||
eGPUTextureFormat format = to_gpu_texture_format(data_.data_type);
|
||||
int channel_count = GPU_texture_component_len(format);
|
||||
GPU_framebuffer_read_color(
|
||||
read_result_fb_, 0, 0, UNPACK2(data_.extent), channel_count, 0, GPU_DATA_FLOAT, data);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
113
source/blender/draw/engines/eevee/eevee_film.hh
Normal file
113
source/blender/draw/engines/eevee/eevee_film.hh
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* A film is a fullscreen buffer (usually at output extent)
|
||||
* that will be able to accumulate sample in any distorted camera_type
|
||||
* using a pixel filter.
|
||||
*
|
||||
* Input needs to be jittered so that the filter converges to the right result.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "eevee_camera.hh"
|
||||
#include "eevee_sampling.hh"
|
||||
#include "eevee_shader.hh"
|
||||
#include "eevee_wrapper.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Film
|
||||
* \{ */
|
||||
|
||||
class Film {
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
/** Owned resources. */
|
||||
Framebuffer read_result_fb_;
|
||||
Framebuffer accumulation_fb_[2];
|
||||
Texture data_tx_[2];
|
||||
Texture weight_tx_[2];
|
||||
/** First sample in case we need to blend using it or just reuse it. */
|
||||
Texture first_sample_tx_ = {"first_sample_tx_"};
|
||||
|
||||
/** Reference to first_sample_tx_ or data_tx_ depending on the context. */
|
||||
GPUTexture *first_sample_ref_;
|
||||
|
||||
// DRWPass *clear_ps_ = nullptr;
|
||||
DRWPass *accumulate_ps_ = nullptr;
|
||||
DRWPass *resolve_ps_ = nullptr;
|
||||
|
||||
/** Shader parameter, not allocated. */
|
||||
GPUTexture *input_tx_;
|
||||
/** ViewProjection matrix used to render the input. */
|
||||
// float src_persmat_[4][4];
|
||||
/** ViewProjection matrix Inverse used to render the input. */
|
||||
// float src_persinv_[4][4];
|
||||
|
||||
draw::UniformBuffer<FilmData> data_;
|
||||
|
||||
/** True if offset or size changed. */
|
||||
bool has_changed_ = true;
|
||||
|
||||
/** Debug static name. */
|
||||
StringRefNull name_;
|
||||
|
||||
public:
|
||||
/* NOTE: name needs to be static. */
|
||||
Film(Instance &inst, eFilmDataType data_type, const char *name)
|
||||
: inst_(inst), data_tx_{{name}, {name}}, weight_tx_{{name}, {name}}, name_(name)
|
||||
{
|
||||
data_.extent[0] = data_.extent[1] = -1;
|
||||
data_.data_type = data_type;
|
||||
data_.use_history = 0;
|
||||
}
|
||||
|
||||
~Film(){};
|
||||
|
||||
void init(const int2 &full_extent, const rcti *output_rect);
|
||||
|
||||
void sync(void);
|
||||
void end_sync(void);
|
||||
|
||||
void accumulate(GPUTexture *input, const DRWView *view);
|
||||
|
||||
void resolve_viewport(GPUFrameBuffer *target);
|
||||
|
||||
void read_result(float *data);
|
||||
|
||||
private:
|
||||
bool do_smooth_viewport_smooth_transition(void)
|
||||
{
|
||||
return ELEM(data_.data_type, FILM_DATA_COLOR, FILM_DATA_COLOR_LOG) &&
|
||||
!DRW_state_is_image_render();
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
258
source/blender/draw/engines/eevee/eevee_gbuffer.hh
Normal file
258
source/blender/draw/engines/eevee/eevee_gbuffer.hh
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Gbuffer layout used for deferred shading pipeline.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "eevee_wrapper.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Gbuffer
|
||||
*
|
||||
* Fullscreen textures containing geometric, surface and volume data.
|
||||
* Used by deferred shading layers. Only one gbuffer is allocated per view
|
||||
* and is reused for each deferred layer. This is why there can only be temporary
|
||||
* texture inside it.
|
||||
* \{ */
|
||||
|
||||
/** NOTE: Theses are used as stencil bits. So we are limited to 8bits. */
|
||||
enum eClosureBits {
|
||||
CLOSURE_DIFFUSE = 1 << 0,
|
||||
CLOSURE_SSS = 1 << 1,
|
||||
CLOSURE_REFLECTION = 1 << 2,
|
||||
CLOSURE_REFRACTION = 1 << 3,
|
||||
CLOSURE_VOLUME = 1 << 4,
|
||||
CLOSURE_EMISSION = 1 << 5,
|
||||
CLOSURE_TRANSPARENCY = 1 << 6,
|
||||
};
|
||||
|
||||
struct GBuffer {
|
||||
TextureFromPool transmit_color_tx = {"GbufferTransmitColor"};
|
||||
TextureFromPool transmit_normal_tx = {"GbufferTransmitNormal"};
|
||||
TextureFromPool transmit_data_tx = {"GbufferTransmitData"};
|
||||
TextureFromPool reflect_color_tx = {"GbufferReflectionColor"};
|
||||
TextureFromPool reflect_normal_tx = {"GbufferReflectionNormal"};
|
||||
TextureFromPool volume_tx = {"GbufferVolume"};
|
||||
TextureFromPool emission_tx = {"GbufferEmission"};
|
||||
TextureFromPool transparency_tx = {"GbufferTransparency"};
|
||||
|
||||
Framebuffer gbuffer_fb = {"Gbuffer"};
|
||||
Framebuffer volume_fb = {"VolumeHeterogeneous"};
|
||||
|
||||
TextureFromPool holdout_tx = {"HoldoutRadiance"};
|
||||
TextureFromPool diffuse_tx = {"DiffuseRadiance"};
|
||||
|
||||
Framebuffer radiance_fb = {"Radiance"};
|
||||
Framebuffer radiance_clear_fb = {"RadianceClear"};
|
||||
|
||||
Framebuffer holdout_fb = {"Holdout"};
|
||||
|
||||
TextureFromPool depth_behind_tx = {"DepthBehind"};
|
||||
|
||||
Framebuffer depth_behind_fb = {"DepthCopy"};
|
||||
|
||||
/** Raytracing. */
|
||||
TextureFromPool ray_data_tx = {"RayData"};
|
||||
TextureFromPool ray_radiance_tx = {"RayRadiance"};
|
||||
TextureFromPool ray_variance_tx = {"RayVariance"};
|
||||
Framebuffer ray_data_fb = {"RayData"};
|
||||
Framebuffer ray_denoise_fb = {"RayDenoise"};
|
||||
|
||||
/* Owner of this GBuffer. Used to query temp textures. */
|
||||
void *owner;
|
||||
|
||||
/* Pointer to the view's buffers. */
|
||||
GPUTexture *depth_tx = nullptr;
|
||||
GPUTexture *combined_tx = nullptr;
|
||||
int layer = -1;
|
||||
|
||||
void sync(GPUTexture *depth_tx_, GPUTexture *combined_tx_, void *owner_, int layer_ = -1)
|
||||
{
|
||||
owner = owner_;
|
||||
depth_tx = depth_tx_;
|
||||
combined_tx = combined_tx_;
|
||||
layer = layer_;
|
||||
transmit_color_tx.sync();
|
||||
transmit_normal_tx.sync();
|
||||
transmit_data_tx.sync();
|
||||
reflect_color_tx.sync();
|
||||
reflect_normal_tx.sync();
|
||||
volume_tx.sync();
|
||||
emission_tx.sync();
|
||||
transparency_tx.sync();
|
||||
holdout_tx.sync();
|
||||
diffuse_tx.sync();
|
||||
depth_behind_tx.sync();
|
||||
ray_data_tx.sync();
|
||||
ray_radiance_tx.sync();
|
||||
ray_variance_tx.sync();
|
||||
}
|
||||
|
||||
void prepare(eClosureBits closures_used)
|
||||
{
|
||||
int2 extent = {GPU_texture_width(depth_tx), GPU_texture_height(depth_tx)};
|
||||
|
||||
/* TODO Reuse for different config. */
|
||||
if (closures_used & (CLOSURE_DIFFUSE | CLOSURE_SSS | CLOSURE_REFRACTION)) {
|
||||
transmit_color_tx.acquire(extent, GPU_R11F_G11F_B10F, owner);
|
||||
}
|
||||
if (closures_used & (CLOSURE_SSS | CLOSURE_REFRACTION)) {
|
||||
transmit_normal_tx.acquire(extent, GPU_RGBA16F, owner);
|
||||
transmit_data_tx.acquire(extent, GPU_R11F_G11F_B10F, owner);
|
||||
}
|
||||
else if (closures_used & CLOSURE_DIFFUSE) {
|
||||
transmit_normal_tx.acquire(extent, GPU_RG16F, owner);
|
||||
}
|
||||
if (closures_used & CLOSURE_SSS) {
|
||||
diffuse_tx.acquire(extent, GPU_RGBA16F, owner);
|
||||
}
|
||||
|
||||
if (closures_used & CLOSURE_REFLECTION) {
|
||||
reflect_color_tx.acquire(extent, GPU_R11F_G11F_B10F, owner);
|
||||
reflect_normal_tx.acquire(extent, GPU_RGBA16F, owner);
|
||||
}
|
||||
|
||||
if (closures_used & CLOSURE_VOLUME) {
|
||||
/* TODO(fclem): This is killing performance.
|
||||
* Idea: use interleaved data pattern to fill only a 32bpp buffer. */
|
||||
volume_tx.acquire(extent, GPU_RGBA32UI, owner);
|
||||
}
|
||||
|
||||
if (closures_used & CLOSURE_EMISSION) {
|
||||
emission_tx.acquire(extent, GPU_R11F_G11F_B10F, owner);
|
||||
}
|
||||
|
||||
if (closures_used & CLOSURE_TRANSPARENCY) {
|
||||
/* TODO(fclem): Speedup by using Dithered holdout and GPU_RGB10_A2. */
|
||||
transparency_tx.acquire(extent, GPU_RGBA16, owner);
|
||||
}
|
||||
|
||||
if (closures_used & (CLOSURE_DIFFUSE | CLOSURE_REFLECTION | CLOSURE_REFRACTION)) {
|
||||
ray_data_tx.acquire(extent, GPU_RGBA16F, owner);
|
||||
ray_radiance_tx.acquire(extent, GPU_RGBA16F, owner);
|
||||
ray_variance_tx.acquire(extent, GPU_R8, owner);
|
||||
}
|
||||
|
||||
holdout_tx.acquire(extent, GPU_R11F_G11F_B10F, owner);
|
||||
depth_behind_tx.acquire(extent, GPU_DEPTH24_STENCIL8, owner);
|
||||
|
||||
/* Layer attachement also works with cubemap. */
|
||||
gbuffer_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer),
|
||||
GPU_ATTACHMENT_TEXTURE(transmit_color_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(transmit_normal_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(transmit_data_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(reflect_color_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(reflect_normal_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(volume_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(emission_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(transparency_tx));
|
||||
}
|
||||
|
||||
void bind(void)
|
||||
{
|
||||
GPU_framebuffer_bind(gbuffer_fb);
|
||||
GPU_framebuffer_clear_stencil(gbuffer_fb, 0x0);
|
||||
}
|
||||
|
||||
void bind_radiance(void)
|
||||
{
|
||||
/* Layer attachement also works with cubemap. */
|
||||
radiance_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer),
|
||||
GPU_ATTACHMENT_TEXTURE(combined_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(diffuse_tx));
|
||||
GPU_framebuffer_bind(radiance_fb);
|
||||
}
|
||||
|
||||
void bind_volume(void)
|
||||
{
|
||||
/* Layer attachement also works with cubemap. */
|
||||
volume_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer),
|
||||
GPU_ATTACHMENT_TEXTURE(volume_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(transparency_tx));
|
||||
GPU_framebuffer_bind(volume_fb);
|
||||
}
|
||||
|
||||
void bind_tracing(void)
|
||||
{
|
||||
/* Layer attachement also works with cubemap. */
|
||||
/* Attach depth_stencil buffer to only trace the surfaces that need it. */
|
||||
ray_data_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer),
|
||||
GPU_ATTACHMENT_TEXTURE(ray_data_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(ray_radiance_tx));
|
||||
GPU_framebuffer_bind(ray_data_fb);
|
||||
|
||||
float color[4] = {0.0f};
|
||||
GPU_framebuffer_clear_color(ray_data_fb, color);
|
||||
}
|
||||
|
||||
void bind_holdout(void)
|
||||
{
|
||||
holdout_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(holdout_tx));
|
||||
GPU_framebuffer_bind(holdout_fb);
|
||||
}
|
||||
|
||||
void copy_depth_behind(void)
|
||||
{
|
||||
depth_behind_fb.ensure(GPU_ATTACHMENT_TEXTURE(depth_behind_tx));
|
||||
GPU_framebuffer_bind(depth_behind_fb);
|
||||
|
||||
GPU_framebuffer_blit(gbuffer_fb, 0, depth_behind_fb, 0, GPU_DEPTH_BIT);
|
||||
}
|
||||
|
||||
void clear_radiance(void)
|
||||
{
|
||||
radiance_clear_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(diffuse_tx));
|
||||
GPU_framebuffer_bind(radiance_clear_fb);
|
||||
|
||||
float color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
GPU_framebuffer_clear_color(radiance_clear_fb, color);
|
||||
}
|
||||
|
||||
void render_end(void)
|
||||
{
|
||||
transmit_color_tx.release();
|
||||
transmit_normal_tx.release();
|
||||
transmit_data_tx.release();
|
||||
reflect_color_tx.release();
|
||||
reflect_normal_tx.release();
|
||||
volume_tx.release();
|
||||
emission_tx.release();
|
||||
transparency_tx.release();
|
||||
holdout_tx.release();
|
||||
diffuse_tx.release();
|
||||
depth_behind_tx.release();
|
||||
ray_data_tx.release();
|
||||
ray_radiance_tx.release();
|
||||
ray_variance_tx.release();
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user