Transform: Implement Snap to Grid mode #116109
|
@ -176,7 +176,6 @@ def main() -> None:
|
|||
# Support version without a minor version "3" (add zero).
|
||||
tuple((0, 0, 0))
|
||||
)
|
||||
python_version_str = "%d.%d" % python_version_number[:2]
|
||||
|
||||
# Get Blender version.
|
||||
blender_version_str = str(make_utils.parse_blender_version())
|
||||
|
|
|
@ -9,4 +9,5 @@ requests==2.31.0
|
|||
|
||||
# Only needed to match the theme used for the official documentation.
|
||||
# Without this theme, the default theme will be used.
|
||||
sphinx_rtd_theme==1.3.0rc1
|
||||
furo==2024.1.29
|
||||
sphinx-basic-ng==1.0.0b2
|
||||
|
|
|
@ -1929,22 +1929,30 @@ def write_sphinx_conf_py(basepath):
|
|||
# The theme 'sphinx_rtd_theme' is no longer distributed with sphinx by default, only use when available.
|
||||
fw(r"""
|
||||
try:
|
||||
__import__('sphinx_rtd_theme')
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
import furo
|
||||
html_theme = "furo"
|
||||
del furo
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
""")
|
||||
if html_theme == "furo":
|
||||
html_theme_options = {
|
||||
"light_css_variables": {
|
||||
"color-brand-primary": "#265787",
|
||||
"color-brand-content": "#265787",
|
||||
},
|
||||
}
|
||||
|
||||
fw("if html_theme == 'sphinx_rtd_theme':\n")
|
||||
fw(" html_theme_options = {\n")
|
||||
fw(" 'display_version': False,\n")
|
||||
# fw(" 'analytics_id': '',\n")
|
||||
# fw(" 'collapse_navigation': True,\n")
|
||||
fw(" 'sticky_navigation': False,\n")
|
||||
fw(" 'navigation_depth': 1,\n")
|
||||
fw(" 'includehidden': False,\n")
|
||||
# fw(" 'titles_only': False\n")
|
||||
fw(" }\n\n")
|
||||
html_sidebars = {
|
||||
"**": [
|
||||
"sidebar/brand.html",
|
||||
"sidebar/search.html",
|
||||
"sidebar/scroll-start.html",
|
||||
"sidebar/navigation.html",
|
||||
"sidebar/scroll-end.html",
|
||||
# "sidebar/variant-selector.html",
|
||||
]
|
||||
}
|
||||
""")
|
||||
|
||||
# not helpful since the source is generated, adds to upload size.
|
||||
fw("html_copy_source = False\n")
|
||||
|
@ -1961,7 +1969,7 @@ except ModuleNotFoundError:
|
|||
fw("html_logo = 'static/blender_logo.svg'\n")
|
||||
# Disable default `last_updated` value, since this is the date of doc generation, not the one of the source commit.
|
||||
fw("html_last_updated_fmt = None\n\n")
|
||||
fw("if html_theme == 'sphinx_rtd_theme':\n")
|
||||
fw("if html_theme == 'furo':\n")
|
||||
fw(" html_css_files = ['css/version_switch.css']\n")
|
||||
fw(" html_js_files = ['js/version_switch.js']\n")
|
||||
|
||||
|
|
|
@ -1,19 +1,323 @@
|
|||
/* Hide home icon in search area */
|
||||
.wy-side-nav-search > a:hover {background: none; opacity: 0.9}
|
||||
.wy-side-nav-search > a.icon::before {content: none}
|
||||
/*
|
||||
* This stylesheet is applied after the theme's default one,
|
||||
* and thus any overrides or additions can be added here.
|
||||
*
|
||||
* More info:
|
||||
* https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx.application.Sphinx.add_css_file
|
||||
*/
|
||||
|
||||
.wy-nav-content {
|
||||
max-width: 1000px !important;
|
||||
body {
|
||||
--sidebar-caption-font-size: var(--font-size--normal);
|
||||
--toc-title-font-size: var(--font-size--normal);
|
||||
--toc-font-size: var(--sidebar-item-font-size);
|
||||
--admonition-font-size: var(--font-size--normal);
|
||||
--admonition-title-font-size: var(--font-size--normal);
|
||||
--color-api-name: #e87d0d;
|
||||
}
|
||||
|
||||
/* Fix long titles on mobile */
|
||||
h1, h2, h3, h4, h5, h6 {word-break: break-all}
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
margin-top: 1.75rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Temp fix for https://github.com/readthedocs/sphinx_rtd_theme/pull/1109 */
|
||||
.hlist tr {
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.hlist td {margin-right: auto}
|
||||
h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
.rubric {
|
||||
margin-top: 1.25rem;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 1.125em;
|
||||
}
|
||||
|
||||
/* Reduce the margins on top/bottom of horizontal lines. */
|
||||
hr.docutils {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
|
||||
/* Slightly decrease text to make the text fit on one line */
|
||||
.sidebar-brand-text {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.toctree-checkbox~label .icon svg {
|
||||
transition: transform 0.25s ease-out;
|
||||
}
|
||||
|
||||
/* Add more visual weight to definition terms */
|
||||
dl dt {
|
||||
font-weight: bold !important
|
||||
}
|
||||
|
||||
/* Fixes to field list, see #104636 */
|
||||
dl.field-list {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(80%, 95%);
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* TABLE & FIGURE */
|
||||
|
||||
/* Cell's vertical align. */
|
||||
/* use "valign" class for middle align */
|
||||
table.docutils:not(.valign) td {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/* Decrease whitespace above figure and add it below */
|
||||
figure {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
figcaption {
|
||||
margin-bottom: 0.5rem !important;
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* End TABLE & FIGURE. */
|
||||
|
||||
/* Force admonition to span the full width if close to a figure */
|
||||
.admonition {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* Use secondary font color for caption text */
|
||||
figcaption,
|
||||
caption {
|
||||
color: var(--color-foreground-secondary);
|
||||
font-size: var(--font-size--small)
|
||||
}
|
||||
|
||||
/* A bit hacky, revert the themes styling of kbd */
|
||||
kbd:not(.compound) {
|
||||
all: revert;
|
||||
}
|
||||
|
||||
/* Only style parent kbd elements instead of the individual children */
|
||||
:not(dl.option-list)> :not(kbd):not(kbd)>kbd,
|
||||
.menuselection {
|
||||
background-color: var(--color-background-secondary);
|
||||
border: 1px solid var(--color-foreground-border);
|
||||
border-radius: .2rem;
|
||||
box-shadow: 0 .0625rem 0 rgba(0, 0, 0, .2), inset 0 0 0 .125rem var(--color-background-secondary);
|
||||
color: var(--color-foreground-primary);
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0 .2rem;
|
||||
}
|
||||
|
||||
.highlight .nc,
|
||||
.highlight .nn,
|
||||
.highlight .gu {
|
||||
text-decoration-line: none !important;
|
||||
}
|
||||
|
||||
.caption .menuselection {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Quotes for Fig. "link". */
|
||||
a[href^="#fig-"]::before {
|
||||
content: "\201c";
|
||||
}
|
||||
|
||||
a[href^="#fig-"]::after {
|
||||
content: "\201d";
|
||||
}
|
||||
|
||||
/* Mark external links. */
|
||||
a.external {
|
||||
filter: brightness(150%);
|
||||
}
|
||||
|
||||
/* List blender.org as internal. */
|
||||
.external[href^="https://www.blender.org"],
|
||||
.external[href^="https://docs.blender.org"],
|
||||
.external[href^="https://projects.blender.org"],
|
||||
.external[href^="https://builder.blender.org"],
|
||||
.external[href^="https://code.blender.org"],
|
||||
.external[href^="https://translate.blender.org"],
|
||||
.external[href^="https://fund.blender.org"],
|
||||
.external[href^="blender_manual_html.zip"],
|
||||
.external[href^="blender_manual_epub.zip"],
|
||||
.external[href^="https://archive.blender.org"] {
|
||||
filter: revert;
|
||||
}
|
||||
|
||||
/* ".. container::" lead, block text float around image. */
|
||||
.lead {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Start reference admonition. */
|
||||
.admonition.refbox {
|
||||
border-color: rgb(50, 50, 50);
|
||||
}
|
||||
|
||||
.admonition.refbox>.admonition-title {
|
||||
background-color: rgba(50, 50, 50, 0.2);
|
||||
border-color: rgb(50, 50, 50);
|
||||
}
|
||||
|
||||
.admonition.refbox>.admonition-title::before {
|
||||
background-color: var(--color-content-foreground);
|
||||
}
|
||||
|
||||
/* 'refbox' field. */
|
||||
.refbox .field-list .field-name,
|
||||
.refbox .field-list .field-body {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.refbox dl dt {
|
||||
font-weight: normal
|
||||
}
|
||||
|
||||
/* End reference admonition. */
|
||||
|
||||
/* Applied on main index:sections. */
|
||||
|
||||
.global-index-toc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Start section cards. */
|
||||
.toc-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||
grid-gap: 20px;
|
||||
list-style-type: none;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: .3em;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.card div.figure,
|
||||
.card figure {
|
||||
margin-bottom: 0px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.card img {
|
||||
border-top-left-radius: .3em;
|
||||
border-top-right-radius: .3em;
|
||||
}
|
||||
|
||||
.card dl {
|
||||
margin-bottom: 10px
|
||||
}
|
||||
|
||||
.card dl dt>a {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.card dl dt a em,
|
||||
.card dl dt a span {
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.card dl dt {
|
||||
padding: 0px 15px 0px !important
|
||||
}
|
||||
|
||||
.card dl dd {
|
||||
padding: 0px 15px 5px 15px;
|
||||
font-style: normal;
|
||||
margin: 0px;
|
||||
color: var(--color-foreground-secondary);
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: 0 .2rem .5rem rgba(0, 0, 0, .05), 0 0 .0625rem rgba(0, 0, 0, .1);
|
||||
}
|
||||
|
||||
#getting-started .card {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* End section cards. */
|
||||
|
||||
/* Start custom toctree. */
|
||||
/* Indent all lines following the first. */
|
||||
.toctree-wrapper * a {
|
||||
display: block;
|
||||
padding-top: 0.25em;
|
||||
}
|
||||
|
||||
.toctree-wrapper ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
/* Underline provided by nested ul (not li). */
|
||||
.toctree-wrapper * ul {
|
||||
margin-bottom: 1rem !important;
|
||||
border-top: solid var(--color-background-border) 1px;
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
/* End custom toctree. */
|
||||
|
||||
/* Start footer contribute link */
|
||||
.footer-contribute {
|
||||
display: block;
|
||||
font-size: var(--font-size--small);
|
||||
}
|
||||
|
||||
.bottom-of-page {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.footer-contribute ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-bottom: 1rem
|
||||
}
|
||||
|
||||
.footer-contribute li {
|
||||
display: inline;
|
||||
list-style-type: none;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.footer-contribute {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* End footer contribute link */
|
|
@ -1,95 +1,98 @@
|
|||
/* Override RTD theme */
|
||||
.rst-versions {
|
||||
display: none;
|
||||
border-top: 0px;
|
||||
overflow: visible;
|
||||
}
|
||||
.version-btn.vdeact {
|
||||
cursor: default;
|
||||
color: dimgray;
|
||||
}
|
||||
|
||||
.version-btn.vdeact::after {
|
||||
content: "";
|
||||
}
|
||||
#versionwrap {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
padding-top: 2px;
|
||||
font-size: 90%;
|
||||
padding-left: 0;
|
||||
font-size: var(--sidebar-item-font-size);
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#versionwrap>ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#versionwrap>li {
|
||||
display: flex;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.version-btn {
|
||||
display: inline-block;
|
||||
background-color: #272525;
|
||||
width: 140px;
|
||||
background-color: var(--color-sidebar-background);
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding: 3px 10px;
|
||||
margin: 0px 5px 4px;
|
||||
vertical-align: middle;
|
||||
color: #27AE60;
|
||||
border: solid 1px #444444;
|
||||
color: var(--color-link);
|
||||
border: solid 1px var(--color-sidebar-background-border);
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
z-index: 400;
|
||||
transition: border-color 0.4s;
|
||||
}
|
||||
.version-btn::after {
|
||||
content:"\f0d8";
|
||||
display: inline;
|
||||
font: normal normal normal 16px/1 FontAwesome;
|
||||
color: #8d8c8c;
|
||||
vertical-align: top;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
.version-btn-open::after {
|
||||
color: gray;
|
||||
}
|
||||
.version-btn:hover, .version-btn:focus {
|
||||
|
||||
.version-btn:hover,
|
||||
.version-btn:focus {
|
||||
border-color: #525252;
|
||||
}
|
||||
|
||||
.version-btn-open {
|
||||
color: gray;
|
||||
border: solid 1px gray;
|
||||
border: solid 1px var(--color-sidebar-background-border);
|
||||
}
|
||||
|
||||
.version-btn.wait {
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.version-btn.disabled {
|
||||
cursor: not-allowed;
|
||||
color: dimgray;
|
||||
}
|
||||
|
||||
.version-dialog {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 28px;
|
||||
width: 140px;
|
||||
width: 50%;
|
||||
margin: 0 5px;
|
||||
padding-bottom: 4px;
|
||||
background-color: #0003;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 6px #000C;
|
||||
z-index: 999;
|
||||
max-height: calc(100vh - 30px);
|
||||
overflow-x: clip;
|
||||
overflow-y: auto;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.version-title {
|
||||
padding: 5px;
|
||||
color: black;
|
||||
color: var(--color-content-foreground);
|
||||
text-align: center;
|
||||
font-size: 102%;
|
||||
background-color: #27ae60;
|
||||
border-bottom: solid 1.5px #444;
|
||||
font-weight: 700;
|
||||
background-color: var(--color-brand-primary);
|
||||
border-bottom: solid 1.5px var(--color-sidebar-background-border);
|
||||
}
|
||||
|
||||
.version-list {
|
||||
padding-left: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 4px;
|
||||
text-align: center;
|
||||
background-color: #000C;
|
||||
border: solid 1px gray;
|
||||
border: solid 1px var(--color-sidebar-background-border);
|
||||
border-radius: 0px 0px 3px 3px;
|
||||
}
|
||||
.version-list a, .version-list span, .version-list li {
|
||||
|
||||
.version-list a,
|
||||
.version-list span,
|
||||
.version-list li {
|
||||
position: relative;
|
||||
display: block;
|
||||
font-size: 98%;
|
||||
|
@ -97,32 +100,21 @@
|
|||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 4px 0px;
|
||||
color: #404040;
|
||||
color: var(--color-sidebar-link-text);
|
||||
}
|
||||
|
||||
.version-list li {
|
||||
background-color: #ede9e9;
|
||||
color: #404040;
|
||||
background-color: var(--color-sidebar-background);
|
||||
color: var(--color-sidebar-link-text);
|
||||
padding: 1px;
|
||||
}
|
||||
.version-list li:hover, .version-list li a:focus {
|
||||
background-color: #b9cfda;
|
||||
|
||||
.version-list li:hover,
|
||||
.version-list li a:focus {
|
||||
background-color: var(--color-background-hover);
|
||||
}
|
||||
.version-list li.selected, .version-list li.selected:hover {
|
||||
background-color: #8d8c8c;
|
||||
}
|
||||
.version-list li.selected span {
|
||||
cursor: default;
|
||||
outline-color: red;
|
||||
}
|
||||
.version-arrow {
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
left: 50%;
|
||||
bottom: 4px;
|
||||
margin-left: -4px;
|
||||
transform: rotate(225deg);
|
||||
background: #ede9e9;
|
||||
border: 1px solid gray;
|
||||
border-width: 1px 0 0 1px;
|
||||
|
||||
.version-list li.selected {
|
||||
background: var(--color-sidebar-item-background--current);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
|
|
@ -1,63 +1,60 @@
|
|||
(function() { // switch: v1.2
|
||||
(function() { // switch: v1.4
|
||||
"use strict";
|
||||
|
||||
var versionsFileUrl = "https://docs.blender.org/PROD/versions.json"
|
||||
|
||||
var all_versions;
|
||||
|
||||
var Popover = function() {
|
||||
function Popover(id)
|
||||
class Popover {
|
||||
constructor(id)
|
||||
{
|
||||
this.isOpen = false;
|
||||
this.type = (id === "version-popover");
|
||||
this.$btn = $('#' + id);
|
||||
this.$dialog = this.$btn.next();
|
||||
this.$list = this.$dialog.children("ul");
|
||||
this.btn = document.querySelector('#' + id);
|
||||
this.dialog = this.btn.nextElementSibling;
|
||||
this.list = this.dialog.querySelector("ul");
|
||||
this.sel = null;
|
||||
this.beforeInit();
|
||||
}
|
||||
|
||||
Popover.prototype = {
|
||||
beforeInit : function() {
|
||||
var that = this;
|
||||
this.$btn.on("click", function(e) {
|
||||
const that = this;
|
||||
this.btnClickHandler = function(e) {
|
||||
that.init();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
this.btnKeyHandler = function(e) {
|
||||
if (that.btnKeyFilter(e)) {
|
||||
that.init();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
this.$btn.on("keydown", function(e) {
|
||||
if (that.btnKeyFilter(e)) {
|
||||
that.init();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
},
|
||||
init : function() {
|
||||
this.$btn.off("click");
|
||||
this.$btn.off("keydown");
|
||||
}
|
||||
};
|
||||
this.btn.addEventListener("click", this.btnClickHandler);
|
||||
this.btn.addEventListener("keydown", this.btnKeyHandler);
|
||||
}
|
||||
|
||||
init()
|
||||
{
|
||||
this.btn.removeEventListener("click", this.btnClickHandler);
|
||||
this.btn.removeEventListener("keydown", this.btnKeyHandler);
|
||||
|
||||
new Promise((resolve, reject) => {
|
||||
if (all_versions === undefined) {
|
||||
this.$btn.addClass("wait");
|
||||
this.loadVL(this);
|
||||
this.btn.classList.add("wait");
|
||||
fetch(versionsFileUrl)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
all_versions = data;
|
||||
resolve();
|
||||
})
|
||||
.catch(() => {
|
||||
console.error("Version Switch Error: versions.json could not be loaded.");
|
||||
this.btn.classList.remove("disabled");
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.afterLoad();
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
loadVL : function(that) {
|
||||
$.getJSON(versionsFileUrl, function(data) {
|
||||
all_versions = data;
|
||||
that.afterLoad();
|
||||
return true;
|
||||
}).fail(function() {
|
||||
console.log("Version Switch Error: versions.json could not be loaded.");
|
||||
that.$btn.addClass("disabled");
|
||||
return false;
|
||||
});
|
||||
},
|
||||
afterLoad : function() {
|
||||
var release = DOCUMENTATION_OPTIONS.VERSION;
|
||||
}).then(() => {
|
||||
let release = DOCUMENTATION_OPTIONS.VERSION;
|
||||
const m = release.match(/\d\.\d+/g);
|
||||
if (m) {
|
||||
release = m[0];
|
||||
|
@ -65,259 +62,274 @@ var Popover = function() {
|
|||
|
||||
this.warnOld(release, all_versions);
|
||||
|
||||
var version = this.getNamed(release);
|
||||
var list = this.buildList(version);
|
||||
const version = this.getNamed(release);
|
||||
this.buildList(version);
|
||||
|
||||
this.$list.children(":first-child").remove();
|
||||
this.$list.append(list);
|
||||
var that = this;
|
||||
this.$list.on("keydown", function(e) {
|
||||
this.list.firstElementChild.remove();
|
||||
const that = this;
|
||||
this.list.addEventListener("keydown", function(e) {
|
||||
that.keyMove(e);
|
||||
});
|
||||
|
||||
this.$btn.removeClass("wait");
|
||||
this.btn.classList.remove("wait");
|
||||
this.btnOpenHandler();
|
||||
this.$btn.on("mousedown", function(e) {
|
||||
this.btn.addEventListener("mousedown", function(e) {
|
||||
that.btnOpenHandler();
|
||||
e.preventDefault()
|
||||
});
|
||||
this.$btn.on("keydown", function(e) {
|
||||
this.btn.addEventListener("keydown", function(e) {
|
||||
if (that.btnKeyFilter(e)) {
|
||||
that.btnOpenHandler();
|
||||
}
|
||||
});
|
||||
},
|
||||
warnOld : function(release, all_versions) {
|
||||
// Note this is effectively disabled now, two issues must fixed:
|
||||
// * versions.js does not contain a current entry, because that leads to
|
||||
// duplicate version numbers in the menu. These need to be deduplicated.
|
||||
// * It only shows the warning after opening the menu to switch version
|
||||
// when versions.js is loaded. This is too late to be useful.
|
||||
var current = all_versions.current
|
||||
if (!current)
|
||||
{
|
||||
// console.log("Version Switch Error: no 'current' in version.json.");
|
||||
return;
|
||||
}
|
||||
const m = current.match(/\d\.\d+/g);
|
||||
if (m) {
|
||||
current = parseFloat(m[0]);
|
||||
}
|
||||
if (release < current) {
|
||||
var currentURL = window.location.pathname.replace(release, current);
|
||||
var warning = $('<div class="admonition warning"> ' +
|
||||
'<p class="first admonition-title">Note</p> ' +
|
||||
'<p class="last"> ' +
|
||||
'You are not using the most up to date version of the documentation. ' +
|
||||
'<a href="#"></a> is the newest version.' +
|
||||
'</p>' +
|
||||
'</div>');
|
||||
|
||||
warning.find('a').attr('href', currentURL).text(current);
|
||||
|
||||
var body = $("div.body");
|
||||
if (!body.length) {
|
||||
body = $("div.document");
|
||||
}
|
||||
body.prepend(warning);
|
||||
}
|
||||
},
|
||||
buildList : function(v) {
|
||||
var url = new URL(window.location.href);
|
||||
let pathSplit = [ "", "api", v ];
|
||||
if (url.pathname.startsWith("/api/")) {
|
||||
pathSplit.push(url.pathname.split('/').slice(3).join('/'));
|
||||
}
|
||||
else {
|
||||
pathSplit.push(url.pathname.substring(1));
|
||||
}
|
||||
if (this.type) {
|
||||
var dyn = all_versions;
|
||||
var cur = v;
|
||||
}
|
||||
var buf = [];
|
||||
var that = this;
|
||||
$.each(dyn, function(ix, title) {
|
||||
buf.push("<li");
|
||||
if (ix === cur) {
|
||||
buf.push(
|
||||
' class="selected" tabindex="-1" role="presentation"><span tabindex="-1" role="menuitem" aria-current="page">' +
|
||||
title + '</spanp></li>');
|
||||
}
|
||||
else {
|
||||
pathSplit[2 + that.type] = ix;
|
||||
var href = new URL(url);
|
||||
href.pathname = pathSplit.join('/');
|
||||
buf.push(' tabindex="-1" role="presentation"><a href ="' + href + '" tabindex="-1">' +
|
||||
title + '</a></li>');
|
||||
}
|
||||
});
|
||||
return buf.join('');
|
||||
},
|
||||
getNamed : function(v) {
|
||||
$.each(all_versions, function(ix, title) {
|
||||
if (ix === "master" || ix === "main" || ix === "latest") {
|
||||
var m = title.match(/\d\.\d[\w\d\.]*/)[0];
|
||||
if (parseFloat(m) == v) {
|
||||
v = ix;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
return v;
|
||||
},
|
||||
dialogToggle : function(speed) {
|
||||
var wasClose = !this.isOpen;
|
||||
var that = this;
|
||||
if (!this.isOpen) {
|
||||
this.$btn.addClass("version-btn-open");
|
||||
this.$btn.attr("aria-pressed", true);
|
||||
this.$dialog.attr("aria-hidden", false);
|
||||
this.$dialog.fadeIn(speed, function() {
|
||||
that.$btn.parent().on("focusout", function(e) {
|
||||
that.focusoutHandler();
|
||||
e.stopImmediatePropagation();
|
||||
})
|
||||
that.$btn.parent().on("mouseleave", function(e) {
|
||||
that.mouseoutHandler();
|
||||
e.stopImmediatePropagation();
|
||||
});
|
||||
});
|
||||
this.isOpen = true;
|
||||
}
|
||||
else {
|
||||
this.$btn.removeClass("version-btn-open");
|
||||
this.$btn.attr("aria-pressed", false);
|
||||
this.$dialog.attr("aria-hidden", true);
|
||||
this.$btn.parent().off("focusout");
|
||||
this.$btn.parent().off("mouseleave");
|
||||
this.$dialog.fadeOut(speed, function() {
|
||||
if (this.$sel) {
|
||||
this.$sel.attr("tabindex", -1);
|
||||
}
|
||||
that.$btn.attr("tabindex", 0);
|
||||
if (document.activeElement !== null && document.activeElement !== document &&
|
||||
document.activeElement !== document.body) {
|
||||
that.$btn.focus();
|
||||
}
|
||||
});
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
if (wasClose) {
|
||||
if (this.$sel) {
|
||||
this.$sel.attr("tabindex", -1);
|
||||
}
|
||||
if (document.activeElement !== null && document.activeElement !== document &&
|
||||
document.activeElement !== document.body) {
|
||||
var $nw = this.listEnter();
|
||||
$nw.attr("tabindex", 0);
|
||||
$nw.focus();
|
||||
this.$sel = $nw;
|
||||
}
|
||||
}
|
||||
},
|
||||
btnOpenHandler : function() {
|
||||
this.dialogToggle(300);
|
||||
},
|
||||
focusoutHandler : function() {
|
||||
var list = this.$list;
|
||||
var that = this;
|
||||
setTimeout(function() {
|
||||
if (list.find(":focus").length === 0) {
|
||||
that.dialogToggle(200);
|
||||
}
|
||||
}, 200);
|
||||
},
|
||||
mouseoutHandler : function() {
|
||||
this.dialogToggle(200);
|
||||
},
|
||||
btnKeyFilter : function(e) {
|
||||
if (e.ctrlKey || e.shiftKey) {
|
||||
return false;
|
||||
}
|
||||
if (e.key === " " || e.key === "Enter" || (e.key === "ArrowDown" && e.altKey) ||
|
||||
e.key === "ArrowDown" || e.key === "ArrowUp") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
keyMove : function(e) {
|
||||
if (e.ctrlKey || e.shiftKey) {
|
||||
return true;
|
||||
}
|
||||
var p = true;
|
||||
var $nw = $(e.target);
|
||||
switch (e.key) {
|
||||
case "ArrowUp":
|
||||
$nw = this.listPrev($nw);
|
||||
break;
|
||||
case "ArrowDown":
|
||||
$nw = this.listNext($nw);
|
||||
break;
|
||||
case "Home":
|
||||
$nw = this.listFirst();
|
||||
break;
|
||||
case "End":
|
||||
$nw = this.listLast();
|
||||
break;
|
||||
case "Escape":
|
||||
$nw = this.listExit();
|
||||
break;
|
||||
case "ArrowLeft":
|
||||
$nw = this.listExit();
|
||||
break;
|
||||
case "ArrowRight":
|
||||
$nw = this.listExit();
|
||||
break;
|
||||
default:
|
||||
p = false;
|
||||
}
|
||||
if (p) {
|
||||
$nw.attr("tabindex", 0);
|
||||
$nw.focus();
|
||||
if (this.$sel) {
|
||||
this.$sel.attr("tabindex", -1);
|
||||
}
|
||||
this.$sel = $nw;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
},
|
||||
listPrev : function($nw) {
|
||||
if ($nw.parent().prev().length !== 0) {
|
||||
return $nw.parent().prev().children(":first-child");
|
||||
}
|
||||
else {
|
||||
return this.listLast();
|
||||
}
|
||||
},
|
||||
listNext : function($nw) {
|
||||
if ($nw.parent().next().length !== 0) {
|
||||
return $nw.parent().next().children(":first-child");
|
||||
}
|
||||
else {
|
||||
return this.listFirst();
|
||||
}
|
||||
},
|
||||
listFirst : function() {
|
||||
return this.$list.children(":first-child").children(":first-child");
|
||||
},
|
||||
listLast : function() {
|
||||
return this.$list.children(":last-child").children(":first-child");
|
||||
},
|
||||
listExit : function() {
|
||||
this.mouseoutHandler();
|
||||
return this.$btn;
|
||||
},
|
||||
listEnter : function() {
|
||||
return this.$list.children(":first-child").children(":first-child");
|
||||
});
|
||||
}
|
||||
warnOld(release, all_versions)
|
||||
{
|
||||
// Note this is effectively disabled now, two issues must fixed:
|
||||
// * versions.js does not contain a current entry, because that leads to
|
||||
// duplicate version numbers in the menu. These need to be deduplicated.
|
||||
// * It only shows the warning after opening the menu to switch version
|
||||
// when versions.js is loaded. This is too late to be useful.
|
||||
let current = all_versions.current
|
||||
if (!current) {
|
||||
// console.log("Version Switch Error: no 'current' in version.json.");
|
||||
return;
|
||||
}
|
||||
};
|
||||
return Popover
|
||||
}();
|
||||
const m = current.match(/\d\.\d+/g);
|
||||
if (m) {
|
||||
current = parseFloat(m[0]);
|
||||
}
|
||||
if (release < current) {
|
||||
const currentURL = window.location.pathname.replace(release, current);
|
||||
const warning =
|
||||
document.querySelector("template#version-warning").firstElementChild.cloneNode(true);
|
||||
const link = warning.querySelector('a');
|
||||
link.setAttribute('href', currentURL);
|
||||
link.textContent = current;
|
||||
|
||||
$(document).ready(function() {
|
||||
var lng_popover = new Popover("version-popover");
|
||||
});
|
||||
let body = document.querySelector("div.body");
|
||||
if (!body.length) {
|
||||
body = document.querySelector("div.document");
|
||||
}
|
||||
body.prepend(warning);
|
||||
}
|
||||
}
|
||||
buildList(v)
|
||||
{
|
||||
const url = new URL(window.location.href);
|
||||
let pathSplit = [ "", "api", v ];
|
||||
if (url.pathname.startsWith("/api/")) {
|
||||
pathSplit.push(url.pathname.split('/').slice(4).join('/'));
|
||||
}
|
||||
else {
|
||||
pathSplit.push(url.pathname.substring(1));
|
||||
}
|
||||
let dyn, cur;
|
||||
if (this.type) {
|
||||
dyn = all_versions;
|
||||
cur = v;
|
||||
}
|
||||
const that = this;
|
||||
const template = document.querySelector("template#version-entry").content;
|
||||
for (let [ix, title] of Object.entries(dyn)) {
|
||||
let clone;
|
||||
if (ix === cur) {
|
||||
clone = template.querySelector("li.selected").cloneNode(true);
|
||||
clone.querySelector("span").innerHTML = title;
|
||||
}
|
||||
else {
|
||||
pathSplit[1 + that.type] = ix;
|
||||
let href = new URL(url);
|
||||
href.pathname = pathSplit.join('/');
|
||||
clone = template.firstElementChild.cloneNode(true);
|
||||
const link = clone.querySelector("a");
|
||||
link.href = href;
|
||||
link.innerHTML = title;
|
||||
}
|
||||
that.list.append(clone);
|
||||
};
|
||||
return this.list;
|
||||
}
|
||||
getNamed(v)
|
||||
{
|
||||
for (let [ix, title] of Object.entries(all_versions)) {
|
||||
if (ix === "master" || ix === "main" || ix === "latest") {
|
||||
const m = title.match(/\d\.\d[\w\d\.]*/)[0];
|
||||
if (parseFloat(m) == v) {
|
||||
v = ix;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
return v;
|
||||
}
|
||||
dialogToggle(speed)
|
||||
{
|
||||
const wasClose = !this.isOpen;
|
||||
const that = this;
|
||||
if (!this.isOpen) {
|
||||
this.btn.classList.add("version-btn-open");
|
||||
this.btn.setAttribute("aria-pressed", true);
|
||||
this.dialog.setAttribute("aria-hidden", false);
|
||||
this.dialog.style.display = "block";
|
||||
this.dialog.animate({opacity : [ 0, 1 ], easing : [ 'ease-in', 'ease-out' ]}, speed)
|
||||
.finished.then(() => {
|
||||
this.focusoutHandlerPrime = function(e) {
|
||||
that.focusoutHandler();
|
||||
e.stopImmediatePropagation();
|
||||
};
|
||||
this.mouseoutHandlerPrime = function(e) {
|
||||
that.mouseoutHandler();
|
||||
e.stopImmediatePropagation();
|
||||
};
|
||||
this.btn.parentNode.addEventListener("focusout", this.focusoutHandlerPrime);
|
||||
this.btn.parentNode.addEventListener("mouseleave", this.mouseoutHandlerPrime);
|
||||
});
|
||||
this.isOpen = true;
|
||||
}
|
||||
else {
|
||||
this.btn.classList.remove("version-btn-open");
|
||||
this.btn.setAttribute("aria-pressed", false);
|
||||
this.dialog.setAttribute("aria-hidden", true);
|
||||
this.btn.parentNode.removeEventListener("focusout", this.focusoutHandlerPrime);
|
||||
this.btn.parentNode.removeEventListener("mouseleave", this.mouseoutHandlerPrime);
|
||||
this.dialog.animate({opacity : [ 1, 0 ], easing : [ 'ease-in', 'ease-out' ]}, speed)
|
||||
.finished.then(() => {
|
||||
this.dialog.style.display = "none";
|
||||
if (this.sel) {
|
||||
this.sel.setAttribute("tabindex", -1);
|
||||
}
|
||||
this.btn.setAttribute("tabindex", 0);
|
||||
if (document.activeElement !== null && document.activeElement !== document &&
|
||||
document.activeElement !== document.body)
|
||||
{
|
||||
this.btn.focus();
|
||||
}
|
||||
});
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
if (wasClose) {
|
||||
if (this.sel) {
|
||||
this.sel.setAttribute("tabindex", -1);
|
||||
}
|
||||
if (document.activeElement !== null && document.activeElement !== document &&
|
||||
document.activeElement !== document.body)
|
||||
{
|
||||
const nw = this.listEnter();
|
||||
nw.setAttribute("tabindex", 0);
|
||||
nw.focus();
|
||||
this.sel = nw;
|
||||
}
|
||||
}
|
||||
}
|
||||
btnOpenHandler()
|
||||
{
|
||||
this.dialogToggle(300);
|
||||
}
|
||||
focusoutHandler()
|
||||
{
|
||||
const list = this.list;
|
||||
const that = this;
|
||||
setTimeout(function() {
|
||||
if (!list.querySelector(":focus")) {
|
||||
that.dialogToggle(200);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
mouseoutHandler()
|
||||
{
|
||||
this.dialogToggle(200);
|
||||
}
|
||||
btnKeyFilter(e)
|
||||
{
|
||||
if (e.ctrlKey || e.shiftKey) {
|
||||
return false;
|
||||
}
|
||||
if (e.key === " " || e.key === "Enter" || (e.key === "ArrowDown" && e.altKey) ||
|
||||
e.key === "ArrowDown" || e.key === "ArrowUp")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
keyMove(e)
|
||||
{
|
||||
if (e.ctrlKey || e.shiftKey) {
|
||||
return true;
|
||||
}
|
||||
let nw = e.target;
|
||||
switch (e.key) {
|
||||
case "ArrowUp":
|
||||
nw = this.listPrev(nw);
|
||||
break;
|
||||
case "ArrowDown":
|
||||
nw = this.listNext(nw);
|
||||
break;
|
||||
case "Home":
|
||||
nw = this.listFirst();
|
||||
break;
|
||||
case "End":
|
||||
nw = this.listLast();
|
||||
break;
|
||||
case "Escape":
|
||||
nw = this.listExit();
|
||||
break;
|
||||
case "ArrowLeft":
|
||||
nw = this.listExit();
|
||||
break;
|
||||
case "ArrowRight":
|
||||
nw = this.listExit();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
nw.setAttribute("tabindex", 0);
|
||||
nw.focus();
|
||||
if (this.sel) {
|
||||
this.sel.setAttribute("tabindex", -1);
|
||||
}
|
||||
this.sel = nw;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
listPrev(nw)
|
||||
{
|
||||
if (nw.parentNode.previousElementSibling.length !== 0) {
|
||||
return nw.parentNode.previousElementSibling.firstElementChild;
|
||||
}
|
||||
else {
|
||||
return this.listLast();
|
||||
}
|
||||
}
|
||||
listNext(nw)
|
||||
{
|
||||
if (nw.parentNode.nextElementSibling.length !== 0) {
|
||||
return nw.parentNode.nextElementSibling.firstElementChild;
|
||||
}
|
||||
else {
|
||||
return this.listFirst();
|
||||
}
|
||||
}
|
||||
listFirst()
|
||||
{
|
||||
return this.list.firstElementChild.firstElementChild;
|
||||
}
|
||||
listLast()
|
||||
{
|
||||
return this.list.lastElementChild.firstElementChild;
|
||||
}
|
||||
listExit()
|
||||
{
|
||||
this.mouseoutHandler();
|
||||
return this.btn;
|
||||
}
|
||||
listEnter()
|
||||
{
|
||||
return this.list.firstElementChild.firstElementChild;
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => { new Popover("version-popover"); });
|
||||
})();
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{%- extends "!base.html" -%}
|
||||
|
||||
{%- block theme_scripts -%}
|
||||
{{ super() }}
|
||||
<script defer data-domain="docs.blender.org" src="https://analytics.blender.org/js/script.js"></script>
|
||||
{%- endblock -%}
|
|
@ -1,6 +1,3 @@
|
|||
{# For the "Report Issue" button on the bottom of pages. #}
|
||||
{%- extends "!footer.html" %}
|
||||
{%- block extrafooter %}
|
||||
{%- if not pagename in ("search", "404", "genindex") and hasdoc(pagename) %}
|
||||
<div class="footer-contribute">
|
||||
<ul>
|
||||
|
@ -13,10 +10,9 @@
|
|||
#}Permanent+Link%5D%28https%3A%2F%2Fdocs.blender.org%2F{#
|
||||
#}api%2F{{ version }}%2F{{ pagename }}{{ file_suffix }}%29%0D%0A%0D%0A%2A%2A{#
|
||||
#}Short+description+of+error%2A%2A%0D%0A%5B{#
|
||||
#}Please+fill+out+a+short+description+of+the+error+here%5D%0D%0A"
|
||||
class="fa fa-bug"> {{ _('Report issue on this page') }}</a>
|
||||
#}Please+fill+out+a+short+description+of+the+error+here%5D%0D%0A" class="fa fa-bug"> {{ _('Report issue
|
||||
on this page') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{%- endif %}
|
||||
{% endblock %}
|
||||
{%- endif %}
|
|
@ -0,0 +1,6 @@
|
|||
{%- extends "!page.html" -%}
|
||||
|
||||
{%- block footer -%}
|
||||
{{ super() }}
|
||||
{%- include "components/footer_contribute.html" -%}
|
||||
{%- endblock footer -%}
|
|
@ -0,0 +1,29 @@
|
|||
<div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="document versions">
|
||||
<ul id="versionwrap" role="presentation">
|
||||
<li role="presentation">
|
||||
<span id="version-popover" class="version-btn" tabindex="0" role="button" aria-label="versions selector"
|
||||
aria-haspopup="true" aria-controls="version-vsnlist" aria-disabled="true">
|
||||
{{ release }}
|
||||
</span>
|
||||
<div class="version-dialog" aria-hidden="true">
|
||||
<div class="version-title">Versions</div>
|
||||
<ul id="version-vsnlist" class="version-list" role="menu" aria-labelledby="version-popover" aria-hidden="true">
|
||||
<li role="presentation">Loading...</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<template id="version-entry">
|
||||
<li tabindex="-1" role="presentation"><a tabindex="-1" role="menuitem"></a></li>
|
||||
<li class="selected" tabindex="-1" role="presentation"><span tabindex="-1" aria-current="page"></span></li>
|
||||
</template>
|
||||
</ul>
|
||||
<template id="version-warning">
|
||||
<div class="admonition warning">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">
|
||||
You are not using the most up to date version of the documentation.
|
||||
<a href="#"></a> is the newest version.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
|
@ -1,19 +0,0 @@
|
|||
<div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="document versions">
|
||||
<ul id="versionwrap" role="presentation">
|
||||
<li role="presentation">
|
||||
<span id="version-popover" class="version-btn" tabindex="0" role="button"
|
||||
aria-label="versions selector" aria-haspopup="true" aria-controls="version-vsnlist"
|
||||
aria-disabled="true">
|
||||
{{ release }}
|
||||
</span>
|
||||
<div class="version-dialog" aria-hidden="true">
|
||||
<div class="version-arrow" aria-hidden="true"></div>
|
||||
<div class="version-title">Versions</div>
|
||||
<ul id="version-vsnlist" class="version-list" role="menu"
|
||||
aria-labelledby="version-popover" aria-hidden="true">
|
||||
<li role="presentation">Loading...</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
|
@ -1,4 +1,3 @@
|
|||
When updating a library remember to:
|
||||
|
||||
* Update the README.blender with the corresponding version.
|
||||
* Update the THIRD-PARTY-LICENSES.txt document
|
||||
|
|
|
@ -119,7 +119,7 @@ BVHMetal::BVHMetal(const BVHParams ¶ms_,
|
|||
|
||||
BVHMetal::~BVHMetal()
|
||||
{
|
||||
/* Clear point used by enqueuing. */
|
||||
/* Clear point used by enqueueing. */
|
||||
device->release_bvh(this);
|
||||
|
||||
if (@available(macos 12.0, *)) {
|
||||
|
|
|
@ -341,7 +341,7 @@ string MetalDevice::preprocess_source(MetalPipelineType pso_type,
|
|||
}
|
||||
|
||||
# ifdef WITH_CYCLES_DEBUG
|
||||
global_defines += "#define __KERNEL_DEBUG__\n";
|
||||
global_defines += "#define WITH_CYCLES_DEBUG\n";
|
||||
# endif
|
||||
|
||||
switch (device_vendor) {
|
||||
|
|
|
@ -144,7 +144,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
|
|||
return false;
|
||||
}
|
||||
|
||||
#if defined(__KERNEL_DEBUG__)
|
||||
#if defined(WITH_CYCLES_DEBUG)
|
||||
if (is_null_instance_acceleration_structure(metal_ancillaries->accel_struct)) {
|
||||
isect->t = ray->tmax;
|
||||
isect->type = PRIMITIVE_NONE;
|
||||
|
@ -281,7 +281,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
|
|||
return false;
|
||||
}
|
||||
|
||||
# if defined(__KERNEL_DEBUG__)
|
||||
# if defined(WITH_CYCLES_DEBUG)
|
||||
if (is_null_instance_acceleration_structure(metal_ancillaries->accel_struct)) {
|
||||
if (local_isect) {
|
||||
local_isect->num_hits = 0;
|
||||
|
@ -383,7 +383,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
|
|||
return false;
|
||||
}
|
||||
|
||||
# if defined(__KERNEL_DEBUG__)
|
||||
# if defined(WITH_CYCLES_DEBUG)
|
||||
if (is_null_instance_acceleration_structure(metal_ancillaries->accel_struct)) {
|
||||
kernel_assert(!"Invalid metal_ancillaries->accel_struct pointer");
|
||||
return false;
|
||||
|
@ -446,7 +446,7 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
|
|||
return false;
|
||||
}
|
||||
|
||||
# if defined(__KERNEL_DEBUG__)
|
||||
# if defined(WITH_CYCLES_DEBUG)
|
||||
if (is_null_instance_acceleration_structure(metal_ancillaries->accel_struct)) {
|
||||
kernel_assert(!"Invalid metal_ancillaries->accel_struct pointer");
|
||||
return false;
|
||||
|
|
|
@ -313,32 +313,6 @@ ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray,
|
|||
return pdf;
|
||||
}
|
||||
|
||||
ccl_device float volume_equiangular_cdf(ccl_private const Ray *ccl_restrict ray,
|
||||
const float3 light_P,
|
||||
const float sample_t)
|
||||
{
|
||||
float delta = dot((light_P - ray->P), ray->D);
|
||||
float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta);
|
||||
if (UNLIKELY(D == 0.0f)) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const float tmin = ray->tmin;
|
||||
const float tmax = ray->tmax;
|
||||
const float t_ = sample_t - delta;
|
||||
|
||||
const float theta_a = atan2f(tmin - delta, D);
|
||||
const float theta_b = atan2f(tmax - delta, D);
|
||||
if (UNLIKELY(theta_b == theta_a)) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const float theta_sample = atan2f(t_, D);
|
||||
const float cdf = (theta_sample - theta_a) / (theta_b - theta_a);
|
||||
|
||||
return cdf;
|
||||
}
|
||||
|
||||
/* Distance sampling */
|
||||
|
||||
ccl_device float volume_distance_sample(float max_t,
|
||||
|
|
|
@ -212,7 +212,7 @@ ccl_device_inline void surface_shader_prepare_closures(KernelGlobals kg,
|
|||
/* BSDF */
|
||||
#ifdef WITH_CYCLES_DEBUG
|
||||
ccl_device_inline void surface_shader_validate_bsdf_sample(const KernelGlobals kg,
|
||||
const ShaderClosure *sc,
|
||||
ccl_private const ShaderClosure *sc,
|
||||
const float3 wo,
|
||||
const int org_label,
|
||||
const float2 org_roughness,
|
||||
|
|
|
@ -311,7 +311,7 @@ ccl_device_inline bool area_light_eval(const ccl_global KernelLight *klight,
|
|||
ls->pdf *= light_pdf_area_to_solid_angle(Ng, -ls->D, ls->t);
|
||||
}
|
||||
|
||||
return ls->eval_fac > 0;
|
||||
return in_volume_segment || ls->eval_fac > 0;
|
||||
}
|
||||
|
||||
template<bool in_volume_segment>
|
||||
|
|
|
@ -9,11 +9,13 @@
|
|||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Transform vector to spot light's local coordinate system. */
|
||||
ccl_device float3 spot_light_to_local(const ccl_global KernelSpotLight *spot, const float3 ray)
|
||||
ccl_device float3 spot_light_to_local(const ccl_global KernelLight *klight, const float3 ray)
|
||||
{
|
||||
return safe_normalize(make_float3(dot(ray, spot->scaled_axis_u),
|
||||
dot(ray, spot->scaled_axis_v),
|
||||
dot(ray, spot->dir * spot->inv_len_z)));
|
||||
const Transform itfm = klight->itfm;
|
||||
float3 transformed_ray = safe_normalize(transform_direction(&itfm, ray));
|
||||
transformed_ray.z = -transformed_ray.z;
|
||||
|
||||
return transformed_ray;
|
||||
}
|
||||
|
||||
/* Compute spot light attenuation of a ray given in local coordinate system. */
|
||||
|
@ -58,7 +60,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
|
|||
ls->t = FLT_MAX;
|
||||
if (d_sq > r_sq) {
|
||||
/* Outside sphere. */
|
||||
const float one_minus_cos_half_spot_spread = 1.0f - klight->spot.cos_half_spot_angle;
|
||||
const float one_minus_cos_half_spot_spread = 1.0f - klight->spot.cos_half_larger_spread;
|
||||
const float one_minus_cos_half_angle = sin_sqr_to_one_minus_cos(r_sq / d_sq);
|
||||
|
||||
if (in_volume_segment || one_minus_cos_half_angle < one_minus_cos_half_spot_spread) {
|
||||
|
@ -92,7 +94,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
|
|||
}
|
||||
|
||||
/* Attenuation. */
|
||||
const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D);
|
||||
const float3 local_ray = spot_light_to_local(klight, -ls->D);
|
||||
if (d_sq > r_sq) {
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
|
||||
}
|
||||
|
@ -128,7 +130,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
|
|||
ls->Ng = -ls->D;
|
||||
|
||||
/* Attenuation. */
|
||||
const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D);
|
||||
const float3 local_ray = spot_light_to_local(klight, -ls->D);
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
|
||||
if (!in_volume_segment && ls->eval_fac == 0.0f) {
|
||||
return false;
|
||||
|
@ -145,7 +147,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
|
|||
return true;
|
||||
}
|
||||
|
||||
ccl_device_forceinline float spot_light_pdf(const float cos_half_spread,
|
||||
ccl_device_forceinline float spot_light_pdf(const ccl_global KernelSpotLight *spot,
|
||||
const float d_sq,
|
||||
const float r_sq,
|
||||
const float3 N,
|
||||
|
@ -153,7 +155,8 @@ ccl_device_forceinline float spot_light_pdf(const float cos_half_spread,
|
|||
const uint32_t path_flag)
|
||||
{
|
||||
if (d_sq > r_sq) {
|
||||
return M_1_2PI_F / min(sin_sqr_to_one_minus_cos(r_sq / d_sq), 1.0f - cos_half_spread);
|
||||
return M_1_2PI_F /
|
||||
min(sin_sqr_to_one_minus_cos(r_sq / d_sq), 1.0f - spot->cos_half_larger_spread);
|
||||
}
|
||||
|
||||
const bool has_transmission = (path_flag & PATH_RAY_MIS_HAD_TRANSMISSION);
|
||||
|
@ -181,7 +184,7 @@ ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global Kerne
|
|||
/* NOTE : preserve pdf in area measure. */
|
||||
const float jacobian_solid_angle_to_area = 0.5f * fabsf(d_sq - r_sq - t_sq) /
|
||||
(radius * ls->t * t_sq);
|
||||
ls->pdf = spot_light_pdf(klight->spot.cos_half_spot_angle, d_sq, r_sq, N, ls->D, path_flag) *
|
||||
ls->pdf = spot_light_pdf(&klight->spot, d_sq, r_sq, N, ls->D, path_flag) *
|
||||
jacobian_solid_angle_to_area;
|
||||
|
||||
ls->Ng = normalize(ls->P - klight->co);
|
||||
|
@ -196,7 +199,7 @@ ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global Kerne
|
|||
}
|
||||
|
||||
/* Attenuation. */
|
||||
const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D);
|
||||
const float3 local_ray = spot_light_to_local(klight, -ls->D);
|
||||
if (use_attenuation) {
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
|
||||
}
|
||||
|
@ -232,7 +235,7 @@ ccl_device_inline bool spot_light_sample_from_intersection(
|
|||
ls->eval_fac = klight->spot.eval_fac;
|
||||
|
||||
if (klight->spot.is_sphere) {
|
||||
ls->pdf = spot_light_pdf(klight->spot.cos_half_spot_angle, d_sq, r_sq, N, ray_D, path_flag);
|
||||
ls->pdf = spot_light_pdf(&klight->spot, d_sq, r_sq, N, ray_D, path_flag);
|
||||
ls->Ng = normalize(ls->P - klight->co);
|
||||
}
|
||||
else {
|
||||
|
@ -248,7 +251,7 @@ ccl_device_inline bool spot_light_sample_from_intersection(
|
|||
}
|
||||
|
||||
/* Attenuation. */
|
||||
const float3 local_ray = spot_light_to_local(&klight->spot, -ray_D);
|
||||
const float3 local_ray = spot_light_to_local(klight, -ray_D);
|
||||
if (!klight->spot.is_sphere || d_sq > r_sq) {
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
|
||||
}
|
||||
|
|
|
@ -1367,16 +1367,15 @@ typedef struct KernelCurveSegment {
|
|||
static_assert_align(KernelCurveSegment, 8);
|
||||
|
||||
typedef struct KernelSpotLight {
|
||||
packed_float3 scaled_axis_u;
|
||||
float radius;
|
||||
packed_float3 scaled_axis_v;
|
||||
float eval_fac;
|
||||
packed_float3 dir;
|
||||
float radius;
|
||||
float eval_fac;
|
||||
float cos_half_spot_angle;
|
||||
float half_cot_half_spot_angle;
|
||||
float inv_len_z;
|
||||
float spot_smooth;
|
||||
int is_sphere;
|
||||
/* For non-uniform object scaling, the actual spread might be different. */
|
||||
float cos_half_larger_spread;
|
||||
} KernelSpotLight;
|
||||
|
||||
/* PointLight is SpotLight with only radius and invarea being used. */
|
||||
|
|
|
@ -1346,23 +1346,22 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce
|
|||
klights[light_index].area.normalize_spread = normalize_spread;
|
||||
}
|
||||
if (light->light_type == LIGHT_SPOT) {
|
||||
/* Scale axes to accommodate non-uniform scaling. */
|
||||
float3 scaled_axis_u = light->get_axisu() / len_squared(light->get_axisu());
|
||||
float3 scaled_axis_v = light->get_axisv() / len_squared(light->get_axisv());
|
||||
float len_z;
|
||||
/* Keep direction normalized. */
|
||||
float3 dir = safe_normalize_len(light->get_dir(), &len_z);
|
||||
const float cos_half_spot_angle = cosf(light->spot_angle * 0.5f);
|
||||
const float spot_smooth = 1.0f / ((1.0f - cos_half_spot_angle) * light->spot_smooth);
|
||||
const float tan_half_spot_angle = tanf(light->spot_angle * 0.5f);
|
||||
|
||||
float cos_half_spot_angle = cosf(light->spot_angle * 0.5f);
|
||||
float spot_smooth = 1.0f / ((1.0f - cos_half_spot_angle) * light->spot_smooth);
|
||||
const float len_w_sq = len_squared(light->get_dir());
|
||||
const float len_u_sq = len_squared(light->get_axisu());
|
||||
const float len_v_sq = len_squared(light->get_axisv());
|
||||
const float tan_sq = sqr(tan_half_spot_angle);
|
||||
|
||||
klights[light_index].spot.scaled_axis_u = scaled_axis_u;
|
||||
klights[light_index].spot.scaled_axis_v = scaled_axis_v;
|
||||
klights[light_index].spot.dir = dir;
|
||||
klights[light_index].spot.dir = safe_normalize(light->get_dir());
|
||||
klights[light_index].spot.cos_half_spot_angle = cos_half_spot_angle;
|
||||
klights[light_index].spot.half_cot_half_spot_angle = 0.5f / tanf(light->spot_angle * 0.5f);
|
||||
klights[light_index].spot.inv_len_z = 1.0f / len_z;
|
||||
klights[light_index].spot.half_cot_half_spot_angle = 0.5f / tan_half_spot_angle;
|
||||
klights[light_index].spot.spot_smooth = spot_smooth;
|
||||
/* Choose the angle which spans a larger cone. */
|
||||
klights[light_index].spot.cos_half_larger_spread = inversesqrtf(
|
||||
1.0f + tan_sq * fmaxf(len_u_sq, len_v_sq) / len_w_sq);
|
||||
}
|
||||
|
||||
klights[light_index].shader_id = shader_id;
|
||||
|
|
|
@ -19,22 +19,24 @@ PERFORMANCE OF THIS SOFTWARE.
|
|||
|
||||
------
|
||||
|
||||
** Audaspace; version 1.4+ (ae29ce2) -- https://audaspace.github.io/
|
||||
** Audaspace; version 0d18fe7 -- https://audaspace.github.io/
|
||||
** Cuda Wrangler; version cbf465b -- https://github.com/CudaWrangler/cuew
|
||||
** Draco; version 1.3.6 -- https://google.github.io/draco/
|
||||
** Embree; version 4.1.0 -- https://github.com/embree/embree
|
||||
** Intel(R) oneAPI DPC++ compiler; version 2022-12 --
|
||||
https://github.com/intel/llvm#oneapi-dpc-compiler
|
||||
** Intel® Open Path Guiding Library; version 0.6.0 -- http://www.openpgl.org/
|
||||
** Intel® Open Path Guiding Library; version 0.5.0 -- http://www.openpgl.org/
|
||||
** Mantaflow; version 0.13 -- http://mantaflow.com/
|
||||
** materialX; version 1.38.6 --
|
||||
** materialX; version 1.38.8 --
|
||||
https://github.com/AcademySoftwareFoundation/MaterialX
|
||||
** meson; version 0.63 -- https://github.com/mesonbuild/meson
|
||||
** oneAPI Threading Building Block; version 2020_U3 --
|
||||
https://software.intel.com/en-us/oneapi/onetbb
|
||||
** OpenImageDenoise; version 1.4.3 -- https://www.openimagedenoise.org/
|
||||
** OpenImageIO; version 2.4.15.0 -- http://www.openimageio.org
|
||||
** OpenSSL; version 3.1.2 -- https://www.openssl.org/
|
||||
** OpenCL Wrangler; version 27a6867 -- https://github.com/OpenCLWrangler/clew
|
||||
** OpenImageDenoise; version 2.2.2 -- https://www.openimagedenoise.org/
|
||||
** OpenImageIO; version 2.5.6.0 --
|
||||
https://github.com/AcademySoftwareFoundation/OpenImageIO
|
||||
** OpenSSL; version 3.1.5 -- https://www.openssl.org/
|
||||
** OpenXR SDK; version 1.0.17 -- https://khronos.org/openxr
|
||||
** RangeTree; version 40ebed8aa209 -- https://github.com/ideasman42/rangetree-c
|
||||
** SDL Extension Wrangler; version 15edf8e --
|
||||
|
@ -42,7 +44,9 @@ https://github.com/SDLWrangler/sdlew
|
|||
** ShaderC; version 2022.3 -- https://github.com/google/shaderc
|
||||
** SYCL Unified Runtime ; version fd711c920acc4434cb52ff18b078c082d9d7f44d --
|
||||
https://github.com/oneapi-src/unified-runtime
|
||||
** Vulkan Loader; version 1.2.198 --
|
||||
** Vulkan Headers; version 1.3.270 --
|
||||
https://github.com/KhronosGroup/Vulkan-Headers
|
||||
** Vulkan Loader; version 1.3.270 --
|
||||
https://github.com/KhronosGroup/Vulkan-Loader
|
||||
|
||||
Apache License
|
||||
|
@ -244,7 +248,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
|
||||
* For Audaspace see also this required NOTICE:
|
||||
Copyright © 2009-2023 Jörg Müller. All rights reserved.
|
||||
Copyright © 2009-2020 Jörg Müller. All rights reserved.
|
||||
* For Cuda Wrangler see also this required NOTICE:
|
||||
Copyright 2011-2014 Blender Foundation
|
||||
* For Draco see also this required NOTICE:
|
||||
|
@ -290,6 +294,8 @@ limitations under the License.
|
|||
Copyright 2015 The Shaderc Authors. All rights reserved.
|
||||
* For SYCL Unified Runtime see also this required NOTICE:
|
||||
Copyright (C) 2022-2023 Intel Corporation
|
||||
* For Vulkan Headers see also this required NOTICE:
|
||||
Copyright 2015-2023 The Khronos Group Inc.
|
||||
* For Vulkan Loader see also this required NOTICE:
|
||||
Copyright (c) 2019 The Khronos Group Inc.
|
||||
Copyright (c) 2019 Valve Corporation
|
||||
|
@ -391,22 +397,21 @@ Copyright (c) 2006, Google Inc.
|
|||
All rights reserved.
|
||||
** Imath; version 3.1.7 -- https://github.com/AcademySoftwareFoundation/Imath
|
||||
Copyright Contributors to the OpenEXR Project. All rights reserved.
|
||||
** ISPC; version 1.17.0 -- https://github.com/ispc/ispc
|
||||
** ISPC; version 1.21.1 -- https://github.com/ispc/ispc
|
||||
Copyright Intel Corporation
|
||||
All rights reserved.
|
||||
** NumPy; version 1.23.5 -- https://numpy.org/
|
||||
** NumPy; version 1.24.3 -- https://numpy.org/
|
||||
Copyright (c) 2005-2022, NumPy Developers.
|
||||
All rights reserved.
|
||||
** Ogg; version 1.3.5 -- https://www.xiph.org/ogg/
|
||||
COPYRIGHT (C) 1994-2019 by the Xiph.Org Foundation https://www.xiph.org/
|
||||
** Open Shading Language; version
|
||||
1.13-dev-1a7670600c8b08c2443a78d03c8c27e9a1149140 --
|
||||
https://github.com/imageworks/OpenShadingLanguage
|
||||
** Open Shading Language; version 1.13.7.0 --
|
||||
https://github.com/AcademySoftwareFoundation/OpenShadingLanguage/
|
||||
Copyright Contributors to the Open Shading Language project.
|
||||
** OpenColorIO; version 2.2.0 --
|
||||
** OpenColorIO; version 2.3.2 --
|
||||
https://github.com/AcademySoftwareFoundation/OpenColorIO
|
||||
Copyright Contributors to the OpenColorIO Project.
|
||||
** OpenEXR; version 3.1.7 --
|
||||
** OpenEXR; version 3.2.1 --
|
||||
https://github.com/AcademySoftwareFoundation/openexr
|
||||
Copyright Contributors to the OpenEXR Project. All rights reserved.
|
||||
** Pystring; version 1.1.3 -- https://github.com/imageworks/pystring
|
||||
|
@ -414,7 +419,7 @@ Copyright (c) 2008-2010, Sony Pictures Imageworks Inc
|
|||
All rights reserved.
|
||||
** Vorbis; version 1.3.7 -- https://xiph.org/vorbis/
|
||||
Copyright (c) 2002-2020 Xiph.org Foundation
|
||||
** VPX; version 1.11.0 -- https://github.com/webmproject/libvpx
|
||||
** VPX; version 1.14.0 -- https://github.com/webmproject/libvpx
|
||||
Copyright (c) 2010, The WebM Project authors. All rights reserved.
|
||||
** WebP; version 1.3.2 -- https://github.com/webmproject/libwebp
|
||||
Copyright (c) 2010, Google Inc. All rights reserved.
|
||||
|
@ -913,7 +918,7 @@ Copyright (c) 2003, 2007-14 Matteo Frigo
|
|||
Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology
|
||||
** GMP; version 6.2.1 -- https://gmplib.org/
|
||||
Copyright 1996-2020 Free Software Foundation, Inc.
|
||||
** OpenAL; version 1.21.1 -- http://openal-soft.org
|
||||
** OpenAL; version 1.23.1 -- http://openal-soft.org
|
||||
Copyright (c) 2015, Archontis Politis
|
||||
Copyright (c) 2019, Christopher Robinson
|
||||
|
||||
|
@ -2804,7 +2809,7 @@ That's all there is to it!
|
|||
|
||||
------
|
||||
|
||||
** FFmpeg; version 6.0 -- http://ffmpeg.org/
|
||||
** FFmpeg; version 6.1.1 -- http://ffmpeg.org/
|
||||
Copyright: The FFmpeg contributors
|
||||
https://github.com/FFmpeg/FFmpeg/blob/master/CREDITS
|
||||
** Libsndfile; version 1.2.2 -- http://libsndfile.github.io/libsndfile/
|
||||
|
@ -3433,6 +3438,33 @@ December 9, 2010
|
|||
|
||||
------
|
||||
|
||||
** libdeflate; version 1.18 -- https://github.com/ebiggers/libdeflate
|
||||
Copyright 2016 Eric Biggers
|
||||
|
||||
Copyright 2016 Eric Biggers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
------
|
||||
|
||||
** vcintrinsics; version 782fbf7301dc73acaa049a4324c976ad94f587f7 --
|
||||
https://github.com/intel/vc-intrinsics
|
||||
Copyright (c) 2019 Intel Corporation
|
||||
|
@ -3471,7 +3503,7 @@ Copyright © 2013 The Khronos Group Inc.
|
|||
** Expat; version 2.5.0 -- https://github.com/libexpat/libexpat/
|
||||
Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper
|
||||
Copyright (c) 2001-2019 Expat maintainers
|
||||
** Intel(R) Graphics Compute Runtime; version 22.38.24278 --
|
||||
** Intel(R) Graphics Compute Runtime; version 23.43.27642.40 --
|
||||
https://github.com/intel/compute-runtime
|
||||
Copyright (C) 2021 Intel Corporation
|
||||
** Intel(R) Graphics Memory Management Library; version 22.1.8 --
|
||||
|
@ -3490,11 +3522,11 @@ Copyright (c) 2006, 2008 Junio C Hamano
|
|||
Copyright © 2017-2018 Red Hat Inc.
|
||||
Copyright © 2012 Collabora, Ltd.
|
||||
Copyright © 2008 Kristian Høgsberg
|
||||
** Libxml2; version 2.10.4 -- http://xmlsoft.org/
|
||||
** Libxml2; version 2.12.3 -- http://xmlsoft.org/
|
||||
Copyright (C) 1998-2012 Daniel Veillard. All Rights Reserved.
|
||||
** Mesa 3D; version 21.1.5 -- https://www.mesa3d.org/
|
||||
** Mesa 3D; version 23.3.0 -- https://www.mesa3d.org/
|
||||
Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
|
||||
** oneAPI Level Zero; version v1.8.8 --
|
||||
** oneAPI Level Zero; version v1.15.8 --
|
||||
https://github.com/oneapi-src/level-zero
|
||||
Copyright (C) 2019-2021 Intel Corporation
|
||||
** OPENCollada; version 1.6.68 -- https://github.com/KhronosGroup/OpenCOLLADA
|
||||
|
@ -3506,11 +3538,12 @@ Copyright (c) 2018 Jingwei Huang, Yichao Zhou, Matthias Niessner,
|
|||
Jonathan Shewchuk and Leonidas Guibas. All rights reserved.
|
||||
** robin-map; version 0.6.2 -- https://github.com/Tessil/robin-map
|
||||
Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
** sse2neon; version 1.6.0 -- https://github.com/DLTcollab/sse2neon
|
||||
** sse2neon; version cfaa59fc04fecb117c0a0f3fe9c82dece6f359ad --
|
||||
https://github.com/DLTcollab/sse2neon
|
||||
Copyright sse2neon contributors
|
||||
** TinyGLTF; version 2.8.21 -- https://github.com/syoyo/tinygltf
|
||||
** TinyGLTF; version 2.5.0 -- https://github.com/syoyo/tinygltf
|
||||
Copyright (c) 2017 Syoyo Fujita, Aurélien Chatelain and many contributors
|
||||
** Wayland protocols; version 1.31 --
|
||||
** Wayland protocols; version 1.32 --
|
||||
https://gitlab.freedesktop.org/wayland/wayland-protocols
|
||||
Copyright © 2008-2013 Kristian Høgsberg
|
||||
Copyright © 2010-2013 Intel Corporation
|
||||
|
@ -3605,7 +3638,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|||
|
||||
------
|
||||
|
||||
** OpenVDB; version 10.0.0 -- http://www.openvdb.org/
|
||||
** OpenVDB; version 11.0.0 -- http://www.openvdb.org/
|
||||
Copyright Contributors to the OpenVDB Project
|
||||
|
||||
Mozilla Public License Version 2.0
|
||||
|
@ -4037,7 +4070,7 @@ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
|
||||
------
|
||||
|
||||
** The LLVM Compiler Infrastructure; version 12.0.0 --
|
||||
** The LLVM Compiler Infrastructure; version 17.0.6 --
|
||||
https://github.com/llvm/llvm-project/
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
@ -4063,9 +4096,9 @@ Software.
|
|||
|
||||
------
|
||||
|
||||
** OpenSubdiv; version 3.5.0 -- http://graphics.pixar.com/opensubdiv
|
||||
** OpenSubdiv; version 3.6.0 -- http://graphics.pixar.com/opensubdiv
|
||||
Copyright 2013 Pixar
|
||||
** Universal Scene Description; version 23.05 -- http://www.openusd.org/
|
||||
** Universal Scene Description; version 23.11 -- http://www.openusd.org/
|
||||
Copyright 2016 Pixar
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "Apache License") with the
|
||||
|
@ -4110,7 +4143,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
------
|
||||
|
||||
** Boost C++ Libraries; version 1.80.0 -- https://www.boost.org/
|
||||
** Boost C++ Libraries; version 1.82.0 -- https://www.boost.org/
|
||||
The Boost license encourages both commercial and non-commercial use and does
|
||||
not require attribution for binary use.
|
||||
|
||||
|
@ -4276,7 +4309,7 @@ MIT Expat
|
|||
|
||||
------
|
||||
|
||||
** Python; version 3.10.13 -- https://www.python.org
|
||||
** Python; version 3.11.7 -- https://www.python.org
|
||||
Copyright (c) 2001-2021 Python Software Foundation. All rights reserved.
|
||||
|
||||
A. HISTORY OF THE SOFTWARE
|
||||
|
@ -4563,6 +4596,33 @@ PERFORMANCE OF THIS SOFTWARE.
|
|||
|
||||
------
|
||||
|
||||
** libffi; version 3.4.4 -- https://github.com/libffi/libffi/
|
||||
libffi - Copyright (c) 1996-2024 Anthony Green, Red Hat, Inc and others.
|
||||
|
||||
libffi - Copyright (c) 1996-2024 Anthony Green, Red Hat, Inc and others.
|
||||
See source files for details.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
``Software''), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
------
|
||||
|
||||
** Flex; version 2.6.4 -- https://github.com/westes/flex
|
||||
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The Flex Project.
|
||||
|
||||
|
|
|
@ -425,10 +425,13 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
|
|||
if bl_info is not None:
|
||||
# Use `_init` to detect when `bl_info` was generated from the manifest, see: `_bl_info_from_extension`.
|
||||
if type(bl_info) is dict and "_init" not in bl_info:
|
||||
print(
|
||||
"Add-on \"%s\" has a \"bl_info\" which will be ignored in favor of \"%s\"" %
|
||||
(module_name, _ext_manifest_filename_toml)
|
||||
)
|
||||
# This print is noisy, hide behind a debug flag.
|
||||
# Once `bl_info` is fully deprecated this should be changed to always print a warning.
|
||||
if _bpy.app.debug_python:
|
||||
print(
|
||||
"Add-on \"%s\" has a \"bl_info\" which will be ignored in favor of \"%s\"" %
|
||||
(module_name, _ext_manifest_filename_toml)
|
||||
)
|
||||
# Always remove as this is not expected to exist and will be lazily initialized.
|
||||
del mod.bl_info
|
||||
|
||||
|
@ -490,7 +493,7 @@ def disable(module_name, *, default_set=False, handle_error=None):
|
|||
# the add-on in the user preferences.
|
||||
if mod and getattr(mod, "__addon_enabled__", False) is not False:
|
||||
mod.__addon_enabled__ = False
|
||||
mod.__addon_persistent = False
|
||||
mod.__addon_persistent__ = False
|
||||
|
||||
try:
|
||||
mod.unregister()
|
||||
|
@ -503,7 +506,7 @@ def disable(module_name, *, default_set=False, handle_error=None):
|
|||
print(
|
||||
"addon_utils.disable: %s not %s" % (
|
||||
module_name,
|
||||
"disabled" if mod is None else "loaded")
|
||||
"loaded" if mod is None else "enabled")
|
||||
)
|
||||
|
||||
# could be in more than once, unlikely but better do this just in case.
|
||||
|
|
|
@ -529,6 +529,15 @@ class ARMATURE_OT_copy_bone_color_to_selected(Operator):
|
|||
return {'FINISHED'}
|
||||
|
||||
|
||||
def _armature_from_context(context):
|
||||
pin_armature = getattr(context, 'armature', None)
|
||||
if pin_armature:
|
||||
return pin_armature
|
||||
if context.object and context.object.type == 'ARMATURE':
|
||||
return context.object.data
|
||||
return None
|
||||
|
||||
|
||||
class ARMATURE_OT_collection_show_all(Operator):
|
||||
"""Show all bone collections"""
|
||||
bl_idname = "armature.collection_show_all"
|
||||
|
@ -537,10 +546,10 @@ class ARMATURE_OT_collection_show_all(Operator):
|
|||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object and context.object.type == 'ARMATURE' and context.object.data
|
||||
return _armature_from_context(context) is not None
|
||||
|
||||
def execute(self, context):
|
||||
arm = context.object.data
|
||||
arm = _armature_from_context(context)
|
||||
for bcoll in arm.collections_all:
|
||||
bcoll.is_visible = True
|
||||
return {'FINISHED'}
|
||||
|
@ -554,15 +563,16 @@ class ARMATURE_OT_collection_unsolo_all(Operator):
|
|||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not (context.object and context.object.type == 'ARMATURE' and context.object.data):
|
||||
armature = _armature_from_context(context)
|
||||
if not armature:
|
||||
return False
|
||||
if not context.object.data.collections.is_solo_active:
|
||||
if not armature.collections.is_solo_active:
|
||||
cls.poll_message_set("None of the bone collections is marked 'solo'")
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
arm = context.object.data
|
||||
arm = _armature_from_context(context)
|
||||
for bcoll in arm.collections_all:
|
||||
bcoll.is_solo = False
|
||||
return {'FINISHED'}
|
||||
|
@ -578,16 +588,16 @@ class ARMATURE_OT_collection_remove_unused(Operator):
|
|||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.object or context.object.type != 'ARMATURE':
|
||||
armature = _armature_from_context(context)
|
||||
if not armature:
|
||||
return False
|
||||
arm = context.object.data
|
||||
return len(arm.collections) > 0
|
||||
return len(armature.collections) > 0
|
||||
|
||||
def execute(self, context):
|
||||
if context.object.mode == 'EDIT':
|
||||
if context.mode == 'EDIT_ARMATURE':
|
||||
return self.execute_edit_mode(context)
|
||||
|
||||
armature = context.object.data
|
||||
armature = _armature_from_context(context)
|
||||
|
||||
# Build a set of bone collections that don't contain any bones, and
|
||||
# whose children also don't contain any bones.
|
||||
|
@ -608,7 +618,7 @@ class ARMATURE_OT_collection_remove_unused(Operator):
|
|||
# edit mode, because that has a completely separate list of edit bones.
|
||||
# This is why edit mode needs separate handling.
|
||||
|
||||
armature = context.object.data
|
||||
armature = _armature_from_context(context)
|
||||
bcolls_with_bones = {
|
||||
bcoll
|
||||
for ebone in armature.edit_bones
|
||||
|
|
|
@ -119,6 +119,7 @@ class DATA_PT_EEVEE_light(DataButtonsPanel, Panel):
|
|||
col.prop(light, "use_shadow", text="Cast Shadow")
|
||||
col.prop(light, "shadow_softness_factor", text="Shadow Softness")
|
||||
col.prop(light, "shadow_filter_radius", text="Filtering Radius")
|
||||
col.prop(light, "shadow_resolution_scale", text="Resolution Scale")
|
||||
|
||||
if light.type == 'SUN':
|
||||
col.prop(light, "shadow_trace_distance", text="Trace Distance")
|
||||
|
|
|
@ -155,6 +155,7 @@ class OBJECT_MT_modifier_add_generate(ModifierAddMenu, Menu):
|
|||
self.operator_modifier_add(layout, 'WIREFRAME')
|
||||
if ob_type == 'GREASEPENCIL':
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_ARRAY')
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_BUILD')
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_DASH')
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_ENVELOPE')
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_LENGTH')
|
||||
|
|
|
@ -198,6 +198,7 @@ class RENDER_PT_eevee_next_horizon_scan(RenderButtonsPanel, Panel):
|
|||
col.prop(props, "horizon_quality", text="Precision")
|
||||
col.prop(props, "horizon_thickness", text="Thickness")
|
||||
col.prop(props, "horizon_bias", text="Bias")
|
||||
col.prop(props, "horizon_resolution", text="Resolution")
|
||||
|
||||
|
||||
class RENDER_PT_eevee_motion_blur(RenderButtonsPanel, Panel):
|
||||
|
@ -726,6 +727,9 @@ class RENDER_PT_eevee_next_shadows(RenderButtonsPanel, Panel):
|
|||
col = layout.column()
|
||||
col.prop(props, "shadow_normal_bias", text="Normal Bias")
|
||||
|
||||
col = layout.column()
|
||||
col.prop(props, "use_shadow_jittered_viewport", text="Jittered Transparency (Viewport)")
|
||||
|
||||
|
||||
class RENDER_PT_eevee_sampling(RenderButtonsPanel, Panel):
|
||||
bl_label = "Sampling"
|
||||
|
|
|
@ -2678,7 +2678,7 @@ class VIEW3D_MT_image_add(Menu):
|
|||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
# Expliclitly set background mode on/off as operator will try to
|
||||
# Explicitly set background mode on/off as operator will try to
|
||||
# auto detect which mode to use otherwise.
|
||||
layout.operator("object.empty_image_add", text="Reference", icon='IMAGE_REFERENCE').background = False
|
||||
layout.operator("object.empty_image_add", text="Background", icon='IMAGE_BACKGROUND').background = True
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "DNA_vec_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_bits.h"
|
||||
#include "BLI_math_color_blend.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_path_util.h"
|
||||
|
@ -344,6 +345,12 @@ void blf_batch_draw()
|
|||
|
||||
GPU_batch_program_set_builtin(g_batch.batch, GPU_SHADER_TEXT);
|
||||
GPU_batch_texture_bind(g_batch.batch, "glyph", texture);
|
||||
/* Setup texture width mask and shift, so that shader can avoid costly divisions. */
|
||||
int tex_width = GPU_texture_width(texture);
|
||||
BLI_assert_msg(is_power_of_2_i(tex_width), "Font texture width must be power of two");
|
||||
int width_shift = 31 - bitscan_reverse_i(tex_width);
|
||||
GPU_batch_uniform_1i(g_batch.batch, "glyph_tex_width_mask", tex_width - 1);
|
||||
GPU_batch_uniform_1i(g_batch.batch, "glyph_tex_width_shift", width_shift);
|
||||
GPU_batch_draw(g_batch.batch);
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
|
|
|
@ -29,7 +29,7 @@ extern "C" {
|
|||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 9
|
||||
#define BLENDER_FILE_SUBVERSION 11
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
|
||||
|
|
|
@ -86,11 +86,11 @@ char BKE_imtype_valid_depths(char imtype);
|
|||
|
||||
/**
|
||||
* String is from command line `--render-format` argument,
|
||||
* keep in sync with `creator_args.c` help info.
|
||||
* keep in sync with `creator_args.cc` help info.
|
||||
*/
|
||||
char BKE_imtype_from_arg(const char *imtype_arg);
|
||||
|
||||
/* Conversion between ImBuf settings. */
|
||||
/* Conversion between #ImBuf settings. */
|
||||
|
||||
void BKE_image_format_from_imbuf(struct ImageFormatData *im_format, const struct ImBuf *imbuf);
|
||||
void BKE_image_format_to_imbuf(struct ImBuf *ibuf, const struct ImageFormatData *imf);
|
||||
|
|
|
@ -112,7 +112,7 @@ void BKE_scene_object_base_flag_sync_from_base(Base *base);
|
|||
*/
|
||||
void BKE_scene_set_background(Main *bmain, Scene *sce);
|
||||
/**
|
||||
* Called from `creator_args.c`.
|
||||
* Called from `creator_args.cc`.
|
||||
*/
|
||||
Scene *BKE_scene_set_name(Main *bmain, const char *name);
|
||||
|
||||
|
|
|
@ -244,6 +244,9 @@ static MutableSpan<T> get_mutable_attribute(CurvesGeometry &curves,
|
|||
const T default_value = T())
|
||||
{
|
||||
const int num = domain_num(curves, domain);
|
||||
if (num <= 0) {
|
||||
return {};
|
||||
}
|
||||
const eCustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>());
|
||||
CustomData &custom_data = domain_custom_data(curves, domain);
|
||||
|
||||
|
|
|
@ -1737,6 +1737,97 @@ static void legacy_object_modifier_weight_lineart(Object &object, GpencilModifie
|
|||
greasepencil::convert::lineart_wrap_v3(&legacy_md_lineart, &md_lineart);
|
||||
}
|
||||
|
||||
static void legacy_object_modifier_build(Object &object, GpencilModifierData &legacy_md)
|
||||
{
|
||||
ModifierData &md = legacy_object_modifier_common(
|
||||
object, eModifierType_GreasePencilBuild, legacy_md);
|
||||
auto &md_build = reinterpret_cast<GreasePencilBuildModifierData &>(md);
|
||||
auto &legacy_md_build = reinterpret_cast<BuildGpencilModifierData &>(legacy_md);
|
||||
|
||||
md_build.flag = 0;
|
||||
if (legacy_md_build.flag & GP_BUILD_RESTRICT_TIME) {
|
||||
md_build.flag |= MOD_GREASE_PENCIL_BUILD_RESTRICT_TIME;
|
||||
}
|
||||
if (legacy_md_build.flag & GP_BUILD_USE_FADING) {
|
||||
md_build.flag |= MOD_GREASE_PENCIL_BUILD_USE_FADING;
|
||||
}
|
||||
|
||||
switch (legacy_md_build.mode) {
|
||||
case GP_BUILD_MODE_ADDITIVE:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_MODE_ADDITIVE;
|
||||
break;
|
||||
case GP_BUILD_MODE_CONCURRENT:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_MODE_CONCURRENT;
|
||||
break;
|
||||
case GP_BUILD_MODE_SEQUENTIAL:
|
||||
default:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_MODE_SEQUENTIAL;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (legacy_md_build.time_alignment) {
|
||||
default:
|
||||
case GP_BUILD_TIMEALIGN_START:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEALIGN_START;
|
||||
break;
|
||||
case GP_BUILD_TIMEALIGN_END:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEALIGN_END;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (legacy_md_build.time_mode) {
|
||||
default:
|
||||
case GP_BUILD_TIMEMODE_FRAMES:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEMODE_FRAMES;
|
||||
break;
|
||||
case GP_BUILD_TIMEMODE_PERCENTAGE:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEMODE_PERCENTAGE;
|
||||
break;
|
||||
case GP_BUILD_TIMEMODE_DRAWSPEED:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEMODE_DRAWSPEED;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (legacy_md_build.transition) {
|
||||
default:
|
||||
case GP_BUILD_TRANSITION_GROW:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TRANSITION_GROW;
|
||||
break;
|
||||
case GP_BUILD_TRANSITION_SHRINK:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TRANSITION_SHRINK;
|
||||
break;
|
||||
case GP_BUILD_TRANSITION_VANISH:
|
||||
md_build.mode = MOD_GREASE_PENCIL_BUILD_TRANSITION_VANISH;
|
||||
break;
|
||||
}
|
||||
|
||||
md_build.start_frame = legacy_md_build.start_frame;
|
||||
md_build.end_frame = legacy_md_build.end_frame;
|
||||
md_build.start_delay = legacy_md_build.start_delay;
|
||||
md_build.length = legacy_md_build.length;
|
||||
md_build.fade_fac = legacy_md_build.fade_fac;
|
||||
md_build.fade_opacity_strength = legacy_md_build.fade_opacity_strength;
|
||||
md_build.fade_thickness_strength = legacy_md_build.fade_thickness_strength;
|
||||
md_build.percentage_fac = legacy_md_build.percentage_fac;
|
||||
md_build.speed_fac = legacy_md_build.speed_fac;
|
||||
md_build.speed_maxgap = legacy_md_build.speed_maxgap;
|
||||
STRNCPY(md_build.target_vgname, legacy_md_build.target_vgname);
|
||||
|
||||
legacy_object_modifier_influence(md_build.influence,
|
||||
legacy_md_build.layername,
|
||||
legacy_md_build.layer_pass,
|
||||
legacy_md_build.flag & GP_WEIGHT_INVERT_LAYER,
|
||||
legacy_md_build.flag & GP_WEIGHT_INVERT_LAYERPASS,
|
||||
&legacy_md_build.material,
|
||||
legacy_md_build.pass_index,
|
||||
legacy_md_build.flag & GP_WEIGHT_INVERT_MATERIAL,
|
||||
legacy_md_build.flag & GP_WEIGHT_INVERT_PASS,
|
||||
legacy_md_build.target_vgname,
|
||||
legacy_md_build.flag & GP_WEIGHT_INVERT_VGROUP,
|
||||
nullptr,
|
||||
false);
|
||||
}
|
||||
|
||||
static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
|
||||
{
|
||||
BLI_assert(BLI_listbase_is_empty(&object.modifiers));
|
||||
|
@ -1818,6 +1909,8 @@ static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
|
|||
legacy_object_modifier_weight_lineart(object, *gpd_md);
|
||||
break;
|
||||
case eGpencilModifierType_Build:
|
||||
legacy_object_modifier_build(object, *gpd_md);
|
||||
break;
|
||||
case eGpencilModifierType_Simplify:
|
||||
case eGpencilModifierType_Texture:
|
||||
break;
|
||||
|
|
|
@ -298,7 +298,7 @@ void BKE_lattice_resize(Lattice *lt, int uNew, int vNew, int wNew, Object *ltOb)
|
|||
calc_lat_fudu(lt->flag, wNew, &fw, &dw);
|
||||
|
||||
/* If old size is different than resolution changed in interface,
|
||||
* try to do clever reinit of points. Pretty simply idea, we just
|
||||
* try to do clever reinitialize of points. Pretty simply idea, we just
|
||||
* deform new verts by old lattice, but scaling them to match old
|
||||
* size first.
|
||||
*/
|
||||
|
|
|
@ -488,11 +488,11 @@ static void libblock_remap_data(
|
|||
Main *bmain, ID *id, eIDRemapType remap_type, IDRemapper &id_remapper, const int remap_flags)
|
||||
{
|
||||
IDRemap id_remap_data = {
|
||||
/*.type=*/remap_type,
|
||||
/*.bmain=*/bmain,
|
||||
/*.id_remapper=*/id_remapper,
|
||||
/*.id_owner=*/nullptr,
|
||||
/*.flag=*/remap_flags,
|
||||
/*type*/ remap_type,
|
||||
/*bmain*/ bmain,
|
||||
/*id_remapper*/ id_remapper,
|
||||
/*id_owner*/ nullptr,
|
||||
/*flag*/ remap_flags,
|
||||
};
|
||||
|
||||
const bool include_ui = (remap_flags & ID_REMAP_FORCE_UI_POINTERS) != 0;
|
||||
|
|
|
@ -2127,11 +2127,10 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
|
|||
}
|
||||
group->geometry_node_asset_traits->flag |= GEO_NODE_ASSET_MODIFIER;
|
||||
|
||||
group->tree_interface.add_socket(DATA_("Geometry"),
|
||||
"",
|
||||
"NodeSocketGeometry",
|
||||
NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT,
|
||||
nullptr);
|
||||
group->tree_interface.add_socket(
|
||||
DATA_("Geometry"), "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
|
||||
group->tree_interface.add_socket(
|
||||
DATA_("Geometry"), "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_INPUT, nullptr);
|
||||
bNodeTreeInterfaceSocket *angle_io_socket = group->tree_interface.add_socket(
|
||||
DATA_("Angle"), "", "NodeSocketFloat", NODE_INTERFACE_SOCKET_INPUT, nullptr);
|
||||
auto &angle_data = *static_cast<bNodeSocketValueFloat *>(angle_io_socket->socket_data);
|
||||
|
@ -2146,7 +2145,7 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
|
|||
group_input_angle->locx = -420.0f;
|
||||
group_input_angle->locy = -300.0f;
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &group_input_angle->outputs) {
|
||||
if (!STREQ(socket->identifier, "Socket_1")) {
|
||||
if (!STREQ(socket->identifier, "Socket_2")) {
|
||||
socket->flag |= SOCK_HIDDEN;
|
||||
}
|
||||
}
|
||||
|
@ -2154,7 +2153,7 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
|
|||
group_input_mesh->locx = -60.0f;
|
||||
group_input_mesh->locy = -100.0f;
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &group_input_mesh->outputs) {
|
||||
if (!STREQ(socket->identifier, "Socket_0")) {
|
||||
if (!STREQ(socket->identifier, "Socket_1")) {
|
||||
socket->flag |= SOCK_HIDDEN;
|
||||
}
|
||||
}
|
||||
|
@ -2197,7 +2196,7 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
|
|||
nodeFindSocket(group_output, SOCK_IN, "Socket_0"));
|
||||
nodeAddLink(group,
|
||||
group_input_angle,
|
||||
nodeFindSocket(group_input_angle, SOCK_OUT, "Socket_1"),
|
||||
nodeFindSocket(group_input_angle, SOCK_OUT, "Socket_2"),
|
||||
less_than_or_equal,
|
||||
nodeFindSocket(less_than_or_equal, SOCK_IN, "B"));
|
||||
nodeAddLink(group,
|
||||
|
@ -2212,7 +2211,7 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
|
|||
nodeFindSocket(boolean_and, SOCK_IN, "Boolean_001"));
|
||||
nodeAddLink(group,
|
||||
group_input_mesh,
|
||||
nodeFindSocket(group_input_mesh, SOCK_OUT, "Socket_0"),
|
||||
nodeFindSocket(group_input_mesh, SOCK_OUT, "Socket_1"),
|
||||
shade_smooth_edge,
|
||||
nodeFindSocket(shade_smooth_edge, SOCK_IN, "Geometry"));
|
||||
nodeAddLink(group,
|
||||
|
@ -2336,14 +2335,14 @@ static ModifierData *create_auto_smooth_modifier(
|
|||
id_us_plus(&md->node_group->id);
|
||||
|
||||
md->settings.properties = idprop::create_group("Nodes Modifier Settings").release();
|
||||
IDProperty *angle_prop = idprop::create("Socket_1", angle).release();
|
||||
IDProperty *angle_prop = idprop::create("Socket_2", angle).release();
|
||||
auto *ui_data = reinterpret_cast<IDPropertyUIDataFloat *>(IDP_ui_data_ensure(angle_prop));
|
||||
ui_data->base.rna_subtype = PROP_ANGLE;
|
||||
ui_data->soft_min = 0.0f;
|
||||
ui_data->soft_max = DEG2RADF(180.0f);
|
||||
IDP_AddToGroup(md->settings.properties, angle_prop);
|
||||
IDP_AddToGroup(md->settings.properties, idprop::create("Socket_1_use_attribute", 0).release());
|
||||
IDP_AddToGroup(md->settings.properties, idprop::create("Socket_1_attribute_name", "").release());
|
||||
IDP_AddToGroup(md->settings.properties, idprop::create("Socket_2_use_attribute", 0).release());
|
||||
IDP_AddToGroup(md->settings.properties, idprop::create("Socket_2_attribute_name", "").release());
|
||||
|
||||
BKE_modifiers_persistent_uid_init(object, md->modifier);
|
||||
return &md->modifier;
|
||||
|
|
|
@ -2900,7 +2900,7 @@ bNodeLink *nodeAddLink(
|
|||
}
|
||||
|
||||
if (link != nullptr && link->tosock->is_multi_input()) {
|
||||
link->multi_input_socket_index = node_count_links(ntree, link->tosock) - 1;
|
||||
link->multi_input_sort_id = node_count_links(ntree, link->tosock) - 1;
|
||||
}
|
||||
|
||||
return link;
|
||||
|
@ -2966,10 +2966,10 @@ static void adjust_multi_input_indices_after_removed_link(bNodeTree *ntree,
|
|||
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
|
||||
/* We only need to adjust those with a greater index, because the others will have the same
|
||||
* index. */
|
||||
if (link->tosock != sock || link->multi_input_socket_index <= deleted_index) {
|
||||
if (link->tosock != sock || link->multi_input_sort_id <= deleted_index) {
|
||||
continue;
|
||||
}
|
||||
link->multi_input_socket_index -= 1;
|
||||
link->multi_input_sort_id -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2995,7 +2995,7 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
|
|||
if (fromlink == nullptr) {
|
||||
if (link->tosock->is_multi_input()) {
|
||||
blender::bke::adjust_multi_input_indices_after_removed_link(
|
||||
ntree, link->tosock, link->multi_input_socket_index);
|
||||
ntree, link->tosock, link->multi_input_sort_id);
|
||||
}
|
||||
nodeRemLink(ntree, link);
|
||||
continue;
|
||||
|
@ -3008,7 +3008,7 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
|
|||
link_to_compare->tosock == link->tosock)
|
||||
{
|
||||
blender::bke::adjust_multi_input_indices_after_removed_link(
|
||||
ntree, link_to_compare->tosock, link_to_compare->multi_input_socket_index);
|
||||
ntree, link_to_compare->tosock, link_to_compare->multi_input_sort_id);
|
||||
duplicate_links_to_remove.append_non_duplicates(link_to_compare);
|
||||
}
|
||||
}
|
||||
|
@ -3395,7 +3395,7 @@ void nodeUnlinkNode(bNodeTree *ntree, bNode *node)
|
|||
/* Only bother adjusting if the socket is not on the node we're deleting. */
|
||||
if (link->tonode != node && link->tosock->is_multi_input()) {
|
||||
adjust_multi_input_indices_after_removed_link(
|
||||
ntree, link->tosock, link->multi_input_socket_index);
|
||||
ntree, link->tosock, link->multi_input_sort_id);
|
||||
}
|
||||
LISTBASE_FOREACH (const bNodeSocket *, sock, lb) {
|
||||
if (link->fromsock == sock || link->tosock == sock) {
|
||||
|
|
|
@ -135,7 +135,7 @@ static void update_directly_linked_links_and_sockets(const bNodeTree &ntree)
|
|||
std::sort(socket->runtime->directly_linked_links.begin(),
|
||||
socket->runtime->directly_linked_links.end(),
|
||||
[&](const bNodeLink *a, const bNodeLink *b) {
|
||||
return a->multi_input_socket_index > b->multi_input_socket_index;
|
||||
return a->multi_input_sort_id > b->multi_input_sort_id;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1190,6 +1190,10 @@ bNodeTreeInterfaceSocket *bNodeTreeInterface::add_socket(const blender::StringRe
|
|||
const NodeTreeInterfaceSocketFlag flag,
|
||||
bNodeTreeInterfacePanel *parent)
|
||||
{
|
||||
/* Check that each interface socket is either an input or an output. Technically, it can be both
|
||||
* at the same time, but we don't want that for the time being. */
|
||||
BLI_assert(((NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT) & flag) !=
|
||||
(NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT));
|
||||
if (parent == nullptr) {
|
||||
parent = &root_panel;
|
||||
}
|
||||
|
|
|
@ -616,9 +616,9 @@ class NodeTreeMainUpdater {
|
|||
struct InternalLink {
|
||||
bNodeSocket *from;
|
||||
bNodeSocket *to;
|
||||
int multi_input_socket_index = 0;
|
||||
int multi_input_sort_id = 0;
|
||||
|
||||
BLI_STRUCT_EQUALITY_OPERATORS_3(InternalLink, from, to, multi_input_socket_index);
|
||||
BLI_STRUCT_EQUALITY_OPERATORS_3(InternalLink, from, to, multi_input_sort_id);
|
||||
};
|
||||
|
||||
const bNodeLink *first_non_dangling_link(const bNodeTree &ntree,
|
||||
|
@ -660,7 +660,7 @@ class NodeTreeMainUpdater {
|
|||
const Span<const bNodeLink *> connected_links = input_socket->directly_linked_links();
|
||||
const bNodeLink *connected_link = first_non_dangling_link(ntree, connected_links);
|
||||
|
||||
const int index = connected_link ? connected_link->multi_input_socket_index :
|
||||
const int index = connected_link ? connected_link->multi_input_sort_id :
|
||||
std::max<int>(0, connected_links.size() - 1);
|
||||
expected_internal_links.append(InternalLink{const_cast<bNodeSocket *>(input_socket),
|
||||
const_cast<bNodeSocket *>(output_socket),
|
||||
|
@ -677,8 +677,7 @@ class NodeTreeMainUpdater {
|
|||
node->runtime->internal_links.begin(),
|
||||
node->runtime->internal_links.end(),
|
||||
[&](const bNodeLink &link) {
|
||||
const InternalLink internal_link{
|
||||
link.fromsock, link.tosock, link.multi_input_socket_index};
|
||||
const InternalLink internal_link{link.fromsock, link.tosock, link.multi_input_sort_id};
|
||||
return expected_internal_links.as_span().contains(internal_link);
|
||||
});
|
||||
|
||||
|
@ -736,7 +735,7 @@ class NodeTreeMainUpdater {
|
|||
link.fromsock = internal_link.from;
|
||||
link.tonode = &node;
|
||||
link.tosock = internal_link.to;
|
||||
link.multi_input_socket_index = internal_link.multi_input_socket_index;
|
||||
link.multi_input_sort_id = internal_link.multi_input_sort_id;
|
||||
link.flag |= NODE_LINK_VALID;
|
||||
node.runtime->internal_links.append(link);
|
||||
}
|
||||
|
|
|
@ -244,33 +244,69 @@ CPPType::CPPType(TypeTag<T> /*type*/,
|
|||
copy_assign_compressed_ = copy_assign_compressed_cb<T>;
|
||||
}
|
||||
if constexpr (std::is_copy_constructible_v<T>) {
|
||||
copy_construct_ = copy_construct_cb<T>;
|
||||
copy_construct_indices_ = copy_construct_indices_cb<T>;
|
||||
copy_construct_compressed_ = copy_construct_compressed_cb<T>;
|
||||
if constexpr (std::is_trivially_copy_constructible_v<T>) {
|
||||
copy_construct_ = copy_assign_;
|
||||
copy_construct_indices_ = copy_assign_indices_;
|
||||
copy_construct_compressed_ = copy_assign_compressed_;
|
||||
}
|
||||
else {
|
||||
copy_construct_ = copy_construct_cb<T>;
|
||||
copy_construct_indices_ = copy_construct_indices_cb<T>;
|
||||
copy_construct_compressed_ = copy_construct_compressed_cb<T>;
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_move_assignable_v<T>) {
|
||||
move_assign_ = move_assign_cb<T>;
|
||||
move_assign_indices_ = move_assign_indices_cb<T>;
|
||||
if constexpr (std::is_trivially_move_assignable_v<T>) {
|
||||
/* This casts away the const from the src pointer. This is fine for trivial types as moving
|
||||
* them does not change the original value. */
|
||||
move_assign_ = reinterpret_cast<decltype(move_assign_)>(copy_assign_);
|
||||
move_assign_indices_ = reinterpret_cast<decltype(move_assign_indices_)>(
|
||||
copy_assign_indices_);
|
||||
}
|
||||
else {
|
||||
move_assign_ = move_assign_cb<T>;
|
||||
move_assign_indices_ = move_assign_indices_cb<T>;
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_move_constructible_v<T>) {
|
||||
move_construct_ = move_construct_cb<T>;
|
||||
move_construct_indices_ = move_construct_indices_cb<T>;
|
||||
if constexpr (std::is_trivially_move_constructible_v<T>) {
|
||||
move_construct_ = move_assign_;
|
||||
move_construct_indices_ = move_assign_indices_;
|
||||
}
|
||||
else {
|
||||
move_construct_ = move_construct_cb<T>;
|
||||
move_construct_indices_ = move_construct_indices_cb<T>;
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_destructible_v<T>) {
|
||||
if constexpr (std::is_move_assignable_v<T>) {
|
||||
relocate_assign_ = relocate_assign_cb<T>;
|
||||
relocate_assign_indices_ = relocate_assign_indices_cb<T>;
|
||||
if constexpr (std::is_trivially_move_assignable_v<T> && std::is_trivially_destructible_v<T>) {
|
||||
relocate_assign_ = move_assign_;
|
||||
relocate_assign_indices_ = move_assign_indices_;
|
||||
|
||||
relocate_construct_ = move_assign_;
|
||||
relocate_construct_indices_ = move_assign_indices_;
|
||||
}
|
||||
if constexpr (std::is_move_constructible_v<T>) {
|
||||
relocate_construct_ = relocate_construct_cb<T>;
|
||||
relocate_construct_indices_ = relocate_construct_indices_cb<T>;
|
||||
else {
|
||||
if constexpr (std::is_move_assignable_v<T>) {
|
||||
relocate_assign_ = relocate_assign_cb<T>;
|
||||
relocate_assign_indices_ = relocate_assign_indices_cb<T>;
|
||||
}
|
||||
if constexpr (std::is_move_constructible_v<T>) {
|
||||
relocate_construct_ = relocate_construct_cb<T>;
|
||||
relocate_construct_indices_ = relocate_construct_indices_cb<T>;
|
||||
}
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_copy_assignable_v<T>) {
|
||||
fill_assign_indices_ = fill_assign_indices_cb<T>;
|
||||
}
|
||||
if constexpr (std::is_copy_constructible_v<T>) {
|
||||
fill_construct_indices_ = fill_construct_indices_cb<T>;
|
||||
if constexpr (std::is_trivially_constructible_v<T>) {
|
||||
fill_construct_indices_ = fill_assign_indices_;
|
||||
}
|
||||
else {
|
||||
fill_construct_indices_ = fill_construct_indices_cb<T>;
|
||||
}
|
||||
}
|
||||
if constexpr ((bool)(Flags & CPPTypeFlags::Hashable)) {
|
||||
hash_ = hash_cb<T>;
|
||||
|
|
|
@ -92,8 +92,19 @@ int BLI_rename(const char *from, const char *to) ATTR_NONNULL();
|
|||
*/
|
||||
int BLI_rename_overwrite(const char *from, const char *to) ATTR_NONNULL();
|
||||
/**
|
||||
* Deletes the specified file or directory (depending on dir), optionally
|
||||
* doing recursive delete of directory contents.
|
||||
* Deletes the specified file or directory.
|
||||
*
|
||||
* \param dir: Delete an empty directory instead of a file.
|
||||
* The value is ignored when `recursive` is true but should true to make the intention clear.
|
||||
* If the directory is not empty, delete fails.
|
||||
* \param recursive: Recursively delete files including `path` which may be a directory of a file.
|
||||
*
|
||||
* \note Symbolic-Links for (UNIX) behave as follows:
|
||||
* - Never followed, treated as regular files.
|
||||
* - Links are removed, not the files/directories they references.
|
||||
* - When `path` itself links to another directory,
|
||||
* deleting `path` behaves as if a regular file is being deleted.
|
||||
* - If `dir` is true and `path` is a link, delete fails.
|
||||
*
|
||||
* \return zero on success (matching 'remove' behavior).
|
||||
*/
|
||||
|
|
|
@ -73,6 +73,7 @@ void parallel_for_weighted_impl(IndexRange range,
|
|||
int64_t grain_size,
|
||||
FunctionRef<void(IndexRange)> function,
|
||||
FunctionRef<void(IndexRange, MutableSpan<int64_t>)> task_sizes_fn);
|
||||
void memory_bandwidth_bound_task_impl(FunctionRef<void()> function);
|
||||
} // namespace detail
|
||||
|
||||
template<typename Function>
|
||||
|
@ -247,4 +248,26 @@ template<typename Function> inline void isolate_task(const Function &function)
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Should surround parallel code that is highly bandwidth intensive, e.g. it just fills a buffer
|
||||
* with no or just few additional operations. If the buffers are large, it's beneficial to limit
|
||||
* the number of threads doing the work because that just creates more overhead on the hardware
|
||||
* level and doesn't provide a notable performance benefit beyond a certain point.
|
||||
*/
|
||||
template<typename Function>
|
||||
inline void memory_bandwidth_bound_task(const int64_t approximate_bytes_touched,
|
||||
const Function &function)
|
||||
{
|
||||
/* Don't limit threading when all touched memory can stay in the CPU cache, because there a much
|
||||
* higher memory bandwidth is available compared to accessing RAM. This value is supposed to be
|
||||
* on the order of the L3 cache size. Accessing that value is not quite straight forward and even
|
||||
* if it was, it's not clear if using the exact cache size would be beneficial because there is
|
||||
* often more stuff going on on the CPU at the same time. */
|
||||
if (approximate_bytes_touched <= 8 * 1024 * 1024) {
|
||||
function();
|
||||
return;
|
||||
}
|
||||
detail::memory_bandwidth_bound_task_impl(function);
|
||||
}
|
||||
|
||||
} // namespace blender::threading
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utils.hh"
|
||||
#include "BLI_sys_types.h" /* for intptr_t support */
|
||||
#include "BLI_sys_types.h" /* For `intptr_t` support. */
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
/** Sizes above this must be allocated. */
|
||||
|
@ -292,19 +292,19 @@ bool BLI_file_is_writable(const char *filepath)
|
|||
{
|
||||
bool writable;
|
||||
if (BLI_access(filepath, W_OK) == 0) {
|
||||
/* file exists and I can write to it */
|
||||
/* File exists and I can write to it. */
|
||||
writable = true;
|
||||
}
|
||||
else if (errno != ENOENT) {
|
||||
/* most likely file or containing directory cannot be accessed */
|
||||
/* Most likely file or containing directory cannot be accessed. */
|
||||
writable = false;
|
||||
}
|
||||
else {
|
||||
/* file doesn't exist -- check I can create it in parent directory */
|
||||
/* File doesn't exist -- check I can create it in parent directory. */
|
||||
char parent[FILE_MAX];
|
||||
BLI_path_split_dir_part(filepath, parent, sizeof(parent));
|
||||
#ifdef WIN32
|
||||
/* windows does not have X_OK */
|
||||
/* Windows does not have X_OK. */
|
||||
writable = BLI_access(parent, W_OK) == 0;
|
||||
#else
|
||||
writable = BLI_access(parent, X_OK | W_OK) == 0;
|
||||
|
@ -321,7 +321,7 @@ bool BLI_file_touch(const char *filepath)
|
|||
int c = getc(f);
|
||||
|
||||
if (c == EOF) {
|
||||
/* Empty file, reopen in truncate write mode... */
|
||||
/* Empty file, reopen in truncate write mode. */
|
||||
fclose(f);
|
||||
f = BLI_fopen(filepath, "w+b");
|
||||
}
|
||||
|
@ -734,6 +734,9 @@ int BLI_delete(const char *path, bool dir, bool recursive)
|
|||
|
||||
BLI_assert(!BLI_path_is_rel(path));
|
||||
|
||||
/* Not an error but avoid ambiguous arguments (recursive file deletion isn't meaningful). */
|
||||
BLI_assert(!(dir == false && recursive == true));
|
||||
|
||||
if (recursive) {
|
||||
err = delete_recursive(path);
|
||||
}
|
||||
|
@ -1089,6 +1092,9 @@ static int recursive_operation_impl(StrBuf *src_buf,
|
|||
* prefixing it with path_dst, recursively scanning subdirectories, and invoking the specified
|
||||
* callbacks for files and subdirectories found as appropriate.
|
||||
*
|
||||
* \note Symbolic links are *not* followed, even when `path_src` links to a directory,
|
||||
* it wont be recursed down. Support for this could be added.
|
||||
*
|
||||
* \param path_src: Top-level source path.
|
||||
* \param path_dst: Top-level destination path.
|
||||
* \param callback_dir_pre: Optional, to be invoked before entering a subdirectory,
|
||||
|
@ -1222,7 +1228,7 @@ static int delete_soft(const char *file, const char **error_message)
|
|||
int pid = fork();
|
||||
|
||||
if (pid != 0) {
|
||||
/* Parent process */
|
||||
/* Parent process. */
|
||||
int wstatus = 0;
|
||||
|
||||
waitpid(pid, &wstatus, 0);
|
||||
|
@ -1243,7 +1249,7 @@ static int delete_soft(const char *file, const char **error_message)
|
|||
execvp(args[0], (char **)args);
|
||||
|
||||
*error_message = "Forking process failed.";
|
||||
return -1; /* This should only be reached if execvp fails and stack isn't replaced. */
|
||||
return -1; /* This should only be reached if `execvp` fails and stack isn't replaced. */
|
||||
}
|
||||
# endif
|
||||
|
||||
|
@ -1278,6 +1284,8 @@ int BLI_access(const char *filepath, int mode)
|
|||
int BLI_delete(const char *path, bool dir, bool recursive)
|
||||
{
|
||||
BLI_assert(!BLI_path_is_rel(path));
|
||||
/* Not an error but avoid ambiguous arguments (recursive file deletion isn't meaningful). */
|
||||
BLI_assert(!(dir == false && recursive == true));
|
||||
|
||||
if (recursive) {
|
||||
return recursive_operation(path, nullptr, nullptr, delete_single_file, delete_callback_post);
|
||||
|
@ -1347,13 +1355,13 @@ static int copy_callback_pre(const char *from, const char *to)
|
|||
return RecursiveOp_Callback_Error;
|
||||
}
|
||||
|
||||
/* create a directory */
|
||||
/* Create a directory. */
|
||||
if (mkdir(to, st.st_mode)) {
|
||||
perror("mkdir");
|
||||
return RecursiveOp_Callback_Error;
|
||||
}
|
||||
|
||||
/* set proper owner and group on new directory */
|
||||
/* Set proper owner and group on new directory. */
|
||||
if (chown(to, st.st_uid, st.st_gid)) {
|
||||
perror("chown");
|
||||
return RecursiveOp_Callback_Error;
|
||||
|
@ -1380,12 +1388,12 @@ static int copy_single_file(const char *from, const char *to)
|
|||
}
|
||||
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
/* symbolic links should be copied in special way */
|
||||
/* Symbolic links should be copied in special way. */
|
||||
char *link_buffer;
|
||||
int need_free;
|
||||
int64_t link_len;
|
||||
|
||||
/* get large enough buffer to read link content */
|
||||
/* Get large enough buffer to read link content. */
|
||||
if ((st.st_size + 1) < sizeof(buf)) {
|
||||
link_buffer = buf;
|
||||
need_free = 0;
|
||||
|
@ -1423,7 +1431,7 @@ static int copy_single_file(const char *from, const char *to)
|
|||
return RecursiveOp_Callback_OK;
|
||||
}
|
||||
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
|
||||
/* copy special type of file */
|
||||
/* Copy special type of file. */
|
||||
if (mknod(to, st.st_mode, st.st_rdev)) {
|
||||
perror("mknod");
|
||||
return RecursiveOp_Callback_Error;
|
||||
|
|
|
@ -467,10 +467,11 @@ static void evaluate_coarse_difference(const Span<DifferenceCourseBoundary> boun
|
|||
* #CoarseSegment::Type::Unknown. Those segments can be evaluated in more detail afterwards.
|
||||
*
|
||||
* \param root_expression: Expression to be evaluated.
|
||||
* \param eval_order: Pre-computed evaluation order. All children of a term must come before
|
||||
* the term itself.
|
||||
* \param eval_bounds: If given, the evaluation is restriced to those bounds. Otherwise, the full
|
||||
* referenced masks are used.
|
||||
* \param eval_order: Pre-computed evaluation order.
|
||||
* All children of a term must come before the term itself.
|
||||
* \param eval_bounds: If given, the evaluation is restricted to those bounds.
|
||||
* Otherwise, the full
|
||||
* referenced masks are used.
|
||||
*/
|
||||
static CoarseResult evaluate_coarse(const Expr &root_expression,
|
||||
const Span<const Expr *> eval_order,
|
||||
|
@ -1148,7 +1149,7 @@ static void evaluate_short_unknown_segments_exactly(
|
|||
}
|
||||
case ExactEvalMode::Indices: {
|
||||
/* #evaluate_exact_with_indices requires that all index masks have a single segment in the
|
||||
* provided bounds. So split up the range into subranges first if necessary. */
|
||||
* provided bounds. So split up the range into sub-ranges first if necessary. */
|
||||
Vector<int64_t, 16> split_indices;
|
||||
/* Always adding the beginning and end of the bounds simplifies the code below. */
|
||||
split_indices.extend({bounds.first(), bounds.one_after_last()});
|
||||
|
|
|
@ -4800,8 +4800,9 @@ void lookat_m4(
|
|||
|
||||
i_multmatrix(mat1, mat);
|
||||
|
||||
mat1[1][1] = mat1[2][2] = 1.0f; /* be careful here to reinit */
|
||||
mat1[1][2] = mat1[2][1] = 0.0f; /* those modified by the last */
|
||||
/* Be careful here to reinitialize those modified by the last. */
|
||||
mat1[1][1] = mat1[2][2] = 1.0f;
|
||||
mat1[1][2] = mat1[2][1] = 0.0f;
|
||||
|
||||
/* paragraph */
|
||||
if (hyp != 0.0f) { /* rotate Y */
|
||||
|
|
|
@ -24,10 +24,12 @@ OffsetIndices<int> accumulate_counts_to_offsets(MutableSpan<int> counts_to_offse
|
|||
|
||||
void fill_constant_group_size(const int size, const int start_offset, MutableSpan<int> offsets)
|
||||
{
|
||||
threading::parallel_for(offsets.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int64_t i : range) {
|
||||
offsets[i] = size * i + start_offset;
|
||||
}
|
||||
threading::memory_bandwidth_bound_task(offsets.size_in_bytes(), [&]() {
|
||||
threading::parallel_for(offsets.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int64_t i : range) {
|
||||
offsets[i] = size * i + start_offset;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -52,11 +54,14 @@ void gather_group_sizes(const OffsetIndices<int> offsets,
|
|||
const Span<int> indices,
|
||||
MutableSpan<int> sizes)
|
||||
{
|
||||
threading::parallel_for(indices.index_range(), 4096, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
sizes[i] = offsets[indices[i]].size();
|
||||
}
|
||||
});
|
||||
threading::memory_bandwidth_bound_task(
|
||||
sizes.size_in_bytes() + offsets.data().size_in_bytes() + indices.size_in_bytes(), [&]() {
|
||||
threading::parallel_for(indices.index_range(), 4096, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
sizes[i] = offsets[indices[i]].size();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
OffsetIndices<int> gather_selected_offsets(const OffsetIndices<int> src_offsets,
|
||||
|
|
|
@ -223,4 +223,32 @@ void parallel_for_weighted_impl(
|
|||
});
|
||||
}
|
||||
|
||||
void memory_bandwidth_bound_task_impl(const FunctionRef<void()> function)
|
||||
{
|
||||
#ifdef WITH_TBB
|
||||
/* This is the maximum number of threads that may perform these memory bandwidth bound tasks at
|
||||
* the same time. Often fewer threads are already enough to use up the full bandwidth capacity.
|
||||
* Additional threads usually have a negligible benefit and can even make performance worse.
|
||||
*
|
||||
* It's better to use fewer threads here so that the CPU cores can do other tasks at the same
|
||||
* time which may be more compute intensive. */
|
||||
const int num_threads = 8;
|
||||
if (num_threads >= BLI_task_scheduler_num_threads()) {
|
||||
/* Avoid overhead of using a task arena when it would not have any effect anyway. */
|
||||
function();
|
||||
return;
|
||||
}
|
||||
static tbb::task_arena arena{num_threads};
|
||||
|
||||
/* Make sure the lazy threading hints are send now, because they shouldn't be send out of an
|
||||
* isolated region. */
|
||||
lazy_threading::send_hint();
|
||||
lazy_threading::ReceiverIsolation isolation;
|
||||
|
||||
arena.execute(function);
|
||||
#else
|
||||
function();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace blender::threading::detail
|
||||
|
|
|
@ -314,7 +314,7 @@ static void area_add_window_regions(ScrArea *area, SpaceLink *sl, ListBase *lb)
|
|||
case SPACE_ACTION: {
|
||||
SpaceAction *saction = (SpaceAction *)sl;
|
||||
|
||||
/* We totally reinit the view for the Action Editor,
|
||||
/* We totally reinitialize the view for the Action Editor,
|
||||
* as some old instances had some weird cruft set. */
|
||||
region->v2d.tot.xmin = -20.0f;
|
||||
region->v2d.tot.ymin = float(-area->winy) / 3.0f;
|
||||
|
|
|
@ -554,11 +554,10 @@ static bNodeTree *add_realize_node_tree(Main *bmain)
|
|||
{
|
||||
bNodeTree *node_tree = ntreeAddTree(bmain, "Realize Instances 2.93 Legacy", "GeometryNodeTree");
|
||||
|
||||
node_tree->tree_interface.add_socket("Geometry",
|
||||
"",
|
||||
"NodeSocketGeometry",
|
||||
NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT,
|
||||
nullptr);
|
||||
node_tree->tree_interface.add_socket(
|
||||
"Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
|
||||
node_tree->tree_interface.add_socket(
|
||||
"Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_INPUT, nullptr);
|
||||
|
||||
bNode *group_input = nodeAddStaticNode(nullptr, node_tree, NODE_GROUP_INPUT);
|
||||
group_input->locx = -400.0f;
|
||||
|
|
|
@ -3056,6 +3056,20 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 10)) {
|
||||
if (!DNA_struct_member_exists(fd->filesdna, "SceneEEVEE", "int", "gtao_resolution")) {
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
scene->eevee.gtao_resolution = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 11)) {
|
||||
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
|
||||
light->shadow_resolution_scale = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
|
||||
|
|
|
@ -299,7 +299,7 @@ void DEG_graph_tag_relations_update(Depsgraph *graph)
|
|||
graph_id_tag_update(deg_graph->bmain,
|
||||
deg_graph,
|
||||
°_graph->scene->id,
|
||||
ID_RECALC_BASE_FLAGS,
|
||||
ID_RECALC_BASE_FLAGS | ID_RECALC_HIERARCHY,
|
||||
deg::DEG_UPDATE_SOURCE_RELATIONS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -498,6 +498,7 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_film_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_film_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_filter_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_forward_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_gbuffer_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_gbuffer_closure_test.glsl
|
||||
|
@ -511,6 +512,7 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_hiz_debug_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_hiz_update_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_horizon_resolve_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_horizon_scan_lib.glsl
|
||||
|
@ -529,6 +531,7 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_irradiance_offset_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_irradiance_world_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_volume_eval_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_lookdev_display_frag.glsl
|
||||
|
@ -558,11 +561,11 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_ray_types_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_convolve_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_irradiance_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_mapping_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_select_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_update_irradiance_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_renderpass_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_sampling_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl
|
||||
|
|
|
@ -13,6 +13,10 @@
|
|||
# pragma once
|
||||
#endif
|
||||
|
||||
#ifndef SQUARE
|
||||
# define SQUARE(x) ((x) * (x))
|
||||
#endif
|
||||
|
||||
/* Look Up Tables. */
|
||||
#define LUT_WORKGROUP_SIZE 16
|
||||
|
||||
|
@ -30,14 +34,17 @@
|
|||
#define CULLING_TILE_GROUP_SIZE 256
|
||||
|
||||
/* Reflection Probes. */
|
||||
#define SPHERE_PROBE_REMAP_GROUP_SIZE 32
|
||||
#define SPHERE_PROBE_GROUP_SIZE 16
|
||||
#define SPHERE_PROBE_SELECT_GROUP_SIZE 64
|
||||
#define SPHERE_PROBE_MIPMAP_LEVELS 5
|
||||
#define SPHERE_PROBE_SH_GROUP_SIZE 512
|
||||
#define SPHERE_PROBE_SH_GROUP_SIZE 256
|
||||
#define SPHERE_PROBE_SH_SAMPLES_PER_GROUP 64
|
||||
/* Must be power of two for correct partitioning. */
|
||||
#define SPHERE_PROBE_ATLAS_MAX_SUBDIV 10
|
||||
#define SPHERE_PROBE_ATLAS_RES (1 << SPHERE_PROBE_ATLAS_MAX_SUBDIV)
|
||||
/* Maximum number of thread-groups dispatched for remapping a probe to octahedral mapping. */
|
||||
#define SPHERE_PROBE_MAX_HARMONIC SQUARE(SPHERE_PROBE_ATLAS_RES / SPHERE_PROBE_REMAP_GROUP_SIZE)
|
||||
/* Start and end value for mixing sphere probe and volume probes. */
|
||||
#define SPHERE_PROBE_MIX_START_ROUGHNESS 0.7
|
||||
#define SPHERE_PROBE_MIX_END_ROUGHNESS 0.9
|
||||
|
|
|
@ -70,8 +70,6 @@ void VolumeProbeModule::init()
|
|||
/* Clear the pool to avoid any interpolation to undefined values. */
|
||||
irradiance_atlas_tx_.clear(float4(0.0f));
|
||||
}
|
||||
|
||||
inst_.sphere_probes.tag_world_irradiance_for_update();
|
||||
}
|
||||
|
||||
if (irradiance_atlas_tx_.is_valid() == false) {
|
||||
|
@ -188,6 +186,7 @@ void VolumeProbeModule::set_view(View & /*view*/)
|
|||
}
|
||||
|
||||
/* Then create brick & grid infos UBOs content. */
|
||||
int world_grid_index = 0;
|
||||
{
|
||||
/* Stable sorting of grids. */
|
||||
std::sort(
|
||||
|
@ -226,6 +225,8 @@ void VolumeProbeModule::set_view(View & /*view*/)
|
|||
}
|
||||
|
||||
/* Insert world grid last. */
|
||||
world_grid_index = grids_len++;
|
||||
|
||||
VolumeProbeData grid;
|
||||
grid.world_to_grid_transposed = float3x4::identity();
|
||||
grid.grid_size = int3(1);
|
||||
|
@ -233,7 +234,8 @@ void VolumeProbeModule::set_view(View & /*view*/)
|
|||
grid.normal_bias = 0.0f;
|
||||
grid.view_bias = 0.0f;
|
||||
grid.facing_bias = 0.0f;
|
||||
grids_infos_buf_[grids_len++] = grid;
|
||||
grids_infos_buf_[world_grid_index] = grid;
|
||||
|
||||
bricks_infos_buf_.append(world_brick_index_);
|
||||
|
||||
if (grids_len < IRRADIANCE_GRID_MAX) {
|
||||
|
@ -245,6 +247,25 @@ void VolumeProbeModule::set_view(View & /*view*/)
|
|||
grids_infos_buf_.push_update();
|
||||
}
|
||||
|
||||
/* Upload data for world. */
|
||||
if (do_update_world_) {
|
||||
grid_upload_ps_.init();
|
||||
grid_upload_ps_.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_WORLD));
|
||||
grid_upload_ps_.bind_ssbo("harmonic_buf", &inst_.sphere_probes.spherical_harmonics_buf());
|
||||
grid_upload_ps_.bind_ubo("grids_infos_buf", &grids_infos_buf_);
|
||||
grid_upload_ps_.bind_ssbo("bricks_infos_buf", &bricks_infos_buf_);
|
||||
grid_upload_ps_.push_constant("grid_index", world_grid_index);
|
||||
grid_upload_ps_.bind_image("irradiance_atlas_img", &irradiance_atlas_tx_);
|
||||
/* Sync with extraction. */
|
||||
grid_upload_ps_.barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
/* Only upload one brick. */
|
||||
grid_upload_ps_.dispatch(int3(1));
|
||||
/* Sync with next load. */
|
||||
grid_upload_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
|
||||
|
||||
inst_.manager->submit(grid_upload_ps_);
|
||||
}
|
||||
|
||||
/* Upload data for each grid that need to be inserted in the atlas.
|
||||
* Upload by order of dependency. */
|
||||
/* Start at world index to not load any other grid (+1 because we decrement at loop start). */
|
||||
|
|
|
@ -192,10 +192,6 @@ class VolumeProbeModule {
|
|||
public:
|
||||
IrradianceBake bake;
|
||||
|
||||
/** True if world irradiance need to be updated. */
|
||||
/* TODO(fclem): move to private once world irradiance extraction is moved to irradiance cache. */
|
||||
bool do_update_world_ = true;
|
||||
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
|
@ -222,12 +218,22 @@ class VolumeProbeModule {
|
|||
bool display_grids_enabled_ = false;
|
||||
PassSimple display_grids_ps_ = {"VolumeProbeModule.Display Grids"};
|
||||
|
||||
/** True if world irradiance need to be updated. */
|
||||
bool do_update_world_ = true;
|
||||
|
||||
public:
|
||||
VolumeProbeModule(Instance &inst) : bake(inst), inst_(inst){};
|
||||
~VolumeProbeModule(){};
|
||||
|
||||
void init();
|
||||
void sync();
|
||||
|
||||
/* Tag all grids for reupload in set_view and composite them with the world irradiance. */
|
||||
void update_world_irradiance()
|
||||
{
|
||||
do_update_world_ = true;
|
||||
}
|
||||
|
||||
void set_view(View &view);
|
||||
void viewport_draw(View &view, GPUFrameBuffer *view_fb);
|
||||
|
||||
|
|
|
@ -80,6 +80,15 @@ void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
|
|||
|
||||
this->pcf_radius = la->shadow_filter_radius;
|
||||
|
||||
float resolution_scale = std::clamp(la->shadow_resolution_scale, 0.0f, 2.0f);
|
||||
if (resolution_scale < 1.0) {
|
||||
this->lod_bias = -log2(resolution_scale);
|
||||
}
|
||||
else {
|
||||
this->lod_bias = -(resolution_scale - 1.0f);
|
||||
}
|
||||
this->lod_bias += shadows.get_global_lod_bias();
|
||||
|
||||
eLightType new_type = to_light_type(la->type, la->area_shape, la->mode & LA_USE_SOFT_FALLOFF);
|
||||
if (assign_if_different(this->type, new_type)) {
|
||||
shadow_discard_safe(shadows);
|
||||
|
|
|
@ -210,7 +210,6 @@ void LightProbeModule::sync_world(const ::World *world, bool has_update)
|
|||
|
||||
if (has_update) {
|
||||
world_sphere_.do_render = true;
|
||||
sph_module.tag_world_irradiance_for_update();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -356,6 +356,10 @@ Material &MaterialModule::material_sync(Object *ob,
|
|||
mat.is_alpha_blend_transparent = use_forward_pipeline &&
|
||||
GPU_material_flag_get(mat.shading.gpumat,
|
||||
GPU_MATFLAG_TRANSPARENT);
|
||||
mat.has_transparent_shadows = blender_mat->blend_flag & MA_BL_TRANSPARENT_SHADOW &&
|
||||
GPU_material_flag_get(mat.shading.gpumat,
|
||||
GPU_MATFLAG_TRANSPARENT);
|
||||
|
||||
return mat;
|
||||
});
|
||||
|
||||
|
|
|
@ -291,6 +291,7 @@ struct MaterialPass {
|
|||
|
||||
struct Material {
|
||||
bool is_alpha_blend_transparent;
|
||||
bool has_transparent_shadows;
|
||||
bool has_surface;
|
||||
bool has_volume;
|
||||
MaterialPass shadow;
|
||||
|
|
|
@ -60,19 +60,14 @@ void RayTraceModule::sync()
|
|||
GPUShader *sh = inst_.shaders.static_shader_get(RAY_TILE_COMPACT);
|
||||
pass.init();
|
||||
pass.specialize_constant(sh, "closure_index", &data_.closure_index);
|
||||
pass.specialize_constant(sh, "resolution_scale", &data_.resolution_scale);
|
||||
pass.shader_set(sh);
|
||||
pass.bind_image("tile_raytrace_denoise_img", &tile_raytrace_denoise_tx_);
|
||||
pass.bind_image("tile_raytrace_tracing_img", &tile_raytrace_tracing_tx_);
|
||||
pass.bind_image("tile_horizon_denoise_img", &tile_horizon_denoise_tx_);
|
||||
pass.bind_image("tile_horizon_tracing_img", &tile_horizon_tracing_tx_);
|
||||
pass.bind_ssbo("raytrace_tracing_dispatch_buf", &raytrace_tracing_dispatch_buf_);
|
||||
pass.bind_ssbo("raytrace_denoise_dispatch_buf", &raytrace_denoise_dispatch_buf_);
|
||||
pass.bind_ssbo("horizon_tracing_dispatch_buf", &horizon_tracing_dispatch_buf_);
|
||||
pass.bind_ssbo("horizon_denoise_dispatch_buf", &horizon_denoise_dispatch_buf_);
|
||||
pass.bind_ssbo("raytrace_tracing_tiles_buf", &raytrace_tracing_tiles_buf_);
|
||||
pass.bind_ssbo("raytrace_denoise_tiles_buf", &raytrace_denoise_tiles_buf_);
|
||||
pass.bind_ssbo("horizon_tracing_tiles_buf", &horizon_tracing_tiles_buf_);
|
||||
pass.bind_ssbo("horizon_denoise_tiles_buf", &horizon_denoise_tiles_buf_);
|
||||
pass.bind_resources(inst_.uniform_data);
|
||||
pass.dispatch(&tile_compact_dispatch_size_);
|
||||
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
|
@ -220,6 +215,24 @@ void RayTraceModule::sync()
|
|||
pass.dispatch(raytrace_denoise_dispatch_buf_);
|
||||
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
|
||||
}
|
||||
{
|
||||
PassSimple &pass = horizon_schedule_ps_;
|
||||
/* Reuse tile compaction shader but feed it with horizon scan specific buffers. */
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(RAY_TILE_COMPACT);
|
||||
pass.init();
|
||||
pass.specialize_constant(sh, "closure_index", 0);
|
||||
pass.specialize_constant(sh, "resolution_scale", &data_.horizon_resolution_scale);
|
||||
pass.shader_set(sh);
|
||||
pass.bind_image("tile_raytrace_denoise_img", &tile_horizon_denoise_tx_);
|
||||
pass.bind_image("tile_raytrace_tracing_img", &tile_horizon_tracing_tx_);
|
||||
pass.bind_ssbo("raytrace_tracing_dispatch_buf", &horizon_tracing_dispatch_buf_);
|
||||
pass.bind_ssbo("raytrace_denoise_dispatch_buf", &horizon_denoise_dispatch_buf_);
|
||||
pass.bind_ssbo("raytrace_tracing_tiles_buf", &horizon_tracing_tiles_buf_);
|
||||
pass.bind_ssbo("raytrace_denoise_tiles_buf", &horizon_denoise_tiles_buf_);
|
||||
pass.bind_resources(inst_.uniform_data);
|
||||
pass.dispatch(&horizon_schedule_dispatch_size_);
|
||||
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
}
|
||||
{
|
||||
PassSimple &pass = horizon_setup_ps_;
|
||||
pass.init();
|
||||
|
@ -232,39 +245,64 @@ void RayTraceModule::sync()
|
|||
pass.bind_image("out_normal_img", &downsampled_in_normal_tx_);
|
||||
pass.bind_resources(inst_.uniform_data);
|
||||
pass.bind_resources(inst_.gbuffer);
|
||||
pass.dispatch(&tracing_dispatch_size_);
|
||||
pass.dispatch(&horizon_tracing_dispatch_size_);
|
||||
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
|
||||
}
|
||||
{
|
||||
PassSimple &pass = horizon_scan_ps_;
|
||||
pass.init();
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(HORIZON_SCAN);
|
||||
pass.specialize_constant(sh, "closure_index", &data_.closure_index);
|
||||
pass.shader_set(sh);
|
||||
pass.bind_image("horizon_radiance_img", &horizon_radiance_tx_);
|
||||
pass.bind_image("horizon_occlusion_img", &horizon_occlusion_tx_);
|
||||
pass.bind_ssbo("tiles_coord_buf", &horizon_tracing_tiles_buf_);
|
||||
pass.bind_texture("screen_radiance_tx", &downsampled_in_radiance_tx_);
|
||||
pass.bind_texture("screen_normal_tx", &downsampled_in_normal_tx_);
|
||||
pass.bind_image("horizon_radiance_0_img", &horizon_radiance_tx_[0]);
|
||||
pass.bind_image("horizon_radiance_1_img", &horizon_radiance_tx_[1]);
|
||||
pass.bind_image("horizon_radiance_2_img", &horizon_radiance_tx_[2]);
|
||||
pass.bind_image("horizon_radiance_3_img", &horizon_radiance_tx_[3]);
|
||||
pass.bind_ssbo("tiles_coord_buf", &horizon_tracing_tiles_buf_);
|
||||
pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
|
||||
pass.bind_resources(inst_.uniform_data);
|
||||
pass.bind_resources(inst_.hiz_buffer.front);
|
||||
pass.bind_resources(inst_.sampling);
|
||||
pass.bind_resources(inst_.gbuffer);
|
||||
pass.dispatch(horizon_tracing_dispatch_buf_);
|
||||
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
|
||||
pass.barrier(GPU_BARRIER_TEXTURE_FETCH);
|
||||
}
|
||||
{
|
||||
PassSimple &pass = horizon_denoise_ps_;
|
||||
pass.init();
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(HORIZON_DENOISE);
|
||||
pass.specialize_constant(sh, "closure_index", &data_.closure_index);
|
||||
pass.shader_set(sh);
|
||||
pass.bind_texture("in_sh_0_tx", &horizon_radiance_tx_[0]);
|
||||
pass.bind_texture("in_sh_1_tx", &horizon_radiance_tx_[1]);
|
||||
pass.bind_texture("in_sh_2_tx", &horizon_radiance_tx_[2]);
|
||||
pass.bind_texture("in_sh_3_tx", &horizon_radiance_tx_[3]);
|
||||
pass.bind_texture("screen_normal_tx", &downsampled_in_normal_tx_);
|
||||
pass.bind_image("out_sh_0_img", &horizon_radiance_denoised_tx_[0]);
|
||||
pass.bind_image("out_sh_1_img", &horizon_radiance_denoised_tx_[1]);
|
||||
pass.bind_image("out_sh_2_img", &horizon_radiance_denoised_tx_[2]);
|
||||
pass.bind_image("out_sh_3_img", &horizon_radiance_denoised_tx_[3]);
|
||||
pass.bind_ssbo("tiles_coord_buf", &horizon_tracing_tiles_buf_);
|
||||
pass.bind_resources(inst_.uniform_data);
|
||||
pass.bind_resources(inst_.sampling);
|
||||
pass.bind_resources(inst_.hiz_buffer.front);
|
||||
pass.dispatch(horizon_tracing_dispatch_buf_);
|
||||
pass.barrier(GPU_BARRIER_TEXTURE_FETCH);
|
||||
}
|
||||
{
|
||||
PassSimple &pass = horizon_resolve_ps_;
|
||||
pass.init();
|
||||
GPUShader *sh = inst_.shaders.static_shader_get(HORIZON_RESOLVE);
|
||||
pass.shader_set(sh);
|
||||
pass.bind_texture("depth_tx", &depth_tx);
|
||||
pass.bind_image("horizon_radiance_img", &horizon_radiance_tx_);
|
||||
pass.bind_image("horizon_occlusion_img", &horizon_occlusion_tx_);
|
||||
pass.bind_image("radiance_img", &horizon_scan_output_tx_);
|
||||
pass.bind_image("tile_mask_img", &tile_horizon_denoise_tx_);
|
||||
pass.bind_texture("horizon_radiance_0_tx", &horizon_radiance_denoised_tx_[0]);
|
||||
pass.bind_texture("horizon_radiance_1_tx", &horizon_radiance_denoised_tx_[1]);
|
||||
pass.bind_texture("horizon_radiance_2_tx", &horizon_radiance_denoised_tx_[2]);
|
||||
pass.bind_texture("horizon_radiance_3_tx", &horizon_radiance_denoised_tx_[3]);
|
||||
pass.bind_texture("screen_normal_tx", &downsampled_in_normal_tx_);
|
||||
pass.bind_image("closure0_img", &horizon_scan_output_tx_[0]);
|
||||
pass.bind_image("closure1_img", &horizon_scan_output_tx_[1]);
|
||||
pass.bind_image("closure2_img", &horizon_scan_output_tx_[2]);
|
||||
pass.bind_ssbo("tiles_coord_buf", &horizon_denoise_tiles_buf_);
|
||||
pass.bind_resources(inst_.uniform_data);
|
||||
pass.bind_resources(inst_.sampling);
|
||||
|
@ -297,19 +335,26 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer,
|
|||
bool use_horizon_scan = options.screen_trace_max_roughness < 1.0f;
|
||||
|
||||
const int resolution_scale = max_ii(1, power_of_2_max_i(options.resolution_scale));
|
||||
const int horizon_resolution_scale = max_ii(
|
||||
1, power_of_2_max_i(inst_.scene->eevee.gtao_resolution));
|
||||
|
||||
const int2 extent = inst_.film.render_extent_get();
|
||||
const int2 tracing_res = math::divide_ceil(extent, int2(resolution_scale));
|
||||
const int2 tracing_res_horizon = math::divide_ceil(extent, int2(horizon_resolution_scale));
|
||||
const int2 dummy_extent(1, 1);
|
||||
const int2 group_size(RAYTRACE_GROUP_SIZE);
|
||||
|
||||
const int2 denoise_tiles = divide_ceil(extent, group_size);
|
||||
const int2 raytrace_tiles = divide_ceil(tracing_res, group_size);
|
||||
const int2 raytrace_tiles_horizon = divide_ceil(tracing_res_horizon, group_size);
|
||||
const int denoise_tile_count = denoise_tiles.x * denoise_tiles.y;
|
||||
const int raytrace_tile_count = raytrace_tiles.x * raytrace_tiles.y;
|
||||
const int raytrace_tile_count_horizon = raytrace_tiles_horizon.x * raytrace_tiles_horizon.y;
|
||||
tile_classify_dispatch_size_ = int3(denoise_tiles, 1);
|
||||
horizon_schedule_dispatch_size_ = int3(divide_ceil(raytrace_tiles_horizon, group_size), 1);
|
||||
tile_compact_dispatch_size_ = int3(divide_ceil(raytrace_tiles, group_size), 1);
|
||||
tracing_dispatch_size_ = int3(divide_ceil(tracing_res, group_size), 1);
|
||||
tracing_dispatch_size_ = int3(raytrace_tiles, 1);
|
||||
horizon_tracing_dispatch_size_ = int3(raytrace_tiles_horizon, 1);
|
||||
|
||||
/* TODO(fclem): Use real max closure count from shader. */
|
||||
const int closure_count = 3;
|
||||
|
@ -317,15 +362,16 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer,
|
|||
eGPUTextureUsage usage_rw = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE;
|
||||
tile_raytrace_denoise_tx_.ensure_2d_array(format, denoise_tiles, closure_count, usage_rw);
|
||||
tile_raytrace_tracing_tx_.ensure_2d_array(format, raytrace_tiles, closure_count, usage_rw);
|
||||
tile_horizon_denoise_tx_.ensure_2d_array(format, denoise_tiles, closure_count, usage_rw);
|
||||
tile_horizon_tracing_tx_.ensure_2d_array(format, raytrace_tiles, closure_count, usage_rw);
|
||||
/* Kept as 2D array for compatibility with the tile compaction shader. */
|
||||
tile_horizon_denoise_tx_.ensure_2d_array(format, denoise_tiles, 1, usage_rw);
|
||||
tile_horizon_tracing_tx_.ensure_2d_array(format, raytrace_tiles_horizon, 1, usage_rw);
|
||||
|
||||
tile_raytrace_denoise_tx_.clear(uint4(0u));
|
||||
tile_raytrace_tracing_tx_.clear(uint4(0u));
|
||||
tile_horizon_denoise_tx_.clear(uint4(0u));
|
||||
tile_horizon_tracing_tx_.clear(uint4(0u));
|
||||
|
||||
horizon_tracing_tiles_buf_.resize(ceil_to_multiple_u(raytrace_tile_count, 512));
|
||||
horizon_tracing_tiles_buf_.resize(ceil_to_multiple_u(raytrace_tile_count_horizon, 512));
|
||||
horizon_denoise_tiles_buf_.resize(ceil_to_multiple_u(denoise_tile_count, 512));
|
||||
raytrace_tracing_tiles_buf_.resize(ceil_to_multiple_u(raytrace_tile_count, 512));
|
||||
raytrace_denoise_tiles_buf_.resize(ceil_to_multiple_u(denoise_tile_count, 512));
|
||||
|
@ -344,6 +390,9 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer,
|
|||
data_.full_resolution = extent;
|
||||
data_.full_resolution_inv = 1.0f / float2(extent);
|
||||
|
||||
data_.horizon_resolution_scale = horizon_resolution_scale;
|
||||
data_.horizon_resolution_bias = int2(inst_.sampling.rng_2d_get(SAMPLING_RAYTRACE_V) *
|
||||
horizon_resolution_scale);
|
||||
/* TODO(fclem): Eventually all uniform data is setup here. */
|
||||
|
||||
inst_.uniform_data.push_update();
|
||||
|
@ -352,15 +401,9 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer,
|
|||
|
||||
DRW_stats_group_start("Raytracing");
|
||||
|
||||
if (use_horizon_scan) {
|
||||
downsampled_in_radiance_tx_.acquire(tracing_res, RAYTRACE_RADIANCE_FORMAT, usage_rw);
|
||||
downsampled_in_normal_tx_.acquire(tracing_res, GPU_RGBA8, usage_rw);
|
||||
const bool has_active_closure = active_closures != CLOSURE_NONE;
|
||||
|
||||
screen_radiance_front_tx_ = screen_radiance_front_tx;
|
||||
inst_.manager->submit(horizon_setup_ps_, render_view);
|
||||
}
|
||||
|
||||
if (active_closures != CLOSURE_NONE) {
|
||||
if (has_active_closure) {
|
||||
inst_.manager->submit(tile_classify_ps_);
|
||||
}
|
||||
|
||||
|
@ -375,12 +418,47 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer,
|
|||
screen_radiance_front_tx,
|
||||
screen_radiance_persmat,
|
||||
main_view,
|
||||
render_view,
|
||||
use_horizon_scan);
|
||||
render_view);
|
||||
}
|
||||
|
||||
downsampled_in_radiance_tx_.release();
|
||||
downsampled_in_normal_tx_.release();
|
||||
if (has_active_closure) {
|
||||
if (use_horizon_scan) {
|
||||
DRW_stats_group_start("Horizon Scan");
|
||||
|
||||
screen_radiance_front_tx_ = screen_radiance_front_tx;
|
||||
|
||||
downsampled_in_radiance_tx_.acquire(tracing_res_horizon, RAYTRACE_RADIANCE_FORMAT, usage_rw);
|
||||
downsampled_in_normal_tx_.acquire(tracing_res_horizon, GPU_RGB10_A2, usage_rw);
|
||||
|
||||
horizon_radiance_tx_[0].acquire(tracing_res_horizon, GPU_RGBA16F, usage_rw);
|
||||
horizon_radiance_denoised_tx_[0].acquire(tracing_res_horizon, GPU_RGBA16F, usage_rw);
|
||||
for (int i : IndexRange(1, 3)) {
|
||||
horizon_radiance_tx_[i].acquire(tracing_res_horizon, GPU_RGBA8, usage_rw);
|
||||
horizon_radiance_denoised_tx_[i].acquire(tracing_res_horizon, GPU_RGBA8, usage_rw);
|
||||
}
|
||||
for (int i : IndexRange(3)) {
|
||||
horizon_scan_output_tx_[i] = result.closures[i].get();
|
||||
}
|
||||
|
||||
horizon_tracing_dispatch_buf_.clear_to_zero();
|
||||
horizon_denoise_dispatch_buf_.clear_to_zero();
|
||||
inst_.manager->submit(horizon_schedule_ps_);
|
||||
|
||||
inst_.manager->submit(horizon_setup_ps_, render_view);
|
||||
inst_.manager->submit(horizon_scan_ps_, render_view);
|
||||
inst_.manager->submit(horizon_denoise_ps_, render_view);
|
||||
inst_.manager->submit(horizon_resolve_ps_, render_view);
|
||||
|
||||
for (int i : IndexRange(4)) {
|
||||
horizon_radiance_tx_[i].release();
|
||||
horizon_radiance_denoised_tx_[i].release();
|
||||
}
|
||||
downsampled_in_radiance_tx_.release();
|
||||
downsampled_in_normal_tx_.release();
|
||||
|
||||
DRW_stats_group_end();
|
||||
}
|
||||
}
|
||||
|
||||
DRW_stats_group_end();
|
||||
|
||||
|
@ -397,8 +475,7 @@ RayTraceResultTexture RayTraceModule::trace(
|
|||
const float4x4 &screen_radiance_persmat,
|
||||
/* TODO(fclem): Maybe wrap these two in some other class. */
|
||||
View &main_view,
|
||||
View &render_view,
|
||||
bool use_horizon_scan)
|
||||
View &render_view)
|
||||
{
|
||||
RayTraceBuffer::DenoiseBuffer *denoise_buf = &rt_buffer.closures[closure_index];
|
||||
|
||||
|
@ -452,8 +529,6 @@ RayTraceResultTexture RayTraceModule::trace(
|
|||
/* Ray setup. */
|
||||
raytrace_tracing_dispatch_buf_.clear_to_zero();
|
||||
raytrace_denoise_dispatch_buf_.clear_to_zero();
|
||||
horizon_tracing_dispatch_buf_.clear_to_zero();
|
||||
horizon_denoise_dispatch_buf_.clear_to_zero();
|
||||
inst_.manager->submit(tile_compact_ps_);
|
||||
|
||||
{
|
||||
|
@ -555,20 +630,6 @@ RayTraceResultTexture RayTraceModule::trace(
|
|||
|
||||
denoise_variance_tx_.release();
|
||||
|
||||
if (use_horizon_scan) {
|
||||
horizon_occlusion_tx_.acquire(tracing_res, GPU_R8, usage_rw);
|
||||
horizon_radiance_tx_.acquire(tracing_res, RAYTRACE_RADIANCE_FORMAT, usage_rw);
|
||||
|
||||
inst_.manager->submit(horizon_scan_ps_, render_view);
|
||||
|
||||
horizon_scan_output_tx_ = result.get();
|
||||
|
||||
inst_.manager->submit(horizon_denoise_ps_, render_view);
|
||||
|
||||
horizon_occlusion_tx_.release();
|
||||
horizon_radiance_tx_.release();
|
||||
}
|
||||
|
||||
DRW_stats_group_end();
|
||||
|
||||
return result;
|
||||
|
|
|
@ -120,16 +120,20 @@ class RayTraceModule {
|
|||
draw::PassSimple denoise_spatial_ps_ = {"DenoiseSpatial"};
|
||||
draw::PassSimple denoise_temporal_ps_ = {"DenoiseTemporal"};
|
||||
draw::PassSimple denoise_bilateral_ps_ = {"DenoiseBilateral"};
|
||||
draw::PassSimple horizon_schedule_ps_ = {"HorizonScan.Schedule"};
|
||||
draw::PassSimple horizon_setup_ps_ = {"HorizonScan.Setup"};
|
||||
draw::PassSimple horizon_scan_ps_ = {"HorizonScan.Trace"};
|
||||
draw::PassSimple horizon_denoise_ps_ = {"HorizonScan.Denoise"};
|
||||
draw::PassSimple horizon_resolve_ps_ = {"HorizonScan.Resolve"};
|
||||
|
||||
/** Dispatch with enough tiles for the whole screen. */
|
||||
int3 tile_classify_dispatch_size_ = int3(1);
|
||||
/** Dispatch with enough tiles for the tile mask. */
|
||||
int3 tile_compact_dispatch_size_ = int3(1);
|
||||
int3 horizon_schedule_dispatch_size_ = int3(1);
|
||||
/** Dispatch with enough tiles for the tracing resolution. */
|
||||
int3 tracing_dispatch_size_ = int3(1);
|
||||
int3 horizon_tracing_dispatch_size_ = int3(1);
|
||||
/** 2D tile mask to check which unused adjacent tile we need to clear and which tile we need to
|
||||
* dispatch for each work type. */
|
||||
Texture tile_raytrace_denoise_tx_ = {"tile_raytrace_denoise_tx_"};
|
||||
|
@ -145,7 +149,7 @@ class RayTraceModule {
|
|||
/** Indirect dispatch denoise full-resolution tiles. */
|
||||
DispatchIndirectBuf horizon_denoise_dispatch_buf_ = {"horizon_denoise_dispatch_buf_"};
|
||||
/** Pointer to the texture to store the result of horizon scan in. */
|
||||
GPUTexture *horizon_scan_output_tx_ = nullptr;
|
||||
GPUTexture *horizon_scan_output_tx_[3] = {nullptr};
|
||||
/** Tile buffer that contains tile coordinates. */
|
||||
RayTraceTileBuf raytrace_tracing_tiles_buf_ = {"raytrace_tracing_tiles_buf_"};
|
||||
RayTraceTileBuf raytrace_denoise_tiles_buf_ = {"raytrace_denoise_tiles_buf_"};
|
||||
|
@ -157,10 +161,9 @@ class RayTraceModule {
|
|||
TextureFromPool ray_time_tx_ = {"ray_data_tx"};
|
||||
/** Texture containing the ray hit radiance (tracing-res). */
|
||||
TextureFromPool ray_radiance_tx_ = {"ray_radiance_tx"};
|
||||
/** Texture containing the horizon visibility mask. */
|
||||
TextureFromPool horizon_occlusion_tx_ = {"horizon_occlusion_tx_"};
|
||||
/** Texture containing the horizon local radiance. */
|
||||
TextureFromPool horizon_radiance_tx_ = {"horizon_radiance_tx_"};
|
||||
TextureFromPool horizon_radiance_tx_[4] = {{"horizon_radiance_tx_"}};
|
||||
TextureFromPool horizon_radiance_denoised_tx_[4] = {{"horizon_radiance_denoised_tx_"}};
|
||||
/** Texture containing the input screen radiance but re-projected. */
|
||||
TextureFromPool downsampled_in_radiance_tx_ = {"downsampled_in_radiance_tx_"};
|
||||
/** Texture containing the view space normal. The BSDF normal is arbitrarily chosen. */
|
||||
|
@ -241,8 +244,7 @@ class RayTraceModule {
|
|||
const float4x4 &screen_radiance_persmat,
|
||||
/* TODO(fclem): Maybe wrap these two in some other class. */
|
||||
View &main_view,
|
||||
View &render_view,
|
||||
bool use_horizon_scan);
|
||||
View &render_view);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -37,12 +37,16 @@ void SphereProbeModule::begin_sync()
|
|||
const RaytraceEEVEE &options = instance_.scene->eevee.ray_tracing_options;
|
||||
float probe_brightness_clamp = (options.sample_clamp > 0.0) ? options.sample_clamp : 1e20;
|
||||
|
||||
GPUShader *shader = instance_.shaders.static_shader_get(SPHERE_PROBE_REMAP);
|
||||
|
||||
PassSimple &pass = remap_ps_;
|
||||
pass.init();
|
||||
pass.shader_set(instance_.shaders.static_shader_get(SPHERE_PROBE_REMAP));
|
||||
pass.specialize_constant(shader, "extract_sh", &extract_sh_);
|
||||
pass.shader_set(shader);
|
||||
pass.bind_texture("cubemap_tx", &cubemap_tx_);
|
||||
pass.bind_texture("atlas_tx", &probes_tx_);
|
||||
pass.bind_image("atlas_img", &probes_tx_);
|
||||
pass.bind_ssbo("out_sh", &tmp_spherical_harmonics_);
|
||||
pass.push_constant("probe_coord_packed", reinterpret_cast<int4 *>(&probe_sampling_coord_));
|
||||
pass.push_constant("write_coord_packed", reinterpret_cast<int4 *>(&probe_write_coord_));
|
||||
pass.push_constant("world_coord_packed", reinterpret_cast<int4 *>(&world_data.atlas_coord));
|
||||
|
@ -64,13 +68,14 @@ void SphereProbeModule::begin_sync()
|
|||
pass.dispatch(&dispatch_probe_convolve_);
|
||||
}
|
||||
{
|
||||
PassSimple &pass = update_irradiance_ps_;
|
||||
PassSimple &pass = sum_sh_ps_;
|
||||
pass.init();
|
||||
pass.shader_set(instance_.shaders.static_shader_get(SPHERE_PROBE_UPDATE_IRRADIANCE));
|
||||
pass.push_constant("world_coord_packed", reinterpret_cast<int4 *>(&world_data.atlas_coord));
|
||||
pass.bind_image("irradiance_atlas_img", &instance_.volume_probes.irradiance_atlas_tx_);
|
||||
pass.bind_texture("reflection_probes_tx", &probes_tx_);
|
||||
pass.dispatch(int2(1, 1));
|
||||
pass.shader_set(instance_.shaders.static_shader_get(SPHERE_PROBE_IRRADIANCE));
|
||||
pass.push_constant("probe_remap_dispatch_size", &dispatch_probe_pack_);
|
||||
pass.bind_ssbo("in_sh", &tmp_spherical_harmonics_);
|
||||
pass.bind_ssbo("out_sh", &spherical_harmonics_);
|
||||
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
pass.dispatch(1);
|
||||
}
|
||||
{
|
||||
PassSimple &pass = select_ps_;
|
||||
|
@ -155,7 +160,7 @@ void SphereProbeModule::ensure_cubemap_render_target(int resolution)
|
|||
/* TODO(fclem): deallocate it. */
|
||||
}
|
||||
|
||||
SphereProbeModule::UpdateInfo SphereProbeModule::update_info_from_probe(const SphereProbe &probe)
|
||||
SphereProbeModule::UpdateInfo SphereProbeModule::update_info_from_probe(SphereProbe &probe)
|
||||
{
|
||||
SphereProbeModule::UpdateInfo info = {};
|
||||
info.atlas_coord = probe.atlas_coord;
|
||||
|
@ -163,22 +168,21 @@ SphereProbeModule::UpdateInfo SphereProbeModule::update_info_from_probe(const Sp
|
|||
info.clipping_distances = probe.clipping_distances;
|
||||
info.probe_pos = probe.location;
|
||||
info.do_render = probe.do_render;
|
||||
info.do_world_irradiance_update = false;
|
||||
|
||||
probe.do_render = false;
|
||||
probe.use_for_render = true;
|
||||
|
||||
ensure_cubemap_render_target(info.cube_target_extent);
|
||||
return info;
|
||||
}
|
||||
|
||||
std::optional<SphereProbeModule::UpdateInfo> SphereProbeModule::world_update_info_pop()
|
||||
{
|
||||
SphereProbe &world_probe = instance_.light_probes.world_sphere_;
|
||||
if (!world_probe.do_render && !do_world_irradiance_update) {
|
||||
return std::nullopt;
|
||||
if (world_probe.do_render) {
|
||||
return update_info_from_probe(world_probe);
|
||||
}
|
||||
SphereProbeModule::UpdateInfo info = update_info_from_probe(world_probe);
|
||||
info.do_world_irradiance_update = do_world_irradiance_update;
|
||||
world_probe.do_render = false;
|
||||
do_world_irradiance_update = false;
|
||||
ensure_cubemap_render_target(info.cube_target_extent);
|
||||
return info;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SphereProbeModule::UpdateInfo> SphereProbeModule::probe_update_info_pop()
|
||||
|
@ -192,23 +196,22 @@ std::optional<SphereProbeModule::UpdateInfo> SphereProbeModule::probe_update_inf
|
|||
if (!probe.do_render) {
|
||||
continue;
|
||||
}
|
||||
SphereProbeModule::UpdateInfo info = update_info_from_probe(probe);
|
||||
probe.do_render = false;
|
||||
probe.use_for_render = true;
|
||||
ensure_cubemap_render_target(info.cube_target_extent);
|
||||
return info;
|
||||
return update_info_from_probe(probe);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void SphereProbeModule::remap_to_octahedral_projection(const SphereProbeAtlasCoord &atlas_coord)
|
||||
void SphereProbeModule::remap_to_octahedral_projection(const SphereProbeAtlasCoord &atlas_coord,
|
||||
bool extract_spherical_harmonics)
|
||||
{
|
||||
/* Update shader parameters that change per dispatch. */
|
||||
probe_sampling_coord_ = atlas_coord.as_sampling_coord();
|
||||
probe_write_coord_ = atlas_coord.as_write_coord(0);
|
||||
int resolution = probe_write_coord_.extent;
|
||||
dispatch_probe_pack_ = int3(int2(ceil_division(resolution, SPHERE_PROBE_GROUP_SIZE)), 1);
|
||||
dispatch_probe_pack_ = int3(
|
||||
int2(math::divide_ceil(int2(resolution), int2(SPHERE_PROBE_REMAP_GROUP_SIZE))), 1);
|
||||
extract_sh_ = extract_spherical_harmonics;
|
||||
instance_.manager->submit(remap_ps_);
|
||||
|
||||
/* Populate the mip levels */
|
||||
|
@ -219,20 +222,21 @@ void SphereProbeModule::remap_to_octahedral_projection(const SphereProbeAtlasCoo
|
|||
probe_read_coord_ = atlas_coord.as_write_coord(i);
|
||||
probe_write_coord_ = atlas_coord.as_write_coord(i + 1);
|
||||
int out_mip_res = probe_write_coord_.extent;
|
||||
dispatch_probe_convolve_ = int3(int2(ceil_division(out_mip_res, SPHERE_PROBE_GROUP_SIZE)), 1);
|
||||
dispatch_probe_convolve_ = int3(
|
||||
math::divide_ceil(int2(out_mip_res), int2(SPHERE_PROBE_GROUP_SIZE)), 1);
|
||||
instance_.manager->submit(convolve_ps_);
|
||||
}
|
||||
|
||||
if (extract_spherical_harmonics) {
|
||||
instance_.manager->submit(sum_sh_ps_);
|
||||
/* All volume probe that needs to composite the world probe need to be updated. */
|
||||
instance_.volume_probes.update_world_irradiance();
|
||||
}
|
||||
|
||||
/* Sync with atlas usage for shading. */
|
||||
GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
|
||||
}
|
||||
|
||||
void SphereProbeModule::update_world_irradiance()
|
||||
{
|
||||
instance_.manager->submit(update_irradiance_ps_);
|
||||
/* All volume probe that needs to composite the world probe need to be updated. */
|
||||
instance_.volume_probes.do_update_world_ = true;
|
||||
}
|
||||
|
||||
void SphereProbeModule::set_view(View & /*view*/)
|
||||
{
|
||||
Vector<SphereProbe *> probe_active;
|
||||
|
|
|
@ -44,8 +44,8 @@ class SphereProbeModule {
|
|||
|
||||
/** Copy the rendered cube-map to the atlas texture. */
|
||||
PassSimple remap_ps_ = {"Probe.CubemapToOctahedral"};
|
||||
/** Extract irradiance information from the world. */
|
||||
PassSimple update_irradiance_ps_ = {"Probe.UpdateIrradiance"};
|
||||
/** Sum irradiance information optionally extracted during `remap_ps_`. */
|
||||
PassSimple sum_sh_ps_ = {"Probe.SumSphericalHarmonics"};
|
||||
/** Copy volume probe irradiance for the center of sphere probes. */
|
||||
PassSimple select_ps_ = {"Probe.Select"};
|
||||
/** Convolve the octahedral map to fill the Mip-map levels. */
|
||||
|
@ -55,6 +55,8 @@ class SphereProbeModule {
|
|||
/** Output mip level for the convolution. */
|
||||
GPUTexture *convolve_output_ = nullptr;
|
||||
int convolve_lod_ = 0;
|
||||
/* True if we extract spherical harmonic during `remap_ps_`. */
|
||||
bool extract_sh_ = false;
|
||||
|
||||
int3 dispatch_probe_pack_ = int3(1);
|
||||
int3 dispatch_probe_convolve_ = int3(1);
|
||||
|
@ -78,6 +80,12 @@ class SphereProbeModule {
|
|||
/** Number of the probe to process in the select phase. */
|
||||
int reflection_probe_count_ = 0;
|
||||
|
||||
/** Intermediate buffer to store spherical harmonics. */
|
||||
StorageArrayBuffer<SphereProbeHarmonic, SPHERE_PROBE_MAX_HARMONIC, true>
|
||||
tmp_spherical_harmonics_ = {"tmp_spherical_harmonics_"};
|
||||
/** Final buffer containing the spherical harmonics for the world. */
|
||||
StorageBuffer<SphereProbeHarmonic, true> spherical_harmonics_ = {"spherical_harmonics_"};
|
||||
|
||||
/**
|
||||
* True if the next redraw will trigger a light-probe sphere update.
|
||||
* As syncing the draw passes for rendering has a significant overhead,
|
||||
|
@ -120,9 +128,9 @@ class SphereProbeModule {
|
|||
*/
|
||||
int probe_render_extent() const;
|
||||
|
||||
void tag_world_irradiance_for_update()
|
||||
StorageBuffer<SphereProbeHarmonic, true> &spherical_harmonics_buf()
|
||||
{
|
||||
do_world_irradiance_update = true;
|
||||
return spherical_harmonics_;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -154,10 +162,9 @@ class SphereProbeModule {
|
|||
SphereProbeAtlasCoord atlas_coord;
|
||||
|
||||
bool do_render;
|
||||
bool do_world_irradiance_update;
|
||||
};
|
||||
|
||||
UpdateInfo update_info_from_probe(const SphereProbe &probe);
|
||||
UpdateInfo update_info_from_probe(SphereProbe &probe);
|
||||
|
||||
/**
|
||||
* Pop the next reflection probe that requires to be updated.
|
||||
|
@ -166,10 +173,13 @@ class SphereProbeModule {
|
|||
std::optional<UpdateInfo> probe_update_info_pop();
|
||||
|
||||
/**
|
||||
* Internal processing passes.
|
||||
* Remap the rendered cube-map `cubemap_tx_` to a octahedral map inside the atlas at the given
|
||||
* coordinate.
|
||||
* If `extract_spherical_harmonics` is true, it will extract the spherical harmonics into
|
||||
* `spherical_harmonics_`.
|
||||
*/
|
||||
void remap_to_octahedral_projection(const SphereProbeAtlasCoord &atlas_coord);
|
||||
void update_world_irradiance();
|
||||
void remap_to_octahedral_projection(const SphereProbeAtlasCoord &atlas_coord,
|
||||
bool extract_spherical_harmonics);
|
||||
|
||||
void sync_display(Vector<SphereProbe *> &probe_active);
|
||||
};
|
||||
|
|
|
@ -119,6 +119,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
|
|||
return "eevee_hiz_update_layer";
|
||||
case HORIZON_DENOISE:
|
||||
return "eevee_horizon_denoise";
|
||||
case HORIZON_RESOLVE:
|
||||
return "eevee_horizon_resolve";
|
||||
case HORIZON_SCAN:
|
||||
return "eevee_horizon_scan";
|
||||
case HORIZON_SETUP:
|
||||
|
@ -215,12 +217,14 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
|
|||
return "eevee_lightprobe_irradiance_ray";
|
||||
case LIGHTPROBE_IRRADIANCE_LOAD:
|
||||
return "eevee_lightprobe_irradiance_load";
|
||||
case LIGHTPROBE_IRRADIANCE_WORLD:
|
||||
return "eevee_lightprobe_irradiance_world";
|
||||
case SPHERE_PROBE_CONVOLVE:
|
||||
return "eevee_reflection_probe_convolve";
|
||||
case SPHERE_PROBE_REMAP:
|
||||
return "eevee_reflection_probe_remap";
|
||||
case SPHERE_PROBE_UPDATE_IRRADIANCE:
|
||||
return "eevee_reflection_probe_update_irradiance";
|
||||
case SPHERE_PROBE_IRRADIANCE:
|
||||
return "eevee_reflection_probe_irradiance";
|
||||
case SPHERE_PROBE_SELECT:
|
||||
return "eevee_reflection_probe_select";
|
||||
case SHADOW_CLIPMAP_CLEAR:
|
||||
|
|
|
@ -73,6 +73,7 @@ enum eShaderType {
|
|||
HIZ_DEBUG,
|
||||
|
||||
HORIZON_DENOISE,
|
||||
HORIZON_RESOLVE,
|
||||
HORIZON_SCAN,
|
||||
HORIZON_SETUP,
|
||||
|
||||
|
@ -86,6 +87,7 @@ enum eShaderType {
|
|||
LIGHTPROBE_IRRADIANCE_OFFSET,
|
||||
LIGHTPROBE_IRRADIANCE_RAY,
|
||||
LIGHTPROBE_IRRADIANCE_LOAD,
|
||||
LIGHTPROBE_IRRADIANCE_WORLD,
|
||||
|
||||
LOOKDEV_DISPLAY,
|
||||
|
||||
|
@ -107,7 +109,7 @@ enum eShaderType {
|
|||
SPHERE_PROBE_CONVOLVE,
|
||||
SPHERE_PROBE_REMAP,
|
||||
SPHERE_PROBE_SELECT,
|
||||
SPHERE_PROBE_UPDATE_IRRADIANCE,
|
||||
SPHERE_PROBE_IRRADIANCE,
|
||||
|
||||
SHADOW_CLIPMAP_CLEAR,
|
||||
SHADOW_DEBUG,
|
||||
|
|
|
@ -797,8 +797,6 @@ struct LightData {
|
|||
float radius_squared;
|
||||
/** Spot angle tangent. */
|
||||
float spot_tan;
|
||||
/** Reuse for directional LOD bias. */
|
||||
#define _clipmap_lod_bias spot_tan
|
||||
|
||||
/** --- Shadow Data --- */
|
||||
/** Near clip distances. Float stored as int for atomic operations. */
|
||||
|
@ -821,7 +819,8 @@ struct LightData {
|
|||
float shadow_trace_distance;
|
||||
/* Radius in pixels for shadow filtering. */
|
||||
float pcf_radius;
|
||||
int _pad0;
|
||||
/* Shadow Map resolution bias. */
|
||||
float lod_bias;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(LightData, 16)
|
||||
|
||||
|
@ -1028,7 +1027,8 @@ struct ShadowSceneData {
|
|||
int step_count;
|
||||
/* Bias the shading point by using the normal to avoid self intersection. */
|
||||
float normal_bias;
|
||||
int _pad0;
|
||||
/* Ratio between tile-map pixel world "radius" and film pixel world "radius". */
|
||||
float tilemap_projection_ratio;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowSceneData, 16)
|
||||
|
||||
|
@ -1105,6 +1105,16 @@ struct SphereProbeDisplayData {
|
|||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(SphereProbeDisplayData, 16)
|
||||
|
||||
/* Used for sphere probe spherical harmonics extraction. Output one for each thread-group
|
||||
* and do a sum afterward. Reduces bandwidth usage. */
|
||||
struct SphereProbeHarmonic {
|
||||
float4 L0_M0;
|
||||
float4 L1_Mn1;
|
||||
float4 L1_M0;
|
||||
float4 L1_Mp1;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(SphereProbeHarmonic, 16)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -1323,6 +1333,9 @@ struct RayTraceData {
|
|||
int resolution_scale;
|
||||
/** View space thickness the objects. */
|
||||
float thickness;
|
||||
/** Scale and bias to go from horizon-trace resolution to input resolution. */
|
||||
int2 horizon_resolution_bias;
|
||||
int horizon_resolution_scale;
|
||||
/** Determine how fast the sample steps are getting bigger. */
|
||||
float quality;
|
||||
/** Maximum brightness during lighting evaluation. */
|
||||
|
@ -1613,6 +1626,5 @@ using VelocityIndexBuf = draw::StorageArrayBuffer<VelocityIndex, 16>;
|
|||
using VelocityObjectBuf = draw::StorageArrayBuffer<float4x4, 16>;
|
||||
using CryptomatteObjectBuf = draw::StorageArrayBuffer<float2, 16>;
|
||||
using ClipPlaneBuf = draw::UniformBuffer<ClipPlaneData>;
|
||||
|
||||
} // namespace blender::eevee
|
||||
#endif
|
||||
|
|
|
@ -531,7 +531,7 @@ void ShadowDirectional::cascade_tilemaps_distribution(Light &light, const Camera
|
|||
/* The bias is applied in cascade_level_range().
|
||||
* Using clipmap_lod_min here simplify code in shadow_directional_level().
|
||||
* Minus 1 because of the ceil(). */
|
||||
light._clipmap_lod_bias = light.clipmap_lod_min - 1;
|
||||
light.lod_bias = light.clipmap_lod_min - 1;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
|
@ -621,7 +621,7 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light,
|
|||
light.clipmap_lod_min = levels_range.first();
|
||||
light.clipmap_lod_max = levels_range.last();
|
||||
|
||||
light._clipmap_lod_bias = lod_bias;
|
||||
light.lod_bias = lod_bias;
|
||||
}
|
||||
|
||||
void ShadowDirectional::sync(const float4x4 &object_mat,
|
||||
|
@ -742,6 +742,9 @@ void ShadowModule::init()
|
|||
}
|
||||
}
|
||||
|
||||
jittered_transparency_ = !inst_.is_viewport() ||
|
||||
scene.eevee.flag & SCE_EEVEE_SHADOW_JITTERED_VIEWPORT;
|
||||
|
||||
data_.ray_count = clamp_i(inst_.scene->eevee.shadow_ray_count, 1, SHADOW_MAX_RAY);
|
||||
data_.step_count = clamp_i(inst_.scene->eevee.shadow_step_count, 1, SHADOW_MAX_STEP);
|
||||
data_.normal_bias = max_ff(inst_.scene->eevee.shadow_normal_bias, 0.0f);
|
||||
|
@ -757,7 +760,7 @@ void ShadowModule::init()
|
|||
simplify_shadows = inst_.is_viewport() ? scene.r.simplify_shadows :
|
||||
scene.r.simplify_shadows_render;
|
||||
}
|
||||
lod_bias_ = math::interpolate(float(SHADOW_TILEMAP_LOD), 0.0f, simplify_shadows);
|
||||
lod_bias_ = -log2(simplify_shadows);
|
||||
|
||||
const int2 atlas_extent = shadow_page_size_ * int2(SHADOW_PAGE_PER_ROW);
|
||||
const int atlas_layers = divide_ceil_u(shadow_page_len_, SHADOW_PAGE_PER_LAYER);
|
||||
|
@ -824,6 +827,8 @@ void ShadowModule::begin_sync()
|
|||
past_casters_updated_.clear();
|
||||
curr_casters_updated_.clear();
|
||||
curr_casters_.clear();
|
||||
jittered_transparent_casters_.clear();
|
||||
update_casters_ = true;
|
||||
|
||||
{
|
||||
Manager &manager = *inst_.manager;
|
||||
|
@ -849,7 +854,7 @@ void ShadowModule::begin_sync()
|
|||
sub.bind_ssbo("surfel_buf", &surfels_buf);
|
||||
sub.bind_ssbo("capture_info_buf", &capture_info_buf);
|
||||
sub.push_constant("directional_level", directional_level);
|
||||
sub.push_constant("tilemap_projection_ratio", projection_ratio);
|
||||
sub.push_constant("tilemap_proj_ratio", projection_ratio);
|
||||
sub.bind_resources(inst_.lights);
|
||||
sub.dispatch(&inst_.volume_probes.bake.dispatch_per_surfel_);
|
||||
|
||||
|
@ -864,7 +869,7 @@ void ShadowModule::begin_sync()
|
|||
sub.bind_ssbo("tilemaps_buf", &tilemap_pool.tilemaps_data);
|
||||
sub.bind_ssbo("tiles_buf", &tilemap_pool.tiles_data);
|
||||
sub.bind_texture("depth_tx", &src_depth_tx_);
|
||||
sub.push_constant("tilemap_projection_ratio", &tilemap_projection_ratio_);
|
||||
sub.push_constant("tilemap_proj_ratio", &data_.tilemap_projection_ratio);
|
||||
sub.bind_resources(inst_.lights);
|
||||
sub.dispatch(&dispatch_depth_scan_size_);
|
||||
}
|
||||
|
@ -880,7 +885,7 @@ void ShadowModule::begin_sync()
|
|||
sub.bind_ssbo("tilemaps_buf", &tilemap_pool.tilemaps_data);
|
||||
sub.bind_ssbo("tiles_buf", &tilemap_pool.tiles_data);
|
||||
sub.bind_ssbo("bounds_buf", &manager.bounds_buf.current());
|
||||
sub.push_constant("tilemap_projection_ratio", &tilemap_projection_ratio_);
|
||||
sub.push_constant("tilemap_proj_ratio", &data_.tilemap_projection_ratio);
|
||||
sub.push_constant("pixel_world_radius", &pixel_world_radius_);
|
||||
sub.push_constant("fb_resolution", &usage_tag_fb_resolution_);
|
||||
sub.push_constant("fb_lod", &usage_tag_fb_lod_);
|
||||
|
@ -897,7 +902,8 @@ void ShadowModule::begin_sync()
|
|||
void ShadowModule::sync_object(const Object *ob,
|
||||
const ObjectHandle &handle,
|
||||
const ResourceHandle &resource_handle,
|
||||
bool is_alpha_blend)
|
||||
bool is_alpha_blend,
|
||||
bool has_transparent_shadows)
|
||||
{
|
||||
bool is_shadow_caster = !(ob->visibility_flag & OB_HIDE_SHADOW);
|
||||
if (!is_shadow_caster && !is_alpha_blend) {
|
||||
|
@ -907,11 +913,18 @@ void ShadowModule::sync_object(const Object *ob,
|
|||
ShadowObject &shadow_ob = objects_.lookup_or_add_default(handle.object_key);
|
||||
shadow_ob.used = true;
|
||||
const bool is_initialized = shadow_ob.resource_handle.raw != 0;
|
||||
if ((handle.recalc != 0 || !is_initialized) && is_shadow_caster) {
|
||||
if (shadow_ob.resource_handle.raw != 0) {
|
||||
const bool has_jittered_transparency = has_transparent_shadows && jittered_transparency_;
|
||||
if (is_shadow_caster && (handle.recalc || !is_initialized || has_jittered_transparency)) {
|
||||
if (handle.recalc && is_initialized) {
|
||||
past_casters_updated_.append(shadow_ob.resource_handle.raw);
|
||||
}
|
||||
curr_casters_updated_.append(resource_handle.raw);
|
||||
|
||||
if (has_jittered_transparency) {
|
||||
jittered_transparent_casters_.append(resource_handle.raw);
|
||||
}
|
||||
else {
|
||||
curr_casters_updated_.append(resource_handle.raw);
|
||||
}
|
||||
}
|
||||
shadow_ob.resource_handle = resource_handle;
|
||||
|
||||
|
@ -932,7 +945,7 @@ void ShadowModule::end_sync()
|
|||
light.shadow_discard_safe(*this);
|
||||
}
|
||||
else if (light.directional != nullptr) {
|
||||
light.directional->release_excess_tilemaps(inst_.camera, lod_bias_);
|
||||
light.directional->release_excess_tilemaps(inst_.camera, light.lod_bias);
|
||||
}
|
||||
else if (light.punctual != nullptr) {
|
||||
light.punctual->release_excess_tilemaps();
|
||||
|
@ -946,10 +959,10 @@ void ShadowModule::end_sync()
|
|||
light.tilemap_index = LIGHT_NO_SHADOW;
|
||||
}
|
||||
else if (light.directional != nullptr) {
|
||||
light.directional->end_sync(light, inst_.camera, lod_bias_);
|
||||
light.directional->end_sync(light, inst_.camera, light.lod_bias);
|
||||
}
|
||||
else if (light.punctual != nullptr) {
|
||||
light.punctual->end_sync(light, lod_bias_);
|
||||
light.punctual->end_sync(light, light.lod_bias);
|
||||
}
|
||||
else {
|
||||
light.tilemap_index = LIGHT_NO_SHADOW;
|
||||
|
@ -973,6 +986,7 @@ void ShadowModule::end_sync()
|
|||
}
|
||||
past_casters_updated_.push_update();
|
||||
curr_casters_updated_.push_update();
|
||||
jittered_transparent_casters_.push_update();
|
||||
|
||||
curr_casters_.push_update();
|
||||
|
||||
|
@ -1080,6 +1094,22 @@ void ShadowModule::end_sync()
|
|||
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
}
|
||||
|
||||
{
|
||||
/* Mark for update all shadow pages touching a jittered transparency shadow caster. */
|
||||
PassSimple &pass = jittered_transparent_caster_update_ps_;
|
||||
pass.init();
|
||||
if (jittered_transparent_casters_.size() > 0) {
|
||||
pass.shader_set(inst_.shaders.static_shader_get(SHADOW_TILEMAP_TAG_UPDATE));
|
||||
pass.bind_ssbo("tilemaps_buf", tilemap_pool.tilemaps_data);
|
||||
pass.bind_ssbo("tiles_buf", tilemap_pool.tiles_data);
|
||||
pass.bind_ssbo("bounds_buf", &manager.bounds_buf.current());
|
||||
pass.bind_ssbo("resource_ids_buf", jittered_transparent_casters_);
|
||||
pass.dispatch(
|
||||
int3(jittered_transparent_casters_.size(), 1, tilemap_pool.tilemaps_data.size()));
|
||||
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Non volume usage tagging happens between these two steps.
|
||||
* (Setup at begin_sync) */
|
||||
|
||||
|
@ -1088,7 +1118,7 @@ void ShadowModule::end_sync()
|
|||
sub.shader_set(inst_.shaders.static_shader_get(SHADOW_TILEMAP_TAG_USAGE_VOLUME));
|
||||
sub.bind_ssbo("tilemaps_buf", &tilemap_pool.tilemaps_data);
|
||||
sub.bind_ssbo("tiles_buf", &tilemap_pool.tiles_data);
|
||||
sub.push_constant("tilemap_projection_ratio", &tilemap_projection_ratio_);
|
||||
sub.push_constant("tilemap_proj_ratio", &data_.tilemap_projection_ratio);
|
||||
sub.bind_resources(inst_.uniform_data);
|
||||
sub.bind_resources(inst_.hiz_buffer.front);
|
||||
sub.bind_resources(inst_.sampling);
|
||||
|
@ -1310,7 +1340,8 @@ void ShadowModule::set_view(View &view, GPUTexture *depth_tx)
|
|||
dispatch_depth_scan_size_ = math::divide_ceil(target_size, int3(SHADOW_DEPTH_SCAN_GROUP_SIZE));
|
||||
|
||||
pixel_world_radius_ = screen_pixel_radius(view, int2(target_size));
|
||||
tilemap_projection_ratio_ = tilemap_pixel_radius() / pixel_world_radius_;
|
||||
data_.tilemap_projection_ratio = tilemap_pixel_radius() / pixel_world_radius_;
|
||||
inst_.uniform_data.push_update();
|
||||
|
||||
usage_tag_fb_resolution_ = math::divide_ceil(int2(target_size),
|
||||
int2(std::exp2(usage_tag_fb_lod_)));
|
||||
|
@ -1338,7 +1369,7 @@ void ShadowModule::set_view(View &view, GPUTexture *depth_tx)
|
|||
}
|
||||
|
||||
inst_.hiz_buffer.update();
|
||||
bool update_casters = true;
|
||||
bool first_loop = true;
|
||||
|
||||
do {
|
||||
DRW_stats_group_start("Shadow");
|
||||
|
@ -1346,12 +1377,15 @@ void ShadowModule::set_view(View &view, GPUTexture *depth_tx)
|
|||
GPU_uniformbuf_clear_to_zero(shadow_multi_view_.matrices_ubo_get());
|
||||
|
||||
inst_.manager->submit(tilemap_setup_ps_, view);
|
||||
if (assign_if_different(update_casters, false)) {
|
||||
if (assign_if_different(update_casters_, false)) {
|
||||
/* Run caster update only once. */
|
||||
/* TODO(fclem): There is an optimization opportunity here where we can
|
||||
* test casters only against the static tilemaps instead of all of them. */
|
||||
inst_.manager->submit(caster_update_ps_, view);
|
||||
}
|
||||
if (assign_if_different(first_loop, false)) {
|
||||
inst_.manager->submit(jittered_transparent_caster_update_ps_, view);
|
||||
}
|
||||
inst_.manager->submit(tilemap_usage_ps_, view);
|
||||
inst_.manager->submit(tilemap_update_ps_, view);
|
||||
|
||||
|
|
|
@ -213,6 +213,11 @@ class ShadowModule {
|
|||
/** Map of shadow casters to track deletion & update of intersected shadows. */
|
||||
Map<ObjectKey, ShadowObject> objects_;
|
||||
|
||||
/* Used to call caster_update_ps_ only once per sync (Initialized on begin_sync). */
|
||||
bool update_casters_ = false;
|
||||
|
||||
bool jittered_transparency_ = false;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Tile-map Management
|
||||
* \{ */
|
||||
|
@ -229,9 +234,11 @@ class ShadowModule {
|
|||
Framebuffer usage_tag_fb;
|
||||
|
||||
PassSimple caster_update_ps_ = {"CasterUpdate"};
|
||||
PassSimple jittered_transparent_caster_update_ps_ = {"TransparentCasterUpdate"};
|
||||
/** List of Resource IDs (to get bounds) for tagging passes. */
|
||||
StorageVectorBuffer<uint, 128> past_casters_updated_ = {"PastCastersUpdated"};
|
||||
StorageVectorBuffer<uint, 128> curr_casters_updated_ = {"CurrCastersUpdated"};
|
||||
StorageVectorBuffer<uint, 128> jittered_transparent_casters_ = {"JitteredTransparentCasters"};
|
||||
/** List of Resource IDs (to get bounds) for getting minimum clip-maps bounds. */
|
||||
StorageVectorBuffer<uint, 128> curr_casters_ = {"CurrCasters"};
|
||||
|
||||
|
@ -249,8 +256,6 @@ class ShadowModule {
|
|||
StorageArrayBuffer<uint, SHADOW_VIEW_MAX, true> viewport_index_buf_ = {"viewport_index_buf"};
|
||||
|
||||
int3 dispatch_depth_scan_size_;
|
||||
/* Ratio between tile-map pixel world "radius" and film pixel world "radius". */
|
||||
float tilemap_projection_ratio_;
|
||||
float pixel_world_radius_;
|
||||
int2 usage_tag_fb_resolution_;
|
||||
int usage_tag_fb_lod_ = 5;
|
||||
|
@ -334,7 +339,8 @@ class ShadowModule {
|
|||
void sync_object(const Object *ob,
|
||||
const ObjectHandle &handle,
|
||||
const ResourceHandle &resource_handle,
|
||||
bool is_alpha_blend);
|
||||
bool is_alpha_blend,
|
||||
bool has_transparent_shadows);
|
||||
void end_sync();
|
||||
|
||||
void set_lights_data();
|
||||
|
@ -359,6 +365,11 @@ class ShadowModule {
|
|||
return data_;
|
||||
}
|
||||
|
||||
float get_global_lod_bias()
|
||||
{
|
||||
return lod_bias_;
|
||||
}
|
||||
|
||||
private:
|
||||
void remove_unused();
|
||||
void debug_page_map_call(DRWPass *pass);
|
||||
|
|
|
@ -114,6 +114,7 @@ void SyncModule::sync_mesh(Object *ob,
|
|||
}
|
||||
|
||||
bool is_alpha_blend = false;
|
||||
bool has_transparent_shadows = false;
|
||||
float inflate_bounds = 0.0f;
|
||||
for (auto i : material_array.gpu_materials.index_range()) {
|
||||
GPUBatch *geom = mat_geom[i];
|
||||
|
@ -147,6 +148,7 @@ void SyncModule::sync_mesh(Object *ob,
|
|||
geometry_call(material.reflection_probe_shading.sub_pass, geom, res_handle);
|
||||
|
||||
is_alpha_blend = is_alpha_blend || material.is_alpha_blend_transparent;
|
||||
has_transparent_shadows = has_transparent_shadows || material.has_transparent_shadows;
|
||||
|
||||
::Material *mat = GPU_material_get_material(gpu_material);
|
||||
inst_.cryptomatte.sync_material(mat);
|
||||
|
@ -162,7 +164,7 @@ void SyncModule::sync_mesh(Object *ob,
|
|||
|
||||
inst_.manager->extract_object_attributes(res_handle, ob_ref, material_array.gpu_materials);
|
||||
|
||||
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend);
|
||||
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend, has_transparent_shadows);
|
||||
inst_.cryptomatte.sync_object(ob, res_handle);
|
||||
}
|
||||
|
||||
|
@ -192,6 +194,7 @@ bool SyncModule::sync_sculpt(Object *ob,
|
|||
MaterialArray &material_array = inst_.materials.material_array_get(ob, has_motion);
|
||||
|
||||
bool is_alpha_blend = false;
|
||||
bool has_transparent_shadows = false;
|
||||
float inflate_bounds = 0.0f;
|
||||
for (SculptBatch &batch :
|
||||
sculpt_batches_per_material_get(ob_ref.object, material_array.gpu_materials))
|
||||
|
@ -226,6 +229,7 @@ bool SyncModule::sync_sculpt(Object *ob,
|
|||
geometry_call(material.reflection_probe_shading.sub_pass, geom, res_handle);
|
||||
|
||||
is_alpha_blend = is_alpha_blend || material.is_alpha_blend_transparent;
|
||||
has_transparent_shadows = has_transparent_shadows || material.has_transparent_shadows;
|
||||
|
||||
GPUMaterial *gpu_material = material_array.gpu_materials[batch.material_slot];
|
||||
::Material *mat = GPU_material_get_material(gpu_material);
|
||||
|
@ -245,7 +249,7 @@ bool SyncModule::sync_sculpt(Object *ob,
|
|||
|
||||
inst_.manager->extract_object_attributes(res_handle, ob_ref, material_array.gpu_materials);
|
||||
|
||||
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend);
|
||||
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend, has_transparent_shadows);
|
||||
inst_.cryptomatte.sync_object(ob, res_handle);
|
||||
|
||||
return true;
|
||||
|
@ -308,13 +312,15 @@ void SyncModule::sync_point_cloud(Object *ob,
|
|||
::Material *mat = GPU_material_get_material(gpu_material);
|
||||
inst_.cryptomatte.sync_material(mat);
|
||||
|
||||
bool is_alpha_blend = material.is_alpha_blend_transparent;
|
||||
|
||||
if (GPU_material_has_displacement_output(gpu_material) && mat->inflate_bounds != 0.0f) {
|
||||
inst_.manager->update_handle_bounds(res_handle, ob_ref, mat->inflate_bounds);
|
||||
}
|
||||
|
||||
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend);
|
||||
inst_.shadows.sync_object(ob,
|
||||
ob_handle,
|
||||
res_handle,
|
||||
material.is_alpha_blend_transparent,
|
||||
material.has_transparent_shadows);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -485,8 +491,9 @@ void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandl
|
|||
|
||||
gpencil_drawcall_flush(iter);
|
||||
|
||||
bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */
|
||||
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend);
|
||||
bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */
|
||||
bool has_transparent_shadows = true; /* TODO material.has_transparent_shadows. */
|
||||
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend, has_transparent_shadows);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -560,13 +567,15 @@ void SyncModule::sync_curves(Object *ob,
|
|||
::Material *mat = GPU_material_get_material(gpu_material);
|
||||
inst_.cryptomatte.sync_material(mat);
|
||||
|
||||
bool is_alpha_blend = material.is_alpha_blend_transparent;
|
||||
|
||||
if (GPU_material_has_displacement_output(gpu_material) && mat->inflate_bounds != 0.0f) {
|
||||
inst_.manager->update_handle_bounds(res_handle, ob_ref, mat->inflate_bounds);
|
||||
}
|
||||
|
||||
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend);
|
||||
inst_.shadows.sync_object(ob,
|
||||
ob_handle,
|
||||
res_handle,
|
||||
material.is_alpha_blend_transparent,
|
||||
material.has_transparent_shadows);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -287,11 +287,7 @@ void CaptureView::render_world()
|
|||
inst_.pipelines.world.render(view);
|
||||
}
|
||||
|
||||
inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord);
|
||||
}
|
||||
|
||||
if (update_info->do_world_irradiance_update) {
|
||||
inst_.sphere_probes.update_world_irradiance();
|
||||
inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord, true);
|
||||
}
|
||||
|
||||
GPU_debug_group_end();
|
||||
|
@ -349,7 +345,7 @@ void CaptureView::render_probes()
|
|||
inst_.render_buffers.release();
|
||||
inst_.gbuffer.release();
|
||||
GPU_debug_group_end();
|
||||
inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord);
|
||||
inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord, false);
|
||||
}
|
||||
|
||||
if (inst_.pipelines.data.is_probe_reflection) {
|
||||
|
|
|
@ -31,22 +31,15 @@ void main()
|
|||
noise.y = utility_tx_fetch(utility_tx, vec2(texel), UTIL_BLUE_NOISE_LAYER).r;
|
||||
noise = fract(noise + sampling_rng_2D_get(SAMPLING_AO_U));
|
||||
|
||||
ClosureOcclusion occlusion;
|
||||
occlusion.N = vN;
|
||||
HorizonScanResult scan = horizon_scan_eval(vP,
|
||||
vN,
|
||||
noise,
|
||||
uniform_buf.ao.pixel_size,
|
||||
uniform_buf.ao.distance,
|
||||
uniform_buf.ao.thickness,
|
||||
uniform_buf.ao.angle_bias,
|
||||
10,
|
||||
false);
|
||||
|
||||
HorizonScanContext ctx;
|
||||
ctx.occlusion = occlusion;
|
||||
|
||||
horizon_scan_eval(vP,
|
||||
ctx,
|
||||
noise,
|
||||
uniform_buf.ao.pixel_size,
|
||||
uniform_buf.ao.distance,
|
||||
uniform_buf.ao.thickness,
|
||||
uniform_buf.ao.angle_bias,
|
||||
10,
|
||||
false);
|
||||
|
||||
imageStore(
|
||||
out_ao_img, ivec3(texel, out_ao_img_layer_index), vec4(saturate(ctx.occlusion_result.r)));
|
||||
imageStore(out_ao_img, ivec3(texel, out_ao_img_layer_index), vec4(saturate(scan.result)));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Filtering utilities.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl)
|
||||
|
||||
/**
|
||||
* Return the factor to filter_gaussian_weight. This is handy utility function to compute your
|
||||
* gaussian parameter in a documented manner.
|
||||
* - `linear_distance` is the distance at which the filter will have covered the given amount of
|
||||
* `standard_deviation`. Must not be null.
|
||||
* - `standard_deviation` is the shape of the bell. Higher values sharpens the filter.
|
||||
*
|
||||
* https://en.wikipedia.org/wiki/Standard_deviation#/media/File:Standard_deviation_diagram.svg
|
||||
*
|
||||
* Example: for a 5px 1d gaussian filter, one would set `linear_distance` of 2.5.
|
||||
* `standard_deviation = 1.0` will cover 68% of the gaussian weight inside the 5px radius.
|
||||
* `standard_deviation = 2.0` will cover 95% of the gaussian weight inside the 5px radius.
|
||||
*/
|
||||
float filter_gaussian_factor(float linear_distance, float standard_deviation)
|
||||
{
|
||||
/* Account for `filter_gaussian_factor` using `exp2` for speed (`exp(x) = exp2(x / log(2))`). */
|
||||
const float log_2_inv = 1.442695041;
|
||||
return log_2_inv * standard_deviation / square(linear_distance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gaussian distance weighting. Allow weighting based on distance without null weight whatever the
|
||||
* distance. `factor` is supposed to be a scaling parameter given by `filter_gaussian_factor`.
|
||||
*/
|
||||
float filter_gaussian_weight(float factor, float square_distance)
|
||||
{
|
||||
/* Using exp2 since it is faster on GPU. `filter_gaussian_factor` account for that. */
|
||||
return exp2(-factor * square_distance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Planar distance weighting. Allow to weight based on geometric neighborhood.
|
||||
*/
|
||||
float filter_planar_weight(vec3 plane_N, vec3 plane_P, vec3 P, float scale)
|
||||
{
|
||||
vec4 plane_eq = vec4(plane_N, -dot(plane_N, plane_P));
|
||||
float plane_distance = dot(plane_eq, vec4(P, 1.0));
|
||||
return filter_gaussian_weight(scale, square(plane_distance));
|
||||
}
|
||||
|
||||
/**
|
||||
* Angle weighting. Mostly used for normals.
|
||||
* Expects both normals to be normalized.
|
||||
*/
|
||||
float filter_angle_weight(vec3 center_N, vec3 sample_N)
|
||||
{
|
||||
float facing_ratio = dot(center_N, sample_N);
|
||||
return saturate(pow8f(facing_ratio));
|
||||
}
|
|
@ -6,161 +6,103 @@
|
|||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_filter_lib.glsl)
|
||||
|
||||
float bilateral_depth_weight(vec3 center_N, vec3 center_P, vec3 sample_P)
|
||||
vec3 sample_normal_get(ivec2 texel, out bool is_processed)
|
||||
{
|
||||
vec4 center_plane_eq = vec4(center_N, -dot(center_N, center_P));
|
||||
/* Only compare distance to the center plane formed by the normal. */
|
||||
float depth_delta = dot(center_plane_eq, vec4(sample_P, 1.0));
|
||||
/* TODO(fclem): Scene parameter. This is dependent on scene scale. */
|
||||
const float scale = 10000.0;
|
||||
float weight = exp2(-scale * square(depth_delta));
|
||||
return weight;
|
||||
vec4 normal = texelFetch(screen_normal_tx, texel, 0);
|
||||
is_processed = (normal.w != 0.0);
|
||||
return normal.xyz * 2.0 - 1.0;
|
||||
}
|
||||
|
||||
float bilateral_spatial_weight(float sigma, vec2 offset_from_center)
|
||||
float sample_weight_get(
|
||||
vec3 center_N, vec3 center_P, ivec2 sample_texel, vec2 sample_uv, ivec2 sample_offset)
|
||||
{
|
||||
/* From https://github.com/tranvansang/bilateral-filter/blob/master/fshader.frag */
|
||||
float fac = -1.0 / square(sigma);
|
||||
/* Take two standard deviation. */
|
||||
fac *= 2.0;
|
||||
float weight = exp2(fac * length_squared(offset_from_center));
|
||||
return weight;
|
||||
ivec2 sample_texel_fullres = sample_texel * uniform_buf.raytrace.horizon_resolution_scale +
|
||||
uniform_buf.raytrace.horizon_resolution_bias;
|
||||
float sample_depth = texelFetch(hiz_tx, sample_texel_fullres, 0).r;
|
||||
|
||||
bool is_valid;
|
||||
vec3 sample_N = sample_normal_get(sample_texel, is_valid);
|
||||
vec3 sample_P = drw_point_screen_to_world(vec3(sample_uv, sample_depth));
|
||||
|
||||
if (!is_valid) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
float gauss = filter_gaussian_factor(1.5, 1.5);
|
||||
|
||||
/* TODO(fclem): Scene parameter. 100.0 is dependent on scene scale. */
|
||||
float depth_weight = filter_planar_weight(center_N, center_P, sample_P, 100.0);
|
||||
float spatial_weight = filter_gaussian_weight(gauss, length_squared(vec2(sample_offset)));
|
||||
float normal_weight = filter_angle_weight(center_N, sample_N);
|
||||
|
||||
return depth_weight * spatial_weight * normal_weight;
|
||||
}
|
||||
|
||||
float bilateral_normal_weight(vec3 center_N, vec3 sample_N)
|
||||
SphericalHarmonicL1 load_spherical_harmonic(ivec2 texel)
|
||||
{
|
||||
float facing_ratio = dot(center_N, sample_N);
|
||||
float weight = saturate(pow8f(facing_ratio));
|
||||
return weight;
|
||||
}
|
||||
|
||||
/* In order to remove some more fireflies, "tone-map" the color samples during the accumulation. */
|
||||
vec3 to_accumulation_space(vec3 color)
|
||||
{
|
||||
/* This 4 factor is to avoid killing too much energy. */
|
||||
/* TODO(fclem): Parameter? */
|
||||
color /= 4.0;
|
||||
color = color / (1.0 + reduce_add(color));
|
||||
return color;
|
||||
}
|
||||
vec3 from_accumulation_space(vec3 color)
|
||||
{
|
||||
color = color / (1.0 - reduce_add(color));
|
||||
color *= 4.0;
|
||||
return color;
|
||||
}
|
||||
|
||||
vec3 load_normal(ivec2 texel)
|
||||
{
|
||||
return gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel).surface_N;
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = texelFetch(in_sh_0_tx, texel, 0);
|
||||
sh.L1.Mn1 = texelFetch(in_sh_1_tx, texel, 0);
|
||||
sh.L1.M0 = texelFetch(in_sh_2_tx, texel, 0);
|
||||
sh.L1.Mp1 = texelFetch(in_sh_3_tx, texel, 0);
|
||||
sh = spherical_harmonics_decompress(sh);
|
||||
return sh;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
const uint tile_size = RAYTRACE_GROUP_SIZE;
|
||||
uvec2 tile_coord = unpackUvec2x16(tiles_coord_buf[gl_WorkGroupID.x]);
|
||||
ivec2 texel = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size);
|
||||
|
||||
ivec2 texel_fullres = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size);
|
||||
ivec2 texel = (texel_fullres) / uniform_buf.raytrace.resolution_scale;
|
||||
vec2 texel_size = 1.0 / vec2(textureSize(in_sh_0_tx, 0).xy);
|
||||
ivec2 texel_fullres = texel * uniform_buf.raytrace.horizon_resolution_scale +
|
||||
uniform_buf.raytrace.horizon_resolution_bias;
|
||||
|
||||
ivec2 extent = textureSize(gbuf_header_tx, 0).xy;
|
||||
if (any(greaterThanEqual(texel_fullres, extent))) {
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 center_uv = (vec2(texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv;
|
||||
float center_depth = texelFetch(depth_tx, texel_fullres, 0).r;
|
||||
bool is_valid;
|
||||
float center_depth = texelFetch(hiz_tx, texel_fullres, 0).r;
|
||||
vec2 center_uv = vec2(texel) * texel_size;
|
||||
vec3 center_P = drw_point_screen_to_world(vec3(center_uv, center_depth));
|
||||
vec3 center_N = sample_normal_get(texel, is_valid);
|
||||
|
||||
if (center_depth == 1.0) {
|
||||
/* Do not trace for background */
|
||||
if (!is_valid) {
|
||||
#if 0 /* This is not needed as the next stage doesn't do bilinear filtering. */
|
||||
imageStore(out_sh_0_img, texel, vec4(0.0));
|
||||
imageStore(out_sh_1_img, texel, vec4(0.0));
|
||||
imageStore(out_sh_2_img, texel, vec4(0.0));
|
||||
imageStore(out_sh_3_img, texel, vec4(0.0));
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
ClosureUndetermined closure_center = gbuffer_read_bin(
|
||||
gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres, closure_index);
|
||||
|
||||
if (closure_center.type == CLOSURE_NONE_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
vec3 center_N = closure_center.N;
|
||||
float roughness = closure_apparent_roughness_get(closure_center);
|
||||
|
||||
float mix_fac = saturate(roughness * uniform_buf.raytrace.roughness_mask_scale -
|
||||
uniform_buf.raytrace.roughness_mask_bias);
|
||||
bool use_raytrace = mix_fac < 1.0;
|
||||
bool use_horizon = mix_fac > 0.0;
|
||||
|
||||
if (use_horizon == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
vec3 accum_radiance = vec3(0.0);
|
||||
float accum_occlusion = 0.0;
|
||||
SphericalHarmonicL1 accum_sh = spherical_harmonics_L1_new();
|
||||
float accum_weight = 0.0;
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
ivec2 offset = ivec2(x, y);
|
||||
ivec2 sample_texel = texel + ivec2(x, y);
|
||||
ivec2 sample_texel_fullres = sample_texel * uniform_buf.raytrace.resolution_scale +
|
||||
uniform_buf.raytrace.resolution_bias;
|
||||
ivec3 sample_tile = ivec3(sample_texel_fullres / RAYTRACE_GROUP_SIZE, closure_index);
|
||||
/* Make sure the sample has been processed and do not contain garbage data. */
|
||||
if (imageLoad(tile_mask_img, sample_tile).r == 0u) {
|
||||
continue;
|
||||
/* 3x3 filter. */
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
ivec2 sample_offset = ivec2(x, y);
|
||||
ivec2 sample_texel = texel + sample_offset;
|
||||
vec2 sample_uv = (vec2(sample_texel) + 0.5) * texel_size;
|
||||
float sample_weight = sample_weight_get(
|
||||
center_N, center_P, sample_texel, sample_uv, sample_offset);
|
||||
/* We need to avoid sampling if there no weight as the texture values could be undefined
|
||||
* (is_valid is false). */
|
||||
if (sample_weight > 0.0) {
|
||||
SphericalHarmonicL1 sample_sh = load_spherical_harmonic(sample_texel);
|
||||
accum_sh = spherical_harmonics_madd(sample_sh, sample_weight, accum_sh);
|
||||
accum_weight += sample_weight;
|
||||
}
|
||||
|
||||
float sample_depth = texelFetch(depth_tx, sample_texel_fullres, 0).r;
|
||||
vec2 sample_uv = (vec2(sample_texel_fullres) + 0.5) *
|
||||
uniform_buf.raytrace.full_resolution_inv;
|
||||
vec3 sample_P = drw_point_screen_to_world(vec3(sample_uv, sample_depth));
|
||||
|
||||
/* Background case. */
|
||||
if (sample_depth == 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
vec3 sample_N = load_normal(sample_texel_fullres);
|
||||
|
||||
float depth_weight = bilateral_depth_weight(center_N, center_P, sample_P);
|
||||
float spatial_weight = bilateral_spatial_weight(1.5, vec2(offset));
|
||||
float normal_weight = bilateral_normal_weight(center_N, sample_N);
|
||||
|
||||
float weight = depth_weight * spatial_weight * normal_weight;
|
||||
|
||||
vec3 radiance = imageLoad(horizon_radiance_img, sample_texel).rgb;
|
||||
/* Do not gather unprocessed pixels. */
|
||||
if (all(equal(radiance, FLT_11_11_10_MAX))) {
|
||||
continue;
|
||||
}
|
||||
float occlusion = imageLoad(horizon_occlusion_img, sample_texel).r;
|
||||
accum_radiance += to_accumulation_space(radiance) * weight;
|
||||
accum_occlusion += occlusion * weight;
|
||||
accum_weight += weight;
|
||||
}
|
||||
}
|
||||
float occlusion = accum_occlusion * safe_rcp(accum_weight);
|
||||
vec3 radiance = from_accumulation_space(accum_radiance * safe_rcp(accum_weight));
|
||||
accum_sh = spherical_harmonics_mul(accum_sh, safe_rcp(accum_weight));
|
||||
|
||||
vec3 P = center_P;
|
||||
vec3 N = center_N;
|
||||
vec3 Ng = center_N;
|
||||
vec3 V = drw_world_incident_vector(P);
|
||||
/* Fallback to nearest light-probe. */
|
||||
LightProbeSample samp = lightprobe_load(P, Ng, V);
|
||||
vec3 radiance_probe = spherical_harmonics_evaluate_lambert(N, samp.volume_irradiance);
|
||||
/* Apply missing distant lighting. */
|
||||
radiance += occlusion * radiance_probe;
|
||||
accum_sh = spherical_harmonics_compress(accum_sh);
|
||||
|
||||
vec4 radiance_horizon = vec4(radiance, 0.0);
|
||||
vec4 radiance_raytrace = use_raytrace ? imageLoad(radiance_img, texel_fullres) : vec4(0.0);
|
||||
|
||||
vec4 radiance_mixed = mix(radiance_raytrace, radiance_horizon, mix_fac);
|
||||
|
||||
imageStore(radiance_img, texel_fullres, radiance_mixed);
|
||||
imageStore(out_sh_0_img, texel, accum_sh.L0.M0);
|
||||
imageStore(out_sh_1_img, texel, accum_sh.L1.Mn1);
|
||||
imageStore(out_sh_2_img, texel, accum_sh.L1.M0);
|
||||
imageStore(out_sh_3_img, texel, accum_sh.L1.Mp1);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_filter_lib.glsl)
|
||||
|
||||
vec3 sample_normal_get(ivec2 texel, out bool is_processed)
|
||||
{
|
||||
vec4 normal = texelFetch(screen_normal_tx, texel, 0);
|
||||
is_processed = (normal.w != 0.0);
|
||||
return drw_normal_view_to_world(normal.xyz * 2.0 - 1.0);
|
||||
}
|
||||
|
||||
float sample_weight_get(vec3 center_N, vec3 center_P, ivec2 center_texel, ivec2 sample_offset)
|
||||
{
|
||||
ivec2 sample_texel = center_texel + sample_offset;
|
||||
ivec2 sample_texel_fullres = sample_texel * uniform_buf.raytrace.horizon_resolution_scale +
|
||||
uniform_buf.raytrace.horizon_resolution_bias;
|
||||
vec2 sample_uv = (vec2(sample_texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv;
|
||||
|
||||
float sample_depth = texelFetch(depth_tx, sample_texel_fullres, 0).r;
|
||||
|
||||
bool is_valid;
|
||||
vec3 sample_N = sample_normal_get(sample_texel, is_valid);
|
||||
vec3 sample_P = drw_point_screen_to_world(vec3(sample_uv, sample_depth));
|
||||
|
||||
if (!is_valid) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/* TODO(fclem): Scene parameter. 10000.0 is dependent on scene scale. */
|
||||
float depth_weight = filter_planar_weight(center_N, center_P, sample_P, 10000.0);
|
||||
float normal_weight = filter_angle_weight(center_N, sample_N);
|
||||
|
||||
return depth_weight * normal_weight;
|
||||
}
|
||||
|
||||
SphericalHarmonicL1 load_spherical_harmonic(ivec2 texel, bool valid)
|
||||
{
|
||||
if (!valid) {
|
||||
/* We need to avoid sampling if there no weight as the texture values could be undefined
|
||||
* (is_valid is false). */
|
||||
return spherical_harmonics_L1_new();
|
||||
}
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = texelFetch(horizon_radiance_0_tx, texel, 0);
|
||||
sh.L1.Mn1 = texelFetch(horizon_radiance_1_tx, texel, 0);
|
||||
sh.L1.M0 = texelFetch(horizon_radiance_2_tx, texel, 0);
|
||||
sh.L1.Mp1 = texelFetch(horizon_radiance_3_tx, texel, 0);
|
||||
return spherical_harmonics_decompress(sh);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
const uint tile_size = RAYTRACE_GROUP_SIZE;
|
||||
uvec2 tile_coord = unpackUvec2x16(tiles_coord_buf[gl_WorkGroupID.x]);
|
||||
ivec2 texel_fullres = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size);
|
||||
|
||||
ivec2 texel = max(ivec2(0), texel_fullres - uniform_buf.raytrace.horizon_resolution_bias) /
|
||||
uniform_buf.raytrace.horizon_resolution_scale;
|
||||
|
||||
ivec2 extent = textureSize(gbuf_header_tx, 0).xy;
|
||||
if (any(greaterThanEqual(texel_fullres, extent))) {
|
||||
return;
|
||||
}
|
||||
|
||||
GBufferReader gbuf = gbuffer_read(
|
||||
gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres);
|
||||
|
||||
if (gbuf.header == 0u) {
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 center_uv = (vec2(texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv;
|
||||
float center_depth = texelFetch(depth_tx, texel_fullres, 0).r;
|
||||
vec3 center_P = drw_point_screen_to_world(vec3(center_uv, center_depth));
|
||||
vec3 center_N = gbuf.surface_N;
|
||||
|
||||
SphericalHarmonicL1 accum_sh;
|
||||
if (uniform_buf.raytrace.horizon_resolution_scale == 1) {
|
||||
accum_sh = load_spherical_harmonic(texel, true);
|
||||
}
|
||||
else {
|
||||
vec2 interp = vec2(texel_fullres - texel * uniform_buf.raytrace.horizon_resolution_scale -
|
||||
uniform_buf.raytrace.horizon_resolution_bias) /
|
||||
vec2(uniform_buf.raytrace.horizon_resolution_scale);
|
||||
vec4 interp4 = vec4(interp, 1.0 - interp);
|
||||
vec4 bilinear_weight = interp4.zxzx * interp4.wwyy;
|
||||
|
||||
vec4 bilateral_weights;
|
||||
bilateral_weights.x = sample_weight_get(center_N, center_P, texel, ivec2(0, 0));
|
||||
bilateral_weights.y = sample_weight_get(center_N, center_P, texel, ivec2(1, 0));
|
||||
bilateral_weights.z = sample_weight_get(center_N, center_P, texel, ivec2(0, 1));
|
||||
bilateral_weights.w = sample_weight_get(center_N, center_P, texel, ivec2(1, 1));
|
||||
|
||||
vec4 weights = bilateral_weights * bilinear_weight;
|
||||
|
||||
SphericalHarmonicL1 sh_00 = load_spherical_harmonic(texel + ivec2(0, 0), weights.x > 0.0);
|
||||
SphericalHarmonicL1 sh_10 = load_spherical_harmonic(texel + ivec2(1, 0), weights.y > 0.0);
|
||||
SphericalHarmonicL1 sh_01 = load_spherical_harmonic(texel + ivec2(0, 1), weights.z > 0.0);
|
||||
SphericalHarmonicL1 sh_11 = load_spherical_harmonic(texel + ivec2(1, 1), weights.w > 0.0);
|
||||
|
||||
/* Avoid another division at the end. Normalize the weights upfront. */
|
||||
weights *= safe_rcp(reduce_add(weights));
|
||||
|
||||
accum_sh = spherical_harmonics_mul(sh_00, weights.x);
|
||||
accum_sh = spherical_harmonics_madd(sh_10, weights.y, accum_sh);
|
||||
accum_sh = spherical_harmonics_madd(sh_01, weights.z, accum_sh);
|
||||
accum_sh = spherical_harmonics_madd(sh_11, weights.w, accum_sh);
|
||||
}
|
||||
|
||||
vec3 P = center_P;
|
||||
vec3 Ng = center_N;
|
||||
vec3 V = drw_world_incident_vector(P);
|
||||
|
||||
LightProbeSample samp = lightprobe_load(P, Ng, V);
|
||||
|
||||
for (int i = 0; i < GBUFFER_LAYER_MAX && i < gbuf.closure_count; i++) {
|
||||
ClosureUndetermined cl = gbuffer_closure_get(gbuf, i);
|
||||
|
||||
float roughness = closure_apparent_roughness_get(cl);
|
||||
|
||||
float mix_fac = saturate(roughness * uniform_buf.raytrace.roughness_mask_scale -
|
||||
uniform_buf.raytrace.roughness_mask_bias);
|
||||
bool use_raytrace = mix_fac < 1.0;
|
||||
bool use_horizon = mix_fac > 0.0;
|
||||
|
||||
if (!use_horizon) {
|
||||
continue;
|
||||
}
|
||||
|
||||
vec3 N = cl.N;
|
||||
|
||||
vec3 L;
|
||||
switch (cl.type) {
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID:
|
||||
L = lightprobe_reflection_dominant_dir(cl.N, V, roughness);
|
||||
break;
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
|
||||
L = lightprobe_refraction_dominant_dir(cl.N, V, to_closure_refraction(cl).ior, roughness);
|
||||
break;
|
||||
case CLOSURE_BSDF_TRANSLUCENT_ID:
|
||||
L = -N;
|
||||
break;
|
||||
default:
|
||||
L = N;
|
||||
break;
|
||||
}
|
||||
vec3 vL = drw_normal_world_to_view(L);
|
||||
|
||||
/* Evaluate lighting from horizon scan. */
|
||||
/* TODO(fclem): Evaluate depending on BSDF. */
|
||||
vec3 radiance = spherical_harmonics_evaluate_lambert(vL, accum_sh);
|
||||
|
||||
/* Evaluate visibility from horizon scan. */
|
||||
SphericalHarmonicL1 sh_visibility = spherical_harmonics_swizzle_wwww(accum_sh);
|
||||
float occlusion = spherical_harmonics_evaluate_lambert(vL, sh_visibility).x;
|
||||
/* FIXME(fclem): Tried to match the old occlusion look. I don't know why it's needed. */
|
||||
occlusion *= 0.5;
|
||||
/* TODO(fclem): Ideally, we should just combine both local and distant irradiance and evaluate
|
||||
* once. Unfortunately, I couldn't find a way to do the same (1.0 - occlusion) with the
|
||||
* spherical harmonic coefficients. */
|
||||
float visibility = saturate(1.0 - occlusion);
|
||||
|
||||
/* Apply missing distant lighting. */
|
||||
vec3 radiance_probe = spherical_harmonics_evaluate_lambert(L, samp.volume_irradiance);
|
||||
radiance += visibility * radiance_probe;
|
||||
|
||||
int layer_index = gbuffer_closure_get_bin_index(gbuf, i);
|
||||
|
||||
vec4 radiance_horizon = vec4(radiance, 0.0);
|
||||
vec4 radiance_raytrace = vec4(0.0);
|
||||
if (use_raytrace) {
|
||||
/* TODO(fclem): Layered texture. */
|
||||
if (layer_index == 0) {
|
||||
radiance_raytrace = imageLoad(closure0_img, texel_fullres);
|
||||
}
|
||||
else if (layer_index == 1) {
|
||||
radiance_raytrace = imageLoad(closure1_img, texel_fullres);
|
||||
}
|
||||
else if (layer_index == 2) {
|
||||
radiance_raytrace = imageLoad(closure2_img, texel_fullres);
|
||||
}
|
||||
}
|
||||
vec4 radiance_mixed = mix(radiance_raytrace, radiance_horizon, mix_fac);
|
||||
|
||||
/* TODO(fclem): Layered texture. */
|
||||
if (layer_index == 0) {
|
||||
imageStore(closure0_img, texel_fullres, radiance_mixed);
|
||||
}
|
||||
else if (layer_index == 1) {
|
||||
imageStore(closure1_img, texel_fullres, radiance_mixed);
|
||||
}
|
||||
else if (layer_index == 2) {
|
||||
imageStore(closure2_img, texel_fullres, radiance_mixed);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@
|
|||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_horizon_scan_eval_lib.glsl)
|
||||
|
||||
void main()
|
||||
|
@ -15,48 +14,52 @@ void main()
|
|||
uvec2 tile_coord = unpackUvec2x16(tiles_coord_buf[gl_WorkGroupID.x]);
|
||||
ivec2 texel = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size);
|
||||
|
||||
ivec2 texel_fullres = texel * uniform_buf.raytrace.resolution_scale +
|
||||
uniform_buf.raytrace.resolution_bias;
|
||||
ivec2 texel_fullres = texel * uniform_buf.raytrace.horizon_resolution_scale +
|
||||
uniform_buf.raytrace.horizon_resolution_bias;
|
||||
|
||||
/* Avoid tracing the outside border if dispatch is too big. */
|
||||
ivec2 extent = textureSize(gbuf_header_tx, 0).xy;
|
||||
if (any(greaterThanEqual(texel_fullres, extent))) {
|
||||
if (any(greaterThanEqual(texel * uniform_buf.raytrace.horizon_resolution_scale, extent))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Avoid loading texels outside texture range.
|
||||
* This can happen even after the check above in non-power-of-2 textures. */
|
||||
texel_fullres = min(texel_fullres, extent - 1);
|
||||
|
||||
/* Do not trace where nothing was rendered. */
|
||||
if (texelFetch(gbuf_header_tx, texel_fullres, 0).r == 0u) {
|
||||
#if 0 /* This is not needed as the next stage doesn't do bilinear filtering. */
|
||||
imageStore(horizon_radiance_0_img, texel, vec4(0.0));
|
||||
imageStore(horizon_radiance_1_img, texel, vec4(0.0));
|
||||
imageStore(horizon_radiance_2_img, texel, vec4(0.0));
|
||||
imageStore(horizon_radiance_3_img, texel, vec4(0.0));
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 uv = (vec2(texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv;
|
||||
float depth = texelFetch(hiz_tx, texel_fullres, 0).r;
|
||||
|
||||
if (depth == 1.0) {
|
||||
/* Do not trace for background */
|
||||
imageStore(horizon_radiance_img, texel, vec4(FLT_11_11_10_MAX, 0.0));
|
||||
return;
|
||||
}
|
||||
|
||||
HorizonScanContext ctx;
|
||||
ctx.closure = gbuffer_read_bin(
|
||||
gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres, closure_index);
|
||||
ctx.closure.N = drw_normal_world_to_view(ctx.closure.N);
|
||||
|
||||
if (ctx.closure.type == CLOSURE_NONE_ID) {
|
||||
imageStore(horizon_radiance_img, texel, vec4(FLT_11_11_10_MAX, 0.0));
|
||||
return;
|
||||
}
|
||||
|
||||
vec3 vP = drw_point_screen_to_view(vec3(uv, depth));
|
||||
vec3 vN = horizon_scan_sample_normal(uv);
|
||||
|
||||
vec2 noise = utility_tx_fetch(utility_tx, vec2(texel), UTIL_BLUE_NOISE_LAYER).rg;
|
||||
noise = fract(noise + sampling_rng_2D_get(SAMPLING_AO_U));
|
||||
|
||||
horizon_scan_eval(vP,
|
||||
ctx,
|
||||
noise,
|
||||
uniform_buf.ao.pixel_size,
|
||||
1.0e16,
|
||||
uniform_buf.ao.thickness,
|
||||
uniform_buf.ao.angle_bias,
|
||||
8,
|
||||
false);
|
||||
HorizonScanResult scan = horizon_scan_eval(vP,
|
||||
vN,
|
||||
noise,
|
||||
uniform_buf.ao.pixel_size,
|
||||
1.0e16,
|
||||
uniform_buf.ao.thickness,
|
||||
uniform_buf.ao.angle_bias,
|
||||
8,
|
||||
false);
|
||||
|
||||
imageStore(horizon_radiance_img, texel, ctx.closure_result);
|
||||
imageStore(horizon_occlusion_img, texel, ctx.closure_result.wwww);
|
||||
scan.result = spherical_harmonics_compress(scan.result);
|
||||
|
||||
imageStore(horizon_radiance_0_img, texel, scan.result.L0.M0);
|
||||
imageStore(horizon_radiance_1_img, texel, scan.result.L1.Mn1);
|
||||
imageStore(horizon_radiance_2_img, texel, scan.result.L1.M0);
|
||||
imageStore(horizon_radiance_3_img, texel, scan.result.L1.Mp1);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,9 @@
|
|||
#pragma BLENDER_REQUIRE(eevee_bxdf_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
|
||||
|
||||
#if defined(MAT_DEFERRED) || defined(MAT_FORWARD)
|
||||
#ifdef HORIZON_OCCLUSION
|
||||
/* Do nothing. */
|
||||
#elif defined(MAT_DEFERRED) || defined(MAT_FORWARD)
|
||||
/* Enable AO node computation for material shaders. */
|
||||
# define HORIZON_OCCLUSION
|
||||
#else
|
||||
|
@ -47,138 +49,6 @@ vec3 horizon_scan_sample_normal(vec2 uv)
|
|||
#endif
|
||||
}
|
||||
|
||||
/* Note: Expects all normals to be in view-space. */
|
||||
struct HorizonScanContextCommon {
|
||||
float N_angle;
|
||||
float N_length;
|
||||
uint bitmask;
|
||||
float weight_slice;
|
||||
float weight_accum;
|
||||
vec3 light_slice;
|
||||
vec4 light_accum;
|
||||
};
|
||||
|
||||
struct HorizonScanContext {
|
||||
#ifdef HORIZON_OCCLUSION
|
||||
ClosureOcclusion occlusion;
|
||||
HorizonScanContextCommon occlusion_common;
|
||||
vec4 occlusion_result;
|
||||
#endif
|
||||
#ifdef HORIZON_CLOSURE
|
||||
ClosureUndetermined closure;
|
||||
HorizonScanContextCommon closure_common;
|
||||
vec4 closure_result;
|
||||
#endif
|
||||
};
|
||||
|
||||
void horizon_scan_context_accumulation_reset(inout HorizonScanContext context)
|
||||
{
|
||||
#ifdef HORIZON_OCCLUSION
|
||||
context.occlusion_common.light_accum = vec4(0.0);
|
||||
context.occlusion_common.weight_accum = 0.0;
|
||||
#endif
|
||||
#ifdef HORIZON_CLOSURE
|
||||
context.closure_common.light_accum = vec4(0.0);
|
||||
context.closure_common.weight_accum = 0.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void horizon_scan_context_slice_start(
|
||||
inout HorizonScanContextCommon context, vec3 vN, vec3 vV, vec3 vT, vec3 vB)
|
||||
{
|
||||
context.bitmask = 0u;
|
||||
context.weight_slice = 0.0;
|
||||
context.light_slice = vec3(0.0);
|
||||
horizon_scan_projected_normal_to_plane_angle_and_length(
|
||||
vN, vV, vT, vB, context.N_length, context.N_angle);
|
||||
}
|
||||
|
||||
void horizon_scan_context_slice_start(inout HorizonScanContext context, vec3 vV, vec3 vT, vec3 vB)
|
||||
{
|
||||
#ifdef HORIZON_OCCLUSION
|
||||
horizon_scan_context_slice_start(context.occlusion_common, context.occlusion.N, vV, vT, vB);
|
||||
#endif
|
||||
#ifdef HORIZON_CLOSURE
|
||||
horizon_scan_context_slice_start(context.closure_common, context.closure.N, vV, vT, vB);
|
||||
#endif
|
||||
}
|
||||
|
||||
void horizon_scan_context_sample_finish(inout HorizonScanContextCommon context,
|
||||
vec3 sample_radiance,
|
||||
float sample_weight,
|
||||
vec2 sample_theta,
|
||||
float angle_bias)
|
||||
{
|
||||
/* Angular bias shrinks the visibility bitmask around the projected normal. */
|
||||
sample_theta = (sample_theta - context.N_angle) * angle_bias;
|
||||
uint sample_bitmask = horizon_scan_angles_to_bitmask(sample_theta);
|
||||
sample_weight *= horizon_scan_bitmask_to_visibility_uniform(sample_bitmask & ~context.bitmask);
|
||||
|
||||
context.weight_slice += sample_weight;
|
||||
context.light_slice += sample_radiance * sample_weight;
|
||||
context.bitmask |= sample_bitmask;
|
||||
}
|
||||
|
||||
void horizon_scan_context_sample_finish(
|
||||
inout HorizonScanContext ctx, vec3 L, vec3 V, vec2 sample_uv, vec2 theta, float bias)
|
||||
{
|
||||
vec3 sample_radiance = horizon_scan_sample_radiance(sample_uv);
|
||||
/* Take emitter surface normal into consideration. */
|
||||
vec3 sample_normal = horizon_scan_sample_normal(sample_uv);
|
||||
/* Discard back-facing samples.
|
||||
* The paper suggests a smooth test which is not physically correct since we
|
||||
* already consider the sample reflected radiance.
|
||||
* Set the weight to allow energy conservation. If we modulate the radiance, we loose energy. */
|
||||
float weight = step(dot(sample_normal, L), 0.0);
|
||||
|
||||
#ifdef HORIZON_OCCLUSION
|
||||
horizon_scan_context_sample_finish(ctx.occlusion_common, sample_radiance, 1.0, theta, bias);
|
||||
#endif
|
||||
#ifdef HORIZON_CLOSURE
|
||||
weight *= bsdf_lambert(ctx.closure.N, L);
|
||||
horizon_scan_context_sample_finish(ctx.closure_common, sample_radiance, weight, theta, bias);
|
||||
#endif
|
||||
}
|
||||
|
||||
void horizon_scan_context_slice_finish(inout HorizonScanContextCommon context)
|
||||
{
|
||||
/* Use uniform visibility since this is what we use for near field lighting.
|
||||
* Also the lighting we are going to mask is already containing the cosine lobe. */
|
||||
float slice_occlusion = horizon_scan_bitmask_to_visibility_uniform(~context.bitmask);
|
||||
/* Normalize radiance since BxDF is applied when merging direct and indirect light. */
|
||||
context.light_slice *= safe_rcp(context.weight_slice) * (1.0 - slice_occlusion);
|
||||
/* Correct normal not on plane (Eq. 8 of GTAO paper). */
|
||||
context.light_accum += vec4(context.light_slice, slice_occlusion) * context.N_length;
|
||||
context.weight_accum += context.N_length;
|
||||
}
|
||||
|
||||
void horizon_scan_context_slice_finish(inout HorizonScanContext context)
|
||||
{
|
||||
#ifdef HORIZON_OCCLUSION
|
||||
float occlusion = horizon_scan_bitmask_to_occlusion_cosine(context.occlusion_common.bitmask);
|
||||
context.occlusion_common.light_accum += vec4(occlusion) * context.occlusion_common.N_length;
|
||||
context.occlusion_common.weight_accum += context.occlusion_common.N_length;
|
||||
#endif
|
||||
#ifdef HORIZON_CLOSURE
|
||||
horizon_scan_context_slice_finish(context.closure_common);
|
||||
#endif
|
||||
}
|
||||
|
||||
void horizon_scan_context_accumulation_finish(HorizonScanContextCommon context, out vec4 result)
|
||||
{
|
||||
result = context.light_accum * safe_rcp(context.weight_accum);
|
||||
}
|
||||
|
||||
void horizon_scan_context_accumulation_finish(inout HorizonScanContext context)
|
||||
{
|
||||
#ifdef HORIZON_OCCLUSION
|
||||
horizon_scan_context_accumulation_finish(context.occlusion_common, context.occlusion_result);
|
||||
#endif
|
||||
#ifdef HORIZON_CLOSURE
|
||||
horizon_scan_context_accumulation_finish(context.closure_common, context.closure_result);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start and end point of a ray clipped to its intersection
|
||||
* with a sphere.
|
||||
|
@ -209,27 +79,38 @@ void horizon_scan_occluder_intersection_ray_sphere_clip(Ray ray,
|
|||
P_exit = ray.origin + ray.direction * t_exit;
|
||||
}
|
||||
|
||||
struct HorizonScanResult {
|
||||
#ifdef HORIZON_OCCLUSION
|
||||
float result;
|
||||
#endif
|
||||
#ifdef HORIZON_CLOSURE
|
||||
SphericalHarmonicL1 result;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Scans the horizon in many directions and returns the indirect lighting radiance.
|
||||
* Returned lighting is stored inside the context in `_accum` members already normalized.
|
||||
* If `reversed` is set to true, the input normal must be negated.
|
||||
*/
|
||||
void horizon_scan_eval(vec3 vP,
|
||||
inout HorizonScanContext context,
|
||||
vec2 noise,
|
||||
vec2 pixel_size,
|
||||
float search_distance,
|
||||
float global_thickness,
|
||||
float angle_bias,
|
||||
const int sample_count,
|
||||
const bool reversed)
|
||||
HorizonScanResult horizon_scan_eval(vec3 vP,
|
||||
vec3 vN,
|
||||
vec2 noise,
|
||||
vec2 pixel_size,
|
||||
float search_distance,
|
||||
float global_thickness,
|
||||
float angle_bias,
|
||||
const int sample_count,
|
||||
const bool reversed)
|
||||
{
|
||||
vec3 vV = drw_view_incident_vector(vP);
|
||||
|
||||
const int slice_len = 2;
|
||||
vec2 v_dir = sample_circle(noise.x * (0.5 / float(slice_len)));
|
||||
|
||||
horizon_scan_context_accumulation_reset(context);
|
||||
float weight_accum = 0.0;
|
||||
float occlusion_accum = 0.0;
|
||||
SphericalHarmonicL1 sh_accum = spherical_harmonics_L1_new();
|
||||
|
||||
for (int slice = 0; slice < slice_len; slice++) {
|
||||
#if 0 /* For debug purpose. For when slice_len is greater than 2. */
|
||||
|
@ -240,7 +121,18 @@ void horizon_scan_eval(vec3 vP,
|
|||
vec3 vB = normalize(cross(vV, vec3(v_dir, 0.0)));
|
||||
vec3 vT = cross(vB, vV);
|
||||
|
||||
horizon_scan_context_slice_start(context, vV, vT, vB);
|
||||
/* Bitmask representing the occluded sectors on the slice. */
|
||||
uint slice_bitmask = 0u;
|
||||
|
||||
/* Angle between vN and the horizon slice plane. */
|
||||
float vN_angle;
|
||||
/* Length of vN projected onto the horizon slice plane. */
|
||||
float vN_length;
|
||||
|
||||
horizon_scan_projected_normal_to_plane_angle_and_length(vN, vV, vT, vB, vN_length, vN_angle);
|
||||
|
||||
SphericalHarmonicL1 sh_slice = spherical_harmonics_L1_new();
|
||||
float weight_slice;
|
||||
|
||||
/* For both sides of the view vector. */
|
||||
for (int side = 0; side < 2; side++) {
|
||||
|
@ -306,15 +198,54 @@ void horizon_scan_eval(vec3 vP,
|
|||
/* If we are tracing backward, the angles are negative. Swizzle to keep correct order. */
|
||||
theta = (side == 0) ? theta.xy : -theta.yx;
|
||||
|
||||
horizon_scan_context_sample_finish(context, vL_front, vV, sample_uv, theta, angle_bias);
|
||||
vec3 sample_radiance = horizon_scan_sample_radiance(sample_uv);
|
||||
/* Take emitter surface normal into consideration. */
|
||||
vec3 sample_normal = horizon_scan_sample_normal(sample_uv);
|
||||
/* Discard back-facing samples.
|
||||
* The 2 factor is to avoid loosing too much energy (which is something not
|
||||
* explained in the paper...). Likely to be wrong, but we need a soft falloff. */
|
||||
float facing_weight = saturate(-dot(sample_normal, vL_front) * 2.0);
|
||||
|
||||
float sample_weight = facing_weight * bsdf_lambert(vN, vL_front);
|
||||
|
||||
/* Angular bias shrinks the visibility bitmask around the projected normal. */
|
||||
vec2 biased_theta = (theta - vN_angle) * angle_bias;
|
||||
uint sample_bitmask = horizon_scan_angles_to_bitmask(biased_theta);
|
||||
float weight_bitmask = horizon_scan_bitmask_to_visibility_uniform(sample_bitmask &
|
||||
~slice_bitmask);
|
||||
|
||||
sample_radiance *= facing_weight * weight_bitmask;
|
||||
/* Encoding using front sample direction gives better result than
|
||||
* `normalize(vL_front + vL_back)` */
|
||||
spherical_harmonics_encode_signal_sample(
|
||||
vL_front, vec4(sample_radiance, weight_bitmask), sh_slice);
|
||||
|
||||
slice_bitmask |= sample_bitmask;
|
||||
}
|
||||
}
|
||||
|
||||
horizon_scan_context_slice_finish(context);
|
||||
float occlusion_slice = horizon_scan_bitmask_to_occlusion_cosine(slice_bitmask);
|
||||
|
||||
/* Correct normal not on plane (Eq. 8 of GTAO paper). */
|
||||
occlusion_accum += occlusion_slice * vN_length;
|
||||
/* Use uniform visibility since this is what we use for near field lighting. */
|
||||
sh_accum = spherical_harmonics_madd(sh_slice, vN_length, sh_accum);
|
||||
|
||||
weight_accum += vN_length;
|
||||
|
||||
/* Rotate 90 degrees. */
|
||||
v_dir = orthogonal(v_dir);
|
||||
}
|
||||
|
||||
horizon_scan_context_accumulation_finish(context);
|
||||
float weight_rcp = safe_rcp(weight_accum);
|
||||
|
||||
HorizonScanResult res;
|
||||
#ifdef HORIZON_OCCLUSION
|
||||
res.result = occlusion_accum * weight_rcp;
|
||||
#endif
|
||||
#ifdef HORIZON_CLOSURE
|
||||
/* Weight by area of the sphere. This is expected for correct SH evaluation. */
|
||||
res.result = spherical_harmonics_mul(sh_accum, weight_rcp * 4.0 * M_PI);
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -16,8 +16,17 @@
|
|||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 texel_fullres = texel * uniform_buf.raytrace.resolution_scale +
|
||||
uniform_buf.raytrace.resolution_bias;
|
||||
ivec2 texel_fullres = texel * uniform_buf.raytrace.horizon_resolution_scale +
|
||||
uniform_buf.raytrace.horizon_resolution_bias;
|
||||
|
||||
/* Return early for padding threads so we can use imageStoreFast. */
|
||||
if (any(greaterThanEqual(texel, imageSize(out_radiance_img).xy))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Avoid loading texels outside texture range. */
|
||||
ivec2 extent = textureSize(gbuf_header_tx, 0).xy;
|
||||
texel_fullres = min(texel_fullres, extent - 1);
|
||||
|
||||
/* Load Gbuffer. */
|
||||
GBufferReader gbuf = gbuffer_read(
|
||||
|
@ -25,12 +34,16 @@ void main()
|
|||
|
||||
/* Export normal. */
|
||||
vec3 N = gbuf.surface_N;
|
||||
/* Background has invalid data. */
|
||||
/* FIXME: This is zero for opaque layer when we are processing the refraction layer. */
|
||||
if (is_zero(N)) {
|
||||
/* Avoid NaN. But should be fixed in any case. */
|
||||
N = vec3(1.0, 0.0, 0.0);
|
||||
}
|
||||
vec3 vN = drw_normal_world_to_view(N);
|
||||
imageStore(out_normal_img, texel, vec4(vN * 0.5 + 0.5, 0.0));
|
||||
/* Tag processed pixel in the normal buffer for denoising speed. */
|
||||
bool is_processed = gbuf.header != 0u;
|
||||
imageStore(out_normal_img, texel, vec4(vN * 0.5 + 0.5, float(is_processed)));
|
||||
|
||||
/* Re-project radiance. */
|
||||
vec2 uv = (vec2(texel_fullres) + 0.5) / vec2(textureSize(depth_tx, 0).xy);
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Load the extracted spherical harmonics from the world into the probe volume atlas.
|
||||
*
|
||||
* The whole thread group will load the same data and write a brick worth of data.
|
||||
*/
|
||||
|
||||
void atlas_store(vec4 sh_coefficient, ivec2 atlas_coord, int layer)
|
||||
{
|
||||
imageStore(irradiance_atlas_img,
|
||||
ivec3(atlas_coord, layer * IRRADIANCE_GRID_BRICK_SIZE) + ivec3(gl_LocalInvocationID),
|
||||
sh_coefficient);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
int brick_index = grids_infos_buf[grid_index].brick_offset;
|
||||
|
||||
/* Brick coordinate in the destination atlas. */
|
||||
IrradianceBrick brick = irradiance_brick_unpack(bricks_infos_buf[brick_index]);
|
||||
ivec2 output_coord = ivec2(brick.atlas_coord);
|
||||
|
||||
atlas_store(harmonic_buf.L0_M0, output_coord, 0);
|
||||
atlas_store(harmonic_buf.L1_Mn1, output_coord, 1);
|
||||
atlas_store(harmonic_buf.L1_M0, output_coord, 2);
|
||||
atlas_store(harmonic_buf.L1_Mp1, output_coord, 3);
|
||||
}
|
|
@ -22,34 +22,7 @@
|
|||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl)
|
||||
|
||||
float bilateral_depth_weight(vec3 center_N, vec3 center_P, vec3 sample_P)
|
||||
{
|
||||
vec4 center_plane_eq = vec4(center_N, -dot(center_N, center_P));
|
||||
/* Only compare distance to the center plane formed by the normal. */
|
||||
float depth_delta = dot(center_plane_eq, vec4(sample_P, 1.0));
|
||||
/* TODO(fclem): Scene parameter. This is dependent on scene scale. */
|
||||
const float scale = 10000.0;
|
||||
float weight = exp2(-scale * square(depth_delta));
|
||||
return weight;
|
||||
}
|
||||
|
||||
float bilateral_spatial_weight(float sigma, vec2 offset_from_center)
|
||||
{
|
||||
/* From https://github.com/tranvansang/bilateral-filter/blob/master/fshader.frag */
|
||||
float fac = -1.0 / square(sigma);
|
||||
/* Take two standard deviation. */
|
||||
fac *= 2.0;
|
||||
float weight = exp2(fac * length_squared(offset_from_center));
|
||||
return weight;
|
||||
}
|
||||
|
||||
float bilateral_normal_weight(vec3 center_N, vec3 sample_N)
|
||||
{
|
||||
float facing_ratio = dot(center_N, sample_N);
|
||||
float weight = saturate(pow8f(facing_ratio));
|
||||
return weight;
|
||||
}
|
||||
#pragma BLENDER_REQUIRE(eevee_filter_lib.glsl)
|
||||
|
||||
/* In order to remove some more fireflies, "tone-map" the color samples during the accumulation. */
|
||||
vec3 to_accumulation_space(vec3 color)
|
||||
|
@ -141,9 +114,12 @@ void main()
|
|||
continue;
|
||||
}
|
||||
|
||||
float depth_weight = bilateral_depth_weight(center_closure.N, center_P, sample_P);
|
||||
float spatial_weight = bilateral_spatial_weight(filter_size, vec2(offset));
|
||||
float normal_weight = bilateral_normal_weight(center_closure.N, sample_closure.N);
|
||||
float gauss = filter_gaussian_factor(filter_size, 1.5);
|
||||
|
||||
/* TODO(fclem): Scene parameter. 10000.0 is dependent on scene scale. */
|
||||
float depth_weight = filter_planar_weight(center_closure.N, center_P, sample_P, 10000.0);
|
||||
float spatial_weight = filter_gaussian_weight(gauss, length_squared(vec2(offset)));
|
||||
float normal_weight = filter_angle_weight(center_closure.N, sample_closure.N);
|
||||
float weight = depth_weight * spatial_weight * normal_weight;
|
||||
|
||||
accum_radiance += to_accumulation_space(radiance) * weight;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl)
|
||||
|
||||
shared uint tile_contains_ray_tracing[GBUFFER_LAYER_MAX];
|
||||
shared uint tile_contains_horizon_scan[GBUFFER_LAYER_MAX];
|
||||
shared uint tile_contains_horizon_scan;
|
||||
|
||||
/* Returns a blend factor between different tracing method. */
|
||||
float ray_roughness_factor(RayTraceData raytrace, float roughness)
|
||||
|
@ -26,9 +26,9 @@ void main()
|
|||
{
|
||||
if (gl_LocalInvocationIndex == 0u) {
|
||||
/* Init shared variables. */
|
||||
tile_contains_horizon_scan = 0;
|
||||
for (int i = 0; i < GBUFFER_LAYER_MAX; i++) {
|
||||
tile_contains_ray_tracing[i] = 0;
|
||||
tile_contains_horizon_scan[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ void main()
|
|||
|
||||
/* We don't care about race condition here. */
|
||||
if (ray_roughness_fac > 0.0) {
|
||||
tile_contains_horizon_scan[i] = 1;
|
||||
tile_contains_horizon_scan = 1;
|
||||
}
|
||||
if (ray_roughness_fac < 1.0) {
|
||||
tile_contains_ray_tracing[i] = 1;
|
||||
|
@ -70,11 +70,12 @@ void main()
|
|||
imageStore(tile_raytrace_denoise_img, ivec3(denoise_tile_co, i), uvec4(1));
|
||||
imageStore(tile_raytrace_tracing_img, ivec3(tracing_tile_co, i), uvec4(1));
|
||||
}
|
||||
}
|
||||
|
||||
if (tile_contains_horizon_scan[i] > 0) {
|
||||
imageStore(tile_horizon_denoise_img, ivec3(denoise_tile_co, i), uvec4(1));
|
||||
imageStore(tile_horizon_tracing_img, ivec3(tracing_tile_co, i), uvec4(1));
|
||||
}
|
||||
if (tile_contains_horizon_scan > 0) {
|
||||
ivec2 tracing_tile_co = denoise_tile_co / uniform_buf.raytrace.horizon_resolution_scale;
|
||||
imageStore(tile_horizon_denoise_img, ivec3(denoise_tile_co, 0), uvec4(1));
|
||||
imageStore(tile_horizon_tracing_img, ivec3(tracing_tile_co, 0), uvec4(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,13 +19,9 @@ void main()
|
|||
if (all(equal(tile, ivec2(0)))) {
|
||||
raytrace_tracing_dispatch_buf.num_groups_y = 1;
|
||||
raytrace_denoise_dispatch_buf.num_groups_y = 1;
|
||||
horizon_tracing_dispatch_buf.num_groups_y = 1;
|
||||
horizon_denoise_dispatch_buf.num_groups_y = 1;
|
||||
|
||||
raytrace_tracing_dispatch_buf.num_groups_z = 1;
|
||||
raytrace_denoise_dispatch_buf.num_groups_z = 1;
|
||||
horizon_tracing_dispatch_buf.num_groups_z = 1;
|
||||
horizon_denoise_dispatch_buf.num_groups_z = 1;
|
||||
}
|
||||
|
||||
if (!in_image_range(tile, tile_raytrace_tracing_img)) {
|
||||
|
@ -34,8 +30,6 @@ void main()
|
|||
|
||||
/* True if this tile is shooting and tracing rays. */
|
||||
bool is_ray_tracing = imageLoad(tile_raytrace_tracing_img, ivec3(tile, closure_index)).r != 0;
|
||||
/* True if this tile is using horizon scan. */
|
||||
bool is_horizon_tracing = imageLoad(tile_horizon_tracing_img, ivec3(tile, closure_index)).r != 0;
|
||||
|
||||
/* True if an adjacent tile is ray tracing and will need this tile data for denoising. */
|
||||
bool tile_is_ray_sampled = false;
|
||||
|
@ -54,23 +48,6 @@ void main()
|
|||
}
|
||||
}
|
||||
|
||||
/* True if an adjacent tile is horizon tracing and will need this tile data for denoising. */
|
||||
bool tile_is_horizon_sampled = false;
|
||||
/* Could be optimized if that becomes an issue (3x3 cross gather + 3x3 "X" shape scatter). */
|
||||
for (int x_tile = -1; x_tile <= 1; x_tile++) {
|
||||
for (int y_tile = -1; y_tile <= 1; y_tile++) {
|
||||
ivec2 tile_adj = tile + ivec2(x_tile, y_tile);
|
||||
bool is_center_tile = (x_tile == 0 && y_tile == 0);
|
||||
if (in_image_range(tile_adj, tile_horizon_tracing_img) && !is_center_tile) {
|
||||
if (imageLoad(tile_horizon_tracing_img, ivec3(tile_adj, closure_index)).r != 0) {
|
||||
/* This tile will sample the target tracing tile. Make sure it is cleared. */
|
||||
tile_is_horizon_sampled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO(fclem): we might want to dispatch another type of shader only for clearing. */
|
||||
if (is_ray_tracing || tile_is_ray_sampled) {
|
||||
/* Dispatch trace resolution tracing tile. */
|
||||
|
@ -78,15 +55,7 @@ void main()
|
|||
raytrace_tracing_tiles_buf[tile_index] = packUvec2x16(uvec2(tile));
|
||||
}
|
||||
|
||||
/* TODO(fclem): we might want to dispatch another type of shader only for clearing. */
|
||||
if (is_horizon_tracing || tile_is_horizon_sampled) {
|
||||
/* Dispatch trace resolution tracing tile. */
|
||||
uint tile_index = atomicAdd(horizon_tracing_dispatch_buf.num_groups_x, 1u);
|
||||
horizon_tracing_tiles_buf[tile_index] = packUvec2x16(uvec2(tile));
|
||||
}
|
||||
|
||||
/* Dispatch denoise tiles. */
|
||||
int resolution_scale = uniform_buf.raytrace.resolution_scale;
|
||||
if (is_ray_tracing) {
|
||||
for (int x_tile = 0; x_tile < resolution_scale; x_tile++) {
|
||||
for (int y_tile = 0; y_tile < resolution_scale; y_tile++) {
|
||||
|
@ -100,17 +69,4 @@ void main()
|
|||
}
|
||||
}
|
||||
}
|
||||
if (is_horizon_tracing) {
|
||||
for (int x_tile = 0; x_tile < resolution_scale; x_tile++) {
|
||||
for (int y_tile = 0; y_tile < resolution_scale; y_tile++) {
|
||||
ivec2 tile_adj = tile * resolution_scale + ivec2(x_tile, y_tile);
|
||||
if (in_image_range(tile_adj, tile_horizon_denoise_img)) {
|
||||
if (imageLoad(tile_horizon_denoise_img, ivec3(tile_adj, closure_index)).r != 0) {
|
||||
uint tile_index = atomicAdd(horizon_denoise_dispatch_buf.num_groups_x, 1u);
|
||||
horizon_denoise_tiles_buf[tile_index] = packUvec2x16(uvec2(tile_adj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/* Sum all spherical harmonic coefficients extracting during remapping to octahedral map.
|
||||
* Dispatch only one thread-group that sums. */
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_reflection_probe_mapping_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
|
||||
shared vec4 local_sh_coefs[gl_WorkGroupSize.x][4];
|
||||
|
||||
void spherical_harmonic_lds_store(uint index, SphericalHarmonicL1 sh)
|
||||
{
|
||||
local_sh_coefs[index][0] = sh.L0.M0;
|
||||
local_sh_coefs[index][1] = sh.L1.Mn1;
|
||||
local_sh_coefs[index][2] = sh.L1.M0;
|
||||
local_sh_coefs[index][3] = sh.L1.Mp1;
|
||||
}
|
||||
|
||||
SphericalHarmonicL1 spherical_harmonic_lds_load(uint index)
|
||||
{
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = local_sh_coefs[index][0];
|
||||
sh.L1.Mn1 = local_sh_coefs[index][1];
|
||||
sh.L1.M0 = local_sh_coefs[index][2];
|
||||
sh.L1.Mp1 = local_sh_coefs[index][3];
|
||||
return sh;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = vec4(0.0);
|
||||
sh.L1.Mn1 = vec4(0.0);
|
||||
sh.L1.M0 = vec4(0.0);
|
||||
sh.L1.Mp1 = vec4(0.0);
|
||||
|
||||
/* First sum onto the local memory. */
|
||||
uint valid_data_len = probe_remap_dispatch_size.x * probe_remap_dispatch_size.y;
|
||||
const uint iter_count = uint(SPHERE_PROBE_MAX_HARMONIC) / gl_WorkGroupSize.x;
|
||||
for (uint i = 0; i < iter_count; i++) {
|
||||
uint index = gl_WorkGroupSize.x * i + gl_LocalInvocationIndex;
|
||||
if (index >= valid_data_len) {
|
||||
break;
|
||||
}
|
||||
SphericalHarmonicL1 sh_sample;
|
||||
sh_sample.L0.M0 = in_sh[index].L0_M0;
|
||||
sh_sample.L1.Mn1 = in_sh[index].L1_Mn1;
|
||||
sh_sample.L1.M0 = in_sh[index].L1_M0;
|
||||
sh_sample.L1.Mp1 = in_sh[index].L1_Mp1;
|
||||
sh = spherical_harmonics_add(sh, sh_sample);
|
||||
}
|
||||
|
||||
/* Then sum across invocations. */
|
||||
const uint local_index = gl_LocalInvocationIndex;
|
||||
local_sh_coefs[local_index][0] = sh.L0.M0;
|
||||
local_sh_coefs[local_index][1] = sh.L1.Mn1;
|
||||
local_sh_coefs[local_index][2] = sh.L1.M0;
|
||||
local_sh_coefs[local_index][3] = sh.L1.Mp1;
|
||||
|
||||
/* Parallel sum. */
|
||||
const uint group_size = gl_WorkGroupSize.x * gl_WorkGroupSize.y;
|
||||
for (uint stride = group_size / 2; stride > 0; stride /= 2) {
|
||||
barrier();
|
||||
if (local_index < stride) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
local_sh_coefs[local_index][i] += local_sh_coefs[local_index + stride][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
barrier();
|
||||
if (gl_LocalInvocationIndex == 0u) {
|
||||
out_sh.L0_M0 = local_sh_coefs[0][0];
|
||||
out_sh.L1_Mn1 = local_sh_coefs[0][1];
|
||||
out_sh.L1_M0 = local_sh_coefs[0][2];
|
||||
out_sh.L1_Mp1 = local_sh_coefs[0][3];
|
||||
}
|
||||
}
|
|
@ -6,6 +6,63 @@
|
|||
|
||||
#pragma BLENDER_REQUIRE(eevee_reflection_probe_mapping_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
|
||||
|
||||
shared vec4 local_radiance[gl_WorkGroupSize.x * gl_WorkGroupSize.y];
|
||||
|
||||
float triangle_solid_angle(vec3 A, vec3 B, vec3 C)
|
||||
{
|
||||
return 2.0 * atan(abs(dot(A, cross(B, C))), (1.0 + dot(B, C) + dot(A, C) + dot(A, B)));
|
||||
}
|
||||
|
||||
float quad_solid_angle(vec3 A, vec3 B, vec3 C, vec3 D)
|
||||
{
|
||||
return triangle_solid_angle(A, B, C) + triangle_solid_angle(C, B, D);
|
||||
}
|
||||
|
||||
float octahedral_texel_solid_angle(ivec2 local_texel,
|
||||
SphereProbePixelArea write_co,
|
||||
SphereProbeUvArea sample_co)
|
||||
{
|
||||
if (any(equal(local_texel, ivec2(write_co.extent - 1)))) {
|
||||
/* Do not weight these border pixels that are redundant. */
|
||||
return 0.0;
|
||||
}
|
||||
/* Since we are pouting texel centers on the edges of the octahedron, the shape of a texel can be
|
||||
* anything from a simple quad (at the Z=0 poles), to a 4 pointed start (at the Z=+-1 poles)
|
||||
* passing by arrow tail shapes (at the X=0 and Y=0 edges). So while it would be more correct to
|
||||
* account for all these shapes (using 8 triangles), it proves to be quite involved with all the
|
||||
* corner cases. Instead, we compute the area as if the texels were not aligned with the edges.
|
||||
* This simplify things at the cost of making the weighting a tiny bit off for every pixels.
|
||||
* The sum of all texels is still giving 4 PI. */
|
||||
vec3 v00 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, -1), write_co, sample_co);
|
||||
vec3 v10 = sphere_probe_texel_to_direction(local_texel + ivec2(+0, -1), write_co, sample_co);
|
||||
vec3 v20 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, -1), write_co, sample_co);
|
||||
vec3 v01 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, +0), write_co, sample_co);
|
||||
vec3 v11 = sphere_probe_texel_to_direction(local_texel + ivec2(+0, +0), write_co, sample_co);
|
||||
vec3 v21 = sphere_probe_texel_to_direction(local_texel + ivec2(+1, +0), write_co, sample_co);
|
||||
vec3 v02 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, +1), write_co, sample_co);
|
||||
vec3 v12 = sphere_probe_texel_to_direction(local_texel + ivec2(+0, +1), write_co, sample_co);
|
||||
vec3 v22 = sphere_probe_texel_to_direction(local_texel + ivec2(+1, +1), write_co, sample_co);
|
||||
/* The solid angle functions expect normalized vectors. */
|
||||
v00 = normalize(v00);
|
||||
v10 = normalize(v10);
|
||||
v20 = normalize(v20);
|
||||
v01 = normalize(v01);
|
||||
v11 = normalize(v11);
|
||||
v21 = normalize(v21);
|
||||
v02 = normalize(v02);
|
||||
v12 = normalize(v12);
|
||||
v22 = normalize(v22);
|
||||
#if 0 /* Has artifacts, is marginally more correct. */
|
||||
/* For some reason quad_solid_angle(v10, v20, v11, v21) gives some strange artifacts at Z=0. */
|
||||
return 0.25 * (quad_solid_angle(v00, v10, v01, v11) + quad_solid_angle(v10, v20, v11, v21) +
|
||||
quad_solid_angle(v01, v11, v02, v12) + quad_solid_angle(v11, v21, v12, v22));
|
||||
#else
|
||||
/* Choosing the positive quad (0,0) > (+1,+1) for stability. */
|
||||
return quad_solid_angle(v11, v21, v12, v22);
|
||||
#endif
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
|
@ -16,11 +73,6 @@ void main()
|
|||
/* Texel in probe. */
|
||||
ivec2 local_texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
/* Exit when pixel being written doesn't fit in the area reserved for the probe. */
|
||||
if (any(greaterThanEqual(local_texel, ivec2(write_coord.extent)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 wrapped_uv;
|
||||
vec3 direction = sphere_probe_texel_to_direction(
|
||||
local_texel, write_coord, sample_coord, wrapped_uv);
|
||||
|
@ -30,7 +82,7 @@ void main()
|
|||
float opacity = 1.0 - radiance_and_transmittance.a;
|
||||
|
||||
/* Composite world into reflection probes. */
|
||||
bool is_world = all(equal(write_coord_packed, world_coord_packed));
|
||||
bool is_world = all(equal(probe_coord_packed, world_coord_packed));
|
||||
if (!is_world && opacity != 1.0) {
|
||||
vec2 world_uv = wrapped_uv * world_coord.scale + world_coord.offset;
|
||||
vec4 world_radiance = textureLod(atlas_tx, vec3(world_uv, world_coord.layer), 0.0);
|
||||
|
@ -39,6 +91,53 @@ void main()
|
|||
|
||||
radiance = colorspace_brightness_clamp_max(radiance, probe_brightness_clamp);
|
||||
|
||||
ivec3 texel = ivec3(local_texel + write_coord.offset, write_coord.layer);
|
||||
imageStore(atlas_img, texel, vec4(radiance, 1.0));
|
||||
if (!any(greaterThanEqual(local_texel, ivec2(write_coord.extent)))) {
|
||||
ivec3 texel = ivec3(local_texel + write_coord.offset, write_coord.layer);
|
||||
imageStore(atlas_img, texel, vec4(radiance, 1.0));
|
||||
}
|
||||
|
||||
if (extract_sh) {
|
||||
float sample_weight = octahedral_texel_solid_angle(local_texel, write_coord, sample_coord);
|
||||
|
||||
const uint local_index = gl_LocalInvocationIndex;
|
||||
const uint group_size = gl_WorkGroupSize.x * gl_WorkGroupSize.y;
|
||||
|
||||
/* Parallel sum. Result is stored inside local_radiance[0]. */
|
||||
local_radiance[local_index] = radiance.xyzz * sample_weight;
|
||||
for (uint stride = group_size / 2; stride > 0; stride /= 2) {
|
||||
barrier();
|
||||
if (local_index < stride) {
|
||||
local_radiance[local_index] += local_radiance[local_index + stride];
|
||||
}
|
||||
}
|
||||
|
||||
barrier();
|
||||
if (gl_LocalInvocationIndex == 0u) {
|
||||
/* Find the middle point of the whole thread-group. Use it as light vector.
|
||||
* Note that this is an approximation since the footprint of a thread-group is not
|
||||
* necessarily a convex polygons (with center of gravity at midpoint).
|
||||
* But the actual error introduce by this approximation is not perceivable. */
|
||||
ivec2 max_group_texel = local_texel + ivec2(gl_WorkGroupSize.xy);
|
||||
/* Min direction is the local direction since this is only ran by thread 0. */
|
||||
vec3 min_direction = normalize(direction);
|
||||
vec3 max_direction = normalize(
|
||||
sphere_probe_texel_to_direction(max_group_texel, write_coord, sample_coord));
|
||||
vec3 L = normalize(min_direction + max_direction);
|
||||
/* Convert radiance to spherical harmonics. */
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = vec4(0.0);
|
||||
sh.L1.Mn1 = vec4(0.0);
|
||||
sh.L1.M0 = vec4(0.0);
|
||||
sh.L1.Mp1 = vec4(0.0);
|
||||
/* TODO(fclem): Cleanup: Should spherical_harmonics_encode_signal_sample return a new sh
|
||||
* instead of adding to it? */
|
||||
spherical_harmonics_encode_signal_sample(L, local_radiance[0], sh);
|
||||
/* Outputs one SH for each thread-group. */
|
||||
uint work_group_index = gl_NumWorkGroups.x * gl_WorkGroupID.y + gl_WorkGroupID.x;
|
||||
out_sh[work_group_index].L0_M0 = sh.L0.M0;
|
||||
out_sh[work_group_index].L1_Mn1 = sh.L1.Mn1;
|
||||
out_sh[work_group_index].L1_M0 = sh.L1.M0;
|
||||
out_sh[work_group_index].L1_Mp1 = sh.L1.Mp1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/* Shader to extract spherical harmonics cooefs from octahedral mapped reflection probe. */
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_reflection_probe_mapping_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
|
||||
void atlas_store(vec4 sh_coefficient, ivec2 atlas_coord, int layer)
|
||||
{
|
||||
for (int x = 0; x < IRRADIANCE_GRID_BRICK_SIZE; x++) {
|
||||
for (int y = 0; y < IRRADIANCE_GRID_BRICK_SIZE; y++) {
|
||||
for (int z = 0; z < IRRADIANCE_GRID_BRICK_SIZE; z++) {
|
||||
ivec3 brick_coord = ivec3(x, y, z);
|
||||
imageStore(irradiance_atlas_img,
|
||||
ivec3(atlas_coord, layer * IRRADIANCE_GRID_BRICK_SIZE) + brick_coord,
|
||||
sh_coefficient);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shared vec4 cooefs[gl_WorkGroupSize.x][4];
|
||||
|
||||
void main()
|
||||
{
|
||||
SphericalHarmonicL1 cooef;
|
||||
cooef.L0.M0 = vec4(0.0);
|
||||
cooef.L1.Mn1 = vec4(0.0);
|
||||
cooef.L1.M0 = vec4(0.0);
|
||||
cooef.L1.Mp1 = vec4(0.0);
|
||||
|
||||
SphereProbeUvArea atlas_coord = reinterpret_as_atlas_coord(world_coord_packed);
|
||||
float layer_mipmap = 2;
|
||||
/* Perform multiple sample. */
|
||||
uint store_index = gl_LocalInvocationID.x;
|
||||
float total_samples = float(gl_WorkGroupSize.x * SPHERE_PROBE_SH_SAMPLES_PER_GROUP);
|
||||
float sample_weight = 4.0 * M_PI / total_samples;
|
||||
float sample_offset = float(gl_LocalInvocationID.x * SPHERE_PROBE_SH_SAMPLES_PER_GROUP);
|
||||
for (int sample_index = 0; sample_index < SPHERE_PROBE_SH_SAMPLES_PER_GROUP; sample_index++) {
|
||||
vec2 rand = fract(hammersley_2d(sample_index + sample_offset, total_samples));
|
||||
vec3 direction = sample_sphere(rand);
|
||||
vec4 light = reflection_probes_sample(direction, layer_mipmap, atlas_coord);
|
||||
spherical_harmonics_encode_signal_sample(direction, light * sample_weight, cooef);
|
||||
}
|
||||
cooefs[store_index][0] = cooef.L0.M0;
|
||||
cooefs[store_index][1] = cooef.L1.Mn1;
|
||||
cooefs[store_index][2] = cooef.L1.M0;
|
||||
cooefs[store_index][3] = cooef.L1.Mp1;
|
||||
|
||||
barrier();
|
||||
if (gl_LocalInvocationIndex == 0u) {
|
||||
/* Join results */
|
||||
vec4 result[4];
|
||||
result[0] = vec4(0.0);
|
||||
result[1] = vec4(0.0);
|
||||
result[2] = vec4(0.0);
|
||||
result[3] = vec4(0.0);
|
||||
|
||||
for (uint i = 0; i < gl_WorkGroupSize.x; i++) {
|
||||
result[0] += cooefs[i][0];
|
||||
result[1] += cooefs[i][1];
|
||||
result[2] += cooefs[i][2];
|
||||
result[3] += cooefs[i][3];
|
||||
}
|
||||
|
||||
ivec2 atlas_coord = ivec2(0, 0);
|
||||
atlas_store(result[0], atlas_coord, 0);
|
||||
atlas_store(result[1], atlas_coord, 1);
|
||||
atlas_store(result[2], atlas_coord, 2);
|
||||
atlas_store(result[3], atlas_coord, 3);
|
||||
}
|
||||
}
|
|
@ -110,22 +110,8 @@ void shadow_tag_usage_tilemap_punctual(
|
|||
/* TODO(fclem): 3D shift for jittered soft shadows. */
|
||||
lP += vec3(0.0, 0.0, -light.shadow_projection_shift);
|
||||
|
||||
/* How much a shadow map pixel covers a final image pixel.
|
||||
* We project a shadow map pixel (as a sphere for simplicity) to the receiver plane.
|
||||
* We then reproject this sphere onto the camera screen and compare it to the film pixel size.
|
||||
* This gives a good approximation of what LOD to select to get a somewhat uniform shadow map
|
||||
* resolution in screen space. */
|
||||
float footprint_ratio = dist_to_light;
|
||||
/* Project the radius to the screen. 1 unit away from the camera the same way
|
||||
* pixel_world_radius_inv was computed. Not needed in orthographic mode. */
|
||||
bool is_persp = (ProjectionMatrix[3][3] == 0.0);
|
||||
if (is_persp) {
|
||||
footprint_ratio /= dist_to_cam;
|
||||
}
|
||||
/* Apply resolution ratio. */
|
||||
footprint_ratio *= tilemap_projection_ratio;
|
||||
/* Take the frustum padding into account. */
|
||||
footprint_ratio *= light.clip_side / orderedIntBitsToFloat(light.clip_near);
|
||||
float footprint_ratio = shadow_punctual_footprint_ratio(
|
||||
light, P, drw_view_is_perspective(), dist_to_cam, tilemap_proj_ratio);
|
||||
|
||||
if (radius == 0) {
|
||||
int face_id = shadow_punctual_face_index_get(lP);
|
||||
|
|
|
@ -23,7 +23,7 @@ void main()
|
|||
light.type = LIGHT_SUN;
|
||||
light.clipmap_lod_min = -5;
|
||||
light.clipmap_lod_max = 8;
|
||||
light._clipmap_lod_bias = 0.0;
|
||||
light.lod_bias = 0.0;
|
||||
float fac = float(SHADOW_TILEMAP_RES - 1) / float(SHADOW_TILEMAP_RES);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 0.0)), light.clipmap_lod_min);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 0.49)), 1);
|
||||
|
@ -51,7 +51,7 @@ void main()
|
|||
light._clipmap_origin_x = 0.0;
|
||||
light._clipmap_origin_y = 0.0;
|
||||
float half_size = exp2(float(light.clipmap_lod_min - 1));
|
||||
light._clipmap_lod_bias = light.clipmap_lod_min - 1;
|
||||
light.lod_bias = light.clipmap_lod_min - 1;
|
||||
float fac = float(SHADOW_TILEMAP_RES - 1) / float(SHADOW_TILEMAP_RES);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * half_size * 0.0, 0.0, 0.0)), 2);
|
||||
EXPECT_EQ(shadow_directional_level(light, vec3(fac * half_size * 0.5, 0.0, 0.0)), 2);
|
||||
|
@ -76,7 +76,7 @@ void main()
|
|||
light._position = vec3(0.0);
|
||||
light._clipmap_origin_x = 0.0;
|
||||
light._clipmap_origin_y = 0.0;
|
||||
light._clipmap_lod_bias = 0;
|
||||
light.lod_bias = 0;
|
||||
|
||||
float lod_min_tile_size = exp2(float(light.clipmap_lod_min)) / float(SHADOW_TILEMAP_RES);
|
||||
float lod_max_half_size = exp2(float(light.clipmap_lod_max)) / 2.0;
|
||||
|
@ -189,7 +189,7 @@ void main()
|
|||
light.clipmap_lod_max = 2; /* 3 tile-maps. */
|
||||
light.tilemap_index = 1;
|
||||
light._position = vec3(0.0);
|
||||
light._clipmap_lod_bias = light.clipmap_lod_min - 1;
|
||||
light.lod_bias = light.clipmap_lod_min - 1;
|
||||
light._clipmap_origin_x = 0.0;
|
||||
light._clipmap_origin_y = 0.0;
|
||||
float lod_tile_size = exp2(float(light.clipmap_lod_min)) / float(SHADOW_TILEMAP_RES);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_shape_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Tile-map data
|
||||
|
@ -106,7 +107,8 @@ ShadowTileData shadow_tile_load(usampler2D tilemaps_tx, ivec2 tile_co, int tilem
|
|||
* \a lP shading point position in light space, relative to the to camera position snapped to
|
||||
* the smallest clip-map level (`shadow_world_to_local(light, P) - light._position`).
|
||||
*/
|
||||
int shadow_directional_level(LightData light, vec3 lP)
|
||||
|
||||
float shadow_directional_level_fractional(LightData light, vec3 lP)
|
||||
{
|
||||
float lod;
|
||||
if (light.type == LIGHT_SUN) {
|
||||
|
@ -124,8 +126,39 @@ int shadow_directional_level(LightData light, vec3 lP)
|
|||
float lod_min_half_size = exp2(float(light.clipmap_lod_min - 1));
|
||||
lod = length(lP.xy) * narrowing / lod_min_half_size;
|
||||
}
|
||||
int clipmap_lod = int(ceil(lod + light._clipmap_lod_bias));
|
||||
return clamp(clipmap_lod, light.clipmap_lod_min, light.clipmap_lod_max);
|
||||
float clipmap_lod = lod + light.lod_bias;
|
||||
return clamp(clipmap_lod, float(light.clipmap_lod_min), float(light.clipmap_lod_max));
|
||||
}
|
||||
|
||||
int shadow_directional_level(LightData light, vec3 lP)
|
||||
{
|
||||
return int(ceil(shadow_directional_level_fractional(light, lP)));
|
||||
}
|
||||
|
||||
/* How much a tilemap pixel covers a final image pixel. */
|
||||
float shadow_punctual_footprint_ratio(LightData light,
|
||||
vec3 P,
|
||||
bool is_perspective,
|
||||
float dist_to_cam,
|
||||
float tilemap_projection_ratio)
|
||||
{
|
||||
/* We project a shadow map pixel (as a sphere for simplicity) to the receiver plane.
|
||||
* We then reproject this sphere onto the camera screen and compare it to the film pixel size.
|
||||
* This gives a good approximation of what LOD to select to get a somewhat uniform shadow map
|
||||
* resolution in screen space. */
|
||||
|
||||
float dist_to_light = distance(P, light._position);
|
||||
float footprint_ratio = dist_to_light;
|
||||
/* Project the radius to the screen. 1 unit away from the camera the same way
|
||||
* pixel_world_radius_inv was computed. Not needed in orthographic mode. */
|
||||
if (is_perspective) {
|
||||
footprint_ratio /= dist_to_cam;
|
||||
}
|
||||
/* Apply resolution ratio. */
|
||||
footprint_ratio *= tilemap_projection_ratio;
|
||||
/* Take the frustum padding into account. */
|
||||
footprint_ratio *= light.clip_side / orderedIntBitsToFloat(light.clip_near);
|
||||
return footprint_ratio;
|
||||
}
|
||||
|
||||
struct ShadowCoordinates {
|
||||
|
|
|
@ -412,7 +412,7 @@ SHADOW_MAP_TRACE_FN(ShadowRayPunctual)
|
|||
|
||||
/* Compute the world space offset of the shading position required for
|
||||
* stochastic percentage closer filtering of shadow-maps. */
|
||||
vec3 shadow_pcf_offset(LightData light, const bool is_directional, vec3 P, vec3 Ng)
|
||||
vec3 shadow_pcf_offset(LightData light, const bool is_directional, vec3 P, vec3 Ng, vec2 random)
|
||||
{
|
||||
if (light.pcf_radius <= 0.001) {
|
||||
/* Early return. */
|
||||
|
@ -461,14 +461,27 @@ vec3 shadow_pcf_offset(LightData light, const bool is_directional, vec3 P, vec3
|
|||
|
||||
/* Compute the actual offset. */
|
||||
|
||||
vec2 rand = vec2(0.0);
|
||||
#ifdef EEVEE_SAMPLING_DATA
|
||||
rand = sampling_rng_2D_get(SAMPLING_SHADOW_V);
|
||||
#endif
|
||||
vec2 pcf_offset = interlieved_gradient_noise(UTIL_TEXEL, vec2(0.0), rand);
|
||||
pcf_offset = pcf_offset * 2.0 - 1.0;
|
||||
vec2 pcf_offset = random * 2.0 - 1.0;
|
||||
pcf_offset *= light.pcf_radius;
|
||||
|
||||
/* Scale the offset based on shadow LOD. */
|
||||
if (is_directional) {
|
||||
vec3 lP = light_world_to_local(light, P);
|
||||
float level = shadow_directional_level_fractional(light, lP - light._position);
|
||||
float pcf_scale = mix(0.5, 1.0, fract(level));
|
||||
pcf_offset *= pcf_scale;
|
||||
}
|
||||
else {
|
||||
bool is_perspective = drw_view_is_perspective();
|
||||
float dist_to_cam = distance(P, drw_view_position());
|
||||
float footprint_ratio = shadow_punctual_footprint_ratio(
|
||||
light, P, is_perspective, dist_to_cam, uniform_buf.shadow.tilemap_projection_ratio);
|
||||
float lod = -log2(footprint_ratio) + light.lod_bias;
|
||||
lod = clamp(lod, 0.0, float(SHADOW_TILEMAP_LOD));
|
||||
float pcf_scale = exp2(lod);
|
||||
pcf_offset *= pcf_scale;
|
||||
}
|
||||
|
||||
vec3 ws_offset = TBN * vec3(pcf_offset, 0.0);
|
||||
vec3 offset_P = P + ws_offset;
|
||||
|
||||
|
@ -515,17 +528,19 @@ ShadowEvalResult shadow_eval(LightData light,
|
|||
# elif defined(GPU_COMPUTE_SHADER)
|
||||
vec2 pixel = vec2(gl_GlobalInvocationID.xy);
|
||||
# endif
|
||||
vec3 random_shadow_3d = utility_tx_fetch(utility_tx, pixel, UTIL_BLUE_NOISE_LAYER).rgb;
|
||||
random_shadow_3d += sampling_rng_3D_get(SAMPLING_SHADOW_U);
|
||||
vec3 blue_noise_3d = utility_tx_fetch(utility_tx, pixel, UTIL_BLUE_NOISE_LAYER).rgb;
|
||||
vec3 random_shadow_3d = blue_noise_3d + sampling_rng_3D_get(SAMPLING_SHADOW_U);
|
||||
vec2 random_pcf_2d = fract(blue_noise_3d.xy + sampling_rng_2D_get(SAMPLING_SHADOW_X));
|
||||
float normal_offset = uniform_buf.shadow.normal_bias;
|
||||
#else
|
||||
/* Case of surfel light eval. */
|
||||
vec3 random_shadow_3d = vec3(0.5);
|
||||
vec2 random_pcf_2d = vec2(0.0);
|
||||
/* TODO(fclem): Parameter on irradiance volumes? */
|
||||
float normal_offset = 0.02;
|
||||
#endif
|
||||
|
||||
P += shadow_pcf_offset(light, is_directional, P, Ng);
|
||||
P += shadow_pcf_offset(light, is_directional, P, Ng, random_pcf_2d);
|
||||
|
||||
/* Avoid self intersection. */
|
||||
P = offset_ray(P, Ng);
|
||||
|
|
|
@ -102,6 +102,102 @@ struct SphericalHarmonicL2 {
|
|||
SphericalHarmonicBandL2 L2;
|
||||
};
|
||||
|
||||
SphericalHarmonicBandL0 spherical_harmonics_band_L0_new()
|
||||
{
|
||||
SphericalHarmonicBandL0 L0;
|
||||
L0.M0 = vec4(0.0);
|
||||
return L0;
|
||||
}
|
||||
|
||||
SphericalHarmonicBandL1 spherical_harmonics_band_L1_new()
|
||||
{
|
||||
SphericalHarmonicBandL1 L1;
|
||||
L1.Mn1 = vec4(0.0);
|
||||
L1.M0 = vec4(0.0);
|
||||
L1.Mp1 = vec4(0.0);
|
||||
return L1;
|
||||
}
|
||||
|
||||
SphericalHarmonicBandL2 spherical_harmonics_band_L2_new()
|
||||
{
|
||||
SphericalHarmonicBandL2 L2;
|
||||
L2.Mn2 = vec4(0.0);
|
||||
L2.Mn1 = vec4(0.0);
|
||||
L2.M0 = vec4(0.0);
|
||||
L2.Mp1 = vec4(0.0);
|
||||
L2.Mp2 = vec4(0.0);
|
||||
return L2;
|
||||
}
|
||||
|
||||
SphericalHarmonicL0 spherical_harmonics_L0_new()
|
||||
{
|
||||
SphericalHarmonicL0 sh;
|
||||
sh.L0 = spherical_harmonics_band_L0_new();
|
||||
return sh;
|
||||
}
|
||||
|
||||
SphericalHarmonicL1 spherical_harmonics_L1_new()
|
||||
{
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0 = spherical_harmonics_band_L0_new();
|
||||
sh.L1 = spherical_harmonics_band_L1_new();
|
||||
return sh;
|
||||
}
|
||||
|
||||
SphericalHarmonicL2 spherical_harmonics_L2_new()
|
||||
{
|
||||
SphericalHarmonicL2 sh;
|
||||
sh.L0 = spherical_harmonics_band_L0_new();
|
||||
sh.L1 = spherical_harmonics_band_L1_new();
|
||||
sh.L2 = spherical_harmonics_band_L2_new();
|
||||
return sh;
|
||||
}
|
||||
|
||||
SphericalHarmonicBandL0 spherical_harmonics_band_L0_swizzle_wwww(SphericalHarmonicBandL0 L0)
|
||||
{
|
||||
L0.M0 = L0.M0.wwww;
|
||||
return L0;
|
||||
}
|
||||
|
||||
SphericalHarmonicBandL1 spherical_harmonics_band_L1_swizzle_wwww(SphericalHarmonicBandL1 L1)
|
||||
{
|
||||
L1.Mn1 = L1.Mn1.wwww;
|
||||
L1.M0 = L1.M0.wwww;
|
||||
L1.Mp1 = L1.Mp1.wwww;
|
||||
return L1;
|
||||
}
|
||||
|
||||
SphericalHarmonicBandL2 spherical_harmonics_band_L2_swizzle_wwww(SphericalHarmonicBandL2 L2)
|
||||
{
|
||||
L2.Mn2 = L2.Mn2.wwww;
|
||||
L2.Mn1 = L2.Mn1.wwww;
|
||||
L2.M0 = L2.M0.wwww;
|
||||
L2.Mp1 = L2.Mp1.wwww;
|
||||
L2.Mp2 = L2.Mp2.wwww;
|
||||
return L2;
|
||||
}
|
||||
|
||||
SphericalHarmonicL0 spherical_harmonics_swizzle_wwww(SphericalHarmonicL0 sh)
|
||||
{
|
||||
sh.L0 = spherical_harmonics_band_L0_swizzle_wwww(sh.L0);
|
||||
return sh;
|
||||
}
|
||||
|
||||
SphericalHarmonicL1 spherical_harmonics_swizzle_wwww(SphericalHarmonicL1 sh)
|
||||
{
|
||||
sh.L0 = spherical_harmonics_band_L0_swizzle_wwww(sh.L0);
|
||||
sh.L1 = spherical_harmonics_band_L1_swizzle_wwww(sh.L1);
|
||||
return sh;
|
||||
}
|
||||
|
||||
SphericalHarmonicL2 spherical_harmonics_swizzle_wwww(SphericalHarmonicL2 sh)
|
||||
{
|
||||
sh.L0 = spherical_harmonics_band_L0_swizzle_wwww(sh.L0);
|
||||
sh.L1 = spherical_harmonics_band_L1_swizzle_wwww(sh.L1);
|
||||
sh.L2 = spherical_harmonics_band_L2_swizzle_wwww(sh.L2);
|
||||
return sh;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -562,3 +658,52 @@ SphericalHarmonicL2 spherical_harmonics_add(SphericalHarmonicL2 a, SphericalHarm
|
|||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Dot
|
||||
* \{ */
|
||||
|
||||
vec4 spherical_harmonics_dot(SphericalHarmonicL1 a, SphericalHarmonicL1 b)
|
||||
{
|
||||
/* Convert coefficients to per channel column. */
|
||||
mat4x4 a_mat = transpose(mat4x4(a.L0.M0, a.L1.Mn1, a.L1.M0, a.L1.Mp1));
|
||||
mat4x4 b_mat = transpose(mat4x4(b.L0.M0, b.L1.Mn1, b.L1.M0, b.L1.Mp1));
|
||||
vec4 result;
|
||||
result[0] = dot(a_mat[0], b_mat[0]);
|
||||
result[1] = dot(a_mat[1], b_mat[1]);
|
||||
result[2] = dot(a_mat[2], b_mat[2]);
|
||||
result[3] = dot(a_mat[3], b_mat[3]);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Compression
|
||||
*
|
||||
* Described by Josh Hobson in "The indirect Lighting Pipeline of God of War" p. 120
|
||||
* \{ */
|
||||
|
||||
SphericalHarmonicL1 spherical_harmonics_compress(SphericalHarmonicL1 sh)
|
||||
{
|
||||
SphericalHarmonicL1 result;
|
||||
result.L0 = sh.L0;
|
||||
vec4 fac = safe_rcp(sh.L0.M0 * M_SQRT3);
|
||||
result.L1.Mn1 = (sh.L1.Mn1 * fac) * 0.5 + 0.5;
|
||||
result.L1.M0 = (sh.L1.M0 * fac) * 0.5 + 0.5;
|
||||
result.L1.Mp1 = (sh.L1.Mp1 * fac) * 0.5 + 0.5;
|
||||
return result;
|
||||
}
|
||||
|
||||
SphericalHarmonicL1 spherical_harmonics_decompress(SphericalHarmonicL1 sh)
|
||||
{
|
||||
SphericalHarmonicL1 result;
|
||||
result.L0 = sh.L0;
|
||||
vec4 fac = sh.L0.M0 * M_SQRT3;
|
||||
result.L1.Mn1 = (sh.L1.Mn1 * 2.0 - 1.0) * fac;
|
||||
result.L1.M0 = (sh.L1.M0 * 2.0 - 1.0) * fac;
|
||||
result.L1.Mp1 = (sh.L1.Mp1 * 2.0 - 1.0) * fac;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_transparency_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
|
||||
|
||||
vec4 closure_to_rgba(Closure cl)
|
||||
{
|
||||
|
@ -25,13 +25,15 @@ vec4 closure_to_rgba(Closure cl)
|
|||
|
||||
void main()
|
||||
{
|
||||
float f_depth = gl_FragCoord.z + fwidth(gl_FragCoord.z);
|
||||
|
||||
#ifdef MAT_TRANSPARENT
|
||||
init_globals();
|
||||
|
||||
nodetree_surface(0.0);
|
||||
|
||||
float noise_offset = sampling_rng_1D_get(SAMPLING_TRANSPARENCY);
|
||||
float random_threshold = transparency_hashed_alpha_threshold(1.0, noise_offset, g_data.P);
|
||||
float random_threshold = pcg4d(vec4(g_data.P, noise_offset)).x;
|
||||
|
||||
float transparency = average(g_transmittance);
|
||||
if (transparency > random_threshold) {
|
||||
|
@ -40,8 +42,6 @@ void main()
|
|||
}
|
||||
#endif
|
||||
|
||||
float f_depth = gl_FragCoord.z + fwidth(gl_FragCoord.z);
|
||||
|
||||
#ifdef SHADOW_UPDATE_ATOMIC_RASTER
|
||||
ivec2 texel_co = ivec2(gl_FragCoord.xy);
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
|
||||
|
||||
/* Based on Frosbite Unified Volumetric.
|
||||
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
|
||||
|
@ -68,6 +69,22 @@ float volume_phase_function(vec3 V, vec3 L, float g)
|
|||
return (1 - sqr_g) / max(1e-8, 4.0 * M_PI * pow(1 + sqr_g - 2 * g * cos_theta, 3.0 / 2.0));
|
||||
}
|
||||
|
||||
SphericalHarmonicL1 volume_phase_function_as_sh_L1(vec3 V, float g)
|
||||
{
|
||||
/* Compute rotated zonal harmonic.
|
||||
* From Bartlomiej Wronsky
|
||||
* "Volumetric Fog: Unified compute shader based solution to atmospheric scattering" page 55
|
||||
* SIGGRAPH 2014
|
||||
* https://bartwronski.files.wordpress.com/2014/08/bwronski_volumetric_fog_siggraph2014.pdf
|
||||
*/
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = spherical_harmonics_L0_M0(V) * vec4(1.0);
|
||||
sh.L1.Mn1 = spherical_harmonics_L1_Mn1(V) * vec4(g);
|
||||
sh.L1.M0 = spherical_harmonics_L1_M0(V) * vec4(g);
|
||||
sh.L1.Mp1 = spherical_harmonics_L1_Mp1(V) * vec4(g);
|
||||
return sh;
|
||||
}
|
||||
|
||||
vec3 volume_light(LightData light, const bool is_directional, LightVector lv)
|
||||
{
|
||||
float power = 1.0;
|
||||
|
@ -153,16 +170,6 @@ vec3 volume_shadow(
|
|||
#endif /* VOLUME_SHADOW */
|
||||
}
|
||||
|
||||
vec3 volume_irradiance(vec3 P)
|
||||
{
|
||||
#ifdef VOLUME_IRRADIANCE
|
||||
SphericalHarmonicL1 irradiance = lightprobe_irradiance_sample(P);
|
||||
return irradiance.L0.M0.rgb * M_PI;
|
||||
#else
|
||||
return vec3(0.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct VolumeResolveSample {
|
||||
vec3 transmittance;
|
||||
vec3 scattering;
|
||||
|
|
|
@ -78,10 +78,13 @@ void main()
|
|||
float s_anisotropy = phase.x / max(1.0, phase.y);
|
||||
|
||||
#ifdef VOLUME_LIGHTING
|
||||
scattering += volume_irradiance(P) * s_scattering * volume_phase_function_isotropic();
|
||||
SphericalHarmonicL1 phase_sh = volume_phase_function_as_sh_L1(V, s_anisotropy);
|
||||
SphericalHarmonicL1 volume_radiance_sh = lightprobe_irradiance_sample(P);
|
||||
|
||||
vec3 light_scattering = spherical_harmonics_dot(volume_radiance_sh, phase_sh).xyz;
|
||||
|
||||
LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) {
|
||||
scattering += volume_scatter_light_eval(true, P, V, l_idx, s_anisotropy) * s_scattering;
|
||||
light_scattering += volume_scatter_light_eval(true, P, V, l_idx, s_anisotropy);
|
||||
}
|
||||
LIGHT_FOREACH_END
|
||||
|
||||
|
@ -89,10 +92,11 @@ void main()
|
|||
uniform_buf.volumes.viewport_size_inv;
|
||||
|
||||
LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, pixel, vP.z, l_idx) {
|
||||
scattering += volume_scatter_light_eval(false, P, V, l_idx, s_anisotropy) * s_scattering;
|
||||
light_scattering += volume_scatter_light_eval(false, P, V, l_idx, s_anisotropy);
|
||||
}
|
||||
LIGHT_FOREACH_END
|
||||
|
||||
scattering += light_scattering * s_scattering;
|
||||
#endif
|
||||
|
||||
/* Catch NaNs. */
|
||||
|
|
|
@ -169,6 +169,20 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_offset)
|
|||
/** \name Runtime
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_world)
|
||||
.local_group_size(IRRADIANCE_GRID_BRICK_SIZE,
|
||||
IRRADIANCE_GRID_BRICK_SIZE,
|
||||
IRRADIANCE_GRID_BRICK_SIZE)
|
||||
.define("IRRADIANCE_GRID_UPLOAD")
|
||||
.additional_info("eevee_shared")
|
||||
.push_constant(Type::INT, "grid_index")
|
||||
.storage_buf(0, Qualifier::READ, "uint", "bricks_infos_buf[]")
|
||||
.storage_buf(1, Qualifier::READ, "SphereProbeHarmonic", "harmonic_buf")
|
||||
.uniform_buf(0, "VolumeProbeData", "grids_infos_buf[IRRADIANCE_GRID_MAX]")
|
||||
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_atlas_img")
|
||||
.compute_source("eevee_lightprobe_irradiance_world_comp.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_load)
|
||||
.local_group_size(IRRADIANCE_GRID_BRICK_SIZE,
|
||||
IRRADIANCE_GRID_BRICK_SIZE,
|
||||
|
|
|
@ -18,29 +18,28 @@ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_data)
|
|||
|
||||
/* Sample cubemap and remap into an octahedral texture. */
|
||||
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_remap)
|
||||
.local_group_size(SPHERE_PROBE_GROUP_SIZE, SPHERE_PROBE_GROUP_SIZE)
|
||||
.local_group_size(SPHERE_PROBE_REMAP_GROUP_SIZE, SPHERE_PROBE_REMAP_GROUP_SIZE)
|
||||
.specialization_constant(Type::BOOL, "extract_sh", true)
|
||||
.push_constant(Type::IVEC4, "probe_coord_packed")
|
||||
.push_constant(Type::IVEC4, "write_coord_packed")
|
||||
.push_constant(Type::IVEC4, "world_coord_packed")
|
||||
.push_constant(Type::FLOAT, "probe_brightness_clamp")
|
||||
.sampler(0, ImageType::FLOAT_CUBE, "cubemap_tx")
|
||||
.sampler(1, ImageType::FLOAT_2D_ARRAY, "atlas_tx")
|
||||
.storage_buf(0, Qualifier::WRITE, "SphereProbeHarmonic", "out_sh[SPHERE_PROBE_MAX_HARMONIC]")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "atlas_img")
|
||||
.compute_source("eevee_reflection_probe_remap_comp.glsl")
|
||||
.additional_info("eevee_shared")
|
||||
.do_static_compilation(true);
|
||||
|
||||
/* Extract spherical harmonics band L0 + L1 from octahedral mapped reflection probe and update the
|
||||
* world brick of the irradiance cache. */
|
||||
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_update_irradiance)
|
||||
.local_group_size(SPHERE_PROBE_SH_GROUP_SIZE, 1)
|
||||
.define("SPHERE_PROBE")
|
||||
.push_constant(Type::IVEC4, "world_coord_packed")
|
||||
.sampler(0, ImageType::FLOAT_2D_ARRAY, "reflection_probes_tx")
|
||||
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_atlas_img")
|
||||
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_irradiance)
|
||||
.local_group_size(SPHERE_PROBE_SH_GROUP_SIZE)
|
||||
.push_constant(Type::IVEC3, "probe_remap_dispatch_size")
|
||||
.storage_buf(0, Qualifier::READ, "SphereProbeHarmonic", "in_sh[SPHERE_PROBE_MAX_HARMONIC]")
|
||||
.storage_buf(1, Qualifier::WRITE, "SphereProbeHarmonic", "out_sh")
|
||||
.additional_info("eevee_shared")
|
||||
.compute_source("eevee_reflection_probe_update_irradiance_comp.glsl")
|
||||
.do_static_compilation(true);
|
||||
.do_static_compilation(true)
|
||||
.compute_source("eevee_reflection_probe_irradiance_comp.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_select)
|
||||
.local_group_size(SPHERE_PROBE_SELECT_GROUP_SIZE)
|
||||
|
|
|
@ -64,7 +64,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_opaque)
|
|||
.sampler(0, ImageType::DEPTH_2D, "depth_tx")
|
||||
.storage_buf(5, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
|
||||
.storage_buf(6, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
|
||||
.push_constant(Type::FLOAT, "tilemap_projection_ratio")
|
||||
.push_constant(Type::FLOAT, "tilemap_proj_ratio")
|
||||
.additional_info("eevee_shared", "draw_view", "draw_view_culling", "eevee_light_data")
|
||||
.compute_source("eevee_shadow_tag_usage_comp.glsl");
|
||||
|
||||
|
@ -75,7 +75,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_surfels)
|
|||
/* ShadowTileDataPacked is uint. But MSL translation need the real type. */
|
||||
.storage_buf(7, Qualifier::READ_WRITE, "uint", "tiles_buf[]")
|
||||
.push_constant(Type::INT, "directional_level")
|
||||
.push_constant(Type::FLOAT, "tilemap_projection_ratio")
|
||||
.push_constant(Type::FLOAT, "tilemap_proj_ratio")
|
||||
.additional_info("eevee_shared",
|
||||
"draw_view",
|
||||
"draw_view_culling",
|
||||
|
@ -96,7 +96,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_transparent)
|
|||
.storage_buf(4, Qualifier::READ, "ObjectBounds", "bounds_buf[]")
|
||||
.storage_buf(5, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
|
||||
.storage_buf(6, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
|
||||
.push_constant(Type::FLOAT, "tilemap_projection_ratio")
|
||||
.push_constant(Type::FLOAT, "tilemap_proj_ratio")
|
||||
.push_constant(Type::FLOAT, "pixel_world_radius")
|
||||
.push_constant(Type::IVEC2, "fb_resolution")
|
||||
.push_constant(Type::INT, "fb_lod")
|
||||
|
@ -117,7 +117,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_volume)
|
|||
.local_group_size(VOLUME_GROUP_SIZE, VOLUME_GROUP_SIZE, VOLUME_GROUP_SIZE)
|
||||
.storage_buf(4, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
|
||||
.storage_buf(5, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
|
||||
.push_constant(Type::FLOAT, "tilemap_projection_ratio")
|
||||
.push_constant(Type::FLOAT, "tilemap_proj_ratio")
|
||||
.additional_info("eevee_volume_properties_data",
|
||||
"eevee_shared",
|
||||
"draw_view",
|
||||
|
|
|
@ -32,17 +32,12 @@ GPU_SHADER_CREATE_INFO(eevee_ray_tile_compact)
|
|||
.typedef_source("draw_shader_shared.h")
|
||||
.image_in(0, RAYTRACE_TILEMASK_FORMAT, ImageType::UINT_2D_ARRAY, "tile_raytrace_denoise_img")
|
||||
.image_in(1, RAYTRACE_TILEMASK_FORMAT, ImageType::UINT_2D_ARRAY, "tile_raytrace_tracing_img")
|
||||
.image_in(2, RAYTRACE_TILEMASK_FORMAT, ImageType::UINT_2D_ARRAY, "tile_horizon_denoise_img")
|
||||
.image_in(3, RAYTRACE_TILEMASK_FORMAT, ImageType::UINT_2D_ARRAY, "tile_horizon_tracing_img")
|
||||
.storage_buf(0, Qualifier::READ_WRITE, "DispatchCommand", "raytrace_tracing_dispatch_buf")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, "DispatchCommand", "raytrace_denoise_dispatch_buf")
|
||||
.storage_buf(2, Qualifier::READ_WRITE, "DispatchCommand", "horizon_tracing_dispatch_buf")
|
||||
.storage_buf(3, Qualifier::READ_WRITE, "DispatchCommand", "horizon_denoise_dispatch_buf")
|
||||
.storage_buf(4, Qualifier::WRITE, "uint", "raytrace_tracing_tiles_buf[]")
|
||||
.storage_buf(5, Qualifier::WRITE, "uint", "raytrace_denoise_tiles_buf[]")
|
||||
.storage_buf(6, Qualifier::WRITE, "uint", "horizon_tracing_tiles_buf[]")
|
||||
.storage_buf(7, Qualifier::WRITE, "uint", "horizon_denoise_tiles_buf[]")
|
||||
.specialization_constant(Type::INT, "closure_index", 0)
|
||||
.specialization_constant(Type::INT, "resolution_scale", 2)
|
||||
.compute_source("eevee_ray_tile_compact_comp.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_ray_generate)
|
||||
|
@ -188,7 +183,7 @@ GPU_SHADER_CREATE_INFO(eevee_horizon_setup)
|
|||
.sampler(0, ImageType::DEPTH_2D, "depth_tx")
|
||||
.sampler(1, ImageType::FLOAT_2D, "in_radiance_tx")
|
||||
.image(2, RAYTRACE_RADIANCE_FORMAT, Qualifier::WRITE, ImageType::FLOAT_2D, "out_radiance_img")
|
||||
.image(3, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "out_normal_img")
|
||||
.image(3, GPU_RGB10_A2, Qualifier::WRITE, ImageType::FLOAT_2D, "out_normal_img")
|
||||
.compute_source("eevee_horizon_setup_comp.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_horizon_scan)
|
||||
|
@ -203,14 +198,31 @@ GPU_SHADER_CREATE_INFO(eevee_horizon_scan)
|
|||
"draw_view")
|
||||
.sampler(0, ImageType::FLOAT_2D, "screen_radiance_tx")
|
||||
.sampler(1, ImageType::FLOAT_2D, "screen_normal_tx")
|
||||
.image(
|
||||
2, RAYTRACE_RADIANCE_FORMAT, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_img")
|
||||
.image(3, GPU_R8, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_occlusion_img")
|
||||
.image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_0_img")
|
||||
.image(3, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_1_img")
|
||||
.image(4, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_2_img")
|
||||
.image(5, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_3_img")
|
||||
.storage_buf(7, Qualifier::READ, "uint", "tiles_coord_buf[]")
|
||||
.specialization_constant(Type::INT, "closure_index", 0)
|
||||
.compute_source("eevee_horizon_scan_comp.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_horizon_denoise)
|
||||
.do_static_compilation(true)
|
||||
.local_group_size(RAYTRACE_GROUP_SIZE, RAYTRACE_GROUP_SIZE)
|
||||
.additional_info(
|
||||
"eevee_shared", "eevee_global_ubo", "eevee_sampling_data", "eevee_hiz_data", "draw_view")
|
||||
.sampler(2, ImageType::FLOAT_2D, "in_sh_0_tx")
|
||||
.sampler(4, ImageType::FLOAT_2D, "in_sh_1_tx")
|
||||
.sampler(5, ImageType::FLOAT_2D, "in_sh_2_tx")
|
||||
.sampler(6, ImageType::FLOAT_2D, "in_sh_3_tx")
|
||||
.sampler(7, ImageType::FLOAT_2D, "screen_normal_tx")
|
||||
.image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_sh_0_img")
|
||||
.image(3, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "out_sh_1_img")
|
||||
.image(4, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "out_sh_2_img")
|
||||
.image(5, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "out_sh_3_img")
|
||||
.storage_buf(7, Qualifier::READ, "uint", "tiles_coord_buf[]")
|
||||
.compute_source("eevee_horizon_denoise_comp.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_horizon_resolve)
|
||||
.do_static_compilation(true)
|
||||
.local_group_size(RAYTRACE_GROUP_SIZE, RAYTRACE_GROUP_SIZE)
|
||||
.additional_info("eevee_shared",
|
||||
|
@ -220,14 +232,16 @@ GPU_SHADER_CREATE_INFO(eevee_horizon_denoise)
|
|||
"eevee_lightprobe_data",
|
||||
"draw_view")
|
||||
.sampler(1, ImageType::DEPTH_2D, "depth_tx")
|
||||
.image(
|
||||
2, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ, ImageType::FLOAT_2D, "horizon_radiance_img")
|
||||
.image(3, GPU_R8, Qualifier::READ, ImageType::FLOAT_2D, "horizon_occlusion_img")
|
||||
.image(4, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "radiance_img")
|
||||
.image(6, RAYTRACE_TILEMASK_FORMAT, Qualifier::READ, ImageType::UINT_2D_ARRAY, "tile_mask_img")
|
||||
.sampler(2, ImageType::FLOAT_2D, "horizon_radiance_0_tx")
|
||||
.sampler(3, ImageType::FLOAT_2D, "horizon_radiance_1_tx")
|
||||
.sampler(4, ImageType::FLOAT_2D, "horizon_radiance_2_tx")
|
||||
.sampler(5, ImageType::FLOAT_2D, "horizon_radiance_3_tx")
|
||||
.sampler(8, ImageType::FLOAT_2D, "screen_normal_tx")
|
||||
.image(3, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "closure0_img")
|
||||
.image(4, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "closure1_img")
|
||||
.image(5, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "closure2_img")
|
||||
.storage_buf(7, Qualifier::READ, "uint", "tiles_coord_buf[]")
|
||||
.specialization_constant(Type::INT, "closure_index", 0)
|
||||
.compute_source("eevee_horizon_denoise_comp.glsl");
|
||||
.compute_source("eevee_horizon_resolve_comp.glsl");
|
||||
|
||||
#undef image_out
|
||||
#undef image_in
|
||||
|
|
|
@ -217,12 +217,14 @@ struct MeshExtractLooseGeom {
|
|||
};
|
||||
|
||||
struct SortedFaceData {
|
||||
/** The first triangle index for each polygon, sorted into slices by material. */
|
||||
Array<int> tri_first_index;
|
||||
/* The total number of visible triangles (a sum of the values in #mat_tri_counts). */
|
||||
int visible_tris_num;
|
||||
/** The number of visible triangles assigned to each material. */
|
||||
Array<int> mat_tri_len;
|
||||
/* The total number of visible triangles (a sum of the values in #mat_tri_len). */
|
||||
int visible_tri_len;
|
||||
Array<int> tris_num_by_material;
|
||||
/**
|
||||
* The first triangle index for each face, sorted into slices by material.
|
||||
*/
|
||||
Array<int> face_tri_offsets;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -400,24 +400,24 @@ BLI_INLINE void extract_task_range_run_iter(const MeshRenderData &mr,
|
|||
case MR_ITER_CORNER_TRI:
|
||||
range_data.elems = is_mesh ? mr.corner_tris.data() : (void *)mr.edit_bmesh->looptris;
|
||||
func = is_mesh ? extract_range_iter_corner_tri_mesh : extract_range_iter_looptri_bm;
|
||||
stop = mr.tri_len;
|
||||
stop = mr.corner_tris_num;
|
||||
break;
|
||||
case MR_ITER_POLY:
|
||||
range_data.elems = is_mesh ? mr.faces.data().data() : (void *)mr.bm->ftable;
|
||||
func = is_mesh ? extract_range_iter_face_mesh : extract_range_iter_face_bm;
|
||||
stop = mr.face_len;
|
||||
stop = mr.faces_num;
|
||||
break;
|
||||
case MR_ITER_LOOSE_EDGE:
|
||||
range_data.loose_elems = mr.loose_edges.data();
|
||||
range_data.elems = is_mesh ? mr.edges.data() : (void *)mr.bm->etable;
|
||||
func = is_mesh ? extract_range_iter_loose_edge_mesh : extract_range_iter_loose_edge_bm;
|
||||
stop = mr.edge_loose_len;
|
||||
stop = mr.loose_edges_num;
|
||||
break;
|
||||
case MR_ITER_LOOSE_VERT:
|
||||
range_data.loose_elems = mr.loose_verts.data();
|
||||
range_data.elems = is_mesh ? mr.vert_positions.data() : (void *)mr.bm->vtable;
|
||||
func = is_mesh ? extract_range_iter_loose_vert_mesh : extract_range_iter_loose_vert_bm;
|
||||
stop = mr.vert_loose_len;
|
||||
stop = mr.loose_verts_num;
|
||||
break;
|
||||
default:
|
||||
BLI_assert(false);
|
||||
|
@ -721,7 +721,7 @@ void mesh_buffer_cache_create_requested(TaskGraph *task_graph,
|
|||
task_graph, *mr, mbc, iter_type, data_flag);
|
||||
|
||||
/* Simple heuristic. */
|
||||
const bool use_thread = (mr->loop_len + mr->loop_loose_len) > MIN_RANGE_LEN;
|
||||
const bool use_thread = (mr->corners_num + mr->loose_indices_num) > MIN_RANGE_LEN;
|
||||
|
||||
if (use_thread) {
|
||||
/* First run the requested extractors that do not support asynchronous ranges. */
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue