Transform: Implement Snap to Grid mode #116109

Merged
Germano Cavalcante merged 4 commits from mano-wii/blender:snap_to_grid into main 2024-03-27 13:17:37 +01:00
223 changed files with 4795 additions and 2189 deletions
Showing only changes of commit bb01343107 - Show all commits

View File

@ -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())

View File

@ -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

View File

@ -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")

View File

@ -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 */

View File

@ -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;
}

View File

@ -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"); });
})();

View File

@ -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 -%}

View File

@ -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 %}

View File

@ -0,0 +1,6 @@
{%- extends "!page.html" -%}
{%- block footer -%}
{{ super() }}
{%- include "components/footer_contribute.html" -%}
{%- endblock footer -%}

View File

@ -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>

View File

@ -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
extern/README vendored
View File

@ -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

View File

@ -119,7 +119,7 @@ BVHMetal::BVHMetal(const BVHParams &params_,
BVHMetal::~BVHMetal()
{
/* Clear point used by enqueuing. */
/* Clear point used by enqueueing. */
device->release_bvh(this);
if (@available(macos 12.0, *)) {

View File

@ -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) {

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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>

View File

@ -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);
}

View File

@ -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. */

View File

@ -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;

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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")

View File

@ -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')

View File

@ -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"

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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.
*/

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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;
});
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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>;

View File

@ -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).
*/

View File

@ -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

View File

@ -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;

View File

@ -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()});

View File

@ -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 */

View File

@ -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,

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -299,7 +299,7 @@ void DEG_graph_tag_relations_update(Depsgraph *graph)
graph_id_tag_update(deg_graph->bmain,
deg_graph,
&deg_graph->scene->id,
ID_RECALC_BASE_FLAGS,
ID_RECALC_BASE_FLAGS | ID_RECALC_HIERARCHY,
deg::DEG_UPDATE_SOURCE_RELATIONS);
}
}

View File

@ -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

View File

@ -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

View File

@ -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). */

View File

@ -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);

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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;
});

View File

@ -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;

View File

@ -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;

View File

@ -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);
};
/** \} */

View File

@ -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;

View File

@ -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);
};

View File

@ -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:

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
/** \} */

View File

@ -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) {

View File

@ -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)));
}

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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;

View File

@ -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));
}
}
}

View File

@ -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));
}
}
}
}
}
}

View File

@ -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];
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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 {

View File

@ -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);

View File

@ -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;
}
/** \} */

View File

@ -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);

View File

@ -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;

View File

@ -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. */

View File

@ -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,

View File

@ -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)

View File

@ -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",

View File

@ -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

View File

@ -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;
};
/**

View File

@ -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