1
1

Compare commits

...

160 Commits

Author SHA1 Message Date
396fe9a98a Researching bottlenecks. 2022-10-14 14:51:10 +02:00
5c0e90033f Fix texturepainting on AMD/VEGA. 2022-10-14 09:47:57 +02:00
67adfe52c1 Fix some issues where painting on the incorrect layer happened. 2022-10-12 12:58:05 +02:00
a5a4dd2090 Fixed some issues when removing unused tiles. 2022-10-12 12:42:20 +02:00
82d9617dab Paint Tile streaming. 2022-10-11 13:45:19 +02:00
d6c1b15527 Use 16F 2022-10-04 15:42:33 +02:00
813a940dfa Remove falloffshape variations. Making space for masking. 2022-10-04 15:20:32 +02:00
71bb98ca97 Initial blending mode. 2022-10-04 15:04:51 +02:00
633dd0c47c Fix hardness. 2022-10-04 14:23:52 +02:00
6cd0c80d97 Falloff curve. (WIP) 2022-10-04 13:26:44 +02:00
df5e02ec47 Select the correct shader variation for circle falloff. 2022-10-04 11:31:46 +02:00
140d32d362 Add support for projected falloff. 2022-10-04 11:17:20 +02:00
85f96255b2 Improve performance by making buffers persistent. 2022-10-04 10:02:59 +02:00
cf493877c3 Reverted change that disabled the logic. 2022-10-03 15:45:19 +02:00
7a2a2a623d Removed debug code. 2022-10-03 15:04:28 +02:00
78f46baa47 Only draw nodes that are used during in current paint frame. 2022-10-03 14:59:18 +02:00
b0d6318436 Cleanup: sort cmake file lists 2022-10-03 14:59:18 +02:00
999dbaa00c CMake: add missing headers 2022-10-03 14:59:18 +02:00
e1ecfc14c5 Cleanup: add license header 2022-10-03 14:59:18 +02:00
8870dd6429 DNA: reduce size of Base struct
Re-order members and remove pad members,
reducing it's size by 24 bytes on 64 bit systems.
2022-10-03 14:59:18 +02:00
f289a2ef3b Cleanup: remove 2D region-relative coordinates from Base
Historically, caching these values may have had some advantages,
simplifying drawing object centers and selecting by object center.

Now the only uses of these values would calculate the projection
before use, so there is no reason to store run-time projection in DNA.

This also quiets a `-Wstring-overflow` warning.
2022-10-03 14:59:18 +02:00
b8436f9929 Cleanup: spelling in comments
Also replace "dm" for evaluated mesh in some comments.
2022-10-03 14:59:18 +02:00
37e54098e8 Cleanup: use function style casts for C++ 2022-10-03 14:59:18 +02:00
6d19b40218 Cleanup: quiet undeclared function warning 2022-10-03 14:59:18 +02:00
ffa6d18e05 Cleanup: Move three modifier files to C++ 2022-10-03 14:59:18 +02:00
49dc9b7405 Fix: build issue with MSVC
Issue introduced by rB208b3a0472b3
which fixed a build issue on linux.

This fix is a band-aid at best but
reverting rB208b3a0472b3 is not
really an option since that would
break linux again.
2022-10-03 14:59:18 +02:00
7edead1e9c GPUTexture: Add GPU_texture_create_single_layer_view
This allows the creation of texture arrays from 1D/2D/Cube texture.
This is useful when the shader expect a texture array but the original
texture isn't.
2022-10-03 14:59:18 +02:00
728d78d6ba Fix build error on Linux after font thumbnail changes 2022-10-03 14:59:18 +02:00
f7a04c301c Cleanup: Rename function gpencil_point_to_parent_space
gpencil_point_to_parent_space -> gpencil_point_to_world_space

The old name was not clear because it looked it was moving into
the object space, but really is doing the opposite.
2022-10-03 14:59:18 +02:00
97aba2fcfd Cleanup: Rename functions
gpencil_apply_parent->gpencil_world_to_object_space
gpencil_apply_parent_point->gpencil_world_to_object_space_point

The old name was not descriptive enough.
2022-10-03 14:59:18 +02:00
feae12a83a Cleanup: Move more files using mesh runtime data to C++
In preparation for moving mesh runtime data out of DNA.
2022-10-03 14:59:18 +02:00
199c825b44 Fix: Mix node did not set clamp result correctly in versioning code
Files were incorrectly loaded without the Clamp Result option set.

Reported in BA by CarlG
2022-10-03 14:59:18 +02:00
Jason Fielder
7ffa2d83e4 GPU: Fix issue with GPU render boundaries being opened while a GPUBackend does not exist.
Fixes regression introduced by rBe8bcca4bdc94 (D15900)

Ref T96261

Reviewed By: fclem

Maniphest Tasks: T96261

Differential Revision: https://developer.blender.org/D16085
2022-10-03 14:59:18 +02:00
Pratik Borhade
52272f2638 Fix T101504: Crash when repeat value is zero
Crash triggers due to division by zero
fix is to increase minimum limit for `seg_repeat` to 1

Reviewed By: antoniov, frogstomp

Maniphest Tasks: T101504

Differential Revision: https://developer.blender.org/D16120
2022-10-03 14:59:18 +02:00
60f00eda48 Cleanup: simplify uv packing for non-square materials 2022-10-03 14:59:18 +02:00
ef189d8790 Sculpt: Fix T101467: Improper vertex count in pbvh draw 2022-10-03 14:59:18 +02:00
e675684f47 Sculpt: Fix sculpt face set undo creating duplicate face set layers 2022-10-03 14:59:18 +02:00
0b57273dfe Sculpt: fix crash in relax face sets brush 2022-10-03 14:59:18 +02:00
1adbedb4d6 Sculpt: Fix T101430: Dyntopo undo crash 2022-10-03 14:59:18 +02:00
24e689e379 Sculpt: Upload white for color attributes in multires in sculpt
Even if multires in sculpt mode doesn't yet support color
attributes, we should at least upload white color to avoid
making everything black.

Also fixed a bug where multires PBVHs didn't have access to
their CustomData attribute layout, which PBVH draw needs.
2022-10-03 14:59:18 +02:00
aedde76ec3 Sculpt: Fix T101503: Memory corruption in multires faceset drawing 2022-10-03 14:59:18 +02:00
3346af48af Sculpt: Fix T101430: Curve shown improperly in mask from cavity redo 2022-10-03 14:59:18 +02:00
3cc6fa2019 Attribute Node: refactor lookup to remove duplication between engines.
Currently lookup of Object and Instancer attributes is completely
duplicated between Cycles, Eevee and Eevee Next. This is bad design,
so this patch aims to deduplicate it by introducing a common API
in blenkernel.

In case of Cycles this requires certain hacks, but according to
Brecht it is planned to be rewritten later for more direct access
to internal Blender data anyway.

Differential Revision: https://developer.blender.org/D16117
2022-10-03 14:59:18 +02:00
a31d529368 Cleanup: Move draw_manager_data.cc to C++
This will help with moving mesh runtime data to C++
2022-10-03 14:59:18 +02:00
57e8423ce7 Fix missing Outliner updates when adding nodetrees
When e.g. grouping nodes into nodegroups, these would not show up
immediately in the Outliner (Blender File / Data API view).

Now send (unique combination, not used elsewhere) notifiers (and listen
for these in the Outliner).

Differential Revision: https://developer.blender.org/D16093
2022-10-03 14:59:18 +02:00
8ec9ed0081 Fix T101347: Curve draw fails to project to cursor depth in ortho views
ED_view3d_win_to_3d_on_plane with do_clip enabled wasn't working in
non-camera orthographic views as it didn't take into account the ray
origin being centered to the view.

Resolve by testing viewport clipping after the ray has been projected.
2022-10-03 14:59:18 +02:00
cc45fa2903 Cleanup: format 2022-10-03 14:59:17 +02:00
63b118a7e3 Cleanup: quiet unused parameter warning in lambda function
Copy-paste error in rB039429faeb41.
2022-10-03 14:59:17 +02:00
839edefaba Fix error in blf_get_sample_text returning a pointer to stack memory
Copy-by-value was used when iterating over unicode_samples which
then referenced an array from the value.

Resolve by referencing a const pointer to the unicode_sample array.
2022-10-03 14:59:17 +02:00
25c593f5d1 Cleanup: use function style casts for C++ 2022-10-03 14:59:17 +02:00
be6456d096 Cleanup: Move files that use mesh runtime data to C++
In preparation for moving the mesh runtime struct out of DNA.
2022-10-03 14:59:17 +02:00
66f0644efa Cleanup: Move object_update.c to C++ 2022-10-03 14:59:17 +02:00
f3db9c99dd Cleanup: Move modifier.c to C++ 2022-10-03 14:59:17 +02:00
e24ce39c8b Fix T101118, T101471: Crash with bevel weight in solidify modifier
Just a missing null check for the original edge weight.
2022-10-03 14:59:17 +02:00
6a01db01c2 Sculpt: fix stroke-only attributes not being freed at stroke end 2022-10-03 14:59:17 +02:00
cb6a8318d1 Sculpt: fix T101465, crash in cloth filter with new automasking modes 2022-10-03 14:59:17 +02:00
9a5060df0f Sculpt: Fix T101464: Crash in mask from cavity 2022-10-03 14:59:17 +02:00
a7cdb679fa Sculpt: Fix T101463: Don't initialize automask to zero in
factor mode if topology or boundary modes are inactive

Factors mode (precomputing the automask) should initialize the
mask to 1.0 if no additive automasking modes are enabled, instead
of zero.
2022-10-03 14:59:17 +02:00
cdd829fc3a Cleanup: Naming in edge vertices node
- Use `enum class`
- Use shorter names for field input classes for better line wrapping
- Use "Vert" instead of "Vertex"
2022-10-03 14:59:17 +02:00
03144a1fac Fix: Order of node mixins in custom nodes python template
See T101259. This order makes the poll not work, even when called from
Python. The bundled template shouldn't be a source of errors for node
addons.
2022-10-03 14:59:17 +02:00
c44e143739 Fix: Viewer node overlay alpha broken on NVidia GPUs
On NVidia GPUs, when the blue channel was between 0 and 0.1,
the overlay's alpha would increase, making it invisible.

With the `overlay_line_fb` frame buffer bound in `overlay_engine.cc`
there are two outputs to write to: the color and the line output, which
is used for making smooth antialiased lines. The overlay is in its
current position in the order of overlays so that it draws on top of
curve wire lines. Not writing to that second output is undefined
behavior, so fix it by writing zeros. In the future, the overlay
could be smoothed on curves using that second texture.

Thanks to Clément for the explanation of the issue!
2022-10-03 14:59:17 +02:00
ef205bb1f6 Cleanup: Simplify code and remove duplications
This commit simplify the previous fix for T101455

f6c2f1c65e
2022-10-03 14:59:17 +02:00
dd0d4266b0 GPencil: Remove Fill use_collide_only option
After a lot of testing, this option is not required and
now this is managed by stroke_collsion.

If the stroke_collision is enabled, only collide strokes 
are used.
2022-10-03 14:59:17 +02:00
ad319c5056 GPencil: Fix missing Fill stroke cross detection
If the cross point was in the extreme of the stroke the
collision was not detected because it could be
outside of the bbox.

Removed the bbox check because now it is
not necessary.
2022-10-03 14:59:17 +02:00
c0d4ac1ec6 Fix T101455: GPencil Gizmo in wrong location when using Active Element
The gizmo was not set when this option was selected.

By design, in grease pencil the active element option uses
the object origin as pivot point.
2022-10-03 14:59:17 +02:00
66853162d5 Fix: Spline Parameter node produces NaN when curve is a single point
Issue found in file from T101256.
2022-10-03 14:59:17 +02:00
c1c66c7b57 Nodes: fix missing update when input of muted node changes 2022-10-03 14:59:17 +02:00
cc9f5906c7 Viewport: Remove unnecassary assert 2022-10-03 14:59:17 +02:00
3d9ae3c0e2 Fix T101410: ignore dangling reroute inputs
For consistency with other node systems in Blender and older
versions of geometry nodes, dangling reroute inputs should
not affect the output. When an input socket is linked to dangling
reroutes, its own value should be used instead.
2022-10-03 14:59:17 +02:00
93c6dc52ff Nodes: fix missing updates with muted nodes and reroutes
Muted nodes and reroutes can potentially affect the output when
they are linked to an input socket and don't have any inputs on
their own.

The issues was that previously "logically linked sockets" where used
which hide reroutes and muted nodes away. The solution is to work
with the directly linked sockets instead and handle reroutes etc
explicitly.
2022-10-03 14:59:17 +02:00
fa75487b9c Sculpt: Reset automask cache on non-color non-mask undo steps 2022-10-03 14:59:17 +02:00
bfd8064f00 UI: Nodes typo in "distribute points in volume" node
Distribute Points In Volume →  Distribute Points in Volume.
2022-10-03 14:59:17 +02:00
b9a6007592 UI: Nodes re-organize the nodes in alphabetical order
This was a regression introduced after the 3.3 release.

This fix T101452
2022-10-03 14:59:17 +02:00
173a41c7da Fix T101451: curve attribute overlay disappears when origin is out of view
The core issue seems to be that `BKE_curve_minmax` does not create the
correct bounding box for `Curve` data passed to the render engine.
That's because this `Curve` object does not contain the legacy curve
data structure.

Fixing this will likely require some more consideration, so this fix just
avoids the culling check for now, which is actually also done in
`OVERLAY_extra_wire` using the same approach.
2022-10-03 14:59:17 +02:00
31317ea4f9 Sculpt: fix draw face sets not updating on first stroke 2022-10-03 14:59:17 +02:00
Andrew Oates
363fe35360 Fix T94441: fix crash parenting object to a bone
This crash occurs when the bone is newly created.  In certain
circumstances the depsgraph data for the armature is not updated,
causing `pchan_eval` to be NULL when the parent is updated.  This causes
a segfault in `ED_object_parent_set` when the flags are updated.

This change fixes the underlying depsgraph bug, and also adds both an
assertion and NULL pointer check to `ED_object_parent_set` to better
handle this scenario if it recurs via another path.

Maniphest Tasks: T94441

Differential Revision: https://developer.blender.org/D16065
2022-10-03 14:59:17 +02:00
97cb2b56bd Sculpt: fix missing nullptr check in pbvh draw
This time it was face sets.
2022-10-03 14:59:17 +02:00
0c5dd4a3c7 Cleanup: Address format-security warning 2022-10-03 14:59:17 +02:00
2cb76bc005 Cleanup: Set but unused variable 2022-10-03 14:59:17 +02:00
0c25584717 Cleanup; Warning about missing override for virtual function 2022-10-03 14:59:17 +02:00
6eed9f1490 Cleanup: quiet warnings (unused arg & trailing fullstop) 2022-10-03 14:59:17 +02:00
ce2d235952 Fix bpy.utils.script_paths() ignoring environment variables
When check_all=True was passed,

- `os.path.join(bpy.utils.resource_path('USER'), "scripts")`
  was used instead of BLENDER_USER_SCRIPTS.
- `os.path.join(bpy.utils.resource_path('SYSTEM'), "scripts")`
  was used instead of BLENDER_SYSTEM_SCRIPTS.

Other minor changes:

- Simplify collecting paths.
- Don't add user-directories multiple times when check_all=True.
- Normalize paths before before checking duplicates to reduce the
  change the same path is added multiple times.

Found these issues while investigating T101389.
2022-10-03 14:59:17 +02:00
56f7b7a9c5 Cleanup: correct API usage for script access
In practice this didn't cause problems, but accessing scripts via
bpy.utils.resource_path('USER') ignores the BLENDER_USER_SCRIPTS
environment variable.
2022-10-03 14:59:17 +02:00
78349c2578 Cleanup: run clang-format in sculpt code 2022-10-03 14:59:17 +02:00
5d1a0167e1 Cleanup: Use c++-style unused paramter form in automasking code.
Also renamed shadowed variable in sculpt_filter_mesh.c
2022-10-03 14:59:17 +02:00
3fa3d6c7aa Sculpt: fix missing nullptr check in pbvh draw
Check for nullptr when building mask vbos.
2022-10-03 14:59:17 +02:00
4b3c2017ef Sculpt: Normal-based automasking modes
Two new normal-based automasking modes.

The first mode, "brush", compares vertex normals with the initial
normal at the beginning of the brush stroke.

The second, "view", compares vertex normals with the view normal.
If "occlusion" is on then rays will be shot from each vertex to test
if it is occluded by other geometry (note: this can be very slow).\
Only geometry inside the sculpt mesh is considered.

Each mode has an associated angular limit and a falloff.

Reviewed by: Julien Kaspar and Jeroen Bakker
Differential Revision: https://developer.blender.org/D15297
Ref D15297
2022-10-03 14:59:17 +02:00
422674f247 Cleanup: Remove more unused CustomData API functions
Due to increased usage of typed arrays in C++ and name/offset based
access for BMesh, these are unlikely to be used again, and haven't been
used in many years.
2022-10-03 14:59:17 +02:00
88339e26ed Cleanup: Deduplicate logic in CustomData access functions
Also remove comments that just restated what the next line
was doing, often incorrectly.
2022-10-03 14:59:17 +02:00
e01022e5d2 Cleanup: Remove unused BMesh function 2022-10-03 14:59:17 +02:00
a36bfb3bbc Cleanup: cmake indentation 2022-10-03 14:59:17 +02:00
b413460096 Cleanup: remove '.' from the end of descriptions
Quiet warnings at startup & build time.
2022-10-03 14:59:17 +02:00
8c16dd9e6c Cleanup: simplify storage when uv island packing
Refactor ahead of upcoming packing changes.

Differential Revision: https://developer.blender.org/D16096
2022-10-03 14:59:17 +02:00
0badbc3dbd Fix wrong version bump to 3.4.1, we are still on 3.4.0
The patch levels are reserved for bugfix releases.
2022-10-03 14:59:17 +02:00
e061a1f175 Cleanup: use doxy-sections in anim_channels_edit.c 2022-10-03 14:59:17 +02:00
abd5f0a77f Cleanup: spelling in comments 2022-10-03 14:59:17 +02:00
0b410eaf63 Cleanup: quiet deprecated copy warning 2022-10-03 14:59:17 +02:00
6d80e24b79 Cleanup: replace UNUSED() with commented arguments
This is the conventional way of dealing with unused arguments in C++.

Also quiet enum conversion warnings.
2022-10-03 14:59:16 +02:00
3ade192420 Cleanup: format 2022-10-03 14:59:16 +02:00
88610fe5f6 Cleanup: fix unused variable warnings in draw_pbvh.cc 2022-10-03 14:59:16 +02:00
fa100b14af Sculpt: New Cavity Automasking Mode
Add new cavity automasking mode based on local mesh
curvature.  Cavity masking is a great way to quickly add
detail in crevices and the like.  It's meant to be used
with the Paint brush in color attribute mode.  It does
work with other brushes but the results can be unpredictable.

{F13131497}

The old "dirty mask" operator has been replace with a new
"mask from cavity" operator that shares the same code with
cavity automasking.

Differences from the sculpt-dev implementation:
    * It uses the word "cavity."  When I first implemented
this I wasn't aware
      this feature existed in other software (and other
paint modes in Blender),
      and for reasons that escape me today I initially
decided to call it a concave or
      concavity mask.
    * The cavity factor works a bit differently.  It's
      no longer non-linear and functions as a simple
      scale around 0.5f.
    * Supports custom curves.
    * Supports blurring.

Reviewed By: Julian Kaspar, Jeroen Bakker and Campbell Barton
Differential Revision: https://developer.blender.org/D15122
Ref D15122
2022-10-03 14:59:16 +02:00
66c190277c Cleanup: remove duplicate declaration
Also got rid of ATTR_NO_OPT macro.
2022-10-03 14:59:16 +02:00
e13205e719 Sculpt: Rewrite PBVH draw
Rewrite PBVH draw to allocate attributes into individual VBOs.
The old system tried to create a single VBO that could feed
every open viewport.  This required uploading every color and
UV attribute  to the viewport whether needed or not, often exceeding
the VBO limit.

This new system creates one VBO per attribute.  Each attribute layout is
given its own GPU batch which is cached inside the owning PBVH node.

Notes:

* This is a full C++ rewrite.  The old code is still there; ripping it out
can happen later.
* PBVH nodes now have a collection of batches, PBVHBatches, that keeps
track of all the batches inside the node.
* Batches are built exclusively from a list of attributes.
* Each attribute has its own VBO.
* Overlays, workbench and EEVEE can all have different attribute
  layouts, each of which will get its own batch.

Reviewed by: Clement Foucault
Differential Revision: https://developer.blender.org/D15428
Ref D15428
2022-10-03 14:59:16 +02:00
0a9fc05eb3 Cleanup: Typo in forward declaration 2022-10-03 14:59:16 +02:00
4e850141d8 Geometry Nodes: Curve and mesh topology access nodes
This patch contains an initial set of nodes to access basic
mesh topology information, as explored in T100020.

The nodes allow six direct topology mappings for meshes:
- **Corner -> Face** The face a corner is in, the index in the face
- **Vertex -> Edge** Choose an edge attached to the vertex
- **Vertex -> Corner** Choose a corner attached to the vertex
- **Corner -> Edge** The next and previous edge at each face corner
- **Corner -> Vertex** The vertex associated with a corner
- **Corner -> Corner** Offset a corner index within a face

And two new topology mappings for curves:
- **Curve -> Points** Choose a point within a curve
- **Point -> Curve** The curve a point is in, the index in the curve

The idea is that some of the 16 possible mesh mappings are more
important, and that this is a useful set of nodes to start exploring
this area. For mappings with an arbitrary number of connections, we
must sort them and use an index to choose a single element, because
geometry nodes does not support list fields. Note that the sort
index has repeating behavior as it goes over the "Total" number of
connections, and negative sort indices choose from the end.

Currently which of the "start" elements is used is determined by the
field context, so the "Field at Index" and "Interpolate Domain" nodes
will be quite important. Also, currently the "Sort Index" inputs are
clamped to the number of connections.

One important feature that isn't implemented here is using the winding
order for the output elements. This can be a separate mode for some
of these nodes. It will be optional because of the performance impact.

There are several todos for separate commits after this:
- Rename "Control Point Neighbors" to be consistent with this naming
- Version away the "Vertex Neighbors" node which is fully redundant now
- Implement a special case for when no weights are used for performance
- De-duplicating some of the sorting logic between the nodes
- Improve performance and memory use of topology mappings
- Look into caching some of the mappings on meshes

Differential Revision: https://developer.blender.org/D16029
2022-10-03 14:59:16 +02:00
a84f8810e4 Mesh: Add C++ implementaiton of topology mappings
Because they are friendlier to use in C++ code than the existing mesh
mapping API, these mappings from one domain to another were often
reimplemented in separate files. This commit moves some basic
implementations to a `mesh_topology` namespace in the existing
mesh mapping header file. These is plenty of room for performance
improvement here, particularly by not using an array of Vectors, but
that can come later.

Split from D16029
2022-10-03 14:59:16 +02:00
25bba92f6e GPencil: Avoid infinite loop in Fill debug mode
If the internal flag is set to debug and the Ctrl key is used
the program keeps running endless.
2022-10-03 14:59:16 +02:00
2a4b18c730 GPencil: Disable Fill visual aids if use Inverted
When the inverted mode is used, the visual aids must
not be displayed.
2022-10-03 14:59:16 +02:00
16b5f822c2 GPencil: Extend Fill lines when visual aids are disabled
When the visual aids are disabled, but the extend factor
is > 0, the lines must be extended, but not displayed.

Also, some variables renamed to clarify.
2022-10-03 14:59:16 +02:00
c51c381b7d GPencil: Change property text for Fill Visual Aids 2022-10-03 14:59:16 +02:00
a9da308ad9 GPencil: Fix unreported memory leak in Fill inverse
There was a memory leak when use inverted fill.
2022-10-03 14:59:16 +02:00
882ab97a04 Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.

**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
  makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
  is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
  its active or not.

**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
  When pinned, the spreadsheet still references the viewer node even
  when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
  the active viewer.

**Attribute**
* When a field is linked to the second input of the viewer node it is
  displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
  automatically. This does not work in all cases. It falls back to the
  face corner domain on meshes and the point domain on curves. When
  necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
  that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.

**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
  setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
  by disabling the "Viewer Node" option in the "View" menu.

**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
  is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
  field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
  attribute.
* The ground truth for the active viewer node is stored in the workspace
  now. Node editors, spreadsheets and viewports retrieve the active
  viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
  the viewed geometry of the corresponding object is part of the iterator
  instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
  to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
  existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
  "preferred domain" for every field input. If there is not exactly one
  preferred domain, the fallback is used.

Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
  added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
  the values might have to be normalized or some should be drawn as arrays.
  For now, we encourage users to build node groups that generate appropriate
  viewer-geometry. We might include some of that functionality in future versions.
  Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
  nvidia gpus, to be investigated.

Differential Revision: https://developer.blender.org/D15954
2022-10-03 14:59:16 +02:00
3b7b0c3aae Cleanup: Rename variables 2022-10-03 14:59:16 +02:00
7fe5373ae3 GPencil: Fix unreported bug for fill closing strokes
The extend lines were included in render by error
when the only collide option was ON.
2022-10-03 14:59:16 +02:00
483eadd401 MSVC: promote C4033 to an error
C4033 'function' must return a value

is a MSVC level-1 warning, clang and GCC
treat this as an error however, this
change promotes it to an error as well
for MSVC to mimic the GCC behaviour.
2022-10-03 14:59:16 +02:00
a876ac0327 GPencil: Fix compiler warning 2022-10-03 14:59:16 +02:00
714e9749fb Fix compiler warnings with clang
This attribute only works on functions in clang, not function pointers.
2022-10-03 14:59:16 +02:00
29e856bd3f GPencil: Fill Tool - Check if extensions collide with real strokes
This commit is an improvement in the previous fill tool
changes in order to improve how the extended strokes
are managed.

* Now, the algorithm checks if the extend cross a standard stroke, not only extend strokes.

* Option to enable or disable the stroke cross checking
because this can be slow in very complex scenes.

* Added `D` key to toggle stroke cross option.

* Option to use only collide strokes to be used as fill limit.

If the option to use only collide strokes is enabled, the
open extensions are in different color.

* Status text now shows mode and the actual extend factor.

This commits also contains a refactor of the loops
to use arrays as much as possible.

Reviewed By: mendio, pepeland

Differential Revision: https://developer.blender.org/D16052
2022-10-03 14:59:16 +02:00
77c9a5e26f Fix macOS cmake warning about missing OpenAL, after Python module changes 2022-10-03 14:59:16 +02:00
76a3e8e235 Bump submodule versions 2022-10-03 14:59:16 +02:00
ef223fe721 Cleanup: decentralize .blend I/O for space types
This adds callbacks to `SpaceType` to make each editor responsible to
manage their own .blend I/O, and moves relevant code from `screen.c`
to the editors files.

Differential Revision: D11069
2022-10-03 14:59:16 +02:00
6b77d21467 Fix GC tracking error for instances of mathutils types
Mathutils types were always GC tracked even when it wasn't intended.
Not having to track objects speeds up Python execution.

In an isolated benchmark created to stress test the GC
creating 4-million vectors (re-assigning them 100 times), this gives
an overall ~2.5x speedup, see: P3221.

Details:

Since [0] (which added support for sub-classed mathutils types)
tp_alloc was called which defaults to PyType_GenericAlloc which always
GC tracked the resulting object when Py_TPFLAGS_HAVE_GC was set.

Avoid using PyType_GenericAlloc unless the type is sub-classed,
in that case the object is un-tracked.

Add asserts that the tracked state is as expected before tracking &
un-tracking, to ensure changes to object creation don't cause objects
to be tracked unintentionally.

Also assign the PyTypeObject.tp_is_gc callback so types optionally GC
track objects only do so when an object is referenced.

[0]: fbd9364944
2022-10-03 14:59:16 +02:00
12cb1049ea Fix T101341: make nodegroups active input/output non-animatable
Active UI list index is usually not animatable.
Here specifically, the active list index is oly used for operators acting
on a specific (active) socket.

Note other props here were already made non-animatable in
rB1d3b92bdeabc.

Maniphest Tasks: T101341

Differential Revision: https://developer.blender.org/D16077
2022-10-03 14:59:16 +02:00
4469e6a09d Fix Python module test failing with macOS + address sanitizer
Based on patch by Ankit Meel.

Ref D10877
2022-10-03 14:59:16 +02:00
d1b8c27586 Fix T101414: in 3d viewport, smart uv project failed to pack
Regression from https://developer.blender.org/rBa5c696a0c2b9
2022-10-03 14:59:16 +02:00
e04e712b3d Cleanup: spelling in comments
Also add missing task ID.
2022-10-03 14:59:16 +02:00
cf317dc047 Cleanup: format 2022-10-03 14:59:16 +02:00
bfbf1c6151 Cleanup: compiler warnings 2022-10-03 14:59:16 +02:00
8bf945d57d Correct sign conversion errors in convexhull_2d.c 2022-10-03 14:59:16 +02:00
031027ae6c Cleanup: improve 2D convex hull
Improve correctness, API, comments, memory usage and performance
of the 2D convex hull calculation.

Pre-requisite for UV packing improvements.

Differential Revision: https://developer.blender.org/D16055
2022-10-03 14:59:16 +02:00
ec66bdd90d Cycles: Add optional per-kernel performance statistics
When verbose level 4 is enabled, Blender prints kernel performance
data for Cycles on GPU backends (except Metal that doesn't use
debug_enqueue_* methods) for groups of kernels.
These changes introduce a new CYCLES_DEBUG_PER_KERNEL_PERFORMANCE
environment variable to allow getting timings for each kernels
separately and not grouped with others. This is done by adding
explicit synchronization after each kernel execution.

Differential Revision: https://developer.blender.org/D15971
2022-10-03 14:59:16 +02:00
86910815fd Cleanup: Unused variable warning with path guiding turned off 2022-10-03 14:59:16 +02:00
b55647e1ed Fix T101348: Sculpt smooth brush artifacts with hidden faces
The brush mixed up the vert and poly hide layers.
2022-10-03 14:59:16 +02:00
382915d241 Fix: Assert in legacy mesh conversion
When setting the old face set custom data type, do it in the
"layers to write" vector instead of the mesh's poly custom
data layers, which weren't "prepared" yet. Otherwise this
could make the mesh's custom data typemap invalid.
2022-10-03 14:59:16 +02:00
7ab2b860c9 Cleanup: Use signed integers for mesh vertex indices 2022-10-03 14:59:16 +02:00
2301a4a42f Cleanup: Use variable and const for sculpt mesh vertex to poly maps 2022-10-03 14:59:16 +02:00
Pablo Vazquez
28be8b4aed WM: Support opening backup .blend files via drag-drop
Add support for opening Blender backup `.blend` files (`.blend1`, `.blend2`, etc) by dropping them into the window, just like regular .blend files.

{F13393482, size=full}

Reviewed By: Severin

Differential Revision: https://developer.blender.org/D15700
2022-10-03 14:59:15 +02:00
d79db0e4a9 Fix: OpenPGL related build error on windows
Debug and Release libs are different libs on
Windows and will give linker errors when you
try to mix and match them.

This changes retrieves both libs and fills the
OPENPGL_LIBRARIES variable appropriately resolving
the linker error.
2022-10-03 14:59:15 +02:00
Michael Jones
e040b3f38d Cycles: Tweak inlining policy on Metal
This patch optimises the Metal inlining policy. It gives a small speedup (2-3% on M1 Max) with no notable compilation slowdown vs what is already in master. Previously noted compilation slowdowns (as reported in T100102) were caused by forcing inlining for `ccl_device`, but we get better rendering perf by relying on compiler heuristics in these cases.

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D16081
2022-10-03 14:59:15 +02:00
7214522e7e Cycles: Disable binary archives on macOS < 13.0
An bug with binary archives was fixed in macOS 13.0 which stops some spurious kernel recompilations. In older macOS versions, falling back on the system shader cache will prevent recompilations in most instances (this is the same behaviour as in Blender 3.1.x and 3.2.x).

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D16082
2022-10-03 14:59:15 +02:00
Aleš Jelovčan
0529570a0a Gpencil: Time Offset modifier new Chain mode
This patch adds 5th mode to Time offset modifier, which should allow 
to create time segments list.

This will allow users to chain together multiple time ranges in 4 modes:

- Forward
- Backwards
- Pingpong
- Reverse  Pingpong

It also comes with additional Repeat parameter which specifies number 
of times particular segment should run.

The mechanic of it is transforming initial parameters into array of frames which 
are mapped to existing cfra (current frame) value.
Prototype : https://jsfiddle.net/ha2sjw8p/3/

This is also closely aligned to community request: 
https://blender.community/c/rightclickselect/Txhbbc/

This should allow creation of complex animations like dancing, 
which consists of repeating loops and transitions to the next.
One important side effect of this is dramatically reduced 
file sizes, as user no longer needs to copy paste keyframes.

Reviewed By: antoniov, mendio, pepeland

Differential Revision: https://developer.blender.org/D15052
2022-10-03 14:59:15 +02:00
3e0c3e01e4 BLF: Editing Text with Combining Characters
Corrections for caret insertion & movement and deletion for text
strings that include non-precomposed diacritical marks (Unicode
combining characters).

See D15659 for more details and examples.

Differential Revision: https://developer.blender.org/D15659

Reviewed by Campbell Barton
2022-10-03 14:59:15 +02:00
88b86516f1 View3D: move view3d_draw.c to c++ 2022-10-03 14:59:15 +02:00
e9ee3074d0 GPencil: New Outline modifier
This modifier converts any stroke (no fill strokes) into perimeter
from camera view.  Also, it's possible to define an alternative 
material for the outline.

There is an option to include a target object to manipulate the start 
point of the strokes. The start point will be the nearest point 
to the target object.

Reviewed By: mendio, frogstomp

Maniphest Tasks: T100826

Differential Revision: https://developer.blender.org/D15882

Note: Icon will be updated in T101155
2022-10-03 14:59:15 +02:00
Sebastian Herhoz
54e9d6876f Cycles: add Path Guiding on CPU through Intel OpenPGL
This adds path guiding features into Cycles by integrating Intel's Open Path
Guiding Library. It can be enabled in the Sampling > Path Guiding panel in the
render properties.

This feature helps reduce noise in scenes where finding a path to light is
difficult for regular path tracing.

The current implementation supports guiding directional sampling decisions on
surfaces, when the material contains a least one diffuse component, and in
volumes with isotropic and anisotropic Henyey-Greenstein phase functions.

On surfaces, the guided sampling decision is proportional to the product of
the incident radiance and the normal-oriented cosine lobe and in volumes it
is proportional to the product of the incident radiance and the phase function.

The incident radiance field of a scene is learned and updated during rendering
after each per-frame rendering iteration/progression.

At the moment, path guiding is only supported by the CPU backend. Support for
GPU backends will be added in future versions of OpenPGL.

Ref T92571

Differential Revision: https://developer.blender.org/D15286
2022-10-03 14:59:15 +02:00
7a7100272f Cycles: BSDF eval refactor to remove separate reflection/refraction methods
Simplifies code overall to do it inside the eval function, most of the BSDFs
already compute the dot product.

The refactoring in bsdf_principled_hair_eval() was needed to avoid a HIP
compiler bug. Cause is unclear, just changing the implementation enough
is meant to sidestep it.

Ref T92571, D15286
2022-10-03 14:59:15 +02:00
Sebastian Herhoz
c4dd826111 Cycles: BSDF changes in preparation of path guiding
* Return roughness and IOR for BSDF sampling
* Add functions to query IOR and label for given BSDF
* Default IOR to 1.0 instead of 0.0 for BSDFs that don't use it
* Ensure pdf >= 0.0 in case of numerical precision issues

Ref T92571, D15286
2022-10-03 14:59:15 +02:00
Germano Cavalcante
687fb41a27 Fix T101231: Console flooded with warnings when fluid type is Domain
Although rB67e23b4b2967 turned the problem more recurrent, the warning
messages in the console always appear when `BKE_fluid_cache_free_all`
is called.

This is because of a bug in `BLI_filelist_dir_contents` as this function
calls `BLI_strdupcat` instead of `BLI_join_dirfile`

NOTE: Other places in Blender avoid this problem by making sure to add
a `SEP_STR` to the end of the directory.

Differential Revision: https://developer.blender.org/D16043
2022-10-03 14:59:15 +02:00
8bb084cda3 Deps: fix builing wayland-scanner
- fix the source package download URL
- patch the `meson.build` file to allow the CentOS 7 meson version to be
  used
- only build what we need (`wayland-scanner`), and not the libraries,
  documentation, and tests.

Ref: D16074

This also reverts commit f6664217b3
"Build: temporarily disable wayland dependency".
2022-10-03 14:59:15 +02:00
47c5d16933 Fix T101398: Transfer Attribute node still defined in menu
Removing this item was missed in 837144b457.
2022-10-03 14:59:15 +02:00
a33f05a93d Fix T101393: Vertex Crease operator does not create vertex crease layer
Typo in rBa8a454287a27 (created layer on edge data instead of vertex
data).

Maniphest Tasks: T101393

Differential Revision: https://developer.blender.org/D16079
2022-10-03 14:59:15 +02:00
b6e80a67c9 Build: temporarily disable wayland dependency
This caused problems on centos7, see D16074 for details.
2022-10-03 14:59:15 +02:00
227d8f4651 Fixed crash when painting on not all PBVH nodes. 2022-09-30 16:06:00 +02:00
cb826f3529 Change merge shader to not fade. 2022-09-30 15:50:11 +02:00
5da71dd437 Use batches drawing. 2022-09-30 15:42:44 +02:00
4ce8b5ca01 Fix incorrect barycentric coords. 2022-09-30 08:24:51 +02:00
f554980580 First working brush stroke. 2022-09-28 16:54:56 +02:00
e13b1bb524 Merge paint image with canvas. 2022-09-28 14:11:54 +02:00
854d3d4645 Painting first pixels to intermediate buffer. 2022-09-28 13:04:18 +02:00
26dc50ac34 SculptPaint: Use GPU shaders.
This is WIP/PoC patch to check how we could increase code reusability between
CPU and GPU versions.

Differential Revision: https://developer.blender.org/D16083
2022-09-27 15:35:53 +02:00
489 changed files with 18107 additions and 7610 deletions

View File

@@ -429,6 +429,7 @@ mark_as_advanced(WITH_CPU_SIMD)
# Cycles
option(WITH_CYCLES "Enable Cycles Render Engine" ON)
option(WITH_CYCLES_OSL "Build Cycles with OpenShadingLanguage support" ON)
option(WITH_CYCLES_PATH_GUIDING "Build Cycles with path guiding support" ON)
option(WITH_CYCLES_EMBREE "Build Cycles with Embree support" ON)
option(WITH_CYCLES_LOGGING "Build Cycles with logging support" ON)
option(WITH_CYCLES_DEBUG "Build Cycles with options useful for debugging (e.g., MIS)" OFF)
@@ -647,8 +648,8 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
unset(_asan_defaults)
if(MSVC)
find_library(
COMPILER_ASAN_LIBRARY NAMES clang_rt.asan-x86_64
find_library(
COMPILER_ASAN_LIBRARY NAMES clang_rt.asan-x86_64
PATHS
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\LLVM\\LLVM;]/lib/clang/7.0.0/lib/windows
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\LLVM\\LLVM;]/lib/clang/6.0.0/lib/windows
@@ -827,7 +828,7 @@ endif()
set_and_warn_dependency(WITH_PUGIXML WITH_OPENIMAGEIO OFF)
if(WITH_BOOST AND NOT (WITH_CYCLES OR WITH_OPENIMAGEIO OR WITH_INTERNATIONAL OR
WITH_OPENVDB OR WITH_OPENCOLORIO OR WITH_USD OR WITH_ALEMBIC))
WITH_OPENVDB OR WITH_OPENCOLORIO OR WITH_USD OR WITH_ALEMBIC))
message(STATUS "No dependencies need 'WITH_BOOST' forcing WITH_BOOST=OFF")
set(WITH_BOOST OFF)
endif()
@@ -1631,6 +1632,7 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "MSVC")
"/we4013" # 'function' undefined; assuming extern returning int
"/we4133" # incompatible pointer types
"/we4431" # missing type specifier - int assumed
"/we4033" # 'function' must return a value
)
string(REPLACE ";" " " _WARNINGS "${_WARNINGS}")

View File

@@ -68,7 +68,7 @@ set(DPCPP_EXTRA_ARGS
)
if(WIN32)
list(APPEND DPCPP_EXTRA_ARGS -DPython3_FIND_REGISTRY=NEVER)
list(APPEND DPCPP_EXTRA_ARGS -DPython3_FIND_REGISTRY=NEVER)
endif()
ExternalProject_Add(external_dpcpp

View File

@@ -460,7 +460,7 @@ set(WL_PROTOCOLS_HASH_TYPE MD5)
set(WAYLAND_VERSION 1.21.0)
set(WAYLAND_FILE wayland-${WAYLAND_VERSION}.tar.xz)
set(WAYLAND_URI https://wayland.freedesktop.org/releases/wayland-${WAYLAND_VERSION}.tar.xz)
set(WAYLAND_URI https://gitlab.freedesktop.org/wayland/wayland/-/releases/1.21.0/downloads/wayland-${WAYLAND_VERSION}.tar.xz)
set(WAYLAND_HASH f2653a2293bcd882d756c6a83d278903)
set(WAYLAND_HASH_TYPE MD5)

View File

@@ -5,9 +5,10 @@ ExternalProject_Add(external_wayland
DOWNLOAD_DIR ${DOWNLOAD_DIR}
URL_HASH ${WAYLAND_HASH_TYPE}=${WAYLAND_HASH}
PREFIX ${BUILD_DIR}/wayland
PATCH_COMMAND ${PATCH_CMD} -d ${BUILD_DIR}/wayland/src/external_wayland < ${PATCH_DIR}/wayland.diff
# Use `-E` so the `PKG_CONFIG_PATH` can be defined to link against our own LIBEXPAT.
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env PKG_CONFIG_PATH=${LIBDIR}/expat/lib/pkgconfig
meson --prefix ${LIBDIR}/wayland . ../external_wayland
meson --prefix ${LIBDIR}/wayland -Ddocumentation=false -Dtests=false -Dlibraries=false . ../external_wayland
BUILD_COMMAND ninja
INSTALL_COMMAND ninja install
)

View File

@@ -0,0 +1,11 @@
--- meson.build.orig 2022-06-30 22:59:11.000000000 +0100
+++ meson.build 2022-09-27 13:21:26.428517668 +0100
@@ -2,7 +2,7 @@
'wayland', 'c',
version: '1.21.0',
license: 'MIT',
- meson_version: '>= 0.56.0',
+ meson_version: '>= 0.55.1',
default_options: [
'warning_level=2',
'buildtype=debugoptimized',

View File

@@ -44,7 +44,7 @@ FIND_PROGRAM(SYCL_COMPILER
# compiler.
if(NOT SYCL_COMPILER)
FIND_PROGRAM(SYCL_COMPILER
NAMES
NAMES
dpcpp
HINTS
${_sycl_search_dirs}

View File

@@ -17,6 +17,7 @@ set(WITH_COMPOSITOR_CPU ON CACHE BOOL "" FORCE)
set(WITH_CYCLES ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_EMBREE ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_OSL ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_PATH_GUIDING ON CACHE BOOL "" FORCE)
set(WITH_DRACO ON CACHE BOOL "" FORCE)
set(WITH_FFTW3 ON CACHE BOOL "" FORCE)
set(WITH_FREESTYLE ON CACHE BOOL "" FORCE)

View File

@@ -18,6 +18,7 @@ set(WITH_COMPOSITOR_CPU ON CACHE BOOL "" FORCE)
set(WITH_CYCLES ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_EMBREE ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_OSL ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_PATH_GUIDING ON CACHE BOOL "" FORCE)
set(WITH_DRACO ON CACHE BOOL "" FORCE)
set(WITH_FFTW3 ON CACHE BOOL "" FORCE)
set(WITH_FREESTYLE ON CACHE BOOL "" FORCE)

View File

@@ -30,16 +30,12 @@ macro(add_bundled_libraries library)
list(APPEND PLATFORM_BUNDLED_LIBRARY_DIRS ${_library_dir})
unset(_all_library_versions)
unset(_library_dir)
endif()
endif()
endmacro()
# ------------------------------------------------------------------------
# Find system provided libraries.
# Avoid searching for headers since this would otherwise override our lib
# directory as well as PYTHON_ROOT_DIR.
set(CMAKE_FIND_FRAMEWORK NEVER)
# Find system ZLIB, not the pre-compiled one supplied with OpenCollada.
set(ZLIB_ROOT /usr)
find_package(ZLIB REQUIRED)
@@ -79,6 +75,10 @@ if(NOT EXISTS "${LIBDIR}/")
message(FATAL_ERROR "Mac OSX requires pre-compiled libs at: '${LIBDIR}'")
endif()
# Avoid searching for headers since this would otherwise override our lib
# directory as well as PYTHON_ROOT_DIR.
set(CMAKE_FIND_FRAMEWORK NEVER)
# Optionally use system Python if PYTHON_ROOT_DIR is specified.
if(WITH_PYTHON AND (WITH_PYTHON_MODULE AND PYTHON_ROOT_DIR))
find_package(PythonLibsUnix REQUIRED)
@@ -324,7 +324,7 @@ if(WITH_LLVM)
if(WITH_CLANG)
find_package(Clang)
if(NOT CLANG_FOUND)
message(FATAL_ERROR "Clang not found.")
message(FATAL_ERROR "Clang not found.")
endif()
endif()

View File

@@ -89,7 +89,7 @@ macro(add_bundled_libraries library)
file(GLOB _all_library_versions ${LIBDIR}/${library}/lib/*\.so*)
list(APPEND PLATFORM_BUNDLED_LIBRARIES ${_all_library_versions})
unset(_all_library_versions)
endif()
endif()
endmacro()
# ----------------------------------------------------------------------------

View File

@@ -347,6 +347,24 @@ if(WITH_OPENCOLORIO)
)
endif()
if(WITH_CYCLES_PATH_GUIDING)
add_definitions(-DWITH_PATH_GUIDING)
# The level of the guiding integration.
# Different levels can be selected to measure the overhead of different stages.
# 1 = recording the path segments
# 2 = 1 + generating (not storing) sample data from the segments
# 3 = 2 + storing the generates sample data
# 4 = 3 + training the guiding fields
# 5 = 4 + querying the trained guiding for sampling (full path guiding)
add_definitions(-DPATH_GUIDING_LEVEL=5)
include_directories(
SYSTEM
${OPENPGL_INCLUDE_DIR}
)
endif()
# NaN debugging
if(WITH_CYCLES_DEBUG_NAN)
add_definitions(-DWITH_CYCLES_DEBUG_NAN)

View File

@@ -7,6 +7,7 @@ set(INC
../../mikktspace
../../../source/blender/makesdna
../../../source/blender/makesrna
../../../source/blender/blenkernel
../../../source/blender/blenlib
../../../source/blender/gpu
../../../source/blender/render

View File

@@ -156,6 +156,11 @@ def with_osl():
return _cycles.with_osl
def with_path_guiding():
import _cycles
return _cycles.with_path_guiding
def system_info():
import _cycles
return _cycles.system_info()

View File

@@ -179,6 +179,12 @@ enum_view3d_shading_render_pass = (
('SAMPLE_COUNT', "Sample Count", "Per-pixel number of samples"),
)
enum_guiding_distribution = (
('PARALLAX_AWARE_VMM', "Parallax-Aware VMM", "Use Parallax-aware von Mises-Fisher models as directional distribution", 0),
('DIRECTIONAL_QUAD_TREE', "Directional Quad Tree", "Use Directional Quad Trees as directional distribution", 1),
('VMM', "VMM", "Use von Mises-Fisher models as directional distribution", 2),
)
def enum_openimagedenoise_denoiser(self, context):
import _cycles
@@ -358,7 +364,9 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
preview_samples: IntProperty(
name="Viewport Samples",
description="Number of samples to render in the viewport, unlimited if 0",
min=0, max=(1 << 24),
min=0,
soft_min=1,
max=(1 << 24),
default=1024,
)
@@ -507,6 +515,78 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
default=1.0,
)
use_guiding: BoolProperty(
name="Guiding",
description="Use path guiding for sampling paths. Path guiding incrementally "
"learns the light distribution of the scene and guides path into directions "
"with high direct and indirect light contributions",
default=False,
)
use_deterministic_guiding: BoolProperty(
name="Deterministic",
description="Makes path guiding deterministic which means renderings will be"
"reproducible with the same pixel values every time. This feature slows down"
"training",
default=True,
)
guiding_distribution_type: EnumProperty(
name="Guiding Distribution Type",
description="Type of representation for the guiding distribution",
items=enum_guiding_distribution,
default='PARALLAX_AWARE_VMM',
)
use_surface_guiding: BoolProperty(
name="Surface Guiding",
description="Use guiding when sampling directions on a surface",
default=True,
)
surface_guiding_probability: FloatProperty(
name="Surface Guiding Probability",
description="The probability of guiding a direction on a surface",
min=0.0, max=1.0,
default=0.5,
)
use_volume_guiding: BoolProperty(
name="Volume Guiding",
description="Use guiding when sampling directions inside a volume",
default=True,
)
guiding_training_samples: IntProperty(
name="Training Samples",
description="The maximum number of samples used for training path guiding. "
"Higher samples lead to more accurate guiding, however may also unnecessarily slow "
"down rendering once guiding is accurate enough. "
"A value 0 will continue training until the last sample",
min=0,
soft_min=1,
default=128,
)
volume_guiding_probability: FloatProperty(
name="Volume Guiding Probability",
description="The probability of guiding a direction inside a volume",
min=0.0, max=1.0,
default=0.5,
)
use_guiding_direct_light: BoolProperty(
name="Guide Direct Light",
description="Consider the contribution of directly visible light sources during guiding",
default=True,
)
use_guiding_mis_weights: BoolProperty(
name="Use MIS Weights",
description="Use the MIS weight to weight the contribution of directly visible light sources during guiding",
default=True,
)
max_bounces: IntProperty(
name="Max Bounces",
description="Total maximum number of bounces",

View File

@@ -278,6 +278,63 @@ class CYCLES_RENDER_PT_sampling_render_denoise(CyclesButtonsPanel, Panel):
col.prop(cscene, "denoising_prefilter", text="Prefilter")
class CYCLES_RENDER_PT_sampling_path_guiding(CyclesButtonsPanel, Panel):
bl_label = "Path Guiding"
bl_parent_id = "CYCLES_RENDER_PT_sampling"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
from . import engine
return use_cpu(context) and engine.with_path_guiding()
def draw_header(self, context):
scene = context.scene
cscene = scene.cycles
self.layout.prop(cscene, "use_guiding", text="")
def draw(self, context):
scene = context.scene
cscene = scene.cycles
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.active = cscene.use_guiding
col = layout.column(align=True)
col.prop(cscene, "use_surface_guiding")
col.prop(cscene, "use_volume_guiding")
col.prop(cscene, "guiding_training_samples")
class CYCLES_RENDER_PT_sampling_path_guiding_debug(CyclesDebugButtonsPanel, Panel):
bl_label = "Debug"
bl_parent_id = "CYCLES_RENDER_PT_sampling_path_guiding"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
scene = context.scene
cscene = scene.cycles
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.active = cscene.use_guiding
layout.prop(cscene, "guiding_distribution_type", text="Distribution Type")
col = layout.column(align=True)
col.prop(cscene, "surface_guiding_probability")
col.prop(cscene, "volume_guiding_probability")
col = layout.column(align=True)
col.prop(cscene, "use_deterministic_guiding")
col.prop(cscene, "use_guiding_direct_light")
col.prop(cscene, "use_guiding_mis_weights")
class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel):
bl_label = "Advanced"
bl_parent_id = "CYCLES_RENDER_PT_sampling"
@@ -2286,6 +2343,8 @@ classes = (
CYCLES_RENDER_PT_sampling_viewport_denoise,
CYCLES_RENDER_PT_sampling_render,
CYCLES_RENDER_PT_sampling_render_denoise,
CYCLES_RENDER_PT_sampling_path_guiding,
CYCLES_RENDER_PT_sampling_path_guiding_debug,
CYCLES_RENDER_PT_sampling_advanced,
CYCLES_RENDER_PT_light_paths,
CYCLES_RENDER_PT_light_paths_max_bounces,

View File

@@ -23,6 +23,8 @@
#include "util/log.h"
#include "util/task.h"
#include "BKE_duplilist.h"
CCL_NAMESPACE_BEGIN
/* Utilities */
@@ -353,79 +355,26 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
return object;
}
/* This function mirrors drw_uniform_property_lookup in draw_instance_data.cpp */
static bool lookup_property(BL::ID b_id, const string &name, float4 *r_value)
{
PointerRNA ptr;
PropertyRNA *prop;
extern "C" DupliObject *rna_hack_DepsgraphObjectInstance_dupli_object_get(PointerRNA *ptr);
if (!RNA_path_resolve(&b_id.ptr, name.c_str(), &ptr, &prop)) {
return false;
}
if (prop == NULL) {
return false;
}
PropertyType type = RNA_property_type(prop);
int arraylen = RNA_property_array_length(&ptr, prop);
if (arraylen == 0) {
float value;
if (type == PROP_FLOAT)
value = RNA_property_float_get(&ptr, prop);
else if (type == PROP_INT)
value = static_cast<float>(RNA_property_int_get(&ptr, prop));
else
return false;
*r_value = make_float4(value, value, value, 1.0f);
return true;
}
else if (type == PROP_FLOAT && arraylen <= 4) {
*r_value = make_float4(0.0f, 0.0f, 0.0f, 1.0f);
RNA_property_float_get_array(&ptr, prop, &r_value->x);
return true;
}
return false;
}
/* This function mirrors drw_uniform_attribute_lookup in draw_instance_data.cpp */
static float4 lookup_instance_property(BL::DepsgraphObjectInstance &b_instance,
const string &name,
bool use_instancer)
{
string idprop_name = string_printf("[\"%s\"]", name.c_str());
float4 value;
::Object *ob = (::Object *)b_instance.object().ptr.data;
::DupliObject *dupli = nullptr;
::Object *dupli_parent = nullptr;
/* If requesting instance data, check the parent particle system and object. */
if (use_instancer && b_instance.is_instance()) {
BL::ParticleSystem b_psys = b_instance.particle_system();
if (b_psys) {
if (lookup_property(b_psys.settings(), idprop_name, &value) ||
lookup_property(b_psys.settings(), name, &value)) {
return value;
}
}
if (lookup_property(b_instance.parent(), idprop_name, &value) ||
lookup_property(b_instance.parent(), name, &value)) {
return value;
}
dupli = rna_hack_DepsgraphObjectInstance_dupli_object_get(&b_instance.ptr);
dupli_parent = (::Object *)b_instance.parent().ptr.data;
}
/* Check the object and mesh. */
BL::Object b_ob = b_instance.object();
BL::ID b_data = b_ob.data();
float4 value;
BKE_object_dupli_find_rgba_attribute(ob, dupli, dupli_parent, name.c_str(), &value.x);
if (lookup_property(b_ob, idprop_name, &value) || lookup_property(b_ob, name, &value) ||
lookup_property(b_data, idprop_name, &value) || lookup_property(b_data, name, &value)) {
return value;
}
return zero_float4();
return value;
}
bool BlenderSync::sync_object_attributes(BL::DepsgraphObjectInstance &b_instance, Object *object)

View File

@@ -15,6 +15,7 @@
#include "util/debug.h"
#include "util/foreach.h"
#include "util/guiding.h"
#include "util/log.h"
#include "util/md5.h"
#include "util/opengl.h"
@@ -1008,6 +1009,15 @@ void *CCL_python_module_init()
PyModule_AddStringConstant(mod, "osl_version_string", "unknown");
#endif
if (ccl::guiding_supported()) {
PyModule_AddObject(mod, "with_path_guiding", Py_True);
Py_INCREF(Py_True);
}
else {
PyModule_AddObject(mod, "with_path_guiding", Py_False);
Py_INCREF(Py_False);
}
#ifdef WITH_EMBREE
PyModule_AddObject(mod, "with_embree", Py_True);
Py_INCREF(Py_True);

View File

@@ -413,6 +413,22 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background)
integrator->set_direct_light_sampling_type(direct_light_sampling_type);
#endif
integrator->set_use_guiding(get_boolean(cscene, "use_guiding"));
integrator->set_use_surface_guiding(get_boolean(cscene, "use_surface_guiding"));
integrator->set_use_volume_guiding(get_boolean(cscene, "use_volume_guiding"));
integrator->set_guiding_training_samples(get_int(cscene, "guiding_training_samples"));
if (use_developer_ui) {
integrator->set_deterministic_guiding(get_boolean(cscene, "use_deterministic_guiding"));
integrator->set_surface_guiding_probability(get_float(cscene, "surface_guiding_probability"));
integrator->set_volume_guiding_probability(get_float(cscene, "volume_guiding_probability"));
integrator->set_use_guiding_direct_light(get_boolean(cscene, "use_guiding_direct_light"));
integrator->set_use_guiding_mis_weights(get_boolean(cscene, "use_guiding_mis_weights"));
GuidingDistributionType guiding_distribution_type = (GuidingDistributionType)get_enum(
cscene, "guiding_distribution_type", GUIDING_NUM_TYPES, GUIDING_TYPE_PARALLAX_AWARE_VMM);
integrator->set_guiding_distribution_type(guiding_distribution_type);
}
DenoiseParams denoise_params = get_denoise_params(b_scene, b_view_layer, background);
/* No denoising support for vertex color baking, vertices packed into image
@@ -737,6 +753,17 @@ void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_v
pass_add(scene, PASS_DENOISING_DEPTH, "Denoising Depth", PassMode::NOISY);
}
#ifdef WITH_CYCLES_DEBUG
b_engine.add_pass("Guiding Color", 3, "RGB", b_view_layer.name().c_str());
pass_add(scene, PASS_GUIDING_COLOR, "Guiding Color", PassMode::NOISY);
b_engine.add_pass("Guiding Probability", 1, "X", b_view_layer.name().c_str());
pass_add(scene, PASS_GUIDING_PROBABILITY, "Guiding Probability", PassMode::NOISY);
b_engine.add_pass("Guiding Average Roughness", 1, "X", b_view_layer.name().c_str());
pass_add(scene, PASS_GUIDING_AVG_ROUGHNESS, "Guiding Average Roughness", PassMode::NOISY);
#endif
/* Custom AOV passes. */
BL::ViewLayer::aovs_iterator b_aov_iter;
for (b_view_layer.aovs.begin(b_aov_iter); b_aov_iter != b_view_layer.aovs.end(); ++b_aov_iter) {

View File

@@ -104,6 +104,10 @@ if(CYCLES_STANDALONE_REPOSITORY)
else()
unset(_cycles_lib_dir)
endif()
else()
if(EXISTS ${LIBDIR})
set(_cycles_lib_dir ${LIBDIR})
endif()
endif()
###########################################################################
@@ -269,6 +273,31 @@ if(CYCLES_STANDALONE_REPOSITORY AND WITH_CYCLES_OSL)
endif()
endif()
###########################################################################
# OpenPGL
###########################################################################
if(WITH_CYCLES_PATH_GUIDING)
if(EXISTS ${_cycles_lib_dir})
set(openpgl_DIR ${_cycles_lib_dir}/openpgl/lib/cmake/openpgl)
endif()
find_package(openpgl QUIET)
if(openpgl_FOUND)
if(WIN32)
get_target_property(OPENPGL_LIBRARIES_RELEASE openpgl::openpgl LOCATION_RELEASE)
get_target_property(OPENPGL_LIBRARIES_DEBUG openpgl::openpgl LOCATION_DEBUG)
set(OPENPGL_LIBRARIES optimized ${OPENPGL_LIBRARIES_RELEASE} debug ${OPENPGL_LIBRARIES_DEBUG})
else()
get_target_property(OPENPGL_LIBRARIES openpgl::openpgl LOCATION)
endif()
get_target_property(OPENPGL_INCLUDE_DIR openpgl::openpgl INTERFACE_INCLUDE_DIRECTORIES)
else()
set(WITH_CYCLES_PATH_GUIDING OFF)
message(STATUS "OpenPGL not found, disabling WITH_CYCLES_PATH_GUIDING")
endif()
endif()
###########################################################################
# OpenColorIO
###########################################################################
@@ -516,7 +545,7 @@ endif()
if(CYCLES_STANDALONE_REPOSITORY)
if((WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI) OR
WITH_CYCLES_HYDRA_RENDER_DELEGATE)
WITH_CYCLES_HYDRA_RENDER_DELEGATE)
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(Epoxy_LIBRARIES "${_cycles_lib_dir}/epoxy/lib/epoxy.lib")
set(Epoxy_INCLUDE_DIRS "${_cycles_lib_dir}/epoxy/include")

View File

@@ -118,6 +118,9 @@ macro(cycles_external_libraries_append libraries)
if(WITH_ALEMBIC)
list(APPEND ${libraries} ${ALEMBIC_LIBRARIES})
endif()
if(WITH_PATH_GUIDING)
target_link_libraries(${target} ${OPENPGL_LIBRARIES})
endif()
list(APPEND ${libraries}
${OPENIMAGEIO_LIBRARIES}

View File

@@ -7,6 +7,7 @@
/* Used for `info.denoisers`. */
/* TODO(sergey): The denoisers are probably to be moved completely out of the device into their
* own class. But until then keep API consistent with how it used to work before. */
#include "util/guiding.h"
#include "util/openimagedenoise.h"
CCL_NAMESPACE_BEGIN
@@ -27,6 +28,12 @@ void device_cpu_info(vector<DeviceInfo> &devices)
info.has_osl = true;
info.has_nanovdb = true;
info.has_profiling = true;
if (guiding_supported()) {
info.has_guiding = true;
}
else {
info.has_guiding = false;
}
if (openimagedenoise_supported()) {
info.denoisers |= DENOISER_OPENIMAGEDENOISE;
}

View File

@@ -38,6 +38,7 @@
#include "util/debug.h"
#include "util/foreach.h"
#include "util/function.h"
#include "util/guiding.h"
#include "util/log.h"
#include "util/map.h"
#include "util/openimagedenoise.h"
@@ -278,6 +279,23 @@ void CPUDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
Device::build_bvh(bvh, progress, refit);
}
void *CPUDevice::get_guiding_device() const
{
#ifdef WITH_PATH_GUIDING
if (!guiding_device) {
if (guiding_device_type() == 8) {
guiding_device = make_unique<openpgl::cpp::Device>(PGL_DEVICE_TYPE_CPU_8);
}
else if (guiding_device_type() == 4) {
guiding_device = make_unique<openpgl::cpp::Device>(PGL_DEVICE_TYPE_CPU_4);
}
}
return guiding_device.get();
#else
return nullptr;
#endif
}
void CPUDevice::get_cpu_kernel_thread_globals(
vector<CPUKernelThreadGlobals> &kernel_thread_globals)
{

View File

@@ -26,6 +26,9 @@
#include "kernel/osl/globals.h"
// clang-format on
#include "util/guiding.h"
#include "util/unique_ptr.h"
CCL_NAMESPACE_BEGIN
class CPUDevice : public Device {
@@ -42,6 +45,9 @@ class CPUDevice : public Device {
RTCScene embree_scene = NULL;
RTCDevice embree_device;
#endif
#ifdef WITH_PATH_GUIDING
mutable unique_ptr<openpgl::cpp::Device> guiding_device;
#endif
CPUDevice(const DeviceInfo &info_, Stats &stats_, Profiler &profiler_);
~CPUDevice();
@@ -72,6 +78,8 @@ class CPUDevice : public Device {
void build_bvh(BVH *bvh, Progress &progress, bool refit) override;
void *get_guiding_device() const override;
virtual void get_cpu_kernel_thread_globals(
vector<CPUKernelThreadGlobals> &kernel_thread_globals) override;
virtual void *get_cpu_osl_memory() override;

View File

@@ -14,19 +14,23 @@ CPUKernelThreadGlobals::CPUKernelThreadGlobals(const KernelGlobalsCPU &kernel_gl
Profiler &cpu_profiler)
: KernelGlobalsCPU(kernel_globals), cpu_profiler_(cpu_profiler)
{
reset_runtime_memory();
clear_runtime_pointers();
#ifdef WITH_OSL
OSLGlobals::thread_init(this, static_cast<OSLGlobals *>(osl_globals_memory));
#else
(void)osl_globals_memory;
#endif
#ifdef WITH_PATH_GUIDING
opgl_path_segment_storage = new openpgl::cpp::PathSegmentStorage();
#endif
}
CPUKernelThreadGlobals::CPUKernelThreadGlobals(CPUKernelThreadGlobals &&other) noexcept
: KernelGlobalsCPU(std::move(other)), cpu_profiler_(other.cpu_profiler_)
{
other.reset_runtime_memory();
other.clear_runtime_pointers();
}
CPUKernelThreadGlobals::~CPUKernelThreadGlobals()
@@ -34,6 +38,12 @@ CPUKernelThreadGlobals::~CPUKernelThreadGlobals()
#ifdef WITH_OSL
OSLGlobals::thread_free(this);
#endif
#ifdef WITH_PATH_GUIDING
delete opgl_path_segment_storage;
delete opgl_surface_sampling_distribution;
delete opgl_volume_sampling_distribution;
#endif
}
CPUKernelThreadGlobals &CPUKernelThreadGlobals::operator=(CPUKernelThreadGlobals &&other)
@@ -44,16 +54,25 @@ CPUKernelThreadGlobals &CPUKernelThreadGlobals::operator=(CPUKernelThreadGlobals
*static_cast<KernelGlobalsCPU *>(this) = *static_cast<KernelGlobalsCPU *>(&other);
other.reset_runtime_memory();
other.clear_runtime_pointers();
return *this;
}
void CPUKernelThreadGlobals::reset_runtime_memory()
void CPUKernelThreadGlobals::clear_runtime_pointers()
{
#ifdef WITH_OSL
osl = nullptr;
#endif
#ifdef WITH_PATH_GUIDING
opgl_sample_data_storage = nullptr;
opgl_guiding_field = nullptr;
opgl_path_segment_storage = nullptr;
opgl_surface_sampling_distribution = nullptr;
opgl_volume_sampling_distribution = nullptr;
#endif
}
void CPUKernelThreadGlobals::start_profiling()

View File

@@ -36,7 +36,7 @@ class CPUKernelThreadGlobals : public KernelGlobalsCPU {
void stop_profiling();
protected:
void reset_runtime_memory();
void clear_runtime_pointers();
Profiler &cpu_profiler_;
};

View File

@@ -79,7 +79,7 @@ bool CUDADeviceQueue::enqueue(DeviceKernel kernel,
return false;
}
debug_enqueue(kernel, work_size);
debug_enqueue_begin(kernel, work_size);
const CUDAContextScope scope(cuda_device_);
const CUDADeviceKernel &cuda_kernel = cuda_device_->kernels.get(kernel);
@@ -121,6 +121,8 @@ bool CUDADeviceQueue::enqueue(DeviceKernel kernel,
0),
"enqueue");
debug_enqueue_end();
return !(cuda_device_->have_error());
}

View File

@@ -352,6 +352,7 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
info.has_nanovdb = true;
info.has_osl = true;
info.has_guiding = true;
info.has_profiling = true;
info.has_peer_memory = false;
info.use_metalrt = false;
@@ -399,6 +400,7 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
/* Accumulate device info. */
info.has_nanovdb &= device.has_nanovdb;
info.has_osl &= device.has_osl;
info.has_guiding &= device.has_guiding;
info.has_profiling &= device.has_profiling;
info.has_peer_memory |= device.has_peer_memory;
info.use_metalrt |= device.use_metalrt;

View File

@@ -66,6 +66,7 @@ class DeviceInfo {
bool display_device; /* GPU is used as a display device. */
bool has_nanovdb; /* Support NanoVDB volumes. */
bool has_osl; /* Support Open Shading Language. */
bool has_guiding; /* Support path guiding. */
bool has_profiling; /* Supports runtime collection of profiling info. */
bool has_peer_memory; /* GPU has P2P access to memory of another GPU. */
bool has_gpu_queue; /* Device supports GPU queue. */
@@ -84,6 +85,7 @@ class DeviceInfo {
display_device = false;
has_nanovdb = false;
has_osl = false;
has_guiding = false;
has_profiling = false;
has_peer_memory = false;
has_gpu_queue = false;
@@ -217,6 +219,15 @@ class Device {
return false;
}
/* Guiding */
/* Returns path guiding device handle. */
virtual void *get_guiding_device() const
{
LOG(ERROR) << "Request guiding field from a device which does not support it.";
return nullptr;
}
/* Buffer denoising. */
/* Returns true if task is fully handled. */

View File

@@ -79,7 +79,7 @@ bool HIPDeviceQueue::enqueue(DeviceKernel kernel,
return false;
}
debug_enqueue(kernel, work_size);
debug_enqueue_begin(kernel, work_size);
const HIPContextScope scope(hip_device_);
const HIPDeviceKernel &hip_kernel = hip_device_->kernels.get(kernel);
@@ -120,6 +120,8 @@ bool HIPDeviceQueue::enqueue(DeviceKernel kernel,
0),
"enqueue");
debug_enqueue_end();
return !(hip_device_->have_error());
}

View File

@@ -308,26 +308,29 @@ MetalKernelPipeline *ShaderCache::get_best_pipeline(DeviceKernel kernel, const M
bool MetalKernelPipeline::should_use_binary_archive() const
{
if (auto str = getenv("CYCLES_METAL_DISABLE_BINARY_ARCHIVES")) {
if (atoi(str) != 0) {
/* Don't archive if we have opted out by env var. */
return false;
/* Issues with binary archives in older macOS versions. */
if (@available(macOS 13.0, *)) {
if (auto str = getenv("CYCLES_METAL_DISABLE_BINARY_ARCHIVES")) {
if (atoi(str) != 0) {
/* Don't archive if we have opted out by env var. */
return false;
}
}
}
if (pso_type == PSO_GENERIC) {
/* Archive the generic kernels. */
return true;
}
if (pso_type == PSO_GENERIC) {
/* Archive the generic kernels. */
return true;
}
if (device_kernel >= DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND &&
device_kernel <= DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW) {
/* Archive all shade kernels - they take a long time to compile. */
return true;
}
if (device_kernel >= DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND &&
device_kernel <= DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW) {
/* Archive all shade kernels - they take a long time to compile. */
return true;
}
/* The remaining kernels are all fast to compile. They may get cached by the system shader cache,
* but will be quick to regenerate if not. */
/* The remaining kernels are all fast to compile. They may get cached by the system shader
* cache, but will be quick to regenerate if not. */
}
return false;
}

View File

@@ -77,7 +77,7 @@ bool OneapiDeviceQueue::enqueue(DeviceKernel kernel,
void **args = const_cast<void **>(_args.values);
debug_enqueue(kernel, signed_kernel_work_size);
debug_enqueue_begin(kernel, signed_kernel_work_size);
assert(signed_kernel_work_size >= 0);
size_t kernel_work_size = (size_t)signed_kernel_work_size;
@@ -97,6 +97,8 @@ bool OneapiDeviceQueue::enqueue(DeviceKernel kernel,
oneapi_device_->oneapi_error_message() + "\"");
}
debug_enqueue_end();
return is_finished_ok;
}

View File

@@ -46,7 +46,7 @@ bool OptiXDeviceQueue::enqueue(DeviceKernel kernel,
return false;
}
debug_enqueue(kernel, work_size);
debug_enqueue_begin(kernel, work_size);
const CUDAContextScope scope(cuda_device_);
@@ -131,6 +131,8 @@ bool OptiXDeviceQueue::enqueue(DeviceKernel kernel,
1,
1));
debug_enqueue_end();
return !(optix_device->have_error());
}

View File

@@ -12,9 +12,13 @@
CCL_NAMESPACE_BEGIN
DeviceQueue::DeviceQueue(Device *device)
: device(device), last_kernels_enqueued_(0), last_sync_time_(0.0)
: device(device),
last_kernels_enqueued_(0),
last_sync_time_(0.0),
is_per_kernel_performance_(false)
{
DCHECK_NE(device, nullptr);
is_per_kernel_performance_ = getenv("CYCLES_DEBUG_PER_KERNEL_PERFORMANCE");
}
DeviceQueue::~DeviceQueue()
@@ -33,11 +37,17 @@ DeviceQueue::~DeviceQueue()
});
VLOG_DEVICE_STATS << "GPU queue stats:";
double total_time = 0.0;
for (const auto &[mask, time] : stats_sorted) {
total_time += time;
VLOG_DEVICE_STATS << " " << std::setfill(' ') << std::setw(10) << std::fixed
<< std::setprecision(5) << std::right << time
<< "s: " << device_kernel_mask_as_string(mask);
}
if (is_per_kernel_performance_)
VLOG_DEVICE_STATS << "GPU queue total time: " << std::fixed << std::setprecision(5)
<< total_time;
}
}
@@ -50,7 +60,7 @@ void DeviceQueue::debug_init_execution()
last_kernels_enqueued_ = 0;
}
void DeviceQueue::debug_enqueue(DeviceKernel kernel, const int work_size)
void DeviceQueue::debug_enqueue_begin(DeviceKernel kernel, const int work_size)
{
if (VLOG_DEVICE_STATS_IS_ON) {
VLOG_DEVICE_STATS << "GPU queue launch " << device_kernel_as_string(kernel) << ", work_size "
@@ -60,6 +70,13 @@ void DeviceQueue::debug_enqueue(DeviceKernel kernel, const int work_size)
last_kernels_enqueued_ |= (uint64_t(1) << (uint64_t)kernel);
}
void DeviceQueue::debug_enqueue_end()
{
if (VLOG_DEVICE_STATS_IS_ON && is_per_kernel_performance_) {
synchronize();
}
}
void DeviceQueue::debug_synchronize()
{
if (VLOG_DEVICE_STATS_IS_ON) {
@@ -67,7 +84,11 @@ void DeviceQueue::debug_synchronize()
const double elapsed_time = new_time - last_sync_time_;
VLOG_DEVICE_STATS << "GPU queue synchronize, elapsed " << std::setw(10) << elapsed_time << "s";
stats_kernel_time_[last_kernels_enqueued_] += elapsed_time;
/* There is no sense to have an entries in the performance data
* container without related kernel information. */
if (last_kernels_enqueued_ != 0) {
stats_kernel_time_[last_kernels_enqueued_] += elapsed_time;
}
last_sync_time_ = new_time;
}

View File

@@ -162,7 +162,8 @@ class DeviceQueue {
/* Implementations call these from the corresponding methods to generate debugging logs. */
void debug_init_execution();
void debug_enqueue(DeviceKernel kernel, const int work_size);
void debug_enqueue_begin(DeviceKernel kernel, const int work_size);
void debug_enqueue_end();
void debug_synchronize();
string debug_active_kernels();
@@ -172,6 +173,9 @@ class DeviceQueue {
double last_sync_time_;
/* Accumulated execution time for combinations of kernels launched together. */
map<DeviceKernelMask, double> stats_kernel_time_;
/* If it is true, then a performance statistics in the debugging logs will have focus on kernels
* and an explicit queue synchronization will be added after each kernel execution. */
bool is_per_kernel_performance_;
};
CCL_NAMESPACE_END

View File

@@ -65,6 +65,12 @@ if(WITH_OPENIMAGEDENOISE)
)
endif()
if(WITH_CYCLES_PATH_GUIDING)
list(APPEND LIB
${OPENPGL_LIBRARIES}
)
endif()
include_directories(${INC})
include_directories(SYSTEM ${INC_SYS})

View File

@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#pragma once
#include "kernel/types.h"
CCL_NAMESPACE_BEGIN
struct GuidingParams {
/* The subset of path guiding parameters that can trigger a creation/rebuild
* of the guiding field. */
bool use = false;
bool use_surface_guiding = false;
bool use_volume_guiding = false;
GuidingDistributionType type = GUIDING_TYPE_PARALLAX_AWARE_VMM;
int training_samples = 128;
bool deterministic = false;
GuidingParams() = default;
bool modified(const GuidingParams &other) const
{
return !((use == other.use) && (use_surface_guiding == other.use_surface_guiding) &&
(use_volume_guiding == other.use_volume_guiding) && (type == other.type) &&
(training_samples == other.training_samples) &&
(deterministic == other.deterministic));
}
};
CCL_NAMESPACE_END

View File

@@ -185,11 +185,25 @@ void PathTrace::render_pipeline(RenderWork render_work)
rebalance(render_work);
/* Prepare all per-thread guiding structures before we start with the next rendering
* iteration/progression. */
const bool use_guiding = device_scene_->data.integrator.use_guiding;
if (use_guiding) {
guiding_prepare_structures();
}
path_trace(render_work);
if (render_cancel_.is_requested) {
return;
}
/* Update the guiding field using the training data/samples collected during the rendering
* iteration/progression. */
const bool train_guiding = device_scene_->data.integrator.train_guiding;
if (use_guiding && train_guiding) {
guiding_update_structures();
}
adaptive_sample(render_work);
if (render_cancel_.is_requested) {
return;
@@ -1241,4 +1255,122 @@ string PathTrace::full_report() const
return result;
}
void PathTrace::set_guiding_params(const GuidingParams &guiding_params, const bool reset)
{
#ifdef WITH_PATH_GUIDING
if (guiding_params_.modified(guiding_params)) {
guiding_params_ = guiding_params;
if (guiding_params_.use) {
PGLFieldArguments field_args;
switch (guiding_params_.type) {
default:
/* Parallax-aware von Mises-Fisher mixture models. */
case GUIDING_TYPE_PARALLAX_AWARE_VMM: {
pglFieldArgumentsSetDefaults(
field_args,
PGL_SPATIAL_STRUCTURE_TYPE::PGL_SPATIAL_STRUCTURE_KDTREE,
PGL_DIRECTIONAL_DISTRIBUTION_TYPE::PGL_DIRECTIONAL_DISTRIBUTION_PARALLAX_AWARE_VMM);
break;
}
/* Directional quad-trees. */
case GUIDING_TYPE_DIRECTIONAL_QUAD_TREE: {
pglFieldArgumentsSetDefaults(
field_args,
PGL_SPATIAL_STRUCTURE_TYPE::PGL_SPATIAL_STRUCTURE_KDTREE,
PGL_DIRECTIONAL_DISTRIBUTION_TYPE::PGL_DIRECTIONAL_DISTRIBUTION_QUADTREE);
break;
}
/* von Mises-Fisher mixture models. */
case GUIDING_TYPE_VMM: {
pglFieldArgumentsSetDefaults(
field_args,
PGL_SPATIAL_STRUCTURE_TYPE::PGL_SPATIAL_STRUCTURE_KDTREE,
PGL_DIRECTIONAL_DISTRIBUTION_TYPE::PGL_DIRECTIONAL_DISTRIBUTION_VMM);
break;
}
}
# if OPENPGL_VERSION_MINOR >= 4
field_args.deterministic = guiding_params.deterministic;
# endif
openpgl::cpp::Device *guiding_device = static_cast<openpgl::cpp::Device *>(
device_->get_guiding_device());
if (guiding_device) {
guiding_sample_data_storage_ = make_unique<openpgl::cpp::SampleStorage>();
guiding_field_ = make_unique<openpgl::cpp::Field>(guiding_device, field_args);
}
else {
guiding_sample_data_storage_ = nullptr;
guiding_field_ = nullptr;
}
}
else {
guiding_sample_data_storage_ = nullptr;
guiding_field_ = nullptr;
}
}
else if (reset) {
if (guiding_field_) {
guiding_field_->Reset();
}
}
#else
(void)guiding_params;
(void)reset;
#endif
}
void PathTrace::guiding_prepare_structures()
{
#ifdef WITH_PATH_GUIDING
const bool train = (guiding_params_.training_samples == 0) ||
(guiding_field_->GetIteration() < guiding_params_.training_samples);
for (auto &&path_trace_work : path_trace_works_) {
path_trace_work->guiding_init_kernel_globals(
guiding_field_.get(), guiding_sample_data_storage_.get(), train);
}
if (train) {
/* For training the guiding distribution we need to force the number of samples
* per update to be limited, for reproducible results and reasonable training size.
*
* Idea: we could stochastically discard samples with a probability of 1/num_samples_per_update
* we can then update only after the num_samples_per_update iterations are rendered. */
render_scheduler_.set_limit_samples_per_update(4);
}
else {
render_scheduler_.set_limit_samples_per_update(0);
}
#endif
}
void PathTrace::guiding_update_structures()
{
#ifdef WITH_PATH_GUIDING
VLOG_WORK << "Update path guiding structures";
VLOG_DEBUG << "Number of surface samples: " << guiding_sample_data_storage_->GetSizeSurface();
VLOG_DEBUG << "Number of volume samples: " << guiding_sample_data_storage_->GetSizeVolume();
const size_t num_valid_samples = guiding_sample_data_storage_->GetSizeSurface() +
guiding_sample_data_storage_->GetSizeVolume();
/* we wait until we have at least 1024 samples */
if (num_valid_samples >= 1024) {
# if OPENPGL_VERSION_MINOR < 4
const size_t num_samples = 1;
guiding_field_->Update(*guiding_sample_data_storage_, num_samples);
# else
guiding_field_->Update(*guiding_sample_data_storage_);
# endif
guiding_update_count++;
VLOG_DEBUG << "Path guiding field valid: " << guiding_field_->Validate();
guiding_sample_data_storage_->Clear();
}
#endif
}
CCL_NAMESPACE_END

View File

@@ -4,11 +4,15 @@
#pragma once
#include "integrator/denoiser.h"
#include "integrator/guiding.h"
#include "integrator/pass_accessor.h"
#include "integrator/path_trace_work.h"
#include "integrator/work_balancer.h"
#include "session/buffers.h"
#include "util/function.h"
#include "util/guiding.h"
#include "util/thread.h"
#include "util/unique_ptr.h"
#include "util/vector.h"
@@ -89,6 +93,10 @@ class PathTrace {
* Use this to configure the adaptive sampler before rendering any samples. */
void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling);
/* Set the parameters for guiding.
* Use to setup the guiding structures before each rendering iteration.*/
void set_guiding_params(const GuidingParams &params, const bool reset);
/* Sets output driver for render buffer output. */
void set_output_driver(unique_ptr<OutputDriver> driver);
@@ -205,6 +213,15 @@ class PathTrace {
void write_tile_buffer(const RenderWork &render_work);
void finalize_full_buffer_on_disk(const RenderWork &render_work);
/* Updates/initializes the guiding structures after a rendering iteration.
* The structures are updated using the training data/samples generated during the previous
* rendering iteration */
void guiding_update_structures();
/* Prepares the per-kernel thread related guiding structures (e.g., PathSegmentStorage,
* pointers to the global Field and SegmentStorage)*/
void guiding_prepare_structures();
/* Get number of samples in the current state of the render buffers. */
int get_num_samples_in_buffer();
@@ -265,6 +282,22 @@ class PathTrace {
/* Denoiser device descriptor which holds the denoised big tile for multi-device workloads. */
unique_ptr<PathTraceWork> big_tile_denoise_work_;
#ifdef WITH_PATH_GUIDING
/* Guiding related attributes */
GuidingParams guiding_params_;
/* The guiding field which holds the representation of the incident radiance field for the
* complete scene. */
unique_ptr<openpgl::cpp::Field> guiding_field_;
/* The storage container which holds the training data/samples generated during the last
* rendering iteration. */
unique_ptr<openpgl::cpp::SampleStorage> guiding_sample_data_storage_;
/* The number of already performed training iterations for the guiding field.*/
int guiding_update_count = 0;
#endif
/* State which is common for all the steps of the render work.
* Is brought up to date in the `render()` call and is accessed from all the steps involved into
* rendering the work. */

View File

@@ -140,6 +140,13 @@ class PathTraceWork {
return device_;
}
#ifdef WITH_PATH_GUIDING
/* Initializes the per-thread guiding kernel data. */
virtual void guiding_init_kernel_globals(void *, void *, const bool)
{
}
#endif
protected:
PathTraceWork(Device *device,
Film *film,

View File

@@ -6,6 +6,7 @@
#include "device/cpu/kernel.h"
#include "device/device.h"
#include "kernel/film/write.h"
#include "kernel/integrator/path_state.h"
#include "integrator/pass_accessor_cpu.h"
@@ -145,6 +146,13 @@ void PathTraceWorkCPU::render_samples_full_pipeline(KernelGlobalsCPU *kernel_glo
kernels_.integrator_megakernel(kernel_globals, state, render_buffer);
#ifdef WITH_PATH_GUIDING
if (kernel_globals->data.integrator.train_guiding) {
/* Push the generated sample data to the global sample data storage. */
guiding_push_sample_data_to_global_storage(kernel_globals, state, render_buffer);
}
#endif
if (shadow_catcher_state) {
kernels_.integrator_megakernel(kernel_globals, shadow_catcher_state, render_buffer);
}
@@ -276,4 +284,106 @@ void PathTraceWorkCPU::cryptomatte_postproces()
});
}
#ifdef WITH_PATH_GUIDING
/* Note: It seems that this is called before every rendering iteration/progression and not once per
* rendering. May be we find a way to call it only once per rendering. */
void PathTraceWorkCPU::guiding_init_kernel_globals(void *guiding_field,
void *sample_data_storage,
const bool train)
{
/* Linking the global guiding structures (e.g., Field and SampleStorage) to the per-thread
* kernel globals. */
for (int thread_index = 0; thread_index < kernel_thread_globals_.size(); thread_index++) {
CPUKernelThreadGlobals &kg = kernel_thread_globals_[thread_index];
openpgl::cpp::Field *field = (openpgl::cpp::Field *)guiding_field;
/* Allocate sampling distributions. */
kg.opgl_guiding_field = field;
# if PATH_GUIDING_LEVEL >= 4
if (kg.opgl_surface_sampling_distribution) {
delete kg.opgl_surface_sampling_distribution;
kg.opgl_surface_sampling_distribution = nullptr;
}
if (kg.opgl_volume_sampling_distribution) {
delete kg.opgl_volume_sampling_distribution;
kg.opgl_volume_sampling_distribution = nullptr;
}
if (field) {
kg.opgl_surface_sampling_distribution = new openpgl::cpp::SurfaceSamplingDistribution(field);
kg.opgl_volume_sampling_distribution = new openpgl::cpp::VolumeSamplingDistribution(field);
}
# endif
/* Reserve storage for training. */
kg.data.integrator.train_guiding = train;
kg.opgl_sample_data_storage = (openpgl::cpp::SampleStorage *)sample_data_storage;
if (train) {
kg.opgl_path_segment_storage->Reserve(kg.data.integrator.transparent_max_bounce +
kg.data.integrator.max_bounce + 3);
kg.opgl_path_segment_storage->Clear();
}
}
}
void PathTraceWorkCPU::guiding_push_sample_data_to_global_storage(
KernelGlobalsCPU *kg, IntegratorStateCPU *state, ccl_global float *ccl_restrict render_buffer)
{
# ifdef WITH_CYCLES_DEBUG
if (VLOG_WORK_IS_ON) {
/* Check if the generated path segments contain valid values. */
const bool validSegments = kg->opgl_path_segment_storage->ValidateSegments();
if (!validSegments) {
VLOG_WORK << "Guiding: invalid path segments!";
}
}
/* Write debug render pass to validate it matches combined pass. */
pgl_vec3f pgl_final_color = kg->opgl_path_segment_storage->CalculatePixelEstimate(false);
const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index);
const uint64_t render_buffer_offset = (uint64_t)render_pixel_index *
kernel_data.film.pass_stride;
ccl_global float *buffer = render_buffer + render_buffer_offset;
float3 final_color = make_float3(pgl_final_color.x, pgl_final_color.y, pgl_final_color.z);
if (kernel_data.film.pass_guiding_color != PASS_UNUSED) {
film_write_pass_float3(buffer + kernel_data.film.pass_guiding_color, final_color);
}
# else
(void)state;
(void)render_buffer;
# endif
/* Convert the path segment representation of the random walk into radiance samples. */
# if PATH_GUIDING_LEVEL >= 2
const bool use_direct_light = kernel_data.integrator.use_guiding_direct_light;
const bool use_mis_weights = kernel_data.integrator.use_guiding_mis_weights;
kg->opgl_path_segment_storage->PrepareSamples(
false, nullptr, use_mis_weights, use_direct_light, false);
# endif
# ifdef WITH_CYCLES_DEBUG
/* Check if the training/radiance samples generated py the path segment storage are valid.*/
if (VLOG_WORK_IS_ON) {
const bool validSamples = kg->opgl_path_segment_storage->ValidateSamples();
if (!validSamples) {
VLOG_WORK
<< "Guiding: path segment storage generated/contains invalid radiance/training samples!";
}
}
# endif
# if PATH_GUIDING_LEVEL >= 3
/* Push radiance samples from current random walk/path to the global sample storage. */
size_t num_samples = 0;
const openpgl::cpp::SampleData *samples = kg->opgl_path_segment_storage->GetSamples(num_samples);
kg->opgl_sample_data_storage->AddSamples(samples, num_samples);
# endif
/* Clear storage for the current path, to be ready for the next path. */
kg->opgl_path_segment_storage->Clear();
}
#endif
CCL_NAMESPACE_END

View File

@@ -16,6 +16,7 @@ CCL_NAMESPACE_BEGIN
struct KernelWorkTile;
struct KernelGlobalsCPU;
struct IntegratorStateCPU;
class CPUKernels;
@@ -50,6 +51,22 @@ class PathTraceWorkCPU : public PathTraceWork {
virtual int adaptive_sampling_converge_filter_count_active(float threshold, bool reset) override;
virtual void cryptomatte_postproces() override;
#ifdef WITH_PATH_GUIDING
/* Initializes the per-thread guiding kernel data. The function sets the pointers to the
* global guiding field and the sample data storage as well es initializes the per-thread
* guided sampling distributions (e.g., SurfaceSamplingDistribution and
* VolumeSamplingDistribution). */
void guiding_init_kernel_globals(void *guiding_field,
void *sample_data_storage,
const bool train) override;
/* Pushes the collected training data/samples of a path to the global sample storage.
* This function is called at the end of a random walk/path generation. */
void guiding_push_sample_data_to_global_storage(KernelGlobalsCPU *kernel_globals,
IntegratorStateCPU *state,
ccl_global float *ccl_restrict render_buffer);
#endif
protected:
/* Core path tracing routine. Renders given work time on the given queue. */
void render_samples_full_pipeline(KernelGlobalsCPU *kernel_globals,

View File

@@ -45,6 +45,11 @@ void RenderScheduler::set_denoiser_params(const DenoiseParams &params)
denoiser_params_ = params;
}
void RenderScheduler::set_limit_samples_per_update(const int limit_samples)
{
limit_samples_per_update_ = limit_samples;
}
void RenderScheduler::set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling)
{
adaptive_sampling_ = adaptive_sampling;
@@ -760,7 +765,13 @@ int RenderScheduler::calculate_num_samples_per_update() const
const double update_interval_in_seconds = guess_display_update_interval_in_seconds();
return max(int(num_samples_in_second * update_interval_in_seconds), 1);
int num_samples_per_update = max(int(num_samples_in_second * update_interval_in_seconds), 1);
if (limit_samples_per_update_) {
num_samples_per_update = min(limit_samples_per_update_, num_samples_per_update);
}
return num_samples_per_update;
}
int RenderScheduler::get_start_sample_to_path_trace() const
@@ -808,7 +819,7 @@ int RenderScheduler::get_num_samples_to_path_trace() const
return 1;
}
const int num_samples_per_update = calculate_num_samples_per_update();
int num_samples_per_update = calculate_num_samples_per_update();
const int path_trace_start_sample = get_start_sample_to_path_trace();
/* Round number of samples to a power of two, so that division of path states into tiles goes in

View File

@@ -187,6 +187,8 @@ class RenderScheduler {
* times, and so on. */
string full_report() const;
void set_limit_samples_per_update(const int limit_samples);
protected:
/* Check whether all work has been scheduled and time limit was not exceeded.
*
@@ -450,6 +452,10 @@ class RenderScheduler {
* (quadratic dependency from the resolution divider): resolution divider of 2 brings render time
* down by a factor of 4. */
int calculate_resolution_divider_for_time(double desired_time, double actual_time);
/* If the number of samples per rendering progression should be limited because of path guiding
* being activated or is still inside its training phase */
int limit_samples_per_update_ = 0;
};
int calculate_resolution_divider_for_resolution(int width, int height, int resolution);

View File

@@ -243,6 +243,7 @@ set(SRC_KERNEL_INTEGRATOR_HEADERS
integrator/intersect_shadow.h
integrator/intersect_subsurface.h
integrator/intersect_volume_stack.h
integrator/guiding.h
integrator/megakernel.h
integrator/mnee.h
integrator/path_state.h

View File

@@ -69,7 +69,11 @@ ccl_device_inline float bsdf_get_roughness_squared(ccl_private const ShaderClosu
* Yining Karl Li and Brent Burley. */
ccl_device_inline float bump_shadowing_term(float3 Ng, float3 N, float3 I)
{
float g = safe_divide(dot(Ng, I), dot(N, I) * dot(Ng, N));
const float cosNI = dot(N, I);
if (cosNI < 0.0f) {
Ng = -Ng;
}
float g = safe_divide(dot(Ng, I), cosNI * dot(Ng, N));
/* If the incoming light is on the unshadowed side, return full brightness. */
if (g >= 1.0f) {
@@ -98,6 +102,12 @@ ccl_device_inline float shift_cos_in(float cos_in, const float frequency_multipl
return val;
}
ccl_device_inline bool bsdf_is_transmission(ccl_private const ShaderClosure *sc,
const float3 omega_in)
{
return dot(sc->N, omega_in) < 0.0f;
}
ccl_device_inline int bsdf_sample(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private const ShaderClosure *sc,
@@ -105,7 +115,9 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
ccl_private float2 *sampled_roughness,
ccl_private float *eta)
{
/* For curves use the smooth normal, particularly for ribbons the geometric
* normal gives too much darkening otherwise. */
@@ -115,78 +127,131 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg,
switch (sc->type) {
case CLOSURE_BSDF_DIFFUSE_ID:
label = bsdf_diffuse_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
*sampled_roughness = one_float2();
*eta = 1.0f;
break;
#if defined(__SVM__) || defined(__OSL__)
case CLOSURE_BSDF_OREN_NAYAR_ID:
label = bsdf_oren_nayar_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
*sampled_roughness = one_float2();
*eta = 1.0f;
break;
# ifdef __OSL__
case CLOSURE_BSDF_PHONG_RAMP_ID:
label = bsdf_phong_ramp_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
label = bsdf_phong_ramp_sample(
sc, Ng, sd->I, randu, randv, eval, omega_in, pdf, sampled_roughness);
*eta = 1.0f;
break;
case CLOSURE_BSDF_DIFFUSE_RAMP_ID:
label = bsdf_diffuse_ramp_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
*sampled_roughness = one_float2();
*eta = 1.0f;
break;
# endif
case CLOSURE_BSDF_TRANSLUCENT_ID:
label = bsdf_translucent_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
*sampled_roughness = one_float2();
*eta = 1.0f;
break;
case CLOSURE_BSDF_REFLECTION_ID:
label = bsdf_reflection_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
label = bsdf_reflection_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf, eta);
*sampled_roughness = zero_float2();
break;
case CLOSURE_BSDF_REFRACTION_ID:
label = bsdf_refraction_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
label = bsdf_refraction_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf, eta);
*sampled_roughness = zero_float2();
break;
case CLOSURE_BSDF_TRANSPARENT_ID:
label = bsdf_transparent_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
*sampled_roughness = zero_float2();
*eta = 1.0f;
break;
case CLOSURE_BSDF_MICROFACET_GGX_ID:
case CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID:
case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID:
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
label = bsdf_microfacet_ggx_sample(kg, sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
label = bsdf_microfacet_ggx_sample(
kg, sc, Ng, sd->I, randu, randv, eval, omega_in, pdf, sampled_roughness, eta);
break;
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID:
label = bsdf_microfacet_multi_ggx_sample(
kg, sc, Ng, sd->I, randu, randv, eval, omega_in, pdf, &sd->lcg_state);
label = bsdf_microfacet_multi_ggx_sample(kg,
sc,
Ng,
sd->I,
randu,
randv,
eval,
omega_in,
pdf,
&sd->lcg_state,
sampled_roughness,
eta);
break;
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID:
label = bsdf_microfacet_multi_ggx_glass_sample(
kg, sc, Ng, sd->I, randu, randv, eval, omega_in, pdf, &sd->lcg_state);
label = bsdf_microfacet_multi_ggx_glass_sample(kg,
sc,
Ng,
sd->I,
randu,
randv,
eval,
omega_in,
pdf,
&sd->lcg_state,
sampled_roughness,
eta);
break;
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
label = bsdf_microfacet_beckmann_sample(
kg, sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
kg, sc, Ng, sd->I, randu, randv, eval, omega_in, pdf, sampled_roughness, eta);
break;
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
label = bsdf_ashikhmin_shirley_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
label = bsdf_ashikhmin_shirley_sample(
sc, Ng, sd->I, randu, randv, eval, omega_in, pdf, sampled_roughness);
*eta = 1.0f;
break;
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
label = bsdf_ashikhmin_velvet_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
*sampled_roughness = one_float2();
*eta = 1.0f;
break;
case CLOSURE_BSDF_DIFFUSE_TOON_ID:
label = bsdf_diffuse_toon_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
*sampled_roughness = one_float2();
*eta = 1.0f;
break;
case CLOSURE_BSDF_GLOSSY_TOON_ID:
label = bsdf_glossy_toon_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
// double check if this is valid
*sampled_roughness = one_float2();
*eta = 1.0f;
break;
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
label = bsdf_hair_reflection_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
label = bsdf_hair_reflection_sample(
sc, Ng, sd->I, randu, randv, eval, omega_in, pdf, sampled_roughness);
*eta = 1.0f;
break;
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID:
label = bsdf_hair_transmission_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
label = bsdf_hair_transmission_sample(
sc, Ng, sd->I, randu, randv, eval, omega_in, pdf, sampled_roughness);
*eta = 1.0f;
break;
case CLOSURE_BSDF_HAIR_PRINCIPLED_ID:
label = bsdf_principled_hair_sample(kg, sc, sd, randu, randv, eval, omega_in, pdf);
label = bsdf_principled_hair_sample(
kg, sc, sd, randu, randv, eval, omega_in, pdf, sampled_roughness, eta);
break;
case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID:
label = bsdf_principled_diffuse_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
*sampled_roughness = one_float2();
*eta = 1.0f;
break;
case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID:
label = bsdf_principled_sheen_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
*sampled_roughness = one_float2();
*eta = 1.0f;
break;
#endif
default:
@@ -209,11 +274,12 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg,
const float frequency_multiplier =
kernel_data_fetch(objects, sd->object).shadow_terminator_shading_offset;
if (frequency_multiplier > 1.0f) {
*eval *= shift_cos_in(dot(*omega_in, sc->N), frequency_multiplier);
const float cosNI = dot(*omega_in, sc->N);
*eval *= shift_cos_in(cosNI, frequency_multiplier);
}
if (label & LABEL_DIFFUSE) {
if (!isequal(sc->N, sd->N)) {
*eval *= bump_shadowing_term((label & LABEL_TRANSMIT) ? -sd->N : sd->N, sc->N, *omega_in);
*eval *= bump_shadowing_term(sd->N, sc->N, *omega_in);
}
}
}
@@ -226,6 +292,246 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg,
return label;
}
ccl_device_inline void bsdf_roughness_eta(const KernelGlobals kg,
ccl_private const ShaderClosure *sc,
ccl_private float2 *roughness,
ccl_private float *eta)
{
bool refractive = false;
float alpha = 1.0f;
switch (sc->type) {
case CLOSURE_BSDF_DIFFUSE_ID:
*roughness = one_float2();
*eta = 1.0f;
break;
#ifdef __SVM__
case CLOSURE_BSDF_OREN_NAYAR_ID:
*roughness = one_float2();
*eta = 1.0f;
break;
# ifdef __OSL__
case CLOSURE_BSDF_PHONG_RAMP_ID:
alpha = phong_ramp_exponent_to_roughness(((ccl_private const PhongRampBsdf *)sc)->exponent);
*roughness = make_float2(alpha, alpha);
*eta = 1.0f;
break;
case CLOSURE_BSDF_DIFFUSE_RAMP_ID:
*roughness = one_float2();
*eta = 1.0f;
break;
# endif
case CLOSURE_BSDF_TRANSLUCENT_ID:
*roughness = one_float2();
*eta = 1.0f;
break;
case CLOSURE_BSDF_REFLECTION_ID: {
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
*roughness = zero_float2();
*eta = bsdf->ior;
break;
}
case CLOSURE_BSDF_REFRACTION_ID: {
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
*roughness = zero_float2();
// do we need to inverse eta??
*eta = bsdf->ior;
break;
}
case CLOSURE_BSDF_TRANSPARENT_ID:
*roughness = zero_float2();
*eta = 1.0f;
break;
case CLOSURE_BSDF_MICROFACET_GGX_ID:
case CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID:
case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID:
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: {
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
*roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
*eta = refractive ? 1.0f / bsdf->ior : bsdf->ior;
break;
}
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID: {
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
*roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
*eta = bsdf->ior;
break;
}
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID: {
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
*roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
*eta = bsdf->ior;
break;
}
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID: {
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
*roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
*eta = refractive ? 1.0f / bsdf->ior : bsdf->ior;
} break;
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID: {
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
*roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
*eta = 1.0f;
break;
}
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
*roughness = one_float2();
*eta = 1.0f;
break;
case CLOSURE_BSDF_DIFFUSE_TOON_ID:
*roughness = one_float2();
*eta = 1.0f;
break;
case CLOSURE_BSDF_GLOSSY_TOON_ID:
// double check if this is valid
*roughness = one_float2();
*eta = 1.0f;
break;
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
*roughness = make_float2(((ccl_private HairBsdf *)sc)->roughness1,
((ccl_private HairBsdf *)sc)->roughness2);
*eta = 1.0f;
break;
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID:
*roughness = make_float2(((ccl_private HairBsdf *)sc)->roughness1,
((ccl_private HairBsdf *)sc)->roughness2);
*eta = 1.0f;
break;
case CLOSURE_BSDF_HAIR_PRINCIPLED_ID:
alpha = ((ccl_private PrincipledHairBSDF *)sc)->m0_roughness;
*roughness = make_float2(alpha, alpha);
*eta = ((ccl_private PrincipledHairBSDF *)sc)->eta;
break;
case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID:
*roughness = one_float2();
*eta = 1.0f;
break;
case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID:
*roughness = one_float2();
*eta = 1.0f;
break;
#endif
default:
*roughness = one_float2();
*eta = 1.0f;
break;
}
}
ccl_device_inline int bsdf_label(const KernelGlobals kg,
ccl_private const ShaderClosure *sc,
const float3 omega_in)
{
/* For curves use the smooth normal, particularly for ribbons the geometric
* normal gives too much darkening otherwise. */
int label;
switch (sc->type) {
case CLOSURE_BSDF_DIFFUSE_ID:
case CLOSURE_BSSRDF_BURLEY_ID:
case CLOSURE_BSSRDF_RANDOM_WALK_ID:
case CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID:
label = LABEL_REFLECT | LABEL_DIFFUSE;
break;
#ifdef __SVM__
case CLOSURE_BSDF_OREN_NAYAR_ID:
label = LABEL_REFLECT | LABEL_DIFFUSE;
break;
# ifdef __OSL__
case CLOSURE_BSDF_PHONG_RAMP_ID:
label = LABEL_REFLECT | LABEL_GLOSSY;
break;
case CLOSURE_BSDF_DIFFUSE_RAMP_ID:
label = LABEL_REFLECT | LABEL_DIFFUSE;
break;
# endif
case CLOSURE_BSDF_TRANSLUCENT_ID:
label = LABEL_TRANSMIT | LABEL_DIFFUSE;
break;
case CLOSURE_BSDF_REFLECTION_ID:
label = LABEL_REFLECT | LABEL_SINGULAR;
break;
case CLOSURE_BSDF_REFRACTION_ID:
label = LABEL_TRANSMIT | LABEL_SINGULAR;
break;
case CLOSURE_BSDF_TRANSPARENT_ID:
label = LABEL_TRANSMIT | LABEL_TRANSPARENT;
break;
case CLOSURE_BSDF_MICROFACET_GGX_ID:
case CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID:
case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID: {
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
label = (bsdf->alpha_x * bsdf->alpha_y <= 1e-7f) ? LABEL_REFLECT | LABEL_SINGULAR :
LABEL_REFLECT | LABEL_GLOSSY;
break;
}
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID: {
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
label = (bsdf->alpha_x * bsdf->alpha_y <= 1e-7f) ? LABEL_TRANSMIT | LABEL_SINGULAR :
LABEL_TRANSMIT | LABEL_GLOSSY;
break;
}
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID:
label = (bsdf_is_transmission(sc, omega_in)) ? LABEL_TRANSMIT | LABEL_GLOSSY :
LABEL_REFLECT | LABEL_GLOSSY;
break;
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
label = LABEL_REFLECT | LABEL_GLOSSY;
break;
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
label = LABEL_REFLECT | LABEL_DIFFUSE;
break;
case CLOSURE_BSDF_DIFFUSE_TOON_ID:
label = LABEL_REFLECT | LABEL_DIFFUSE;
break;
case CLOSURE_BSDF_GLOSSY_TOON_ID:
label = LABEL_REFLECT | LABEL_GLOSSY;
break;
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
label = LABEL_REFLECT | LABEL_GLOSSY;
break;
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID:
label = LABEL_TRANSMIT | LABEL_GLOSSY;
break;
case CLOSURE_BSDF_HAIR_PRINCIPLED_ID:
if (bsdf_is_transmission(sc, omega_in))
label = LABEL_TRANSMIT | LABEL_GLOSSY;
else
label = LABEL_REFLECT | LABEL_GLOSSY;
break;
case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID:
label = LABEL_REFLECT | LABEL_DIFFUSE;
break;
case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID:
label = LABEL_REFLECT | LABEL_DIFFUSE;
break;
#endif
default:
label = LABEL_NONE;
break;
}
/* Test if BSDF sample should be treated as transparent for background. */
if (label & LABEL_TRANSMIT) {
float threshold_squared = kernel_data.background.transparent_roughness_squared_threshold;
if (threshold_squared >= 0.0f) {
if (bsdf_get_specular_roughness_squared(sc) <= threshold_squared) {
label |= LABEL_TRANSMIT_TRANSPARENT;
}
}
}
return label;
}
#ifndef __KERNEL_CUDA__
ccl_device
#else
@@ -236,179 +542,104 @@ ccl_device_inline
ccl_private ShaderData *sd,
ccl_private const ShaderClosure *sc,
const float3 omega_in,
const bool is_transmission,
ccl_private float *pdf)
{
Spectrum eval = zero_spectrum();
if (!is_transmission) {
switch (sc->type) {
case CLOSURE_BSDF_DIFFUSE_ID:
eval = bsdf_diffuse_eval_reflect(sc, sd->I, omega_in, pdf);
break;
switch (sc->type) {
case CLOSURE_BSDF_DIFFUSE_ID:
eval = bsdf_diffuse_eval(sc, sd->I, omega_in, pdf);
break;
#if defined(__SVM__) || defined(__OSL__)
case CLOSURE_BSDF_OREN_NAYAR_ID:
eval = bsdf_oren_nayar_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_OREN_NAYAR_ID:
eval = bsdf_oren_nayar_eval(sc, sd->I, omega_in, pdf);
break;
# ifdef __OSL__
case CLOSURE_BSDF_PHONG_RAMP_ID:
eval = bsdf_phong_ramp_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_DIFFUSE_RAMP_ID:
eval = bsdf_diffuse_ramp_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_PHONG_RAMP_ID:
eval = bsdf_phong_ramp_eval(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_DIFFUSE_RAMP_ID:
eval = bsdf_diffuse_ramp_eval(sc, sd->I, omega_in, pdf);
break;
# endif
case CLOSURE_BSDF_TRANSLUCENT_ID:
eval = bsdf_translucent_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_REFLECTION_ID:
eval = bsdf_reflection_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_REFRACTION_ID:
eval = bsdf_refraction_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_TRANSPARENT_ID:
eval = bsdf_transparent_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_MICROFACET_GGX_ID:
case CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID:
case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID:
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
eval = bsdf_microfacet_ggx_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID:
eval = bsdf_microfacet_multi_ggx_eval_reflect(sc, sd->I, omega_in, pdf, &sd->lcg_state);
break;
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID:
eval = bsdf_microfacet_multi_ggx_glass_eval_reflect(
sc, sd->I, omega_in, pdf, &sd->lcg_state);
break;
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
eval = bsdf_microfacet_beckmann_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
eval = bsdf_ashikhmin_shirley_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
eval = bsdf_ashikhmin_velvet_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_DIFFUSE_TOON_ID:
eval = bsdf_diffuse_toon_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_GLOSSY_TOON_ID:
eval = bsdf_glossy_toon_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_HAIR_PRINCIPLED_ID:
eval = bsdf_principled_hair_eval(kg, sd, sc, omega_in, pdf);
break;
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
eval = bsdf_hair_reflection_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID:
eval = bsdf_hair_transmission_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID:
eval = bsdf_principled_diffuse_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID:
eval = bsdf_principled_sheen_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_TRANSLUCENT_ID:
eval = bsdf_translucent_eval(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_REFLECTION_ID:
eval = bsdf_reflection_eval(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_REFRACTION_ID:
eval = bsdf_refraction_eval(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_TRANSPARENT_ID:
eval = bsdf_transparent_eval(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_MICROFACET_GGX_ID:
case CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID:
case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID:
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
eval = bsdf_microfacet_ggx_eval(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID:
eval = bsdf_microfacet_multi_ggx_eval(sc, sd->I, omega_in, pdf, &sd->lcg_state);
break;
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID:
eval = bsdf_microfacet_multi_ggx_glass_eval(sc, sd->I, omega_in, pdf, &sd->lcg_state);
break;
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
eval = bsdf_microfacet_beckmann_eval(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
eval = bsdf_ashikhmin_shirley_eval(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
eval = bsdf_ashikhmin_velvet_eval(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_DIFFUSE_TOON_ID:
eval = bsdf_diffuse_toon_eval(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_GLOSSY_TOON_ID:
eval = bsdf_glossy_toon_eval(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_HAIR_PRINCIPLED_ID:
eval = bsdf_principled_hair_eval(kg, sd, sc, omega_in, pdf);
break;
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
eval = bsdf_hair_reflection_eval(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID:
eval = bsdf_hair_transmission_eval(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID:
eval = bsdf_principled_diffuse_eval(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID:
eval = bsdf_principled_sheen_eval(sc, sd->I, omega_in, pdf);
break;
#endif
default:
break;
}
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) {
if (!isequal(sc->N, sd->N)) {
eval *= bump_shadowing_term(sd->N, sc->N, omega_in);
}
}
/* Shadow terminator offset. */
const float frequency_multiplier =
kernel_data_fetch(objects, sd->object).shadow_terminator_shading_offset;
if (frequency_multiplier > 1.0f) {
eval *= shift_cos_in(dot(omega_in, sc->N), frequency_multiplier);
default:
break;
}
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) {
if (!isequal(sc->N, sd->N)) {
eval *= bump_shadowing_term(sd->N, sc->N, omega_in);
}
}
else {
switch (sc->type) {
case CLOSURE_BSDF_DIFFUSE_ID:
eval = bsdf_diffuse_eval_transmit(sc, sd->I, omega_in, pdf);
break;
#if defined(__SVM__) || defined(__OSL__)
case CLOSURE_BSDF_OREN_NAYAR_ID:
eval = bsdf_oren_nayar_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_TRANSLUCENT_ID:
eval = bsdf_translucent_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_REFLECTION_ID:
eval = bsdf_reflection_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_REFRACTION_ID:
eval = bsdf_refraction_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_TRANSPARENT_ID:
eval = bsdf_transparent_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_MICROFACET_GGX_ID:
case CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID:
case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID:
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
eval = bsdf_microfacet_ggx_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID:
eval = bsdf_microfacet_multi_ggx_eval_transmit(sc, sd->I, omega_in, pdf, &sd->lcg_state);
break;
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID:
eval = bsdf_microfacet_multi_ggx_glass_eval_transmit(
sc, sd->I, omega_in, pdf, &sd->lcg_state);
break;
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
eval = bsdf_microfacet_beckmann_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
eval = bsdf_ashikhmin_shirley_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
eval = bsdf_ashikhmin_velvet_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_DIFFUSE_TOON_ID:
eval = bsdf_diffuse_toon_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_GLOSSY_TOON_ID:
eval = bsdf_glossy_toon_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_HAIR_PRINCIPLED_ID:
eval = bsdf_principled_hair_eval(kg, sd, sc, omega_in, pdf);
break;
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
eval = bsdf_hair_reflection_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID:
eval = bsdf_hair_transmission_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID:
eval = bsdf_principled_diffuse_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID:
eval = bsdf_principled_sheen_eval_transmit(sc, sd->I, omega_in, pdf);
break;
#endif
default:
break;
}
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) {
if (!isequal(sc->N, sd->N)) {
eval *= bump_shadowing_term(-sd->N, sc->N, omega_in);
}
/* Shadow terminator offset. */
const float frequency_multiplier =
kernel_data_fetch(objects, sd->object).shadow_terminator_shading_offset;
if (frequency_multiplier > 1.0f) {
const float cosNI = dot(omega_in, sc->N);
if (cosNI >= 0.0f) {
eval *= shift_cos_in(cosNI, frequency_multiplier);
}
}
#ifdef WITH_CYCLES_DEBUG
kernel_assert(*pdf >= 0.0f);
kernel_assert(eval.x >= 0.0f && eval.y >= 0.0f && eval.z >= 0.0f);

View File

@@ -39,11 +39,10 @@ ccl_device_inline float bsdf_ashikhmin_shirley_roughness_to_exponent(float rough
return 2.0f / (roughness * roughness) - 2.0f;
}
ccl_device_forceinline Spectrum
bsdf_ashikhmin_shirley_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device_forceinline Spectrum bsdf_ashikhmin_shirley_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
float3 N = bsdf->N;
@@ -53,70 +52,60 @@ bsdf_ashikhmin_shirley_eval_reflect(ccl_private const ShaderClosure *sc,
float out = 0.0f;
if (fmaxf(bsdf->alpha_x, bsdf->alpha_y) <= 1e-4f) {
if (fmaxf(bsdf->alpha_x, bsdf->alpha_y) <= 1e-4f || !(NdotI > 0.0f && NdotO > 0.0f)) {
*pdf = 0.0f;
return zero_spectrum();
}
if (NdotI > 0.0f && NdotO > 0.0f) {
NdotI = fmaxf(NdotI, 1e-6f);
NdotO = fmaxf(NdotO, 1e-6f);
float3 H = normalize(omega_in + I);
float HdotI = fmaxf(fabsf(dot(H, I)), 1e-6f);
float HdotN = fmaxf(dot(H, N), 1e-6f);
/* pump from original paper
* (first derivative disc., but cancels the HdotI in the pdf nicely) */
float pump = 1.0f / fmaxf(1e-6f, (HdotI * fmaxf(NdotO, NdotI)));
/* pump from d-brdf paper */
/*float pump = 1.0f / fmaxf(1e-4f, ((NdotO + NdotI) * (NdotO*NdotI))); */
NdotI = fmaxf(NdotI, 1e-6f);
NdotO = fmaxf(NdotO, 1e-6f);
float3 H = normalize(omega_in + I);
float HdotI = fmaxf(fabsf(dot(H, I)), 1e-6f);
float HdotN = fmaxf(dot(H, N), 1e-6f);
float n_x = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_x);
float n_y = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_y);
/* pump from original paper
* (first derivative disc., but cancels the HdotI in the pdf nicely) */
float pump = 1.0f / fmaxf(1e-6f, (HdotI * fmaxf(NdotO, NdotI)));
/* pump from d-brdf paper */
/*float pump = 1.0f / fmaxf(1e-4f, ((NdotO + NdotI) * (NdotO*NdotI))); */
if (n_x == n_y) {
/* isotropic */
float e = n_x;
float lobe = powf(HdotN, e);
float norm = (n_x + 1.0f) / (8.0f * M_PI_F);
float n_x = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_x);
float n_y = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_y);
out = NdotO * norm * lobe * pump;
/* this is p_h / 4(H.I) (conversion from 'wh measure' to 'wi measure', eq. 8 in paper). */
*pdf = norm * lobe / HdotI;
if (n_x == n_y) {
/* isotropic */
float e = n_x;
float lobe = powf(HdotN, e);
float norm = (n_x + 1.0f) / (8.0f * M_PI_F);
out = NdotO * norm * lobe * pump;
/* this is p_h / 4(H.I) (conversion from 'wh measure' to 'wi measure', eq. 8 in paper). */
*pdf = norm * lobe / HdotI;
}
else {
/* anisotropic */
float3 X, Y;
make_orthonormals_tangent(N, bsdf->T, &X, &Y);
float HdotX = dot(H, X);
float HdotY = dot(H, Y);
float lobe;
if (HdotN < 1.0f) {
float e = (n_x * HdotX * HdotX + n_y * HdotY * HdotY) / (1.0f - HdotN * HdotN);
lobe = powf(HdotN, e);
}
else {
/* anisotropic */
float3 X, Y;
make_orthonormals_tangent(N, bsdf->T, &X, &Y);
float HdotX = dot(H, X);
float HdotY = dot(H, Y);
float lobe;
if (HdotN < 1.0f) {
float e = (n_x * HdotX * HdotX + n_y * HdotY * HdotY) / (1.0f - HdotN * HdotN);
lobe = powf(HdotN, e);
}
else {
lobe = 1.0f;
}
float norm = sqrtf((n_x + 1.0f) * (n_y + 1.0f)) / (8.0f * M_PI_F);
out = NdotO * norm * lobe * pump;
*pdf = norm * lobe / HdotI;
lobe = 1.0f;
}
float norm = sqrtf((n_x + 1.0f) * (n_y + 1.0f)) / (8.0f * M_PI_F);
out = NdotO * norm * lobe * pump;
*pdf = norm * lobe / HdotI;
}
return make_spectrum(out);
}
ccl_device Spectrum bsdf_ashikhmin_shirley_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
}
ccl_device_inline void bsdf_ashikhmin_shirley_sample_first_quadrant(float n_x,
float n_y,
float randu,
@@ -137,88 +126,93 @@ ccl_device int bsdf_ashikhmin_shirley_sample(ccl_private const ShaderClosure *sc
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
ccl_private float2 *sampled_roughness)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
*sampled_roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
float3 N = bsdf->N;
int label = LABEL_REFLECT | LABEL_GLOSSY;
float NdotI = dot(N, I);
if (NdotI > 0.0f) {
if (!(NdotI > 0.0f)) {
*pdf = 0.0f;
*eval = zero_spectrum();
return LABEL_NONE;
}
float n_x = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_x);
float n_y = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_y);
float n_x = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_x);
float n_y = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_y);
/* get x,y basis on the surface for anisotropy */
float3 X, Y;
/* get x,y basis on the surface for anisotropy */
float3 X, Y;
if (n_x == n_y)
make_orthonormals(N, &X, &Y);
else
make_orthonormals_tangent(N, bsdf->T, &X, &Y);
if (n_x == n_y)
make_orthonormals(N, &X, &Y);
else
make_orthonormals_tangent(N, bsdf->T, &X, &Y);
/* sample spherical coords for h in tangent space */
float phi;
float cos_theta;
if (n_x == n_y) {
/* isotropic sampling */
phi = M_2PI_F * randu;
cos_theta = powf(randv, 1.0f / (n_x + 1.0f));
/* sample spherical coords for h in tangent space */
float phi;
float cos_theta;
if (n_x == n_y) {
/* isotropic sampling */
phi = M_2PI_F * randu;
cos_theta = powf(randv, 1.0f / (n_x + 1.0f));
}
else {
/* anisotropic sampling */
if (randu < 0.25f) { /* first quadrant */
float remapped_randu = 4.0f * randu;
bsdf_ashikhmin_shirley_sample_first_quadrant(
n_x, n_y, remapped_randu, randv, &phi, &cos_theta);
}
else {
/* anisotropic sampling */
if (randu < 0.25f) { /* first quadrant */
float remapped_randu = 4.0f * randu;
bsdf_ashikhmin_shirley_sample_first_quadrant(
n_x, n_y, remapped_randu, randv, &phi, &cos_theta);
}
else if (randu < 0.5f) { /* second quadrant */
float remapped_randu = 4.0f * (.5f - randu);
bsdf_ashikhmin_shirley_sample_first_quadrant(
n_x, n_y, remapped_randu, randv, &phi, &cos_theta);
phi = M_PI_F - phi;
}
else if (randu < 0.75f) { /* third quadrant */
float remapped_randu = 4.0f * (randu - 0.5f);
bsdf_ashikhmin_shirley_sample_first_quadrant(
n_x, n_y, remapped_randu, randv, &phi, &cos_theta);
phi = M_PI_F + phi;
}
else { /* fourth quadrant */
float remapped_randu = 4.0f * (1.0f - randu);
bsdf_ashikhmin_shirley_sample_first_quadrant(
n_x, n_y, remapped_randu, randv, &phi, &cos_theta);
phi = 2.0f * M_PI_F - phi;
}
else if (randu < 0.5f) { /* second quadrant */
float remapped_randu = 4.0f * (.5f - randu);
bsdf_ashikhmin_shirley_sample_first_quadrant(
n_x, n_y, remapped_randu, randv, &phi, &cos_theta);
phi = M_PI_F - phi;
}
/* get half vector in tangent space */
float sin_theta = sqrtf(fmaxf(0.0f, 1.0f - cos_theta * cos_theta));
float cos_phi = cosf(phi);
float sin_phi = sinf(phi); /* no sqrt(1-cos^2) here b/c it causes artifacts */
float3 h = make_float3(sin_theta * cos_phi, sin_theta * sin_phi, cos_theta);
/* half vector to world space */
float3 H = h.x * X + h.y * Y + h.z * N;
float HdotI = dot(H, I);
if (HdotI < 0.0f)
H = -H;
/* reflect I on H to get omega_in */
*omega_in = -I + (2.0f * HdotI) * H;
if (fmaxf(bsdf->alpha_x, bsdf->alpha_y) <= 1e-4f) {
/* Some high number for MIS. */
*pdf = 1e6f;
*eval = make_spectrum(1e6f);
label = LABEL_REFLECT | LABEL_SINGULAR;
else if (randu < 0.75f) { /* third quadrant */
float remapped_randu = 4.0f * (randu - 0.5f);
bsdf_ashikhmin_shirley_sample_first_quadrant(
n_x, n_y, remapped_randu, randv, &phi, &cos_theta);
phi = M_PI_F + phi;
}
else {
/* leave the rest to eval_reflect */
*eval = bsdf_ashikhmin_shirley_eval_reflect(sc, I, *omega_in, pdf);
else { /* fourth quadrant */
float remapped_randu = 4.0f * (1.0f - randu);
bsdf_ashikhmin_shirley_sample_first_quadrant(
n_x, n_y, remapped_randu, randv, &phi, &cos_theta);
phi = 2.0f * M_PI_F - phi;
}
}
/* get half vector in tangent space */
float sin_theta = sqrtf(fmaxf(0.0f, 1.0f - cos_theta * cos_theta));
float cos_phi = cosf(phi);
float sin_phi = sinf(phi); /* no sqrt(1-cos^2) here b/c it causes artifacts */
float3 h = make_float3(sin_theta * cos_phi, sin_theta * sin_phi, cos_theta);
/* half vector to world space */
float3 H = h.x * X + h.y * Y + h.z * N;
float HdotI = dot(H, I);
if (HdotI < 0.0f)
H = -H;
/* reflect I on H to get omega_in */
*omega_in = -I + (2.0f * HdotI) * H;
if (fmaxf(bsdf->alpha_x, bsdf->alpha_y) <= 1e-4f) {
/* Some high number for MIS. */
*pdf = 1e6f;
*eval = make_spectrum(1e6f);
label = LABEL_REFLECT | LABEL_SINGULAR;
}
else {
/* leave the rest to eval */
*eval = bsdf_ashikhmin_shirley_eval(sc, I, *omega_in, pdf);
}
return label;
}

View File

@@ -31,10 +31,10 @@ ccl_device int bsdf_ashikhmin_velvet_setup(ccl_private VelvetBsdf *bsdf)
return SD_BSDF | SD_BSDF_HAS_EVAL;
}
ccl_device Spectrum bsdf_ashikhmin_velvet_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_ashikhmin_velvet_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
ccl_private const VelvetBsdf *bsdf = (ccl_private const VelvetBsdf *)sc;
float m_invsigma2 = bsdf->invsigma2;
@@ -42,46 +42,37 @@ ccl_device Spectrum bsdf_ashikhmin_velvet_eval_reflect(ccl_private const ShaderC
float cosNO = dot(N, I);
float cosNI = dot(N, omega_in);
if (cosNO > 0 && cosNI > 0) {
float3 H = normalize(omega_in + I);
float cosNH = dot(N, H);
float cosHO = fabsf(dot(I, H));
if (!(fabsf(cosNH) < 1.0f - 1e-5f && cosHO > 1e-5f)) {
*pdf = 0.0f;
return zero_spectrum();
}
float cosNHdivHO = cosNH / cosHO;
cosNHdivHO = fmaxf(cosNHdivHO, 1e-5f);
float fac1 = 2 * fabsf(cosNHdivHO * cosNO);
float fac2 = 2 * fabsf(cosNHdivHO * cosNI);
float sinNH2 = 1 - cosNH * cosNH;
float sinNH4 = sinNH2 * sinNH2;
float cotangent2 = (cosNH * cosNH) / sinNH2;
float D = expf(-cotangent2 * m_invsigma2) * m_invsigma2 * M_1_PI_F / sinNH4;
float G = fminf(1.0f, fminf(fac1, fac2)); // TODO: derive G from D analytically
float out = 0.25f * (D * G) / cosNO;
*pdf = 0.5f * M_1_PI_F;
return make_spectrum(out);
if (!(cosNO > 0 && cosNI > 0)) {
*pdf = 0.0f;
return zero_spectrum();
}
*pdf = 0.0f;
return zero_spectrum();
}
float3 H = normalize(omega_in + I);
ccl_device Spectrum bsdf_ashikhmin_velvet_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
float cosNH = dot(N, H);
float cosHO = fabsf(dot(I, H));
if (!(fabsf(cosNH) < 1.0f - 1e-5f && cosHO > 1e-5f)) {
*pdf = 0.0f;
return zero_spectrum();
}
float cosNHdivHO = cosNH / cosHO;
cosNHdivHO = fmaxf(cosNHdivHO, 1e-5f);
float fac1 = 2 * fabsf(cosNHdivHO * cosNO);
float fac2 = 2 * fabsf(cosNHdivHO * cosNI);
float sinNH2 = 1 - cosNH * cosNH;
float sinNH4 = sinNH2 * sinNH2;
float cotangent2 = (cosNH * cosNH) / sinNH2;
float D = expf(-cotangent2 * m_invsigma2) * m_invsigma2 * M_1_PI_F / sinNH4;
float G = fminf(1.0f, fminf(fac1, fac2)); // TODO: derive G from D analytically
float out = 0.25f * (D * G) / cosNO;
*pdf = 0.5f * M_1_PI_F;
return make_spectrum(out);
}
ccl_device int bsdf_ashikhmin_velvet_sample(ccl_private const ShaderClosure *sc,
@@ -101,41 +92,42 @@ ccl_device int bsdf_ashikhmin_velvet_sample(ccl_private const ShaderClosure *sc,
// distribution over the hemisphere
sample_uniform_hemisphere(N, randu, randv, omega_in, pdf);
if (dot(Ng, *omega_in) > 0) {
float3 H = normalize(*omega_in + I);
float cosNI = dot(N, *omega_in);
float cosNO = dot(N, I);
float cosNH = dot(N, H);
float cosHO = fabsf(dot(I, H));
if (fabsf(cosNO) > 1e-5f && fabsf(cosNH) < 1.0f - 1e-5f && cosHO > 1e-5f) {
float cosNHdivHO = cosNH / cosHO;
cosNHdivHO = fmaxf(cosNHdivHO, 1e-5f);
float fac1 = 2 * fabsf(cosNHdivHO * cosNO);
float fac2 = 2 * fabsf(cosNHdivHO * cosNI);
float sinNH2 = 1 - cosNH * cosNH;
float sinNH4 = sinNH2 * sinNH2;
float cotangent2 = (cosNH * cosNH) / sinNH2;
float D = expf(-cotangent2 * m_invsigma2) * m_invsigma2 * M_1_PI_F / sinNH4;
float G = fminf(1.0f, fminf(fac1, fac2)); // TODO: derive G from D analytically
float power = 0.25f * (D * G) / cosNO;
*eval = make_spectrum(power);
}
else {
*pdf = 0.0f;
*eval = zero_spectrum();
}
}
else {
if (!(dot(Ng, *omega_in) > 0)) {
*pdf = 0.0f;
*eval = zero_spectrum();
return LABEL_NONE;
}
float3 H = normalize(*omega_in + I);
float cosNI = dot(N, *omega_in);
float cosNO = dot(N, I);
float cosNH = dot(N, H);
float cosHO = fabsf(dot(I, H));
if (!(fabsf(cosNO) > 1e-5f && fabsf(cosNH) < 1.0f - 1e-5f && cosHO > 1e-5f)) {
*pdf = 0.0f;
*eval = zero_spectrum();
return LABEL_NONE;
}
float cosNHdivHO = cosNH / cosHO;
cosNHdivHO = fmaxf(cosNHdivHO, 1e-5f);
float fac1 = 2 * fabsf(cosNHdivHO * cosNO);
float fac2 = 2 * fabsf(cosNHdivHO * cosNI);
float sinNH2 = 1 - cosNH * cosNH;
float sinNH4 = sinNH2 * sinNH2;
float cotangent2 = (cosNH * cosNH) / sinNH2;
float D = expf(-cotangent2 * m_invsigma2) * m_invsigma2 * M_1_PI_F / sinNH4;
float G = fminf(1.0f, fminf(fac1, fac2)); // TODO: derive G from D analytically
float power = 0.25f * (D * G) / cosNO;
*eval = make_spectrum(power);
return LABEL_REFLECT | LABEL_DIFFUSE;
}

View File

@@ -26,10 +26,10 @@ ccl_device int bsdf_diffuse_setup(ccl_private DiffuseBsdf *bsdf)
return SD_BSDF | SD_BSDF_HAS_EVAL;
}
ccl_device Spectrum bsdf_diffuse_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_diffuse_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc;
float3 N = bsdf->N;
@@ -39,15 +39,6 @@ ccl_device Spectrum bsdf_diffuse_eval_reflect(ccl_private const ShaderClosure *s
return make_spectrum(cos_pi);
}
ccl_device Spectrum bsdf_diffuse_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
}
ccl_device int bsdf_diffuse_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
@@ -81,19 +72,10 @@ ccl_device int bsdf_translucent_setup(ccl_private DiffuseBsdf *bsdf)
return SD_BSDF | SD_BSDF_HAS_EVAL;
}
ccl_device Spectrum bsdf_translucent_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
}
ccl_device Spectrum bsdf_translucent_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_translucent_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc;
float3 N = bsdf->N;

View File

@@ -47,25 +47,23 @@ ccl_device void bsdf_diffuse_ramp_blur(ccl_private ShaderClosure *sc, float roug
{
}
ccl_device Spectrum bsdf_diffuse_ramp_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_diffuse_ramp_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
const DiffuseRampBsdf *bsdf = (const DiffuseRampBsdf *)sc;
float3 N = bsdf->N;
float cos_pi = fmaxf(dot(N, omega_in), 0.0f);
*pdf = cos_pi * M_1_PI_F;
return rgb_to_spectrum(bsdf_diffuse_ramp_get_color(bsdf->colors, cos_pi) * M_1_PI_F);
}
ccl_device Spectrum bsdf_diffuse_ramp_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
return zero_spectrum();
if (cos_pi >= 0.0f) {
*pdf = cos_pi * M_1_PI_F;
return rgb_to_spectrum(bsdf_diffuse_ramp_get_color(bsdf->colors, cos_pi) * M_1_PI_F);
}
else {
*pdf = 0.0f;
return zero_spectrum();
}
}
ccl_device int bsdf_diffuse_ramp_sample(ccl_private const ShaderClosure *sc,

View File

@@ -37,12 +37,17 @@ ccl_device int bsdf_hair_transmission_setup(ccl_private HairBsdf *bsdf)
return SD_BSDF | SD_BSDF_HAS_EVAL;
}
ccl_device Spectrum bsdf_hair_reflection_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_hair_reflection_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc;
if (dot(bsdf->N, omega_in) < 0.0f) {
*pdf = 0.0f;
return zero_spectrum();
}
float offset = bsdf->offset;
float3 Tg = bsdf->T;
float roughness1 = bsdf->roughness1;
@@ -84,30 +89,17 @@ ccl_device Spectrum bsdf_hair_reflection_eval_reflect(ccl_private const ShaderCl
return make_spectrum(*pdf);
}
ccl_device Spectrum bsdf_hair_transmission_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
}
ccl_device Spectrum bsdf_hair_reflection_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
}
ccl_device Spectrum bsdf_hair_transmission_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_hair_transmission_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc;
if (dot(bsdf->N, omega_in) >= 0.0f) {
*pdf = 0.0f;
return zero_spectrum();
}
float offset = bsdf->offset;
float3 Tg = bsdf->T;
float roughness1 = bsdf->roughness1;
@@ -155,13 +147,15 @@ ccl_device int bsdf_hair_reflection_sample(ccl_private const ShaderClosure *sc,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
ccl_private float2 *sampled_roughness)
{
ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc;
float offset = bsdf->offset;
float3 Tg = bsdf->T;
float roughness1 = bsdf->roughness1;
float roughness2 = bsdf->roughness2;
*sampled_roughness = make_float2(roughness1, roughness2);
float Iz = dot(Tg, I);
float3 locy = normalize(I - Tg * Iz);
float3 locx = cross(locy, Tg);
@@ -206,13 +200,15 @@ ccl_device int bsdf_hair_transmission_sample(ccl_private const ShaderClosure *sc
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
ccl_private float2 *sampled_roughness)
{
ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc;
float offset = bsdf->offset;
float3 Tg = bsdf->T;
float roughness1 = bsdf->roughness1;
float roughness2 = bsdf->roughness2;
*sampled_roughness = make_float2(roughness1, roughness2);
float Iz = dot(Tg, I);
float3 locy = normalize(I - Tg * Iz);
float3 locx = cross(locy, Tg);

View File

@@ -56,13 +56,7 @@ ccl_device_inline float delta_phi(int p, float gamma_o, float gamma_t)
/* Remaps the given angle to [-pi, pi]. */
ccl_device_inline float wrap_angle(float a)
{
while (a > M_PI_F) {
a -= M_2PI_F;
}
while (a < -M_PI_F) {
a += M_2PI_F;
}
return a;
return (a + M_PI_F) - M_2PI_F * floorf((a + M_PI_F) / M_2PI_F) - M_PI_F;
}
/* Logistic distribution function. */
@@ -271,76 +265,72 @@ ccl_device Spectrum bsdf_principled_hair_eval(KernelGlobals kg,
kernel_assert(isfinite_safe(sd->P) && isfinite_safe(sd->ray_length));
ccl_private const PrincipledHairBSDF *bsdf = (ccl_private const PrincipledHairBSDF *)sc;
float3 Y = float4_to_float3(bsdf->extra->geom);
const float3 Y = float4_to_float3(bsdf->extra->geom);
float3 X = safe_normalize(sd->dPdu);
const float3 X = safe_normalize(sd->dPdu);
kernel_assert(fabsf(dot(X, Y)) < 1e-3f);
float3 Z = safe_normalize(cross(X, Y));
const float3 Z = safe_normalize(cross(X, Y));
float3 wo = make_float3(dot(sd->I, X), dot(sd->I, Y), dot(sd->I, Z));
float3 wi = make_float3(dot(omega_in, X), dot(omega_in, Y), dot(omega_in, Z));
const float3 wo = make_float3(dot(sd->I, X), dot(sd->I, Y), dot(sd->I, Z));
const float3 wi = make_float3(dot(omega_in, X), dot(omega_in, Y), dot(omega_in, Z));
float sin_theta_o = wo.x;
float cos_theta_o = cos_from_sin(sin_theta_o);
float phi_o = atan2f(wo.z, wo.y);
const float sin_theta_o = wo.x;
const float cos_theta_o = cos_from_sin(sin_theta_o);
const float phi_o = atan2f(wo.z, wo.y);
float sin_theta_t = sin_theta_o / bsdf->eta;
float cos_theta_t = cos_from_sin(sin_theta_t);
const float sin_theta_t = sin_theta_o / bsdf->eta;
const float cos_theta_t = cos_from_sin(sin_theta_t);
float sin_gamma_o = bsdf->extra->geom.w;
float cos_gamma_o = cos_from_sin(sin_gamma_o);
float gamma_o = safe_asinf(sin_gamma_o);
const float sin_gamma_o = bsdf->extra->geom.w;
const float cos_gamma_o = cos_from_sin(sin_gamma_o);
const float gamma_o = safe_asinf(sin_gamma_o);
float sin_gamma_t = sin_gamma_o * cos_theta_o / sqrtf(sqr(bsdf->eta) - sqr(sin_theta_o));
float cos_gamma_t = cos_from_sin(sin_gamma_t);
float gamma_t = safe_asinf(sin_gamma_t);
const float sin_gamma_t = sin_gamma_o * cos_theta_o / sqrtf(sqr(bsdf->eta) - sqr(sin_theta_o));
const float cos_gamma_t = cos_from_sin(sin_gamma_t);
const float gamma_t = safe_asinf(sin_gamma_t);
Spectrum T = exp(-bsdf->sigma * (2.0f * cos_gamma_t / cos_theta_t));
const Spectrum T = exp(-bsdf->sigma * (2.0f * cos_gamma_t / cos_theta_t));
Spectrum Ap[4];
float Ap_energy[4];
hair_attenuation(
kg, fresnel_dielectric_cos(cos_theta_o * cos_gamma_o, bsdf->eta), T, Ap, Ap_energy);
float sin_theta_i = wi.x;
float cos_theta_i = cos_from_sin(sin_theta_i);
float phi_i = atan2f(wi.z, wi.y);
const float sin_theta_i = wi.x;
const float cos_theta_i = cos_from_sin(sin_theta_i);
const float phi_i = atan2f(wi.z, wi.y);
float phi = phi_i - phi_o;
const float phi = phi_i - phi_o;
float angles[6];
hair_alpha_angles(sin_theta_i, cos_theta_i, bsdf->alpha, angles);
Spectrum F;
float F_energy;
float Mp, Np;
Spectrum F = zero_spectrum();
float F_energy = 0.0f;
/* Primary specular (R). */
Mp = longitudinal_scattering(angles[0], angles[1], sin_theta_o, cos_theta_o, bsdf->m0_roughness);
Np = azimuthal_scattering(phi, 0, bsdf->s, gamma_o, gamma_t);
F = Ap[0] * Mp * Np;
F_energy = Ap_energy[0] * Mp * Np;
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
/* Transmission (TT). */
Mp = longitudinal_scattering(angles[2], angles[3], sin_theta_o, cos_theta_o, 0.25f * bsdf->v);
Np = azimuthal_scattering(phi, 1, bsdf->s, gamma_o, gamma_t);
F += Ap[1] * Mp * Np;
F_energy += Ap_energy[1] * Mp * Np;
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
/* Secondary specular (TRT). */
Mp = longitudinal_scattering(angles[4], angles[5], sin_theta_o, cos_theta_o, 4.0f * bsdf->v);
Np = azimuthal_scattering(phi, 2, bsdf->s, gamma_o, gamma_t);
F += Ap[2] * Mp * Np;
F_energy += Ap_energy[2] * Mp * Np;
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
/* Primary specular (R), Transmission (TT) and Secondary Specular (TRT). */
for (int i = 0; i < 3; i++) {
const float Mp = longitudinal_scattering(angles[2 * i],
angles[2 * i + 1],
sin_theta_o,
cos_theta_o,
(i == 0) ? bsdf->m0_roughness :
(i == 1) ? 0.25f * bsdf->v :
4.0f * bsdf->v);
const float Np = azimuthal_scattering(phi, i, bsdf->s, gamma_o, gamma_t);
F += Ap[i] * Mp * Np;
F_energy += Ap_energy[i] * Mp * Np;
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
}
/* Residual component (TRRT+). */
Mp = longitudinal_scattering(sin_theta_i, cos_theta_i, sin_theta_o, cos_theta_o, 4.0f * bsdf->v);
Np = M_1_2PI_F;
F += Ap[3] * Mp * Np;
F_energy += Ap_energy[3] * Mp * Np;
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
{
const float Mp = longitudinal_scattering(
sin_theta_i, cos_theta_i, sin_theta_o, cos_theta_o, 4.0f * bsdf->v);
const float Np = M_1_2PI_F;
F += Ap[3] * Mp * Np;
F_energy += Ap_energy[3] * Mp * Np;
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
}
*pdf = F_energy;
return F;
@@ -354,39 +344,44 @@ ccl_device int bsdf_principled_hair_sample(KernelGlobals kg,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
ccl_private float2 *sampled_roughness,
ccl_private float *eta)
{
ccl_private PrincipledHairBSDF *bsdf = (ccl_private PrincipledHairBSDF *)sc;
float3 Y = float4_to_float3(bsdf->extra->geom);
*sampled_roughness = make_float2(bsdf->m0_roughness, bsdf->m0_roughness);
*eta = bsdf->eta;
float3 X = safe_normalize(sd->dPdu);
const float3 Y = float4_to_float3(bsdf->extra->geom);
const float3 X = safe_normalize(sd->dPdu);
kernel_assert(fabsf(dot(X, Y)) < 1e-3f);
float3 Z = safe_normalize(cross(X, Y));
const float3 Z = safe_normalize(cross(X, Y));
float3 wo = make_float3(dot(sd->I, X), dot(sd->I, Y), dot(sd->I, Z));
const float3 wo = make_float3(dot(sd->I, X), dot(sd->I, Y), dot(sd->I, Z));
float2 u[2];
u[0] = make_float2(randu, randv);
u[1].x = lcg_step_float(&sd->lcg_state);
u[1].y = lcg_step_float(&sd->lcg_state);
float sin_theta_o = wo.x;
float cos_theta_o = cos_from_sin(sin_theta_o);
float phi_o = atan2f(wo.z, wo.y);
const float sin_theta_o = wo.x;
const float cos_theta_o = cos_from_sin(sin_theta_o);
const float phi_o = atan2f(wo.z, wo.y);
float sin_theta_t = sin_theta_o / bsdf->eta;
float cos_theta_t = cos_from_sin(sin_theta_t);
const float sin_theta_t = sin_theta_o / bsdf->eta;
const float cos_theta_t = cos_from_sin(sin_theta_t);
float sin_gamma_o = bsdf->extra->geom.w;
float cos_gamma_o = cos_from_sin(sin_gamma_o);
float gamma_o = safe_asinf(sin_gamma_o);
const float sin_gamma_o = bsdf->extra->geom.w;
const float cos_gamma_o = cos_from_sin(sin_gamma_o);
const float gamma_o = safe_asinf(sin_gamma_o);
float sin_gamma_t = sin_gamma_o * cos_theta_o / sqrtf(sqr(bsdf->eta) - sqr(sin_theta_o));
float cos_gamma_t = cos_from_sin(sin_gamma_t);
float gamma_t = safe_asinf(sin_gamma_t);
const float sin_gamma_t = sin_gamma_o * cos_theta_o / sqrtf(sqr(bsdf->eta) - sqr(sin_theta_o));
const float cos_gamma_t = cos_from_sin(sin_gamma_t);
const float gamma_t = safe_asinf(sin_gamma_t);
Spectrum T = exp(-bsdf->sigma * (2.0f * cos_gamma_t / cos_theta_t));
const Spectrum T = exp(-bsdf->sigma * (2.0f * cos_gamma_t / cos_theta_t));
Spectrum Ap[4];
float Ap_energy[4];
hair_attenuation(
@@ -409,7 +404,7 @@ ccl_device int bsdf_principled_hair_sample(KernelGlobals kg,
}
u[1].x = max(u[1].x, 1e-5f);
float fac = 1.0f + v * logf(u[1].x + (1.0f - u[1].x) * expf(-2.0f / v));
const float fac = 1.0f + v * logf(u[1].x + (1.0f - u[1].x) * expf(-2.0f / v));
float sin_theta_i = -fac * sin_theta_o +
cos_from_sin(fac) * cosf(M_2PI_F * u[1].y) * cos_theta_o;
float cos_theta_i = cos_from_sin(sin_theta_i);
@@ -428,41 +423,37 @@ ccl_device int bsdf_principled_hair_sample(KernelGlobals kg,
else {
phi = M_2PI_F * u[0].y;
}
float phi_i = phi_o + phi;
const float phi_i = phi_o + phi;
hair_alpha_angles(sin_theta_i, cos_theta_i, bsdf->alpha, angles);
Spectrum F;
float F_energy;
float Mp, Np;
Spectrum F = zero_spectrum();
float F_energy = 0.0f;
/* Primary specular (R). */
Mp = longitudinal_scattering(angles[0], angles[1], sin_theta_o, cos_theta_o, bsdf->m0_roughness);
Np = azimuthal_scattering(phi, 0, bsdf->s, gamma_o, gamma_t);
F = Ap[0] * Mp * Np;
F_energy = Ap_energy[0] * Mp * Np;
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
/* Transmission (TT). */
Mp = longitudinal_scattering(angles[2], angles[3], sin_theta_o, cos_theta_o, 0.25f * bsdf->v);
Np = azimuthal_scattering(phi, 1, bsdf->s, gamma_o, gamma_t);
F += Ap[1] * Mp * Np;
F_energy += Ap_energy[1] * Mp * Np;
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
/* Secondary specular (TRT). */
Mp = longitudinal_scattering(angles[4], angles[5], sin_theta_o, cos_theta_o, 4.0f * bsdf->v);
Np = azimuthal_scattering(phi, 2, bsdf->s, gamma_o, gamma_t);
F += Ap[2] * Mp * Np;
F_energy += Ap_energy[2] * Mp * Np;
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
/* Primary specular (R), Transmission (TT) and Secondary Specular (TRT). */
for (int i = 0; i < 3; i++) {
const float Mp = longitudinal_scattering(angles[2 * i],
angles[2 * i + 1],
sin_theta_o,
cos_theta_o,
(i == 0) ? bsdf->m0_roughness :
(i == 1) ? 0.25f * bsdf->v :
4.0f * bsdf->v);
const float Np = azimuthal_scattering(phi, i, bsdf->s, gamma_o, gamma_t);
F += Ap[i] * Mp * Np;
F_energy += Ap_energy[i] * Mp * Np;
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
}
/* Residual component (TRRT+). */
Mp = longitudinal_scattering(sin_theta_i, cos_theta_i, sin_theta_o, cos_theta_o, 4.0f * bsdf->v);
Np = M_1_2PI_F;
F += Ap[3] * Mp * Np;
F_energy += Ap_energy[3] * Mp * Np;
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
{
const float Mp = longitudinal_scattering(
sin_theta_i, cos_theta_i, sin_theta_o, cos_theta_o, 4.0f * bsdf->v);
const float Np = M_1_2PI_F;
F += Ap[3] * Mp * Np;
F_energy += Ap_energy[3] * Mp * Np;
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
}
*eval = F;
*pdf = F_energy;

View File

@@ -357,146 +357,129 @@ ccl_device void bsdf_microfacet_ggx_blur(ccl_private ShaderClosure *sc, float ro
bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y);
}
ccl_device Spectrum bsdf_microfacet_ggx_eval_reflect(ccl_private const ShaderClosure *sc,
ccl_device Spectrum bsdf_microfacet_ggx_eval_reflect(ccl_private const MicrofacetBsdf *bsdf,
const float3 N,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
const float alpha_x,
const float alpha_y,
const float cosNO,
const float cosNI)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
float alpha_x = bsdf->alpha_x;
float alpha_y = bsdf->alpha_y;
bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
float3 N = bsdf->N;
if (m_refractive || alpha_x * alpha_y <= 1e-7f) {
if (!(cosNI > 0 && cosNO > 0)) {
*pdf = 0.0f;
return zero_spectrum();
}
float cosNO = dot(N, I);
float cosNI = dot(N, omega_in);
/* get half vector */
float3 m = normalize(omega_in + I);
float alpha2 = alpha_x * alpha_y;
float D, G1o, G1i;
if (cosNI > 0 && cosNO > 0) {
/* get half vector */
float3 m = normalize(omega_in + I);
float alpha2 = alpha_x * alpha_y;
float D, G1o, G1i;
if (alpha_x == alpha_y) {
/* isotropic
* eq. 20: (F*G*D)/(4*in*on)
* eq. 33: first we calculate D(m) */
float cosThetaM = dot(N, m);
float cosThetaM2 = cosThetaM * cosThetaM;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2;
if (alpha_x == alpha_y) {
/* isotropic
* eq. 20: (F*G*D)/(4*in*on)
* eq. 33: first we calculate D(m) */
float cosThetaM = dot(N, m);
float cosThetaM2 = cosThetaM * cosThetaM;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2;
if (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID) {
/* use GTR1 for clearcoat */
D = D_GTR1(cosThetaM, bsdf->alpha_x);
if (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID) {
/* use GTR1 for clearcoat */
D = D_GTR1(cosThetaM, bsdf->alpha_x);
/* the alpha value for clearcoat is a fixed 0.25 => alpha2 = 0.25 * 0.25 */
alpha2 = 0.0625f;
}
else {
/* use GTR2 otherwise */
D = alpha2 / (M_PI_F * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2));
}
/* eq. 34: now calculate G1(i,m) and G1(o,m) */
G1o = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNO * cosNO) / (cosNO * cosNO)));
G1i = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNI * cosNI) / (cosNI * cosNI)));
/* the alpha value for clearcoat is a fixed 0.25 => alpha2 = 0.25 * 0.25 */
alpha2 = 0.0625f;
}
else {
/* anisotropic */
float3 X, Y, Z = N;
make_orthonormals_tangent(Z, bsdf->T, &X, &Y);
/* distribution */
float3 local_m = make_float3(dot(X, m), dot(Y, m), dot(Z, m));
float slope_x = -local_m.x / (local_m.z * alpha_x);
float slope_y = -local_m.y / (local_m.z * alpha_y);
float slope_len = 1 + slope_x * slope_x + slope_y * slope_y;
float cosThetaM = local_m.z;
float cosThetaM2 = cosThetaM * cosThetaM;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
D = 1 / ((slope_len * slope_len) * M_PI_F * alpha2 * cosThetaM4);
/* G1(i,m) and G1(o,m) */
float tanThetaO2 = (1 - cosNO * cosNO) / (cosNO * cosNO);
float cosPhiO = dot(I, X);
float sinPhiO = dot(I, Y);
float alphaO2 = (cosPhiO * cosPhiO) * (alpha_x * alpha_x) +
(sinPhiO * sinPhiO) * (alpha_y * alpha_y);
alphaO2 /= cosPhiO * cosPhiO + sinPhiO * sinPhiO;
G1o = 2 / (1 + safe_sqrtf(1 + alphaO2 * tanThetaO2));
float tanThetaI2 = (1 - cosNI * cosNI) / (cosNI * cosNI);
float cosPhiI = dot(omega_in, X);
float sinPhiI = dot(omega_in, Y);
float alphaI2 = (cosPhiI * cosPhiI) * (alpha_x * alpha_x) +
(sinPhiI * sinPhiI) * (alpha_y * alpha_y);
alphaI2 /= cosPhiI * cosPhiI + sinPhiI * sinPhiI;
G1i = 2 / (1 + safe_sqrtf(1 + alphaI2 * tanThetaI2));
/* use GTR2 otherwise */
D = alpha2 / (M_PI_F * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2));
}
float G = G1o * G1i;
/* eq. 34: now calculate G1(i,m) and G1(o,m) */
G1o = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNO * cosNO) / (cosNO * cosNO)));
G1i = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNI * cosNI) / (cosNI * cosNI)));
}
else {
/* anisotropic */
float3 X, Y, Z = N;
make_orthonormals_tangent(Z, bsdf->T, &X, &Y);
/* eq. 20 */
float common = D * 0.25f / cosNO;
/* distribution */
float3 local_m = make_float3(dot(X, m), dot(Y, m), dot(Z, m));
float slope_x = -local_m.x / (local_m.z * alpha_x);
float slope_y = -local_m.y / (local_m.z * alpha_y);
float slope_len = 1 + slope_x * slope_x + slope_y * slope_y;
Spectrum F = reflection_color(bsdf, omega_in, m);
if (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID) {
F *= 0.25f * bsdf->extra->clearcoat;
}
float cosThetaM = local_m.z;
float cosThetaM2 = cosThetaM * cosThetaM;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
Spectrum out = F * G * common;
D = 1 / ((slope_len * slope_len) * M_PI_F * alpha2 * cosThetaM4);
/* eq. 2 in distribution of visible normals sampling
* `pm = Dw = G1o * dot(m, I) * D / dot(N, I);` */
/* G1(i,m) and G1(o,m) */
float tanThetaO2 = (1 - cosNO * cosNO) / (cosNO * cosNO);
float cosPhiO = dot(I, X);
float sinPhiO = dot(I, Y);
/* eq. 38 - but see also:
* eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
* `pdf = pm * 0.25 / dot(m, I);` */
*pdf = G1o * common;
float alphaO2 = (cosPhiO * cosPhiO) * (alpha_x * alpha_x) +
(sinPhiO * sinPhiO) * (alpha_y * alpha_y);
alphaO2 /= cosPhiO * cosPhiO + sinPhiO * sinPhiO;
return out;
G1o = 2 / (1 + safe_sqrtf(1 + alphaO2 * tanThetaO2));
float tanThetaI2 = (1 - cosNI * cosNI) / (cosNI * cosNI);
float cosPhiI = dot(omega_in, X);
float sinPhiI = dot(omega_in, Y);
float alphaI2 = (cosPhiI * cosPhiI) * (alpha_x * alpha_x) +
(sinPhiI * sinPhiI) * (alpha_y * alpha_y);
alphaI2 /= cosPhiI * cosPhiI + sinPhiI * sinPhiI;
G1i = 2 / (1 + safe_sqrtf(1 + alphaI2 * tanThetaI2));
}
return zero_spectrum();
float G = G1o * G1i;
/* eq. 20 */
float common = D * 0.25f / cosNO;
Spectrum F = reflection_color(bsdf, omega_in, m);
if (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID) {
F *= 0.25f * bsdf->extra->clearcoat;
}
Spectrum out = F * G * common;
/* eq. 2 in distribution of visible normals sampling
* `pm = Dw = G1o * dot(m, I) * D / dot(N, I);` */
/* eq. 38 - but see also:
* eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
* `pdf = pm * 0.25 / dot(m, I);` */
*pdf = G1o * common;
return out;
}
ccl_device Spectrum bsdf_microfacet_ggx_eval_transmit(ccl_private const ShaderClosure *sc,
ccl_device Spectrum bsdf_microfacet_ggx_eval_transmit(ccl_private const MicrofacetBsdf *bsdf,
const float3 N,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
const float alpha_x,
const float alpha_y,
const float cosNO,
const float cosNI)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
float alpha_x = bsdf->alpha_x;
float alpha_y = bsdf->alpha_y;
float m_eta = bsdf->ior;
bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
float3 N = bsdf->N;
if (!m_refractive || alpha_x * alpha_y <= 1e-7f) {
*pdf = 0.0f;
return zero_spectrum();
}
float cosNO = dot(N, I);
float cosNI = dot(N, omega_in);
if (cosNO <= 0 || cosNI >= 0) {
*pdf = 0.0f;
return zero_spectrum(); /* vectors on same side -- not possible */
}
/* compute half-vector of the refraction (eq. 16) */
float m_eta = bsdf->ior;
float3 ht = -(m_eta * omega_in + I);
float3 Ht = normalize(ht);
float cosHO = dot(Ht, I);
@@ -533,6 +516,30 @@ ccl_device Spectrum bsdf_microfacet_ggx_eval_transmit(ccl_private const ShaderCl
return make_spectrum(out);
}
ccl_device Spectrum bsdf_microfacet_ggx_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
const float alpha_x = bsdf->alpha_x;
const float alpha_y = bsdf->alpha_y;
const bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
const float3 N = bsdf->N;
const float cosNO = dot(N, I);
const float cosNI = dot(N, omega_in);
if (((cosNI < 0.0f) != m_refractive) || alpha_x * alpha_y <= 1e-7f) {
*pdf = 0.0f;
return zero_spectrum();
}
return (cosNI < 0.0f) ? bsdf_microfacet_ggx_eval_transmit(
bsdf, N, I, omega_in, pdf, alpha_x, alpha_y, cosNO, cosNI) :
bsdf_microfacet_ggx_eval_reflect(
bsdf, N, I, omega_in, pdf, alpha_x, alpha_y, cosNO, cosNI);
}
ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
float3 Ng,
@@ -541,12 +548,18 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
ccl_private float2 *sampled_roughness,
ccl_private float *eta)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
float alpha_x = bsdf->alpha_x;
float alpha_y = bsdf->alpha_y;
bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
*sampled_roughness = make_float2(alpha_x, alpha_y);
*eta = m_refractive ? 1.0f / bsdf->ior : bsdf->ior;
float3 N = bsdf->N;
int label;
@@ -805,111 +818,95 @@ ccl_device_inline float bsdf_beckmann_aniso_G1(
return ((2.181f * a + 3.535f) * a) / ((2.577f * a + 2.276f) * a + 1.0f);
}
ccl_device Spectrum bsdf_microfacet_beckmann_eval_reflect(ccl_private const ShaderClosure *sc,
ccl_device Spectrum bsdf_microfacet_beckmann_eval_reflect(ccl_private const MicrofacetBsdf *bsdf,
const float3 N,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
const float alpha_x,
const float alpha_y,
const float cosNO,
const float cosNI)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
float alpha_x = bsdf->alpha_x;
float alpha_y = bsdf->alpha_y;
bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
float3 N = bsdf->N;
if (m_refractive || alpha_x * alpha_y <= 1e-7f) {
if (!(cosNO > 0 && cosNI > 0)) {
*pdf = 0.0f;
return zero_spectrum();
}
float cosNO = dot(N, I);
float cosNI = dot(N, omega_in);
/* get half vector */
float3 m = normalize(omega_in + I);
if (cosNO > 0 && cosNI > 0) {
/* get half vector */
float3 m = normalize(omega_in + I);
float alpha2 = alpha_x * alpha_y;
float D, G1o, G1i;
float alpha2 = alpha_x * alpha_y;
float D, G1o, G1i;
if (alpha_x == alpha_y) {
/* isotropic
* eq. 20: (F*G*D)/(4*in*on)
* eq. 25: first we calculate D(m) */
float cosThetaM = dot(N, m);
float cosThetaM2 = cosThetaM * cosThetaM;
float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
D = expf(-tanThetaM2 / alpha2) / (M_PI_F * alpha2 * cosThetaM4);
if (alpha_x == alpha_y) {
/* isotropic
* eq. 20: (F*G*D)/(4*in*on)
* eq. 25: first we calculate D(m) */
float cosThetaM = dot(N, m);
float cosThetaM2 = cosThetaM * cosThetaM;
float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
D = expf(-tanThetaM2 / alpha2) / (M_PI_F * alpha2 * cosThetaM4);
/* eq. 26, 27: now calculate G1(i,m) and G1(o,m) */
G1o = bsdf_beckmann_G1(alpha_x, cosNO);
G1i = bsdf_beckmann_G1(alpha_x, cosNI);
}
else {
/* anisotropic */
float3 X, Y, Z = N;
make_orthonormals_tangent(Z, bsdf->T, &X, &Y);
/* eq. 26, 27: now calculate G1(i,m) and G1(o,m) */
G1o = bsdf_beckmann_G1(alpha_x, cosNO);
G1i = bsdf_beckmann_G1(alpha_x, cosNI);
}
else {
/* anisotropic */
float3 X, Y, Z = N;
make_orthonormals_tangent(Z, bsdf->T, &X, &Y);
/* distribution */
float3 local_m = make_float3(dot(X, m), dot(Y, m), dot(Z, m));
float slope_x = -local_m.x / (local_m.z * alpha_x);
float slope_y = -local_m.y / (local_m.z * alpha_y);
/* distribution */
float3 local_m = make_float3(dot(X, m), dot(Y, m), dot(Z, m));
float slope_x = -local_m.x / (local_m.z * alpha_x);
float slope_y = -local_m.y / (local_m.z * alpha_y);
float cosThetaM = local_m.z;
float cosThetaM2 = cosThetaM * cosThetaM;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
float cosThetaM = local_m.z;
float cosThetaM2 = cosThetaM * cosThetaM;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
D = expf(-slope_x * slope_x - slope_y * slope_y) / (M_PI_F * alpha2 * cosThetaM4);
D = expf(-slope_x * slope_x - slope_y * slope_y) / (M_PI_F * alpha2 * cosThetaM4);
/* G1(i,m) and G1(o,m) */
G1o = bsdf_beckmann_aniso_G1(alpha_x, alpha_y, cosNO, dot(I, X), dot(I, Y));
G1i = bsdf_beckmann_aniso_G1(alpha_x, alpha_y, cosNI, dot(omega_in, X), dot(omega_in, Y));
}
float G = G1o * G1i;
/* eq. 20 */
float common = D * 0.25f / cosNO;
float out = G * common;
/* eq. 2 in distribution of visible normals sampling
* pm = Dw = G1o * dot(m, I) * D / dot(N, I); */
/* eq. 38 - but see also:
* eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
* pdf = pm * 0.25 / dot(m, I); */
*pdf = G1o * common;
return make_spectrum(out);
/* G1(i,m) and G1(o,m) */
G1o = bsdf_beckmann_aniso_G1(alpha_x, alpha_y, cosNO, dot(I, X), dot(I, Y));
G1i = bsdf_beckmann_aniso_G1(alpha_x, alpha_y, cosNI, dot(omega_in, X), dot(omega_in, Y));
}
return zero_spectrum();
float G = G1o * G1i;
/* eq. 20 */
float common = D * 0.25f / cosNO;
float out = G * common;
/* eq. 2 in distribution of visible normals sampling
* pm = Dw = G1o * dot(m, I) * D / dot(N, I); */
/* eq. 38 - but see also:
* eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
* pdf = pm * 0.25 / dot(m, I); */
*pdf = G1o * common;
return make_spectrum(out);
}
ccl_device Spectrum bsdf_microfacet_beckmann_eval_transmit(ccl_private const ShaderClosure *sc,
ccl_device Spectrum bsdf_microfacet_beckmann_eval_transmit(ccl_private const MicrofacetBsdf *bsdf,
const float3 N,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
const float alpha_x,
const float alpha_y,
const float cosNO,
const float cosNI)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
float alpha_x = bsdf->alpha_x;
float alpha_y = bsdf->alpha_y;
float m_eta = bsdf->ior;
bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
float3 N = bsdf->N;
if (!m_refractive || alpha_x * alpha_y <= 1e-7f) {
*pdf = 0.0f;
return zero_spectrum();
}
float cosNO = dot(N, I);
float cosNI = dot(N, omega_in);
if (cosNO <= 0 || cosNI >= 0) {
*pdf = 0.0f;
return zero_spectrum();
}
const float m_eta = bsdf->ior;
/* compute half-vector of the refraction (eq. 16) */
float3 ht = -(m_eta * omega_in + I);
float3 Ht = normalize(ht);
@@ -944,6 +941,30 @@ ccl_device Spectrum bsdf_microfacet_beckmann_eval_transmit(ccl_private const Sha
return make_spectrum(out);
}
ccl_device Spectrum bsdf_microfacet_beckmann_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
const float alpha_x = bsdf->alpha_x;
const float alpha_y = bsdf->alpha_y;
const bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
const float3 N = bsdf->N;
const float cosNO = dot(N, I);
const float cosNI = dot(N, omega_in);
if (((cosNI < 0.0f) != m_refractive) || alpha_x * alpha_y <= 1e-7f) {
*pdf = 0.0f;
return zero_spectrum();
}
return (cosNI < 0.0f) ? bsdf_microfacet_beckmann_eval_transmit(
bsdf, N, I, omega_in, pdf, alpha_x, alpha_y, cosNO, cosNI) :
bsdf_microfacet_beckmann_eval_reflect(
bsdf, N, I, omega_in, pdf, alpha_x, alpha_y, cosNO, cosNI);
}
ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
float3 Ng,
@@ -952,7 +973,9 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
ccl_private float2 *sampled_roughness,
ccl_private float *eta)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
float alpha_x = bsdf->alpha_x;
@@ -961,6 +984,9 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg,
float3 N = bsdf->N;
int label;
*sampled_roughness = make_float2(alpha_x, alpha_y);
*eta = m_refractive ? 1.0f / bsdf->ior : bsdf->ior;
float cosNO = dot(N, I);
if (cosNO > 0) {
float3 X, Y, Z = N;

View File

@@ -415,21 +415,11 @@ ccl_device int bsdf_microfacet_multi_ggx_refraction_setup(ccl_private Microfacet
return bsdf_microfacet_multi_ggx_common_setup(bsdf);
}
ccl_device Spectrum bsdf_microfacet_multi_ggx_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf,
ccl_private uint *lcg_state)
{
*pdf = 0.0f;
return zero_spectrum();
}
ccl_device Spectrum bsdf_microfacet_multi_ggx_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf,
ccl_private uint *lcg_state)
ccl_device Spectrum bsdf_microfacet_multi_ggx_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf,
ccl_private uint *lcg_state)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
@@ -462,6 +452,12 @@ ccl_device Spectrum bsdf_microfacet_multi_ggx_eval_reflect(ccl_private const Sha
*pdf = mf_ggx_aniso_pdf(localI, localO, make_float2(bsdf->alpha_x, bsdf->alpha_y));
else
*pdf = mf_ggx_pdf(localI, localO, bsdf->alpha_x);
if (*pdf <= 0.f) {
*pdf = 0.f;
return make_float3(0.f, 0.f, 0.f);
}
return mf_eval_glossy(localI,
localO,
true,
@@ -483,7 +479,9 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals kg,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float *pdf,
ccl_private uint *lcg_state)
ccl_private uint *lcg_state,
ccl_private float2 *sampled_roughness,
ccl_private float *eta)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
@@ -511,6 +509,9 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals kg,
bool use_fresnel = (bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID);
*eta = bsdf->ior;
*sampled_roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
bool is_aniso = (bsdf->alpha_x != bsdf->alpha_y);
if (is_aniso)
make_orthonormals_tangent(Z, bsdf->T, &X, &Y);
@@ -541,6 +542,7 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals kg,
*pdf = mf_ggx_aniso_pdf(localI, localO, make_float2(bsdf->alpha_x, bsdf->alpha_y));
else
*pdf = mf_ggx_pdf(localI, localO, bsdf->alpha_x);
*pdf = fmaxf(0.f, *pdf);
*eval *= *pdf;
return LABEL_REFLECT | LABEL_GLOSSY;
@@ -576,12 +578,11 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_fresnel_setup(ccl_private Microfa
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG;
}
ccl_device Spectrum
bsdf_microfacet_multi_ggx_glass_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf,
ccl_private uint *lcg_state)
ccl_device Spectrum bsdf_microfacet_multi_ggx_glass_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf,
ccl_private uint *lcg_state)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
@@ -597,53 +598,22 @@ bsdf_microfacet_multi_ggx_glass_eval_transmit(ccl_private const ShaderClosure *s
float3 localI = make_float3(dot(I, X), dot(I, Y), dot(I, Z));
float3 localO = make_float3(dot(omega_in, X), dot(omega_in, Y), dot(omega_in, Z));
*pdf = mf_glass_pdf(localI, localO, bsdf->alpha_x, bsdf->ior);
return mf_eval_glass(localI,
localO,
false,
bsdf->extra->color,
bsdf->alpha_x,
bsdf->alpha_y,
lcg_state,
bsdf->ior,
false,
bsdf->extra->color);
}
ccl_device Spectrum
bsdf_microfacet_multi_ggx_glass_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf,
ccl_private uint *lcg_state)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) {
*pdf = 0.0f;
return zero_spectrum();
}
bool use_fresnel = (bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID);
float3 X, Y, Z;
Z = bsdf->N;
make_orthonormals(Z, &X, &Y);
float3 localI = make_float3(dot(I, X), dot(I, Y), dot(I, Z));
float3 localO = make_float3(dot(omega_in, X), dot(omega_in, Y), dot(omega_in, Z));
const bool is_transmission = localO.z < 0.0f;
const bool use_fresnel = !is_transmission &&
(bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID);
*pdf = mf_glass_pdf(localI, localO, bsdf->alpha_x, bsdf->ior);
kernel_assert(*pdf >= 0.f);
return mf_eval_glass(localI,
localO,
true,
!is_transmission,
bsdf->extra->color,
bsdf->alpha_x,
bsdf->alpha_y,
lcg_state,
bsdf->ior,
use_fresnel,
bsdf->extra->cspec0);
(is_transmission) ? bsdf->extra->color : bsdf->extra->cspec0);
}
ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals kg,
@@ -655,13 +625,18 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals kg,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float *pdf,
ccl_private uint *lcg_state)
ccl_private uint *lcg_state,
ccl_private float2 *sampled_roughness,
ccl_private float *eta)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
float3 X, Y, Z;
Z = bsdf->N;
*eta = bsdf->ior;
*sampled_roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) {
float3 R, T;
bool inside;
@@ -696,6 +671,7 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals kg,
use_fresnel,
bsdf->extra->cspec0);
*pdf = mf_glass_pdf(localI, localO, bsdf->alpha_x, bsdf->ior);
kernel_assert(*pdf >= 0.f);
*eval *= *pdf;
*omega_in = X * localO.x + Y * localO.y + Z * localO.z;

View File

@@ -47,10 +47,10 @@ ccl_device int bsdf_oren_nayar_setup(ccl_private OrenNayarBsdf *bsdf)
return SD_BSDF | SD_BSDF_HAS_EVAL;
}
ccl_device Spectrum bsdf_oren_nayar_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_oren_nayar_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
ccl_private const OrenNayarBsdf *bsdf = (ccl_private const OrenNayarBsdf *)sc;
if (dot(bsdf->N, omega_in) > 0.0f) {
@@ -63,15 +63,6 @@ ccl_device Spectrum bsdf_oren_nayar_eval_reflect(ccl_private const ShaderClosure
}
}
ccl_device Spectrum bsdf_oren_nayar_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
}
ccl_device int bsdf_oren_nayar_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,

View File

@@ -44,10 +44,10 @@ ccl_device int bsdf_phong_ramp_setup(ccl_private PhongRampBsdf *bsdf)
return SD_BSDF | SD_BSDF_HAS_EVAL;
}
ccl_device Spectrum bsdf_phong_ramp_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_phong_ramp_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
ccl_private const PhongRampBsdf *bsdf = (ccl_private const PhongRampBsdf *)sc;
float m_exponent = bsdf->exponent;
@@ -70,13 +70,9 @@ ccl_device Spectrum bsdf_phong_ramp_eval_reflect(ccl_private const ShaderClosure
return zero_spectrum();
}
ccl_device float3 bsdf_phong_ramp_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device_inline float phong_ramp_exponent_to_roughness(float exponent)
{
*pdf = 0.0f;
return make_float3(0.0f, 0.0f, 0.0f);
return sqrt(1.0f / ((exponent + 2.0f) / 2.0f));
}
ccl_device int bsdf_phong_ramp_sample(ccl_private const ShaderClosure *sc,
@@ -86,11 +82,14 @@ ccl_device int bsdf_phong_ramp_sample(ccl_private const ShaderClosure *sc,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
ccl_private float2 *sampled_roughness)
{
ccl_private const PhongRampBsdf *bsdf = (ccl_private const PhongRampBsdf *)sc;
float cosNO = dot(bsdf->N, I);
float m_exponent = bsdf->exponent;
const float m_roughness = phong_ramp_exponent_to_roughness(m_exponent);
*sampled_roughness = make_float2(m_roughness, m_roughness);
if (cosNO > 0) {
// reflect the view vector

View File

@@ -109,18 +109,17 @@ ccl_device int bsdf_principled_diffuse_setup(ccl_private PrincipledDiffuseBsdf *
return SD_BSDF | SD_BSDF_HAS_EVAL;
}
ccl_device Spectrum bsdf_principled_diffuse_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_principled_diffuse_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
ccl_private const PrincipledDiffuseBsdf *bsdf = (ccl_private const PrincipledDiffuseBsdf *)sc;
float3 N = bsdf->N;
float3 V = I; // outgoing
float3 L = omega_in; // incoming
const float3 N = bsdf->N;
if (dot(N, omega_in) > 0.0f) {
const float3 V = I; // outgoing
const float3 L = omega_in; // incoming
*pdf = fmaxf(dot(N, omega_in), 0.0f) * M_1_PI_F;
return bsdf_principled_diffuse_compute_brdf(bsdf, N, V, L, pdf);
}
@@ -130,15 +129,6 @@ ccl_device Spectrum bsdf_principled_diffuse_eval_reflect(ccl_private const Shade
}
}
ccl_device Spectrum bsdf_principled_diffuse_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
}
ccl_device int bsdf_principled_diffuse_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,

View File

@@ -59,19 +59,19 @@ ccl_device int bsdf_principled_sheen_setup(ccl_private const ShaderData *sd,
return SD_BSDF | SD_BSDF_HAS_EVAL;
}
ccl_device Spectrum bsdf_principled_sheen_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_principled_sheen_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
ccl_private const PrincipledSheenBsdf *bsdf = (ccl_private const PrincipledSheenBsdf *)sc;
float3 N = bsdf->N;
float3 V = I; // outgoing
float3 L = omega_in; // incoming
float3 H = normalize(L + V);
const float3 N = bsdf->N;
if (dot(N, omega_in) > 0.0f) {
const float3 V = I; // outgoing
const float3 L = omega_in; // incoming
const float3 H = normalize(L + V);
*pdf = fmaxf(dot(N, omega_in), 0.0f) * M_1_PI_F;
return calculate_principled_sheen_brdf(N, V, L, H, pdf);
}
@@ -81,15 +81,6 @@ ccl_device Spectrum bsdf_principled_sheen_eval_reflect(ccl_private const ShaderC
}
}
ccl_device Spectrum bsdf_principled_sheen_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
}
ccl_device int bsdf_principled_sheen_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,

View File

@@ -18,19 +18,10 @@ ccl_device int bsdf_reflection_setup(ccl_private MicrofacetBsdf *bsdf)
return SD_BSDF;
}
ccl_device Spectrum bsdf_reflection_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
}
ccl_device Spectrum bsdf_reflection_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_reflection_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
@@ -43,10 +34,12 @@ ccl_device int bsdf_reflection_sample(ccl_private const ShaderClosure *sc,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
ccl_private float *eta)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
float3 N = bsdf->N;
*eta = bsdf->ior;
// only one direction is possible
float cosNO = dot(N, I);

View File

@@ -18,19 +18,10 @@ ccl_device int bsdf_refraction_setup(ccl_private MicrofacetBsdf *bsdf)
return SD_BSDF;
}
ccl_device Spectrum bsdf_refraction_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
}
ccl_device Spectrum bsdf_refraction_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_refraction_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
@@ -43,10 +34,13 @@ ccl_device int bsdf_refraction_sample(ccl_private const ShaderClosure *sc,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
ccl_private float *eta)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
float m_eta = bsdf->ior;
*eta = 1.0f / m_eta;
float3 N = bsdf->N;
float3 R, T;

View File

@@ -49,33 +49,29 @@ ccl_device float bsdf_toon_get_sample_angle(float max_angle, float smooth)
return fminf(max_angle + smooth, M_PI_2_F);
}
ccl_device Spectrum bsdf_diffuse_toon_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_diffuse_toon_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc;
float max_angle = bsdf->size * M_PI_2_F;
float smooth = bsdf->smooth * M_PI_2_F;
float angle = safe_acosf(fmaxf(dot(bsdf->N, omega_in), 0.0f));
float cosNI = dot(bsdf->N, omega_in);
float eval = bsdf_toon_get_intensity(max_angle, smooth, angle);
if (cosNI >= 0.0f) {
float max_angle = bsdf->size * M_PI_2_F;
float smooth = bsdf->smooth * M_PI_2_F;
float angle = safe_acosf(fmaxf(cosNI, 0.0f));
if (eval > 0.0f) {
float sample_angle = bsdf_toon_get_sample_angle(max_angle, smooth);
float eval = bsdf_toon_get_intensity(max_angle, smooth, angle);
*pdf = 0.5f * M_1_PI_F / (1.0f - cosf(sample_angle));
return make_spectrum(*pdf * eval);
if (eval > 0.0f) {
float sample_angle = bsdf_toon_get_sample_angle(max_angle, smooth);
*pdf = 0.5f * M_1_PI_F / (1.0f - cosf(sample_angle));
return make_spectrum(*pdf * eval);
}
}
*pdf = 0.0f;
return zero_spectrum();
}
ccl_device Spectrum bsdf_diffuse_toon_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
}
@@ -125,10 +121,10 @@ ccl_device int bsdf_glossy_toon_setup(ccl_private ToonBsdf *bsdf)
return SD_BSDF | SD_BSDF_HAS_EVAL;
}
ccl_device Spectrum bsdf_glossy_toon_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_glossy_toon_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc;
float max_angle = bsdf->size * M_PI_2_F;
@@ -153,15 +149,6 @@ ccl_device Spectrum bsdf_glossy_toon_eval_reflect(ccl_private const ShaderClosur
return zero_spectrum();
}
ccl_device Spectrum bsdf_glossy_toon_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
}
ccl_device int bsdf_glossy_toon_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,

View File

@@ -59,19 +59,10 @@ ccl_device void bsdf_transparent_setup(ccl_private ShaderData *sd,
}
}
ccl_device Spectrum bsdf_transparent_eval_reflect(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();
}
ccl_device Spectrum bsdf_transparent_eval_transmit(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
ccl_device Spectrum bsdf_transparent_eval(ccl_private const ShaderClosure *sc,
const float3 I,
const float3 omega_in,
ccl_private float *pdf)
{
*pdf = 0.0f;
return zero_spectrum();

View File

@@ -133,6 +133,10 @@ KERNEL_STRUCT_MEMBER(film, int, pass_bake_primitive)
KERNEL_STRUCT_MEMBER(film, int, pass_bake_differential)
/* Shadow catcher. */
KERNEL_STRUCT_MEMBER(film, int, use_approximate_shadow_catcher)
/* Path Guiding */
KERNEL_STRUCT_MEMBER(film, int, pass_guiding_color)
KERNEL_STRUCT_MEMBER(film, int, pass_guiding_probability)
KERNEL_STRUCT_MEMBER(film, int, pass_guiding_avg_roughness)
/* Padding. */
KERNEL_STRUCT_MEMBER(film, int, pad1)
KERNEL_STRUCT_MEMBER(film, int, pad2)
@@ -190,8 +194,17 @@ KERNEL_STRUCT_MEMBER(integrator, int, has_shadow_catcher)
KERNEL_STRUCT_MEMBER(integrator, int, filter_closures)
/* MIS debugging. */
KERNEL_STRUCT_MEMBER(integrator, int, direct_light_sampling_type)
/* Padding */
KERNEL_STRUCT_MEMBER(integrator, int, pad1)
/* Path Guiding */
KERNEL_STRUCT_MEMBER(integrator, float, surface_guiding_probability)
KERNEL_STRUCT_MEMBER(integrator, float, volume_guiding_probability)
KERNEL_STRUCT_MEMBER(integrator, int, guiding_distribution_type)
KERNEL_STRUCT_MEMBER(integrator, int, use_guiding)
KERNEL_STRUCT_MEMBER(integrator, int, train_guiding)
KERNEL_STRUCT_MEMBER(integrator, int, use_surface_guiding)
KERNEL_STRUCT_MEMBER(integrator, int, use_volume_guiding)
KERNEL_STRUCT_MEMBER(integrator, int, use_guiding_direct_light)
KERNEL_STRUCT_MEMBER(integrator, int, use_guiding_mis_weights)
KERNEL_STRUCT_END(KernelIntegrator)
/* SVM. For shader specialization. */

View File

@@ -9,6 +9,8 @@
#include "kernel/types.h"
#include "kernel/util/profiling.h"
#include "util/guiding.h"
CCL_NAMESPACE_BEGIN
/* On the CPU, we pass along the struct KernelGlobals to nearly everywhere in
@@ -43,9 +45,20 @@ typedef struct KernelGlobalsCPU {
#ifdef __OSL__
/* On the CPU, we also have the OSL globals here. Most data structures are shared
* with SVM, the difference is in the shaders and object/mesh attributes. */
OSLGlobals *osl;
OSLShadingSystem *osl_ss;
OSLThreadData *osl_tdata;
OSLGlobals *osl = nullptr;
OSLShadingSystem *osl_ss = nullptr;
OSLThreadData *osl_tdata = nullptr;
#endif
#ifdef __PATH_GUIDING__
/* Pointers to global data structures. */
openpgl::cpp::SampleStorage *opgl_sample_data_storage = nullptr;
openpgl::cpp::Field *opgl_guiding_field = nullptr;
/* Local data structures owned by the thread. */
openpgl::cpp::PathSegmentStorage *opgl_path_segment_storage = nullptr;
openpgl::cpp::SurfaceSamplingDistribution *opgl_surface_sampling_distribution = nullptr;
openpgl::cpp::VolumeSamplingDistribution *opgl_volume_sampling_distribution = nullptr;
#endif
/* **** Run-time data **** */

View File

@@ -29,28 +29,13 @@ using namespace metal::raytracing;
/* Qualifiers */
/* Inline everything for Apple GPUs. This gives ~1.1x speedup and 10% spill
* reduction for integator_shade_surface. However it comes at the cost of
* longer compile times (~4.5 minutes on M1 Max) and is disabled for that
* reason, until there is a user option to manually enable it. */
#if 0 // defined(__KERNEL_METAL_APPLE__)
# define ccl_device __attribute__((always_inline))
# define ccl_device_inline __attribute__((always_inline))
# define ccl_device_forceinline __attribute__((always_inline))
# define ccl_device_noinline __attribute__((always_inline))
#define ccl_device
#define ccl_device_inline ccl_device __attribute__((always_inline))
#define ccl_device_forceinline ccl_device __attribute__((always_inline))
#if defined(__KERNEL_METAL_APPLE__)
# define ccl_device_noinline ccl_device
#else
# define ccl_device
# define ccl_device_inline ccl_device
# define ccl_device_forceinline ccl_device
# if defined(__KERNEL_METAL_APPLE__)
# define ccl_device_noinline ccl_device
# else
# define ccl_device_noinline ccl_device __attribute__((noinline))
# endif
# define ccl_device_noinline ccl_device __attribute__((noinline))
#endif
#define ccl_device_noinline_cpu ccl_device

View File

@@ -0,0 +1,542 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#pragma once
#include "kernel/closure/alloc.h"
#include "kernel/closure/bsdf.h"
#include "kernel/film/write.h"
CCL_NAMESPACE_BEGIN
/* Utilities. */
#if defined(__PATH_GUIDING__)
static pgl_vec3f guiding_vec3f(const float3 v)
{
return openpgl::cpp::Vector3(v.x, v.y, v.z);
}
static pgl_point3f guiding_point3f(const float3 v)
{
return openpgl::cpp::Point3(v.x, v.y, v.z);
}
#endif
/* Path recording for guiding. */
/* Record Surface Interactions */
/* Records/Adds a new path segment with the current path vertex on a surface.
* If the path is not terminated this call is usually followed by a call of
* guiding_record_surface_bounce. */
ccl_device_forceinline void guiding_record_surface_segment(KernelGlobals kg,
IntegratorState state,
ccl_private const ShaderData *sd)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
if (!kernel_data.integrator.train_guiding) {
return;
}
const pgl_vec3f zero = guiding_vec3f(zero_float3());
const pgl_vec3f one = guiding_vec3f(one_float3());
state->guiding.path_segment = kg->opgl_path_segment_storage->NextSegment();
openpgl::cpp::SetPosition(state->guiding.path_segment, guiding_point3f(sd->P));
openpgl::cpp::SetDirectionOut(state->guiding.path_segment, guiding_vec3f(sd->I));
openpgl::cpp::SetVolumeScatter(state->guiding.path_segment, false);
openpgl::cpp::SetScatteredContribution(state->guiding.path_segment, zero);
openpgl::cpp::SetDirectContribution(state->guiding.path_segment, zero);
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, one);
openpgl::cpp::SetEta(state->guiding.path_segment, 1.0);
#endif
}
/* Records the surface scattering event at the current vertex position of the segment.*/
ccl_device_forceinline void guiding_record_surface_bounce(KernelGlobals kg,
IntegratorState state,
ccl_private const ShaderData *sd,
const Spectrum weight,
const float pdf,
const float3 N,
const float3 omega_in,
const float2 roughness,
const float eta)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (!kernel_data.integrator.train_guiding) {
return;
}
const float min_roughness = safe_sqrtf(fminf(roughness.x, roughness.y));
const bool is_delta = (min_roughness == 0.0f);
const float3 weight_rgb = spectrum_to_rgb(weight);
const float3 normal = clamp(N, -one_float3(), one_float3());
kernel_assert(state->guiding.path_segment != nullptr);
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, guiding_vec3f(one_float3()));
openpgl::cpp::SetVolumeScatter(state->guiding.path_segment, false);
openpgl::cpp::SetNormal(state->guiding.path_segment, guiding_vec3f(normal));
openpgl::cpp::SetDirectionIn(state->guiding.path_segment, guiding_vec3f(omega_in));
openpgl::cpp::SetPDFDirectionIn(state->guiding.path_segment, pdf);
openpgl::cpp::SetScatteringWeight(state->guiding.path_segment, guiding_vec3f(weight_rgb));
openpgl::cpp::SetIsDelta(state->guiding.path_segment, is_delta);
openpgl::cpp::SetEta(state->guiding.path_segment, eta);
openpgl::cpp::SetRoughness(state->guiding.path_segment, min_roughness);
#endif
}
/* Records the emission at the current surface intersection (physical or virtual) */
ccl_device_forceinline void guiding_record_surface_emission(KernelGlobals kg,
IntegratorState state,
const Spectrum Le,
const float mis_weight)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
if (!kernel_data.integrator.train_guiding) {
return;
}
const float3 Le_rgb = spectrum_to_rgb(Le);
openpgl::cpp::SetDirectContribution(state->guiding.path_segment, guiding_vec3f(Le_rgb));
openpgl::cpp::SetMiWeight(state->guiding.path_segment, mis_weight);
#endif
}
/* Record BSSRDF Interactions */
/* Records/Adds a new path segment where the vertex position is the point of entry
* of the sub surface scattering boundary.
* If the path is not terminated this call is usually followed by a call of
* guiding_record_bssrdf_weight and guiding_record_bssrdf_bounce. */
ccl_device_forceinline void guiding_record_bssrdf_segment(KernelGlobals kg,
IntegratorState state,
const float3 P,
const float3 I)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
if (!kernel_data.integrator.train_guiding) {
return;
}
const pgl_vec3f zero = guiding_vec3f(zero_float3());
const pgl_vec3f one = guiding_vec3f(one_float3());
state->guiding.path_segment = kg->opgl_path_segment_storage->NextSegment();
openpgl::cpp::SetPosition(state->guiding.path_segment, guiding_point3f(P));
openpgl::cpp::SetDirectionOut(state->guiding.path_segment, guiding_vec3f(I));
openpgl::cpp::SetVolumeScatter(state->guiding.path_segment, true);
openpgl::cpp::SetScatteredContribution(state->guiding.path_segment, zero);
openpgl::cpp::SetDirectContribution(state->guiding.path_segment, zero);
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, one);
openpgl::cpp::SetEta(state->guiding.path_segment, 1.0);
#endif
}
/* Records the transmission of the path at the point of entry while passing
* the surface boundary.*/
ccl_device_forceinline void guiding_record_bssrdf_weight(KernelGlobals kg,
IntegratorState state,
const Spectrum weight,
const Spectrum albedo)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
if (!kernel_data.integrator.train_guiding) {
return;
}
/* Note albedo left out here, will be included in guiding_record_bssrdf_bounce. */
const float3 weight_rgb = spectrum_to_rgb(safe_divide_color(weight, albedo));
kernel_assert(state->guiding.path_segment != nullptr);
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, guiding_vec3f(zero_float3()));
openpgl::cpp::SetScatteringWeight(state->guiding.path_segment, guiding_vec3f(weight_rgb));
openpgl::cpp::SetIsDelta(state->guiding.path_segment, false);
openpgl::cpp::SetEta(state->guiding.path_segment, 1.0f);
openpgl::cpp::SetRoughness(state->guiding.path_segment, 1.0f);
#endif
}
/* Records the direction at the point of entry the path takes when sampling the SSS contribution.
* If not terminated this function is usually followed by a call of
* guiding_record_volume_transmission to record the transmittance between the point of entry and
* the point of exit.*/
ccl_device_forceinline void guiding_record_bssrdf_bounce(KernelGlobals kg,
IntegratorState state,
const float pdf,
const float3 N,
const float3 omega_in,
const Spectrum weight,
const Spectrum albedo)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
if (!kernel_data.integrator.train_guiding) {
return;
}
const float3 normal = clamp(N, -one_float3(), one_float3());
const float3 weight_rgb = spectrum_to_rgb(weight * albedo);
kernel_assert(state->guiding.path_segment != nullptr);
openpgl::cpp::SetVolumeScatter(state->guiding.path_segment, false);
openpgl::cpp::SetNormal(state->guiding.path_segment, guiding_vec3f(normal));
openpgl::cpp::SetDirectionIn(state->guiding.path_segment, guiding_vec3f(omega_in));
openpgl::cpp::SetPDFDirectionIn(state->guiding.path_segment, pdf);
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, guiding_vec3f(weight_rgb));
#endif
}
/* Record Volume Interactions */
/* Records/Adds a new path segment with the current path vertex being inside a volume.
* If the path is not terminated this call is usually followed by a call of
* guiding_record_volume_bounce. */
ccl_device_forceinline void guiding_record_volume_segment(KernelGlobals kg,
IntegratorState state,
const float3 P,
const float3 I)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
if (!kernel_data.integrator.train_guiding) {
return;
}
const pgl_vec3f zero = guiding_vec3f(zero_float3());
const pgl_vec3f one = guiding_vec3f(one_float3());
state->guiding.path_segment = kg->opgl_path_segment_storage->NextSegment();
openpgl::cpp::SetPosition(state->guiding.path_segment, guiding_point3f(P));
openpgl::cpp::SetDirectionOut(state->guiding.path_segment, guiding_vec3f(I));
openpgl::cpp::SetVolumeScatter(state->guiding.path_segment, true);
openpgl::cpp::SetScatteredContribution(state->guiding.path_segment, zero);
openpgl::cpp::SetDirectContribution(state->guiding.path_segment, zero);
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, one);
openpgl::cpp::SetEta(state->guiding.path_segment, 1.0);
#endif
}
/* Records the volume scattering event at the current vertex position of the segment.*/
ccl_device_forceinline void guiding_record_volume_bounce(KernelGlobals kg,
IntegratorState state,
ccl_private const ShaderData *sd,
const Spectrum weight,
const float pdf,
const float3 omega_in,
const float roughness)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (!kernel_data.integrator.train_guiding) {
return;
}
const float3 weight_rgb = spectrum_to_rgb(weight);
const float3 normal = make_float3(0.0f, 0.0f, 1.0f);
kernel_assert(state->guiding.path_segment != nullptr);
openpgl::cpp::SetVolumeScatter(state->guiding.path_segment, true);
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, guiding_vec3f(one_float3()));
openpgl::cpp::SetNormal(state->guiding.path_segment, guiding_vec3f(normal));
openpgl::cpp::SetDirectionIn(state->guiding.path_segment, guiding_vec3f(omega_in));
openpgl::cpp::SetPDFDirectionIn(state->guiding.path_segment, pdf);
openpgl::cpp::SetScatteringWeight(state->guiding.path_segment, guiding_vec3f(weight_rgb));
openpgl::cpp::SetIsDelta(state->guiding.path_segment, false);
openpgl::cpp::SetEta(state->guiding.path_segment, 1.f);
openpgl::cpp::SetRoughness(state->guiding.path_segment, roughness);
#endif
}
/* Records the transmission (a.k.a. transmittance weight) between the current path segment
* and the next one, when the path is inside or passes a volume.*/
ccl_device_forceinline void guiding_record_volume_transmission(KernelGlobals kg,
IntegratorState state,
const float3 transmittance_weight)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
if (!kernel_data.integrator.train_guiding) {
return;
}
if (state->guiding.path_segment) {
// TODO (sherholz): need to find a better way to avoid this check
if ((transmittance_weight[0] < 0.f || !std::isfinite(transmittance_weight[0]) ||
std::isnan(transmittance_weight[0])) ||
(transmittance_weight[1] < 0.f || !std::isfinite(transmittance_weight[1]) ||
std::isnan(transmittance_weight[1])) ||
(transmittance_weight[2] < 0.f || !std::isfinite(transmittance_weight[2]) ||
std::isnan(transmittance_weight[2]))) {
}
else {
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment,
guiding_vec3f(transmittance_weight));
}
}
#endif
}
/* Records the emission of a volume at the vertex of the current path segment. */
ccl_device_forceinline void guiding_record_volume_emission(KernelGlobals kg,
IntegratorState state,
const Spectrum Le)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
if (!kernel_data.integrator.train_guiding) {
return;
}
if (state->guiding.path_segment) {
const float3 Le_rgb = spectrum_to_rgb(Le);
openpgl::cpp::SetDirectContribution(state->guiding.path_segment, guiding_vec3f(Le_rgb));
openpgl::cpp::SetMiWeight(state->guiding.path_segment, 1.0f);
}
#endif
}
/* Record Light Interactions */
/* Adds a pseudo path vertex/segment when intersecting a virtual light source.
* (e.g., area, sphere, or disk light). This call is often followed
* a call of guiding_record_surface_emission, if the intersected light source
* emits light in the direction of the path. */
ccl_device_forceinline void guiding_record_light_surface_segment(
KernelGlobals kg, IntegratorState state, ccl_private const Intersection *ccl_restrict isect)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
if (!kernel_data.integrator.train_guiding) {
return;
}
const pgl_vec3f zero = guiding_vec3f(zero_float3());
const pgl_vec3f one = guiding_vec3f(one_float3());
const float3 ray_P = INTEGRATOR_STATE(state, ray, P);
const float3 ray_D = INTEGRATOR_STATE(state, ray, D);
const float3 P = ray_P + isect->t * ray_D;
state->guiding.path_segment = kg->opgl_path_segment_storage->NextSegment();
openpgl::cpp::SetPosition(state->guiding.path_segment, guiding_point3f(P));
openpgl::cpp::SetDirectionOut(state->guiding.path_segment, guiding_vec3f(-ray_D));
openpgl::cpp::SetNormal(state->guiding.path_segment, guiding_vec3f(-ray_D));
openpgl::cpp::SetDirectionIn(state->guiding.path_segment, guiding_vec3f(ray_D));
openpgl::cpp::SetPDFDirectionIn(state->guiding.path_segment, 1.0f);
openpgl::cpp::SetVolumeScatter(state->guiding.path_segment, false);
openpgl::cpp::SetScatteredContribution(state->guiding.path_segment, zero);
openpgl::cpp::SetDirectContribution(state->guiding.path_segment, zero);
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, one);
openpgl::cpp::SetScatteringWeight(state->guiding.path_segment, one);
openpgl::cpp::SetEta(state->guiding.path_segment, 1.0f);
#endif
}
/* Records/Adds a final path segment when the path leaves the scene and
* intersects with a background light (e.g., background color,
* distant light, or env map). The vertex for this segment is placed along
* the current ray far out the scene.*/
ccl_device_forceinline void guiding_record_background(KernelGlobals kg,
IntegratorState state,
const Spectrum L,
const float mis_weight)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
if (!kernel_data.integrator.train_guiding) {
return;
}
const float3 L_rgb = spectrum_to_rgb(L);
const float3 ray_P = INTEGRATOR_STATE(state, ray, P);
const float3 ray_D = INTEGRATOR_STATE(state, ray, D);
const float3 P = ray_P + (1e6f) * ray_D;
const float3 normal = make_float3(0.0f, 0.0f, 1.0f);
openpgl::cpp::PathSegment background_segment;
openpgl::cpp::SetPosition(&background_segment, guiding_vec3f(P));
openpgl::cpp::SetNormal(&background_segment, guiding_vec3f(normal));
openpgl::cpp::SetDirectionOut(&background_segment, guiding_vec3f(-ray_D));
openpgl::cpp::SetDirectContribution(&background_segment, guiding_vec3f(L_rgb));
openpgl::cpp::SetMiWeight(&background_segment, mis_weight);
kg->opgl_path_segment_storage->AddSegment(background_segment);
#endif
}
/* Records the scattered contribution of a next event estimation
* (i.e., a direct light estimate scattered at the current path vertex
* towards the previous vertex).*/
ccl_device_forceinline void guiding_record_direct_light(KernelGlobals kg,
IntegratorShadowState state)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
if (!kernel_data.integrator.train_guiding) {
return;
}
if (state->shadow_path.path_segment) {
const Spectrum Lo = safe_divide_color(INTEGRATOR_STATE(state, shadow_path, throughput),
INTEGRATOR_STATE(state, shadow_path, unlit_throughput));
const float3 Lo_rgb = spectrum_to_rgb(Lo);
openpgl::cpp::AddScatteredContribution(state->shadow_path.path_segment, guiding_vec3f(Lo_rgb));
}
#endif
}
/* Record Russian Roulette */
/* Records the probability of continuing the path at the current path segment. */
ccl_device_forceinline void guiding_record_continuation_probability(
KernelGlobals kg, IntegratorState state, const float continuation_probability)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
if (!kernel_data.integrator.train_guiding) {
return;
}
if (state->guiding.path_segment) {
openpgl::cpp::SetRussianRouletteProbability(state->guiding.path_segment,
continuation_probability);
}
#endif
}
/* Path guiding debug render passes. */
/* Write a set of path guiding related debug information (e.g., guiding probability at first
* bounce) into separate rendering passes.*/
ccl_device_forceinline void guiding_write_debug_passes(KernelGlobals kg,
IntegratorState state,
ccl_private const ShaderData *sd,
ccl_global float *ccl_restrict
render_buffer)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
# ifdef WITH_CYCLES_DEBUG
if (!kernel_data.integrator.train_guiding) {
return;
}
if (INTEGRATOR_STATE(state, path, bounce) != 0) {
return;
}
const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index);
const uint64_t render_buffer_offset = (uint64_t)render_pixel_index *
kernel_data.film.pass_stride;
ccl_global float *buffer = render_buffer + render_buffer_offset;
if (kernel_data.film.pass_guiding_probability != PASS_UNUSED) {
float guiding_prob = state->guiding.surface_guiding_sampling_prob;
film_write_pass_float(buffer + kernel_data.film.pass_guiding_probability, guiding_prob);
}
if (kernel_data.film.pass_guiding_avg_roughness != PASS_UNUSED) {
float avg_roughness = 0.0f;
float sum_sample_weight = 0.0f;
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (!CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
continue;
}
avg_roughness += sc->sample_weight * bsdf_get_specular_roughness_squared(sc);
sum_sample_weight += sc->sample_weight;
}
avg_roughness = avg_roughness > 0.f ? avg_roughness / sum_sample_weight : 0.f;
film_write_pass_float(buffer + kernel_data.film.pass_guiding_avg_roughness, avg_roughness);
}
# endif
#endif
}
/* Guided BSDFs */
ccl_device_forceinline bool guiding_bsdf_init(KernelGlobals kg,
IntegratorState state,
const float3 P,
const float3 N,
ccl_private float &rand)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (kg->opgl_surface_sampling_distribution->Init(
kg->opgl_guiding_field, guiding_point3f(P), rand, true)) {
kg->opgl_surface_sampling_distribution->ApplyCosineProduct(guiding_point3f(N));
return true;
}
#endif
return false;
}
ccl_device_forceinline float guiding_bsdf_sample(KernelGlobals kg,
IntegratorState state,
const float2 rand_bsdf,
ccl_private float3 *omega_in)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
pgl_vec3f wo;
const pgl_point2f rand = openpgl::cpp::Point2(rand_bsdf.x, rand_bsdf.y);
const float pdf = kg->opgl_surface_sampling_distribution->SamplePDF(rand, wo);
*omega_in = make_float3(wo.x, wo.y, wo.z);
return pdf;
#else
return 0.0f;
#endif
}
ccl_device_forceinline float guiding_bsdf_pdf(KernelGlobals kg,
IntegratorState state,
const float3 omega_in)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
return kg->opgl_surface_sampling_distribution->PDF(guiding_vec3f(omega_in));
#else
return 0.0f;
#endif
}
/* Guided Volume Phases */
ccl_device_forceinline bool guiding_phase_init(KernelGlobals kg,
IntegratorState state,
const float3 P,
const float3 D,
const float g,
ccl_private float &rand)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (kg->opgl_volume_sampling_distribution->Init(
kg->opgl_guiding_field, guiding_point3f(P), rand, true)) {
kg->opgl_volume_sampling_distribution->ApplySingleLobeHenyeyGreensteinProduct(guiding_vec3f(D),
g);
return true;
}
#endif
return false;
}
ccl_device_forceinline float guiding_phase_sample(KernelGlobals kg,
IntegratorState state,
const float2 rand_phase,
ccl_private float3 *omega_in)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
pgl_vec3f wo;
const pgl_point2f rand = openpgl::cpp::Point2(rand_phase.x, rand_phase.y);
const float pdf = kg->opgl_volume_sampling_distribution->SamplePDF(rand, wo);
*omega_in = make_float3(wo.x, wo.y, wo.z);
return pdf;
#else
return 0.0f;
#endif
}
ccl_device_forceinline float guiding_phase_pdf(KernelGlobals kg,
IntegratorState state,
const float3 omega_in)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
return kg->opgl_volume_sampling_distribution->PDF(guiding_vec3f(omega_in));
#else
return 0.0f;
#endif
}
CCL_NAMESPACE_END

View File

@@ -7,6 +7,7 @@
#include "kernel/film/light_passes.h"
#include "kernel/integrator/guiding.h"
#include "kernel/integrator/path_state.h"
#include "kernel/integrator/shadow_catcher.h"
@@ -48,13 +49,15 @@ ccl_device_forceinline bool integrator_intersect_terminate(KernelGlobals kg,
* surfaces in front of emission do we need to evaluate the shader, since we
* perform MIS as part of indirect rays. */
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
const float probability = path_state_continuation_probability(kg, state, path_flag);
INTEGRATOR_STATE_WRITE(state, path, continuation_probability) = probability;
const float continuation_probability = path_state_continuation_probability(kg, state, path_flag);
INTEGRATOR_STATE_WRITE(state, path, continuation_probability) = continuation_probability;
if (probability != 1.0f) {
guiding_record_continuation_probability(kg, state, continuation_probability);
if (continuation_probability != 1.0f) {
const float terminate = path_state_rng_1D(kg, &rng_state, PRNG_TERMINATE);
if (probability == 0.0f || terminate >= probability) {
if (continuation_probability == 0.0f || terminate >= continuation_probability) {
if (shader_flags & SD_HAS_EMISSION) {
/* Mark path to be terminated right after shader evaluation on the surface. */
INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_TERMINATE_ON_NEXT_SURFACE;

View File

@@ -807,7 +807,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg,
float3 wo = normalize_len(vertices[0].p - sd->P, &wo_len);
/* Initialize throughput and evaluate receiver bsdf * |n.wo|. */
surface_shader_bsdf_eval(kg, sd, wo, false, throughput, ls->shader);
surface_shader_bsdf_eval(kg, state, sd, wo, throughput, ls->shader);
/* Update light sample with new position / direct.ion
* and keep pdf in vertex area measure */

View File

@@ -56,6 +56,11 @@ ccl_device_inline void path_state_init_integrator(KernelGlobals kg,
INTEGRATOR_STATE_WRITE(state, path, continuation_probability) = 1.0f;
INTEGRATOR_STATE_WRITE(state, path, throughput) = one_spectrum();
#ifdef __PATH_GUIDING__
INTEGRATOR_STATE_WRITE(state, path, unguided_throughput) = 1.0f;
INTEGRATOR_STATE_WRITE(state, guiding, path_segment) = nullptr;
#endif
#ifdef __MNEE__
INTEGRATOR_STATE_WRITE(state, path, mnee) = 0;
#endif
@@ -249,7 +254,11 @@ ccl_device_inline float path_state_continuation_probability(KernelGlobals kg,
/* Probabilistic termination: use sqrt() to roughly match typical view
* transform and do path termination a bit later on average. */
return min(sqrtf(reduce_max(fabs(INTEGRATOR_STATE(state, path, throughput)))), 1.0f);
Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
throughput *= INTEGRATOR_STATE(state, path, unguided_throughput);
#endif
return min(sqrtf(reduce_max(fabs(throughput))), 1.0f);
}
ccl_device_inline bool path_state_ao_bounce(KernelGlobals kg, ConstIntegratorState state)

View File

@@ -5,6 +5,7 @@
#include "kernel/film/light_passes.h"
#include "kernel/integrator/guiding.h"
#include "kernel/integrator/surface_shader.h"
#include "kernel/light/light.h"
@@ -124,6 +125,7 @@ ccl_device_inline void integrate_background(KernelGlobals kg,
mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, pdf);
}
guiding_record_background(kg, state, L, mis_weight);
L *= mis_weight;
}
@@ -185,6 +187,7 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg,
}
/* Write to render buffer. */
guiding_record_background(kg, state, light_eval, mis_weight);
film_write_surface_emission(
kg, state, light_eval, mis_weight, render_buffer, kernel_data.background.lightgroup);
}

View File

@@ -18,6 +18,8 @@ ccl_device_inline void integrate_light(KernelGlobals kg,
Intersection isect ccl_optional_struct_init;
integrator_state_read_isect(kg, state, &isect);
guiding_record_light_surface_segment(kg, state, &isect);
float3 ray_P = INTEGRATOR_STATE(state, ray, P);
const float3 ray_D = INTEGRATOR_STATE(state, ray, D);
const float ray_time = INTEGRATOR_STATE(state, ray, time);
@@ -66,6 +68,7 @@ ccl_device_inline void integrate_light(KernelGlobals kg,
}
/* Write to render buffer. */
guiding_record_surface_emission(kg, state, light_eval, mis_weight);
film_write_surface_emission(kg, state, light_eval, mis_weight, render_buffer, ls.group);
}

View File

@@ -3,6 +3,7 @@
#pragma once
#include "kernel/integrator/guiding.h"
#include "kernel/integrator/shade_volume.h"
#include "kernel/integrator/surface_shader.h"
#include "kernel/integrator/volume_stack.h"
@@ -165,6 +166,7 @@ ccl_device void integrator_shade_shadow(KernelGlobals kg,
return;
}
else {
guiding_record_direct_light(kg, state);
film_write_direct_light(kg, state, render_buffer);
integrator_shadow_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW);
return;

View File

@@ -9,6 +9,7 @@
#include "kernel/integrator/mnee.h"
#include "kernel/integrator/guiding.h"
#include "kernel/integrator/path_state.h"
#include "kernel/integrator/subsurface.h"
#include "kernel/integrator/surface_shader.h"
@@ -101,7 +102,7 @@ ccl_device_forceinline bool integrate_surface_holdout(KernelGlobals kg,
}
ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg,
ConstIntegratorState state,
IntegratorState state,
ccl_private const ShaderData *sd,
ccl_global float *ccl_restrict
render_buffer)
@@ -128,6 +129,7 @@ ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg,
mis_weight = light_sample_mis_weight_forward(kg, bsdf_pdf, pdf);
}
guiding_record_surface_emission(kg, state, L, mis_weight);
film_write_surface_emission(
kg, state, L, mis_weight, render_buffer, object_lightgroup(kg, sd->object));
}
@@ -171,7 +173,8 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
Ray ray ccl_optional_struct_init;
BsdfEval bsdf_eval ccl_optional_struct_init;
const bool is_transmission = surface_shader_is_transmission(sd, ls.D);
const bool is_transmission = dot(ls.D, sd->N) < 0.0f;
#ifdef __MNEE__
int mnee_vertex_count = 0;
@@ -182,13 +185,15 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
const bool use_caustics = kernel_data_fetch(lights, ls.lamp).use_caustics;
if (use_caustics) {
/* Are we on a caustic caster? */
if (is_transmission && (sd->object_flag & SD_OBJECT_CAUSTICS_CASTER))
if (is_transmission && (sd->object_flag & SD_OBJECT_CAUSTICS_CASTER)) {
return;
}
/* Are we on a caustic receiver? */
if (!is_transmission && (sd->object_flag & SD_OBJECT_CAUSTICS_RECEIVER))
if (!is_transmission && (sd->object_flag & SD_OBJECT_CAUSTICS_RECEIVER)) {
mnee_vertex_count = kernel_path_mnee_sample(
kg, state, sd, emission_sd, rng_state, &ls, &bsdf_eval);
}
}
}
}
@@ -207,8 +212,7 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
}
/* Evaluate BSDF. */
const float bsdf_pdf = surface_shader_bsdf_eval(
kg, sd, ls.D, is_transmission, &bsdf_eval, ls.shader);
const float bsdf_pdf = surface_shader_bsdf_eval(kg, state, sd, ls.D, &bsdf_eval, ls.shader);
bsdf_eval_mul(&bsdf_eval, light_eval / ls.pdf);
if (ls.shader & SHADER_USE_MIS) {
@@ -256,8 +260,8 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
/* Copy state from main path to shadow path. */
uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag);
shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0;
const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput) *
bsdf_eval_sum(&bsdf_eval);
const Spectrum unlit_throughput = INTEGRATOR_STATE(state, path, throughput);
const Spectrum throughput = unlit_throughput * bsdf_eval_sum(&bsdf_eval);
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
PackedSpectrum pass_diffuse_weight;
@@ -327,6 +331,11 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
shadow_state, shadow_path, lightgroup) = (ls.type != LIGHT_BACKGROUND) ?
ls.group + 1 :
kernel_data.background.lightgroup + 1;
#ifdef __PATH_GUIDING__
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unlit_throughput) = unlit_throughput;
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = INTEGRATOR_STATE(
state, guiding, path_segment);
#endif
}
/* Path tracing: bounce off or through surface with new direction. */
@@ -352,16 +361,52 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
#endif
/* BSDF closure, sample direction. */
float bsdf_pdf;
float bsdf_pdf = 0.0f, unguided_bsdf_pdf = 0.0f;
BsdfEval bsdf_eval ccl_optional_struct_init;
float3 bsdf_omega_in ccl_optional_struct_init;
int label;
label = surface_shader_bsdf_sample_closure(
kg, sd, sc, rand_bsdf, &bsdf_eval, &bsdf_omega_in, &bsdf_pdf);
float2 bsdf_sampled_roughness = make_float2(1.0f, 1.0f);
float bsdf_eta = 1.0f;
if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) {
return LABEL_NONE;
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (kernel_data.integrator.use_surface_guiding) {
label = surface_shader_bsdf_guided_sample_closure(kg,
state,
sd,
sc,
rand_bsdf,
&bsdf_eval,
&bsdf_omega_in,
&bsdf_pdf,
&unguided_bsdf_pdf,
&bsdf_sampled_roughness,
&bsdf_eta);
if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) {
return LABEL_NONE;
}
INTEGRATOR_STATE_WRITE(state, path, unguided_throughput) *= bsdf_pdf / unguided_bsdf_pdf;
}
else
#endif
{
label = surface_shader_bsdf_sample_closure(kg,
sd,
sc,
rand_bsdf,
&bsdf_eval,
&bsdf_omega_in,
&bsdf_pdf,
&bsdf_sampled_roughness,
&bsdf_eta);
if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) {
return LABEL_NONE;
}
unguided_bsdf_pdf = bsdf_pdf;
}
if (label & LABEL_TRANSPARENT) {
@@ -381,9 +426,8 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
}
/* Update throughput. */
Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
throughput *= bsdf_eval_sum(&bsdf_eval) / bsdf_pdf;
INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput;
const Spectrum bsdf_weight = bsdf_eval_sum(&bsdf_eval) / bsdf_pdf;
INTEGRATOR_STATE_WRITE(state, path, throughput) *= bsdf_weight;
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
if (INTEGRATOR_STATE(state, path, bounce) == 0) {
@@ -398,10 +442,21 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
if (!(label & LABEL_TRANSPARENT)) {
INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = bsdf_pdf;
INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf(
bsdf_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
unguided_bsdf_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
}
path_state_next(kg, state, label);
guiding_record_surface_bounce(kg,
state,
sd,
bsdf_weight,
bsdf_pdf,
sd->N,
normalize(bsdf_omega_in),
bsdf_sampled_roughness,
bsdf_eta);
return label;
}
@@ -423,14 +478,15 @@ ccl_device_forceinline int integrate_surface_volume_only_bounce(IntegratorState
ccl_device_forceinline bool integrate_surface_terminate(IntegratorState state,
const uint32_t path_flag)
{
const float probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ?
0.0f :
INTEGRATOR_STATE(state, path, continuation_probability);
if (probability == 0.0f) {
const float continuation_probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ?
0.0f :
INTEGRATOR_STATE(
state, path, continuation_probability);
if (continuation_probability == 0.0f) {
return true;
}
else if (probability != 1.0f) {
INTEGRATOR_STATE_WRITE(state, path, throughput) /= probability;
else if (continuation_probability != 1.0f) {
INTEGRATOR_STATE_WRITE(state, path, throughput) /= continuation_probability;
}
return false;
@@ -538,6 +594,8 @@ ccl_device bool integrate_surface(KernelGlobals kg,
#ifdef __VOLUME__
if (!(sd.flag & SD_HAS_ONLY_VOLUME)) {
#endif
guiding_record_surface_segment(kg, state, &sd);
#ifdef __SUBSURFACE__
/* Can skip shader evaluation for BSSRDF exit point without bump mapping. */
if (!(path_flag & PATH_RAY_SUBSURFACE) || ((sd.flag & SD_HAS_BSSRDF_BUMP)))
@@ -603,6 +661,10 @@ ccl_device bool integrate_surface(KernelGlobals kg,
RNGState rng_state;
path_state_rng_load(state, &rng_state);
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
surface_shader_prepare_guiding(kg, state, &sd, &rng_state);
guiding_write_debug_passes(kg, state, &sd, render_buffer);
#endif
/* Direct light. */
PROFILING_EVENT(PROFILING_SHADE_SURFACE_DIRECT_LIGHT);
integrate_surface_direct_light<node_feature_mask>(kg, state, &sd, &rng_state);

View File

@@ -7,6 +7,7 @@
#include "kernel/film/denoising_passes.h"
#include "kernel/film/light_passes.h"
#include "kernel/integrator/guiding.h"
#include "kernel/integrator/intersect_closest.h"
#include "kernel/integrator/path_state.h"
#include "kernel/integrator/volume_shader.h"
@@ -612,6 +613,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
const Spectrum emission = volume_emission_integrate(
&coeff, closure_flag, transmittance, dt);
accum_emission += result.indirect_throughput * emission;
guiding_record_volume_emission(kg, state, emission);
}
}
@@ -761,7 +763,7 @@ ccl_device_forceinline void integrate_volume_direct_light(
/* Evaluate BSDF. */
BsdfEval phase_eval ccl_optional_struct_init;
const float phase_pdf = volume_shader_phase_eval(kg, sd, phases, ls->D, &phase_eval);
float phase_pdf = volume_shader_phase_eval(kg, state, sd, phases, ls->D, &phase_eval);
if (ls->shader & SHADER_USE_MIS) {
float mis_weight = light_sample_mis_weight_nee(kg, ls->pdf, phase_pdf);
@@ -848,6 +850,12 @@ ccl_device_forceinline void integrate_volume_direct_light(
ls->group + 1 :
kernel_data.background.lightgroup + 1;
# ifdef __PATH_GUIDING__
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unlit_throughput) = throughput;
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = INTEGRATOR_STATE(
state, guiding, path_segment);
# endif
integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state);
}
@@ -861,18 +869,54 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
{
PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_INDIRECT_LIGHT);
const float2 rand_phase = path_state_rng_2D(kg, rng_state, PRNG_VOLUME_PHASE);
float2 rand_phase = path_state_rng_2D(kg, rng_state, PRNG_VOLUME_PHASE);
ccl_private const ShaderVolumeClosure *svc = volume_shader_phase_pick(phases, &rand_phase);
/* Phase closure, sample direction. */
float phase_pdf;
float phase_pdf = 0.0f, unguided_phase_pdf = 0.0f;
BsdfEval phase_eval ccl_optional_struct_init;
float3 phase_omega_in ccl_optional_struct_init;
float sampled_roughness = 1.0f;
int label;
const int label = volume_shader_phase_sample(
kg, sd, phases, rand_phase, &phase_eval, &phase_omega_in, &phase_pdf);
# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (kernel_data.integrator.use_guiding) {
label = volume_shader_phase_guided_sample(kg,
state,
sd,
svc,
rand_phase,
&phase_eval,
&phase_omega_in,
&phase_pdf,
&unguided_phase_pdf,
&sampled_roughness);
if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) {
return false;
if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) {
return false;
}
INTEGRATOR_STATE_WRITE(state, path, unguided_throughput) *= phase_pdf / unguided_phase_pdf;
}
else
# endif
{
label = volume_shader_phase_sample(kg,
sd,
phases,
svc,
rand_phase,
&phase_eval,
&phase_omega_in,
&phase_pdf,
&sampled_roughness);
if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) {
return false;
}
unguided_phase_pdf = phase_pdf;
}
/* Setup ray. */
@@ -887,9 +931,15 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
INTEGRATOR_STATE_WRITE(state, isect, prim) = sd->prim;
INTEGRATOR_STATE_WRITE(state, isect, object) = sd->object;
const Spectrum phase_weight = bsdf_eval_sum(&phase_eval) / phase_pdf;
/* Add phase function sampling data to the path segment. */
guiding_record_volume_bounce(
kg, state, sd, phase_weight, phase_pdf, normalize(phase_omega_in), sampled_roughness);
/* Update throughput. */
const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
const Spectrum throughput_phase = throughput * bsdf_eval_sum(&phase_eval) / phase_pdf;
const Spectrum throughput_phase = throughput * phase_weight;
INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput_phase;
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
@@ -900,7 +950,7 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
/* Update path state */
INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = phase_pdf;
INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf(
phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
unguided_phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
path_state_next(kg, state, label);
return true;
@@ -939,6 +989,10 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
VOLUME_READ_LAMBDA(integrator_state_read_volume_stack(state, i))
const float step_size = volume_stack_step_size(kg, volume_read_lambda_pass);
# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
const float3 initial_throughput = INTEGRATOR_STATE(state, path, throughput);
# endif
/* TODO: expensive to zero closures? */
VolumeIntegrateResult result = {};
volume_integrate_heterogeneous(kg,
@@ -956,17 +1010,50 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
* to be terminated. That will shading evaluating to leave out any scattering closures,
* but emission and absorption are still handled for multiple importance sampling. */
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
const float probability = (path_flag & PATH_RAY_TERMINATE_IN_NEXT_VOLUME) ?
0.0f :
INTEGRATOR_STATE(state, path, continuation_probability);
if (probability == 0.0f) {
const float continuation_probability = (path_flag & PATH_RAY_TERMINATE_IN_NEXT_VOLUME) ?
0.0f :
INTEGRATOR_STATE(
state, path, continuation_probability);
if (continuation_probability == 0.0f) {
return VOLUME_PATH_MISSED;
}
# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
bool guiding_generated_new_segment = false;
if (kernel_data.integrator.use_guiding) {
/* Record transmittance using change in throughput. */
float3 transmittance_weight = spectrum_to_rgb(
safe_divide_color(result.indirect_throughput, initial_throughput));
guiding_record_volume_transmission(kg, state, transmittance_weight);
if (result.indirect_scatter) {
const float3 P = ray->P + result.indirect_t * ray->D;
/* Record volume segment up to direct scatter position.
* TODO: volume segment is wrong when direct_t and indirect_t. */
if (result.direct_scatter && (result.direct_t == result.indirect_t)) {
guiding_record_volume_segment(kg, state, P, sd.I);
guiding_generated_new_segment = true;
}
# if PATH_GUIDING_LEVEL >= 4
/* TODO: this position will be wrong for direct light pdf computation,
* since the direct light position may be different? */
volume_shader_prepare_guiding(
kg, state, &sd, &rng_state, P, ray->D, &result.direct_phases, direct_sample_method);
# endif
}
else {
/* No guiding if we don't scatter. */
state->guiding.use_volume_guiding = false;
}
}
# endif
/* Direct light. */
if (result.direct_scatter) {
const float3 direct_P = ray->P + result.direct_t * ray->D;
result.direct_throughput /= probability;
result.direct_throughput /= continuation_probability;
integrate_volume_direct_light(kg,
state,
&sd,
@@ -979,16 +1066,22 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
/* Indirect light.
*
* Only divide throughput by probability if we scatter. For the attenuation
* Only divide throughput by continuation_probability if we scatter. For the attenuation
* case the next surface will already do this division. */
if (result.indirect_scatter) {
result.indirect_throughput /= probability;
result.indirect_throughput /= continuation_probability;
}
INTEGRATOR_STATE_WRITE(state, path, throughput) = result.indirect_throughput;
if (result.indirect_scatter) {
sd.P = ray->P + result.indirect_t * ray->D;
# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
if (!guiding_generated_new_segment) {
guiding_record_volume_segment(kg, state, sd.P, sd.I);
}
# endif
if (integrate_volume_phase_scatter(kg, state, &sd, &rng_state, &result.indirect_phases)) {
return VOLUME_PATH_SCATTERED;
}

View File

@@ -40,6 +40,16 @@ KERNEL_STRUCT_MEMBER(shadow_path, PackedSpectrum, pass_glossy_weight, KERNEL_FEA
KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, num_hits, KERNEL_FEATURE_PATH_TRACING)
/* Light group. */
KERNEL_STRUCT_MEMBER(shadow_path, uint8_t, lightgroup, KERNEL_FEATURE_PATH_TRACING)
/* Path guiding. */
KERNEL_STRUCT_MEMBER(shadow_path, PackedSpectrum, unlit_throughput, KERNEL_FEATURE_PATH_GUIDING)
#ifdef __PATH_GUIDING__
KERNEL_STRUCT_MEMBER(shadow_path,
openpgl::cpp::PathSegment *,
path_segment,
KERNEL_FEATURE_PATH_GUIDING)
#else
KERNEL_STRUCT_MEMBER(shadow_path, uint64_t, path_segment, KERNEL_FEATURE_PATH_GUIDING)
#endif
KERNEL_STRUCT_END(shadow_path)
/********************************** Shadow Ray *******************************/

View File

@@ -31,6 +31,10 @@
#include "util/types.h"
#ifdef __PATH_GUIDING__
# include "util/guiding.h"
#endif
#pragma once
CCL_NAMESPACE_BEGIN

View File

@@ -47,6 +47,9 @@ KERNEL_STRUCT_MEMBER(path, float, min_ray_pdf, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER(path, float, continuation_probability, KERNEL_FEATURE_PATH_TRACING)
/* Throughput. */
KERNEL_STRUCT_MEMBER(path, PackedSpectrum, throughput, KERNEL_FEATURE_PATH_TRACING)
/* Factor to multiple with throughput to get remove any guiding PDFS.
* Such throughput without guiding PDFS is used for Russian roulette termination. */
KERNEL_STRUCT_MEMBER(path, float, unguided_throughput, KERNEL_FEATURE_PATH_GUIDING)
/* Ratio of throughput to distinguish diffuse / glossy / transmission render passes. */
KERNEL_STRUCT_MEMBER(path, PackedSpectrum, pass_diffuse_weight, KERNEL_FEATURE_LIGHT_PASSES)
KERNEL_STRUCT_MEMBER(path, PackedSpectrum, pass_glossy_weight, KERNEL_FEATURE_LIGHT_PASSES)
@@ -98,3 +101,33 @@ KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, shader, KERNEL_FEATURE_VOLUME)
KERNEL_STRUCT_END_ARRAY(volume_stack,
KERNEL_STRUCT_VOLUME_STACK_SIZE,
KERNEL_STRUCT_VOLUME_STACK_SIZE)
/************************************ Path Guiding *****************************/
KERNEL_STRUCT_BEGIN(guiding)
#ifdef __PATH_GUIDING__
/* Current path segment of the random walk/path. */
KERNEL_STRUCT_MEMBER(guiding,
openpgl::cpp::PathSegment *,
path_segment,
KERNEL_FEATURE_PATH_GUIDING)
#else
/* Current path segment of the random walk/path. */
KERNEL_STRUCT_MEMBER(guiding, uint64_t, path_segment, KERNEL_FEATURE_PATH_GUIDING)
#endif
/* If surface guiding is enabled */
KERNEL_STRUCT_MEMBER(guiding, bool, use_surface_guiding, KERNEL_FEATURE_PATH_GUIDING)
/* Random number used for additional guiding decisions (e.g., cache query, selection to use guiding
* or BSDF sampling) */
KERNEL_STRUCT_MEMBER(guiding, float, sample_surface_guiding_rand, KERNEL_FEATURE_PATH_GUIDING)
/* The probability to use surface guiding (i.e., diffuse sampling prob * guiding prob)*/
KERNEL_STRUCT_MEMBER(guiding, float, surface_guiding_sampling_prob, KERNEL_FEATURE_PATH_GUIDING)
/* Probability of sampling a BSSRDF closure instead of a BSDF closure*/
KERNEL_STRUCT_MEMBER(guiding, float, bssrdf_sampling_prob, KERNEL_FEATURE_PATH_GUIDING)
/* If volume guiding is enabled */
KERNEL_STRUCT_MEMBER(guiding, bool, use_volume_guiding, KERNEL_FEATURE_PATH_GUIDING)
/* Random number used for additional guiding decisions (e.g., cache query, selection to use guiding
* or BSDF sampling) */
KERNEL_STRUCT_MEMBER(guiding, float, sample_volume_guiding_rand, KERNEL_FEATURE_PATH_GUIDING)
/* The probability to use surface guiding (i.e., diffuse sampling prob * guiding prob). */
KERNEL_STRUCT_MEMBER(guiding, float, volume_guiding_sampling_prob, KERNEL_FEATURE_PATH_GUIDING)
KERNEL_STRUCT_END(guiding)

View File

@@ -78,6 +78,9 @@ ccl_device int subsurface_bounce(KernelGlobals kg,
INTEGRATOR_STATE_WRITE(state, subsurface, radius) = bssrdf->radius;
INTEGRATOR_STATE_WRITE(state, subsurface, anisotropy) = bssrdf->anisotropy;
/* Path guiding. */
guiding_record_bssrdf_weight(kg, state, weight, bssrdf->albedo);
return LABEL_SUBSURFACE_SCATTER;
}

View File

@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#include "kernel/integrator/guiding.h"
CCL_NAMESPACE_BEGIN
/* BSSRDF using disk based importance sampling.
@@ -173,8 +175,8 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg,
if (r < next_sum) {
/* Return exit point. */
INTEGRATOR_STATE_WRITE(state, path, throughput) *= weight * sum_weights / sample_weight;
const Spectrum resampled_weight = weight * sum_weights / sample_weight;
INTEGRATOR_STATE_WRITE(state, path, throughput) *= resampled_weight;
ss_isect.hits[0] = ss_isect.hits[hit];
ss_isect.Ng[0] = ss_isect.Ng[hit];
@@ -182,6 +184,9 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg,
ray.D = ss_isect.Ng[hit];
ray.tmin = 0.0f;
ray.tmax = 1.0f;
guiding_record_bssrdf_bounce(
kg, state, 1.0f, Ng, -Ng, resampled_weight, INTEGRATOR_STATE(state, subsurface, albedo));
return true;
}

View File

@@ -5,6 +5,8 @@
#include "kernel/bvh/bvh.h"
#include "kernel/integrator/guiding.h"
CCL_NAMESPACE_BEGIN
/* Random walk subsurface scattering.
@@ -203,7 +205,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
const float anisotropy = INTEGRATOR_STATE(state, subsurface, anisotropy);
Spectrum sigma_t, alpha;
Spectrum throughput = INTEGRATOR_STATE_WRITE(state, path, throughput);
Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
subsurface_random_walk_coefficients(albedo, radius, anisotropy, &sigma_t, &alpha, &throughput);
Spectrum sigma_s = sigma_t * alpha;
@@ -350,7 +352,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
}
}
/* Sample direction along ray. */
/* Sample distance along ray. */
float t = -logf(1.0f - randt) / sample_sigma_t;
/* On the first bounce, we use the ray-cast to check if the opposite side is nearby.
@@ -432,6 +434,16 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
if (hit) {
kernel_assert(isfinite_safe(throughput));
guiding_record_bssrdf_bounce(
kg,
state,
pdf,
N,
D,
safe_divide_color(throughput, INTEGRATOR_STATE(state, path, throughput)),
albedo);
INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput;
}

View File

@@ -10,6 +10,8 @@
#include "kernel/closure/bsdf_util.h"
#include "kernel/closure/emissive.h"
#include "kernel/integrator/guiding.h"
#ifdef __SVM__
# include "kernel/svm/svm.h"
#endif
@@ -19,6 +21,67 @@
CCL_NAMESPACE_BEGIN
/* Guiding */
#ifdef __PATH_GUIDING__
ccl_device_inline void surface_shader_prepare_guiding(KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
ccl_private const RNGState *rng_state)
{
/* Have any BSDF to guide? */
if (!(kernel_data.integrator.use_surface_guiding && (sd->flag & SD_BSDF_HAS_EVAL))) {
state->guiding.use_surface_guiding = false;
return;
}
const float surface_guiding_probability = kernel_data.integrator.surface_guiding_probability;
float rand_bsdf_guiding = path_state_rng_1D(kg, rng_state, PRNG_SURFACE_BSDF_GUIDING);
/* Compute proportion of diffuse BSDF and BSSRDFs .*/
float diffuse_sampling_fraction = 0.0f;
float bssrdf_sampling_fraction = 0.0f;
float bsdf_bssrdf_sampling_sum = 0.0f;
for (int i = 0; i < sd->num_closure; i++) {
ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
const float sweight = sc->sample_weight;
kernel_assert(sweight >= 0.0f);
bsdf_bssrdf_sampling_sum += sweight;
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type) && sc->type < CLOSURE_BSDF_TRANSLUCENT_ID) {
diffuse_sampling_fraction += sweight;
}
if (CLOSURE_IS_BSSRDF(sc->type)) {
bssrdf_sampling_fraction += sweight;
}
}
}
if (bsdf_bssrdf_sampling_sum > 0.0f) {
diffuse_sampling_fraction /= bsdf_bssrdf_sampling_sum;
bssrdf_sampling_fraction /= bsdf_bssrdf_sampling_sum;
}
/* Init guiding (diffuse BSDFs only for now). */
if (!(diffuse_sampling_fraction > 0.0f &&
guiding_bsdf_init(kg, state, sd->P, sd->N, rand_bsdf_guiding))) {
state->guiding.use_surface_guiding = false;
return;
}
state->guiding.use_surface_guiding = true;
state->guiding.surface_guiding_sampling_prob = surface_guiding_probability *
diffuse_sampling_fraction;
state->guiding.bssrdf_sampling_prob = bssrdf_sampling_fraction;
state->guiding.sample_surface_guiding_rand = rand_bsdf_guiding;
kernel_assert(state->guiding.surface_guiding_sampling_prob > 0.0f &&
state->guiding.surface_guiding_sampling_prob <= 1.0f);
}
#endif
ccl_device_inline void surface_shader_prepare_closures(KernelGlobals kg,
ConstIntegratorState state,
ccl_private ShaderData *sd,
@@ -108,12 +171,27 @@ ccl_device_inline void surface_shader_prepare_closures(KernelGlobals kg,
}
/* BSDF */
ccl_device_inline bool surface_shader_is_transmission(ccl_private const ShaderData *sd,
const float3 omega_in)
#if 0
ccl_device_inline void surface_shader_validate_bsdf_sample(const KernelGlobals kg,
const ShaderClosure *sc,
const float3 omega_in,
const int org_label,
const float2 org_roughness,
const float org_eta)
{
return dot(sd->N, omega_in) < 0.0f;
/* Validate the the bsdf_label and bsdf_roughness_eta functions
* by estimating the values after a bsdf sample. */
const int comp_label = bsdf_label(kg, sc, omega_in);
kernel_assert(org_label == comp_label);
float2 comp_roughness;
float comp_eta;
bsdf_roughness_eta(kg, sc, &comp_roughness, &comp_eta);
kernel_assert(org_eta == comp_eta);
kernel_assert(org_roughness.x == comp_roughness.x);
kernel_assert(org_roughness.y == comp_roughness.y);
}
#endif
ccl_device_forceinline bool _surface_shader_exclude(ClosureType type, uint light_shader_flags)
{
@@ -141,7 +219,6 @@ ccl_device_forceinline bool _surface_shader_exclude(ClosureType type, uint light
ccl_device_inline float _surface_shader_bsdf_eval_mis(KernelGlobals kg,
ccl_private ShaderData *sd,
const float3 omega_in,
const bool is_transmission,
ccl_private const ShaderClosure *skip_sc,
ccl_private BsdfEval *result_eval,
float sum_pdf,
@@ -160,7 +237,7 @@ ccl_device_inline float _surface_shader_bsdf_eval_mis(KernelGlobals kg,
if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
if (CLOSURE_IS_BSDF(sc->type) && !_surface_shader_exclude(sc->type, light_shader_flags)) {
float bsdf_pdf = 0.0f;
Spectrum eval = bsdf_eval(kg, sd, sc, omega_in, is_transmission, &bsdf_pdf);
Spectrum eval = bsdf_eval(kg, sd, sc, omega_in, &bsdf_pdf);
if (bsdf_pdf != 0.0f) {
bsdf_eval_accum(result_eval, sc->type, eval * sc->weight);
@@ -175,6 +252,55 @@ ccl_device_inline float _surface_shader_bsdf_eval_mis(KernelGlobals kg,
return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f;
}
ccl_device_inline float surface_shader_bsdf_eval_pdfs(const KernelGlobals kg,
ccl_private ShaderData *sd,
const float3 omega_in,
ccl_private BsdfEval *result_eval,
ccl_private float *pdfs,
const uint light_shader_flags)
{
/* This is the veach one-sample model with balance heuristic, some pdf
* factors drop out when using balance heuristic weighting. */
float sum_pdf = 0.0f;
float sum_sample_weight = 0.0f;
bsdf_eval_init(result_eval, CLOSURE_NONE_ID, zero_spectrum());
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
if (CLOSURE_IS_BSDF(sc->type) && !_surface_shader_exclude(sc->type, light_shader_flags)) {
float bsdf_pdf = 0.0f;
Spectrum eval = bsdf_eval(kg, sd, sc, omega_in, &bsdf_pdf);
kernel_assert(bsdf_pdf >= 0.0f);
if (bsdf_pdf != 0.0f) {
bsdf_eval_accum(result_eval, sc->type, eval * sc->weight);
sum_pdf += bsdf_pdf * sc->sample_weight;
kernel_assert(bsdf_pdf * sc->sample_weight >= 0.0f);
pdfs[i] = bsdf_pdf * sc->sample_weight;
}
else {
pdfs[i] = 0.0f;
}
}
else {
pdfs[i] = 0.0f;
}
sum_sample_weight += sc->sample_weight;
}
else {
pdfs[i] = 0.0f;
}
}
if (sum_pdf > 0.0f) {
for (int i = 0; i < sd->num_closure; i++) {
pdfs[i] /= sum_pdf;
}
}
return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f;
}
#ifndef __KERNEL_CUDA__
ccl_device
#else
@@ -182,16 +308,28 @@ ccl_device_inline
#endif
float
surface_shader_bsdf_eval(KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
const float3 omega_in,
const bool is_transmission,
ccl_private BsdfEval *bsdf_eval,
const uint light_shader_flags)
{
bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, zero_spectrum());
return _surface_shader_bsdf_eval_mis(
kg, sd, omega_in, is_transmission, NULL, bsdf_eval, 0.0f, 0.0f, light_shader_flags);
float pdf = _surface_shader_bsdf_eval_mis(
kg, sd, omega_in, NULL, bsdf_eval, 0.0f, 0.0f, light_shader_flags);
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (state->guiding.use_surface_guiding) {
const float guiding_sampling_prob = state->guiding.surface_guiding_sampling_prob;
const float bssrdf_sampling_prob = state->guiding.bssrdf_sampling_prob;
const float guide_pdf = guiding_bsdf_pdf(kg, state, omega_in);
pdf = (guiding_sampling_prob * guide_pdf * (1.0f - bssrdf_sampling_prob)) +
(1.0f - guiding_sampling_prob) * pdf;
}
#endif
return pdf;
}
/* Randomly sample a BSSRDF or BSDF proportional to ShaderClosure.sample_weight. */
@@ -259,6 +397,135 @@ surface_shader_bssrdf_sample_weight(ccl_private const ShaderData *ccl_restrict s
return weight;
}
#ifdef __PATH_GUIDING__
/* Sample direction for picked BSDF, and return evaluation and pdf for all
* BSDFs combined using MIS. */
ccl_device int surface_shader_bsdf_guided_sample_closure(KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
ccl_private const ShaderClosure *sc,
const float2 rand_bsdf,
ccl_private BsdfEval *bsdf_eval,
ccl_private float3 *omega_in,
ccl_private float *bsdf_pdf,
ccl_private float *unguided_bsdf_pdf,
ccl_private float2 *sampled_rougness,
ccl_private float *eta)
{
/* BSSRDF should already have been handled elsewhere. */
kernel_assert(CLOSURE_IS_BSDF(sc->type));
const bool use_surface_guiding = state->guiding.use_surface_guiding;
const float guiding_sampling_prob = state->guiding.surface_guiding_sampling_prob;
const float bssrdf_sampling_prob = state->guiding.bssrdf_sampling_prob;
/* Decide between sampling guiding distribution and BSDF. */
bool sample_guiding = false;
float rand_bsdf_guiding = state->guiding.sample_surface_guiding_rand;
if (use_surface_guiding && rand_bsdf_guiding < guiding_sampling_prob) {
sample_guiding = true;
rand_bsdf_guiding /= guiding_sampling_prob;
}
else {
rand_bsdf_guiding -= guiding_sampling_prob;
rand_bsdf_guiding /= (1.0f - guiding_sampling_prob);
}
/* Initialize to zero. */
int label = LABEL_NONE;
Spectrum eval = zero_spectrum();
bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, eval);
*unguided_bsdf_pdf = 0.0f;
float guide_pdf = 0.0f;
if (sample_guiding) {
/* Sample guiding distribution. */
guide_pdf = guiding_bsdf_sample(kg, state, rand_bsdf, omega_in);
*bsdf_pdf = 0.0f;
if (guide_pdf != 0.0f) {
float unguided_bsdf_pdfs[MAX_CLOSURE];
*unguided_bsdf_pdf = surface_shader_bsdf_eval_pdfs(
kg, sd, *omega_in, bsdf_eval, unguided_bsdf_pdfs, 0);
*bsdf_pdf = (guiding_sampling_prob * guide_pdf * (1.0f - bssrdf_sampling_prob)) +
((1.0f - guiding_sampling_prob) * (*unguided_bsdf_pdf));
float sum_pdfs = 0.0f;
if (*unguided_bsdf_pdf > 0.0f) {
int idx = -1;
for (int i = 0; i < sd->num_closure; i++) {
sum_pdfs += unguided_bsdf_pdfs[i];
if (rand_bsdf_guiding <= sum_pdfs) {
idx = i;
break;
}
}
kernel_assert(idx >= 0);
/* Set the default idx to the last in the list.
* in case of numerical problems and rand_bsdf_guiding is just >=1.0f and
* the sum of all unguided_bsdf_pdfs is just < 1.0f. */
idx = (rand_bsdf_guiding > sum_pdfs) ? sd->num_closure - 1 : idx;
label = bsdf_label(kg, &sd->closure[idx], *omega_in);
}
}
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
*sampled_rougness = make_float2(1.0f, 1.0f);
*eta = 1.0f;
}
else {
/* Sample BSDF. */
*bsdf_pdf = 0.0f;
label = bsdf_sample(kg,
sd,
sc,
rand_bsdf.x,
rand_bsdf.y,
&eval,
omega_in,
unguided_bsdf_pdf,
sampled_rougness,
eta);
# if 0
if (*unguided_bsdf_pdf > 0.0f) {
surface_shader_validate_bsdf_sample(kg, sc, *omega_in, label, sampled_roughness, eta);
}
# endif
if (*unguided_bsdf_pdf != 0.0f) {
bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight);
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
if (sd->num_closure > 1) {
float sweight = sc->sample_weight;
*unguided_bsdf_pdf = _surface_shader_bsdf_eval_mis(
kg, sd, *omega_in, sc, bsdf_eval, (*unguided_bsdf_pdf) * sweight, sweight, 0);
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
}
*bsdf_pdf = *unguided_bsdf_pdf;
if (use_surface_guiding) {
guide_pdf = guiding_bsdf_pdf(kg, state, *omega_in);
*bsdf_pdf *= 1.0f - guiding_sampling_prob;
*bsdf_pdf += guiding_sampling_prob * guide_pdf * (1.0f - bssrdf_sampling_prob);
}
}
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
}
return label;
}
#endif
/* Sample direction for picked BSDF, and return evaluation and pdf for all
* BSDFs combined using MIS. */
ccl_device int surface_shader_bsdf_sample_closure(KernelGlobals kg,
@@ -267,7 +534,9 @@ ccl_device int surface_shader_bsdf_sample_closure(KernelGlobals kg,
const float2 rand_bsdf,
ccl_private BsdfEval *bsdf_eval,
ccl_private float3 *omega_in,
ccl_private float *pdf)
ccl_private float *pdf,
ccl_private float2 *sampled_roughness,
ccl_private float *eta)
{
/* BSSRDF should already have been handled elsewhere. */
kernel_assert(CLOSURE_IS_BSDF(sc->type));
@@ -276,18 +545,21 @@ ccl_device int surface_shader_bsdf_sample_closure(KernelGlobals kg,
Spectrum eval = zero_spectrum();
*pdf = 0.0f;
label = bsdf_sample(kg, sd, sc, rand_bsdf.x, rand_bsdf.y, &eval, omega_in, pdf);
label = bsdf_sample(
kg, sd, sc, rand_bsdf.x, rand_bsdf.y, &eval, omega_in, pdf, sampled_roughness, eta);
if (*pdf != 0.0f) {
bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight);
if (sd->num_closure > 1) {
const bool is_transmission = surface_shader_is_transmission(sd, *omega_in);
float sweight = sc->sample_weight;
*pdf = _surface_shader_bsdf_eval_mis(
kg, sd, *omega_in, is_transmission, sc, bsdf_eval, *pdf * sweight, sweight, 0);
kg, sd, *omega_in, sc, bsdf_eval, *pdf * sweight, sweight, 0);
}
}
else {
bsdf_eval_init(bsdf_eval, sc->type, zero_spectrum());
}
return label;
}

View File

@@ -22,6 +22,7 @@ CCL_NAMESPACE_BEGIN
#ifdef __VOLUME__
/* Merging */
ccl_device_inline void volume_shader_merge_closures(ccl_private ShaderData *sd)
{
/* Merge identical closures to save closure space with stacked volumes. */
@@ -88,6 +89,119 @@ ccl_device_inline void volume_shader_copy_phases(ccl_private ShaderVolumePhases
}
}
/* Guiding */
# ifdef __PATH_GUIDING__
ccl_device_inline void volume_shader_prepare_guiding(KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
ccl_private const RNGState *rng_state,
const float3 P,
const float3 D,
ccl_private ShaderVolumePhases *phases,
const VolumeSampleMethod direct_sample_method)
{
/* Have any phase functions to guide? */
const int num_phases = phases->num_closure;
if (!kernel_data.integrator.use_volume_guiding || num_phases == 0) {
state->guiding.use_volume_guiding = false;
return;
}
const float volume_guiding_probability = kernel_data.integrator.volume_guiding_probability;
float rand_phase_guiding = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_PHASE_GUIDING);
/* If we have more than one phase function we select one random based on its
* sample weight to calculate the product distribution for guiding. */
int phase_id = 0;
float phase_weight = 1.0f;
if (num_phases > 1) {
/* Pick a phase closure based on sample weights. */
float sum = 0.0f;
for (phase_id = 0; phase_id < num_phases; phase_id++) {
ccl_private const ShaderVolumeClosure *svc = &phases->closure[phase_id];
sum += svc->sample_weight;
}
float r = rand_phase_guiding * sum;
float partial_sum = 0.0f;
for (phase_id = 0; phase_id < num_phases; phase_id++) {
ccl_private const ShaderVolumeClosure *svc = &phases->closure[phase_id];
float next_sum = partial_sum + svc->sample_weight;
if (r <= next_sum) {
/* Rescale to reuse. */
rand_phase_guiding = (r - partial_sum) / svc->sample_weight;
phase_weight = svc->sample_weight / sum;
break;
}
partial_sum = next_sum;
}
/* Adjust the sample weight of the component used for guiding. */
phases->closure[phase_id].sample_weight *= volume_guiding_probability;
}
/* Init guiding for selected phase function. */
ccl_private const ShaderVolumeClosure *svc = &phases->closure[phase_id];
if (!guiding_phase_init(kg, state, P, D, svc->g, rand_phase_guiding)) {
state->guiding.use_volume_guiding = false;
return;
}
state->guiding.use_volume_guiding = true;
state->guiding.sample_volume_guiding_rand = rand_phase_guiding;
state->guiding.volume_guiding_sampling_prob = volume_guiding_probability * phase_weight;
kernel_assert(state->guiding.volume_guiding_sampling_prob > 0.0f &&
state->guiding.volume_guiding_sampling_prob <= 1.0f);
}
# endif
/* Phase Evaluation & Sampling */
/* Randomly sample a volume phase function proportional to ShaderClosure.sample_weight. */
ccl_device_inline ccl_private const ShaderVolumeClosure *volume_shader_phase_pick(
ccl_private const ShaderVolumePhases *phases, ccl_private float2 *rand_phase)
{
int sampled = 0;
if (phases->num_closure > 1) {
/* pick a phase closure based on sample weights */
float sum = 0.0f;
for (int i = 0; i < phases->num_closure; i++) {
ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled];
sum += svc->sample_weight;
}
float r = (*rand_phase).x * sum;
float partial_sum = 0.0f;
for (int i = 0; i < phases->num_closure; i++) {
ccl_private const ShaderVolumeClosure *svc = &phases->closure[i];
float next_sum = partial_sum + svc->sample_weight;
if (r <= next_sum) {
/* Rescale to reuse for volume phase direction sample. */
sampled = i;
(*rand_phase).x = (r - partial_sum) / svc->sample_weight;
break;
}
partial_sum = next_sum;
}
}
/* todo: this isn't quite correct, we don't weight anisotropy properly
* depending on color channels, even if this is perhaps not a common case */
return &phases->closure[sampled];
}
ccl_device_inline float _volume_shader_phase_eval_mis(ccl_private const ShaderData *sd,
ccl_private const ShaderVolumePhases *phases,
const float3 omega_in,
@@ -116,6 +230,23 @@ ccl_device_inline float _volume_shader_phase_eval_mis(ccl_private const ShaderDa
}
ccl_device float volume_shader_phase_eval(KernelGlobals kg,
ccl_private const ShaderData *sd,
ccl_private const ShaderVolumeClosure *svc,
const float3 omega_in,
ccl_private BsdfEval *phase_eval)
{
float phase_pdf = 0.0f;
Spectrum eval = volume_phase_eval(sd, svc, omega_in, &phase_pdf);
if (phase_pdf != 0.0f) {
bsdf_eval_accum(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
}
return phase_pdf;
}
ccl_device float volume_shader_phase_eval(KernelGlobals kg,
IntegratorState state,
ccl_private const ShaderData *sd,
ccl_private const ShaderVolumePhases *phases,
const float3 omega_in,
@@ -123,82 +254,116 @@ ccl_device float volume_shader_phase_eval(KernelGlobals kg,
{
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum());
return _volume_shader_phase_eval_mis(sd, phases, omega_in, -1, phase_eval, 0.0f, 0.0f);
float pdf = _volume_shader_phase_eval_mis(sd, phases, omega_in, -1, phase_eval, 0.0f, 0.0f);
# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (state->guiding.use_volume_guiding) {
const float guiding_sampling_prob = state->guiding.volume_guiding_sampling_prob;
const float guide_pdf = guiding_phase_pdf(kg, state, omega_in);
pdf = (guiding_sampling_prob * guide_pdf) + (1.0f - guiding_sampling_prob) * pdf;
}
# endif
return pdf;
}
ccl_device int volume_shader_phase_sample(KernelGlobals kg,
ccl_private const ShaderData *sd,
ccl_private const ShaderVolumePhases *phases,
float2 rand_phase,
ccl_private BsdfEval *phase_eval,
ccl_private float3 *omega_in,
ccl_private float *pdf)
# ifdef __PATH_GUIDING__
ccl_device int volume_shader_phase_guided_sample(KernelGlobals kg,
IntegratorState state,
ccl_private const ShaderData *sd,
ccl_private const ShaderVolumeClosure *svc,
const float2 rand_phase,
ccl_private BsdfEval *phase_eval,
ccl_private float3 *omega_in,
ccl_private float *phase_pdf,
ccl_private float *unguided_phase_pdf,
ccl_private float *sampled_roughness)
{
int sampled = 0;
const bool use_volume_guiding = state->guiding.use_volume_guiding;
const float guiding_sampling_prob = state->guiding.volume_guiding_sampling_prob;
if (phases->num_closure > 1) {
/* pick a phase closure based on sample weights */
float sum = 0.0f;
for (sampled = 0; sampled < phases->num_closure; sampled++) {
ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled];
sum += svc->sample_weight;
}
float r = rand_phase.x * sum;
float partial_sum = 0.0f;
for (sampled = 0; sampled < phases->num_closure; sampled++) {
ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled];
float next_sum = partial_sum + svc->sample_weight;
if (r <= next_sum) {
/* Rescale to reuse for BSDF direction sample. */
rand_phase.x = (r - partial_sum) / svc->sample_weight;
break;
}
partial_sum = next_sum;
}
if (sampled == phases->num_closure) {
*pdf = 0.0f;
return LABEL_NONE;
}
/* Decide between sampling guiding distribution and phase. */
float rand_phase_guiding = state->guiding.sample_volume_guiding_rand;
bool sample_guiding = false;
if (use_volume_guiding && rand_phase_guiding < guiding_sampling_prob) {
sample_guiding = true;
rand_phase_guiding /= guiding_sampling_prob;
}
else {
rand_phase_guiding -= guiding_sampling_prob;
rand_phase_guiding /= (1.0f - guiding_sampling_prob);
}
/* todo: this isn't quite correct, we don't weight anisotropy properly
* depending on color channels, even if this is perhaps not a common case */
ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled];
int label;
/* Initialize to zero. */
int label = LABEL_NONE;
Spectrum eval = zero_spectrum();
*pdf = 0.0f;
label = volume_phase_sample(sd, svc, rand_phase.x, rand_phase.y, &eval, omega_in, pdf);
*unguided_phase_pdf = 0.0f;
float guide_pdf = 0.0f;
*sampled_roughness = 1.0f - fabsf(svc->g);
if (*pdf != 0.0f) {
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum());
if (sample_guiding) {
/* Sample guiding distribution. */
guide_pdf = guiding_phase_sample(kg, state, rand_phase, omega_in);
*phase_pdf = 0.0f;
if (guide_pdf != 0.0f) {
*unguided_phase_pdf = volume_shader_phase_eval(kg, sd, svc, *omega_in, phase_eval);
*phase_pdf = (guiding_sampling_prob * guide_pdf) +
((1.0f - guiding_sampling_prob) * (*unguided_phase_pdf));
label = LABEL_VOLUME_SCATTER;
}
}
else {
/* Sample phase. */
*phase_pdf = 0.0f;
label = volume_phase_sample(
sd, svc, rand_phase.x, rand_phase.y, &eval, omega_in, unguided_phase_pdf);
if (*unguided_phase_pdf != 0.0f) {
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
*phase_pdf = *unguided_phase_pdf;
if (use_volume_guiding) {
guide_pdf = guiding_phase_pdf(kg, state, *omega_in);
*phase_pdf *= 1.0f - guiding_sampling_prob;
*phase_pdf += guiding_sampling_prob * guide_pdf;
}
kernel_assert(reduce_min(bsdf_eval_sum(phase_eval)) >= 0.0f);
}
else {
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum());
}
kernel_assert(reduce_min(bsdf_eval_sum(phase_eval)) >= 0.0f);
}
return label;
}
# endif
ccl_device int volume_shader_phase_sample_closure(KernelGlobals kg,
ccl_private const ShaderData *sd,
ccl_private const ShaderVolumeClosure *sc,
const float2 rand_phase,
ccl_private BsdfEval *phase_eval,
ccl_private float3 *omega_in,
ccl_private float *pdf)
ccl_device int volume_shader_phase_sample(KernelGlobals kg,
ccl_private const ShaderData *sd,
ccl_private const ShaderVolumePhases *phases,
ccl_private const ShaderVolumeClosure *svc,
float2 rand_phase,
ccl_private BsdfEval *phase_eval,
ccl_private float3 *omega_in,
ccl_private float *pdf,
ccl_private float *sampled_roughness)
{
int label;
*sampled_roughness = 1.0f - fabsf(svc->g);
Spectrum eval = zero_spectrum();
*pdf = 0.0f;
label = volume_phase_sample(sd, sc, rand_phase.x, rand_phase.y, &eval, omega_in, pdf);
int label = volume_phase_sample(sd, svc, rand_phase.x, rand_phase.y, &eval, omega_in, pdf);
if (*pdf != 0.0f)
if (*pdf != 0.0f) {
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
}
return label;
}

View File

@@ -428,7 +428,7 @@ ccl_device void osl_closure_microfacet_multi_ggx_setup(
bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N);
bsdf->alpha_x = closure->alpha_x;
bsdf->alpha_y = bsdf->alpha_x;
bsdf->ior = 0.0f;
bsdf->ior = 1.0f;
bsdf->extra = extra;
bsdf->extra->color = rgb_to_spectrum(closure->color);
@@ -510,7 +510,7 @@ ccl_device void osl_closure_microfacet_multi_ggx_aniso_setup(
bsdf->N = ensure_valid_reflection(sd->Ng, sd->I, closure->N);
bsdf->alpha_x = closure->alpha_x;
bsdf->alpha_y = closure->alpha_y;
bsdf->ior = 0.0f;
bsdf->ior = 1.0f;
bsdf->extra = extra;
bsdf->extra->color = rgb_to_spectrum(closure->color);

View File

@@ -31,7 +31,7 @@ ccl_device void svm_node_glass_setup(ccl_private ShaderData *sd,
else {
bsdf->alpha_y = 0.0f;
bsdf->alpha_x = 0.0f;
bsdf->ior = 0.0f;
bsdf->ior = eta;
sd->flag |= bsdf_reflection_setup(bsdf);
}
}
@@ -542,7 +542,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
float roughness = sqr(param1);
bsdf->N = N;
bsdf->ior = 0.0f;
bsdf->ior = 1.0f;
bsdf->extra = NULL;
if (data_node.y == SVM_STACK_INVALID) {

View File

@@ -79,6 +79,9 @@ CCL_NAMESPACE_BEGIN
# ifdef WITH_OSL
# define __OSL__
# endif
# ifdef WITH_PATH_GUIDING
# define __PATH_GUIDING__
# endif
# define __VOLUME_RECORD_ALL__
#endif /* !__KERNEL_GPU__ */
@@ -146,12 +149,14 @@ enum PathTraceDimension {
PRNG_SURFACE_BSDF = 3,
PRNG_SURFACE_AO = 4,
PRNG_SURFACE_BEVEL = 5,
PRNG_SURFACE_BSDF_GUIDING = 6,
/* Volume */
PRNG_VOLUME_PHASE = 3,
PRNG_VOLUME_PHASE_CHANNEL = 4,
PRNG_VOLUME_SCATTER_DISTANCE = 5,
PRNG_VOLUME_OFFSET = 6,
PRNG_VOLUME_SHADE_OFFSET = 7,
PRNG_VOLUME_PHASE_GUIDING = 8,
/* Subsurface random walk bounces */
PRNG_SUBSURFACE_BSDF = 0,
@@ -387,6 +392,14 @@ typedef enum PassType {
PASS_SHADOW_CATCHER_SAMPLE_COUNT,
PASS_SHADOW_CATCHER_MATTE,
/* Guiding related debug rendering passes */
/* The estimated sample color from the PathSegmentStorage. If everything is integrated correctly
* the output should be similar to PASS_COMBINED. */
PASS_GUIDING_COLOR,
/* The guiding probability at the first bounce. */
PASS_GUIDING_PROBABILITY,
/* The avg. roughness at the first bounce. */
PASS_GUIDING_AVG_ROUGHNESS,
PASS_CATEGORY_DATA_END = 63,
PASS_BAKE_PRIMITIVE,
@@ -455,6 +468,16 @@ typedef enum LightType {
LIGHT_TRIANGLE
} LightType;
/* Guiding Distribution Type */
typedef enum GuidingDistributionType {
GUIDING_TYPE_PARALLAX_AWARE_VMM = 0,
GUIDING_TYPE_DIRECTIONAL_QUAD_TREE = 1,
GUIDING_TYPE_VMM = 2,
GUIDING_NUM_TYPES,
} GuidingDistributionType;
/* Camera Type */
enum CameraType { CAMERA_PERSPECTIVE, CAMERA_ORTHOGRAPHIC, CAMERA_PANORAMA };
@@ -1502,6 +1525,9 @@ enum KernelFeatureFlag : uint32_t {
/* MNEE. */
KERNEL_FEATURE_MNEE = (1U << 25U),
/* Path guiding. */
KERNEL_FEATURE_PATH_GUIDING = (1U << 26U),
};
/* Shader node feature mask, to specialize shader evaluation for kernels. */

View File

@@ -200,6 +200,10 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
kfilm->pass_shadow_catcher_sample_count = PASS_UNUSED;
kfilm->pass_shadow_catcher_matte = PASS_UNUSED;
kfilm->pass_guiding_color = PASS_UNUSED;
kfilm->pass_guiding_probability = PASS_UNUSED;
kfilm->pass_guiding_avg_roughness = PASS_UNUSED;
bool have_cryptomatte = false;
bool have_aov_color = false;
bool have_aov_value = false;
@@ -382,6 +386,15 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
have_aov_value = true;
}
break;
case PASS_GUIDING_COLOR:
kfilm->pass_guiding_color = kfilm->pass_stride;
break;
case PASS_GUIDING_PROBABILITY:
kfilm->pass_guiding_probability = kfilm->pass_stride;
break;
case PASS_GUIDING_AVG_ROUGHNESS:
kfilm->pass_guiding_avg_roughness = kfilm->pass_stride;
break;
default:
assert(false);
break;

View File

@@ -60,6 +60,25 @@ NODE_DEFINE(Integrator)
SOCKET_INT(volume_max_steps, "Volume Max Steps", 1024);
SOCKET_FLOAT(volume_step_rate, "Volume Step Rate", 1.0f);
static NodeEnum guiding_ditribution_enum;
guiding_ditribution_enum.insert("PARALLAX_AWARE_VMM", GUIDING_TYPE_PARALLAX_AWARE_VMM);
guiding_ditribution_enum.insert("DIRECTIONAL_QUAD_TREE", GUIDING_TYPE_DIRECTIONAL_QUAD_TREE);
guiding_ditribution_enum.insert("VMM", GUIDING_TYPE_VMM);
SOCKET_BOOLEAN(use_guiding, "Guiding", false);
SOCKET_BOOLEAN(deterministic_guiding, "Deterministic Guiding", true);
SOCKET_BOOLEAN(use_surface_guiding, "Surface Guiding", true);
SOCKET_FLOAT(surface_guiding_probability, "Surface Guiding Probability", 0.5f);
SOCKET_BOOLEAN(use_volume_guiding, "Volume Guiding", true);
SOCKET_FLOAT(volume_guiding_probability, "Volume Guiding Probability", 0.5f);
SOCKET_INT(guiding_training_samples, "Training Samples", 128);
SOCKET_BOOLEAN(use_guiding_direct_light, "Guide Direct Light", true);
SOCKET_BOOLEAN(use_guiding_mis_weights, "Use MIS Weights", true);
SOCKET_ENUM(guiding_distribution_type,
"Guiding Distribution Type",
guiding_ditribution_enum,
GUIDING_TYPE_PARALLAX_AWARE_VMM);
SOCKET_BOOLEAN(caustics_reflective, "Reflective Caustics", true);
SOCKET_BOOLEAN(caustics_refractive, "Refractive Caustics", true);
SOCKET_FLOAT(filter_glossy, "Filter Glossy", 0.0f);
@@ -209,6 +228,17 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
kintegrator->filter_closures |= FILTER_CLOSURE_TRANSPARENT;
}
GuidingParams guiding_params = get_guiding_params(device);
kintegrator->use_guiding = guiding_params.use;
kintegrator->train_guiding = kintegrator->use_guiding;
kintegrator->use_surface_guiding = guiding_params.use_surface_guiding;
kintegrator->use_volume_guiding = guiding_params.use_volume_guiding;
kintegrator->surface_guiding_probability = surface_guiding_probability;
kintegrator->volume_guiding_probability = volume_guiding_probability;
kintegrator->use_guiding_direct_light = use_guiding_direct_light;
kintegrator->use_guiding_mis_weights = use_guiding_mis_weights;
kintegrator->guiding_distribution_type = guiding_params.type;
kintegrator->seed = seed;
kintegrator->sample_clamp_direct = (sample_clamp_direct == 0.0f) ? FLT_MAX :
@@ -354,4 +384,20 @@ DenoiseParams Integrator::get_denoise_params() const
return denoise_params;
}
GuidingParams Integrator::get_guiding_params(const Device *device) const
{
const bool use = use_guiding && device->info.has_guiding;
GuidingParams guiding_params;
guiding_params.use_surface_guiding = use && use_surface_guiding &&
surface_guiding_probability > 0.0f;
guiding_params.use_volume_guiding = use && use_volume_guiding &&
volume_guiding_probability > 0.0f;
guiding_params.use = guiding_params.use_surface_guiding || guiding_params.use_volume_guiding;
guiding_params.type = guiding_distribution_type;
guiding_params.training_samples = guiding_training_samples;
guiding_params.deterministic = deterministic_guiding;
return guiding_params;
}
CCL_NAMESPACE_END

View File

@@ -9,6 +9,7 @@
#include "device/denoise.h" /* For the parameters and type enum. */
#include "graph/node.h"
#include "integrator/adaptive_sampling.h"
#include "integrator/guiding.h"
CCL_NAMESPACE_BEGIN
@@ -43,6 +44,17 @@ class Integrator : public Node {
NODE_SOCKET_API(int, volume_max_steps)
NODE_SOCKET_API(float, volume_step_rate)
NODE_SOCKET_API(bool, use_guiding);
NODE_SOCKET_API(bool, deterministic_guiding);
NODE_SOCKET_API(bool, use_surface_guiding);
NODE_SOCKET_API(float, surface_guiding_probability);
NODE_SOCKET_API(bool, use_volume_guiding);
NODE_SOCKET_API(float, volume_guiding_probability);
NODE_SOCKET_API(int, guiding_training_samples);
NODE_SOCKET_API(bool, use_guiding_direct_light);
NODE_SOCKET_API(bool, use_guiding_mis_weights);
NODE_SOCKET_API(GuidingDistributionType, guiding_distribution_type);
NODE_SOCKET_API(bool, caustics_reflective)
NODE_SOCKET_API(bool, caustics_refractive)
NODE_SOCKET_API(float, filter_glossy)
@@ -105,6 +117,7 @@ class Integrator : public Node {
AdaptiveSampling get_adaptive_sampling() const;
DenoiseParams get_denoise_params() const;
GuidingParams get_guiding_params(const Device *device) const;
};
CCL_NAMESPACE_END

View File

@@ -96,6 +96,12 @@ const NodeEnum *Pass::get_type_enum()
pass_type_enum.insert("bake_primitive", PASS_BAKE_PRIMITIVE);
pass_type_enum.insert("bake_differential", PASS_BAKE_DIFFERENTIAL);
#ifdef WITH_CYCLES_DEBUG
pass_type_enum.insert("guiding_color", PASS_GUIDING_COLOR);
pass_type_enum.insert("guiding_probability", PASS_GUIDING_PROBABILITY);
pass_type_enum.insert("guiding_avg_roughness", PASS_GUIDING_AVG_ROUGHNESS);
#endif
}
return &pass_type_enum;
@@ -341,6 +347,15 @@ PassInfo Pass::get_info(const PassType type, const bool include_albedo, const bo
LOG(DFATAL) << "Unexpected pass type is used " << type;
pass_info.num_components = 0;
break;
case PASS_GUIDING_COLOR:
pass_info.num_components = 3;
break;
case PASS_GUIDING_PROBABILITY:
pass_info.num_components = 1;
break;
case PASS_GUIDING_AVG_ROUGHNESS:
pass_info.num_components = 1;
break;
}
return pass_info;

View File

@@ -439,9 +439,9 @@ bool Scene::need_data_update()
film->is_modified() || procedural_manager->need_update());
}
bool Scene::need_reset()
bool Scene::need_reset(const bool check_camera)
{
return need_data_update() || camera->is_modified();
return need_data_update() || (check_camera && camera->is_modified());
}
void Scene::reset()
@@ -549,6 +549,10 @@ void Scene::update_kernel_features()
kernel_features |= KERNEL_FEATURE_MNEE;
}
if (integrator->get_guiding_params(device).use) {
kernel_features |= KERNEL_FEATURE_PATH_GUIDING;
}
if (bake_manager->get_baking()) {
kernel_features |= KERNEL_FEATURE_BAKING;
}

View File

@@ -261,7 +261,7 @@ class Scene : public NodeOwner {
float motion_shutter_time();
bool need_update();
bool need_reset();
bool need_reset(const bool check_camera = true);
void reset();
void device_free();

View File

@@ -318,6 +318,13 @@ RenderWork Session::run_update_for_next_iteration()
path_trace_->set_adaptive_sampling(adaptive_sampling);
}
/* Update path guiding. */
{
const GuidingParams guiding_params = scene->integrator->get_guiding_params(device);
const bool guiding_reset = (guiding_params.use) ? scene->need_reset(false) : false;
path_trace_->set_guiding_params(guiding_params, guiding_reset);
}
render_scheduler_.set_num_samples(params.samples);
render_scheduler_.set_start_sample(params.sample_offset);
render_scheduler_.set_time_limit(params.time_limit);

View File

@@ -49,6 +49,7 @@ set(SRC_HEADERS
foreach.h
function.h
guarded_allocator.h
guiding.h
half.h
hash.h
ies.h

View File

@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 Blender Foundation */
#pragma once
#ifdef WITH_PATH_GUIDING
# include <openpgl/cpp/OpenPGL.h>
# include <openpgl/version.h>
#endif
#include "util/system.h"
CCL_NAMESPACE_BEGIN
static int guiding_device_type()
{
#ifdef WITH_PATH_GUIDING
# if defined(__ARM_NEON)
return 8;
# else
# if OPENPGL_VERSION_MINOR >= 4
if (system_cpu_support_avx2()) {
return 8;
}
# endif
if (system_cpu_support_sse41()) {
return 4;
}
return 0;
# endif
#else
return 0;
#endif
}
static inline bool guiding_supported()
{
return guiding_device_type() != 0;
}
CCL_NAMESPACE_END

View File

@@ -589,7 +589,7 @@ ccl_device_inline float fast_erfcf(float x)
ccl_device_inline float fast_ierff(float x)
{
/* From: Approximating the `erfinv` function by Mike Giles. */
/* To avoid trouble at the limit, clamp input to 1-eps. */
/* To avoid trouble at the limit, clamp input to 1-epsilon. */
float a = fabsf(x);
if (a > 0.99999994f) {
a = 0.99999994f;

View File

@@ -741,7 +741,7 @@ extern unsigned int GHOST_GetContextDefaultOpenGLFramebuffer(GHOST_ContextHandle
extern unsigned int GHOST_GetDefaultOpenGLFramebuffer(GHOST_WindowHandle windowhandle);
/**
* Use multitouch gestures if supported.
* Use multi-touch gestures if supported.
* \param systemhandle: The handle to the system.
* \param use: Enable or disable.
*/

View File

@@ -422,7 +422,7 @@ class GHOST_ISystem {
virtual GHOST_TSuccess getButtonState(GHOST_TButton mask, bool &isDown) const = 0;
/**
* Enable multitouch gestures if supported.
* Enable multi-touch gestures if supported.
* \param use: Enable or disable.
*/
virtual void setMultitouchGestures(const bool use) = 0;

View File

@@ -240,7 +240,7 @@ class GHOST_System : public GHOST_ISystem {
GHOST_TSuccess getButtonState(GHOST_TButton mask, bool &isDown) const;
/**
* Enable multitouch gestures if supported.
* Enable multi-touch gestures if supported.
* \param use: Enable or disable.
*/
void setMultitouchGestures(const bool use);
@@ -408,7 +408,7 @@ class GHOST_System : public GHOST_ISystem {
/** Settings of the display before the display went full-screen. */
GHOST_DisplaySetting m_preFullScreenSetting;
/* Use multitouch gestures? */
/* Use multi-touch gestures? */
bool m_multitouchGestures;
/** Which tablet API to use. */

View File

@@ -14,7 +14,7 @@
#include "../GHOST_Types.h"
#include "GHOST_System.h"
// For tablets
/* For tablets. */
#ifdef WITH_X11_XINPUT
# include <X11/extensions/XInput.h>

View File

@@ -25,18 +25,18 @@
#include "GHOST_ContextEGL.h"
#include "GHOST_ContextGLX.h"
/* for XIWarpPointer */
/* For #XIWarpPointer. */
#ifdef WITH_X11_XINPUT
# include <X11/extensions/XInput2.h>
#endif
// For DPI value
/* For DPI value. */
#include <X11/Xresource.h>
#include <cstdio>
#include <cstring>
/* gethostname */
/* For `gethostname`. */
#include <unistd.h>
#include <algorithm>

View File

@@ -11,7 +11,7 @@
#include "GHOST_Window.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
// For tablets
/* For tablets. */
#ifdef WITH_X11_XINPUT
# include <X11/extensions/XInput.h>
#endif

Some files were not shown because too many files have changed in this diff Show More