Cycles: Add Portal BSDF #114386

Merged
Brecht Van Lommel merged 34 commits from david494/blender:cycles-portal-bsdf into main 2024-04-29 12:38:02 +02:00
Contributor

Initial implementation of a portal closure.

This will allow various non physical effects to be rendered directly in cycles without the need for compositing. In this implementation the portals also allow passes and AOVs through, which has more possibilities such as reflected and refracted passes and AOVs.


This is just a first stab at an implementation, it is at least missing:

  • documentation
  • examples of usage

The actual code is mostly a duplication of the transparent closure, with changes at some key points.

I am unsure about a couple of things:

  • using sd->P and sd->wi to set the new ray origin and direction, maybe this should be stored somewhere else?
  • changing state->isect.object in shade_surface.h to give the next ray the possibility to hit the same polygon again
  • the value of SH_NODE_BSDF_PORTAL
  • whether the new ray direction should be set as "incoming" or "outgoing". Incoming has the advantage of being immediately accessible on the geometry node, but somehow seems backwards for a portal node.
  • the copyright info on new files: I copied what was at the top of the files for the transparent closure.
  • sd->closure_transparent_extinction: This is currently shared with transparent closures, and I think that is the best solution, but mixing transparency and portals might lead to weird results.

edit: added example of camera filming its own screen
edit2: added obvious test image :)
edit3: added some of the scenes that I used for testing

Initial implementation of a portal closure. This will allow various non physical effects to be rendered directly in cycles without the need for compositing. In this implementation the portals also allow passes and AOVs through, which has more possibilities such as reflected and refracted passes and AOVs. --- This is just a first stab at an implementation, it is at least missing: - documentation - examples of usage The actual code is mostly a duplication of the transparent closure, with changes at some key points. I am unsure about a couple of things: - using `sd->P` and `sd->wi` to set the new ray origin and direction, maybe this should be stored somewhere else? - changing `state->isect.object` in `shade_surface.h` to give the next ray the possibility to hit the same polygon again - the value of `SH_NODE_BSDF_PORTAL` - whether the new ray direction should be set as "incoming" or "outgoing". Incoming has the advantage of being immediately accessible on the geometry node, but somehow seems backwards for a portal node. - the copyright info on new files: I copied what was at the top of the files for the transparent closure. - `sd->closure_transparent_extinction`: This is currently shared with transparent closures, and I think that is the best solution, but mixing transparency and portals might lead to weird results. edit: added example of camera filming its own screen edit2: added obvious test image :) edit3: added some of the scenes that I used for testing
david494 added 1 commit 2023-11-02 03:35:02 +01:00
a52acf2cb2 Cycles: Add Portal BSDF
Initial implementation of a portal closure.
david494 added 1 commit 2023-11-02 04:39:05 +01:00
david494 added 2 commits 2023-11-02 21:50:04 +01:00

I am unsure about a couple of things:

  • using sd->P and sd->wi to set the new ray origin and direction, maybe this should be stored somewhere else?
  • changing state->isect.object in shade_surface.h to give the next ray the possibility to hit the same polygon again

Modifying these in the BSDF sampling code is problematic. We should handle it as a separate case like we do for subsurface scatting, since it's not a great fit to handle it as a regular BSDF.

  if (CLOSURE_IS_BSSRDF(sc->type)) {
    return subsurface_bounce(kg, state, sd, sc);
  }
  else if (CLOSURE_IS_PORTAL(sc->type) {
    return ...;
  }
  • the value of SH_NODE_BSDF_PORTAL

Better add this add the end of the list, after SH_NODE_MIX. Otherwise it reuses the value from an old node.

  • whether the new ray direction should be set as "incoming" or "outgoing". Incoming has the advantage of being immediately accessible on the geometry node, but somehow seems backwards for a portal node.

Outgoing seems more natural. I would name it "Position" and "Direction".

  • the copyright info on new files: I copied what was at the top of the files for the transparent closure.

Use:

/* SPDX-FileCopyrightText: 2023 Blender Authors
 *
 * SPDX-License-Identifier: Apache-2.0 */
  • sd->closure_transparent_extinction: This is currently shared with transparent closures, and I think that is the best solution, but mixing transparency and portals might lead to weird results.

That seems fine.

> I am unsure about a couple of things: > - using `sd->P` and `sd->wi` to set the new ray origin and direction, maybe this should be stored somewhere else? > - changing `state->isect.object` in `shade_surface.h` to give the next ray the possibility to hit the same polygon again Modifying these in the BSDF sampling code is problematic. We should handle it as a separate case like we do for subsurface scatting, since it's not a great fit to handle it as a regular BSDF. ``` if (CLOSURE_IS_BSSRDF(sc->type)) { return subsurface_bounce(kg, state, sd, sc); } else if (CLOSURE_IS_PORTAL(sc->type) { return ...; } ``` > - the value of `SH_NODE_BSDF_PORTAL` Better add this add the end of the list, after `SH_NODE_MIX`. Otherwise it reuses the value from an old node. > - whether the new ray direction should be set as "incoming" or "outgoing". Incoming has the advantage of being immediately accessible on the geometry node, but somehow seems backwards for a portal node. Outgoing seems more natural. I would name it "Position" and "Direction". > - the copyright info on new files: I copied what was at the top of the files for the transparent closure. Use: ``` /* SPDX-FileCopyrightText: 2023 Blender Authors * * SPDX-License-Identifier: Apache-2.0 */ ``` > - `sd->closure_transparent_extinction`: This is currently shared with transparent closures, and I think that is the best solution, but mixing transparency and portals might lead to weird results. That seems fine.
david494 added 1 commit 2023-11-03 22:35:38 +01:00
david494 added 1 commit 2023-11-04 00:42:57 +01:00
david494 added 1 commit 2023-11-04 01:17:10 +01:00
david494 added 1 commit 2023-11-04 01:20:46 +01:00
Author
Contributor

Thanks so much for taking the time to look at this! <3

I tried integrating all the comments. I still have some questions:

  • I left a part of the copied transparent closure code in bsdf_portal.h that I am not sure about. If we don't increment sd->num_closure_left if the path would terminate, I guess we just terminate one bounce earlier? I am not sure about the intention of this in the case of the transparent closure.

  • I still overwrite sd->P, sd->wi and sd->object, but now this is handled as a special case in shade_surface.h instead of the bsdf code, so it might be fine?

  • Since it is no longer handled as a bsdf, I removed the CLOSURE_BSDF_PORTAL_ID case from bsdf_sample(), but I am not sure if that function may be called in some other path, where handling a portal closure might be useful?

Also just wanted to say, I really enjoy reading the code, I am impressed and surprised how readable it is for a production renderer!

Thanks so much for taking the time to look at this! <3 I tried integrating all the comments. I still have some questions: - I left a part of the copied transparent closure code in `bsdf_portal.h` that I am not sure about. If we don't increment `sd->num_closure_left` if the path would terminate, I guess we just terminate one bounce earlier? I am not sure about the intention of this in the case of the transparent closure. - I still overwrite `sd->P`, `sd->wi` and `sd->object`, but now this is handled as a special case in `shade_surface.h` instead of the bsdf code, so it might be fine? - Since it is no longer handled as a bsdf, I removed the `CLOSURE_BSDF_PORTAL_ID` case from `bsdf_sample()`, but I am not sure if that function may be called in some other path, where handling a portal closure might be useful? Also just wanted to say, I really enjoy reading the code, I am impressed and surprised how readable it is for a production renderer!
david494 changed title from WIP: Cycles: Add Portal BSDF to Cycles: Add Portal BSDF 2023-11-04 02:40:52 +01:00
Author
Contributor

Regarding this:

  • I left a part of the copied transparent closure code in bsdf_portal.h that I am not sure about. If we don't increment sd->num_closure_left if the path would terminate, I guess we just terminate one bounce earlier? I am not sure about the intention of this in the case of the transparent closure.

I found this comment that seems relevant:

  /* Ray is to be terminated, but continue with transparent bounces and
   * emission as long as we encounter them. This is required to make the
   * MIS between direct and indirect light rays match, as shadow rays go
   * through transparent surfaces to reach emission too. */
  PATH_RAY_TERMINATE_AFTER_TRANSPARENT = (1U << 18U),

I think that explains the intention, and I believe we then want to have the same code for the portal closure, as it is also transparent for shadow rays, because it reuses the transparency and extinction logic. I updated the patch to do that.

And regarding this:

  • I still overwrite sd->P, sd->wi and sd->object, but now this is handled as a special case in shade_surface.h instead of the bsdf code, so it might be fine?

After testing some more, this is obviously not fine, so I updated the code to use a custom closure to hold the needed parameters.

Regarding this: > - I left a part of the copied transparent closure code in `bsdf_portal.h` that I am not sure about. If we don't increment `sd->num_closure_left` if the path would terminate, I guess we just terminate one bounce earlier? I am not sure about the intention of this in the case of the transparent closure. I found this comment that seems relevant: ``` /* Ray is to be terminated, but continue with transparent bounces and * emission as long as we encounter them. This is required to make the * MIS between direct and indirect light rays match, as shadow rays go * through transparent surfaces to reach emission too. */ PATH_RAY_TERMINATE_AFTER_TRANSPARENT = (1U << 18U), ``` I think that explains the intention, and I believe we then want to have the same code for the portal closure, as it is also transparent for shadow rays, because it reuses the transparency and extinction logic. I updated the patch to do that. And regarding this: > - I still overwrite sd->P, sd->wi and sd->object, but now this is handled as a special case in shade_surface.h instead of the bsdf code, so it might be fine? After testing some more, this is obviously not fine, so I updated the code to use a custom closure to hold the needed parameters.
david494 added 1 commit 2023-11-04 12:39:39 +01:00
david494 added 1 commit 2023-11-04 19:26:15 +01:00
david494 added 1 commit 2023-11-05 23:52:56 +01:00
david494 added 3 commits 2023-11-06 04:55:17 +01:00

This is looking good, but I need some time to look into the finer details for transparency, AOVs and path termination. Hopefully I will have time for it next week.

This is looking good, but I need some time to look into the finer details for transparency, AOVs and path termination. Hopefully I will have time for it next week.
Author
Contributor

Just some of the things about AOVs that I thought about while writing this:

  • some passes are straightforward and obviously still correct through portals, such as light passes, UV, or cryptomattes. We also probably don't want to touch custom AOVs
  • position and normal pass are still in world space and not offset or rotated by the portal, which I think is the best solution?
  • Z pass is the problematic one, and does not take portals into account. It just calculates world position to camera position. To change that I think we would have to track path length, not just current ray length in the integrator state. (which might be useful even without portals, maybe even exposed on the lightpath node?)
  • similarly the mist pass is weird at the moment

All in all, as a user I think I would not be surprised that not all passes make sense through a portal, but of course it would be nice if we could do better.

Just some of the things about AOVs that I thought about while writing this: - some passes are straightforward and obviously still correct through portals, such as light passes, UV, or cryptomattes. We also probably don't want to touch custom AOVs - position and normal pass are still in world space and not offset or rotated by the portal, which I think is the best solution? - Z pass is the problematic one, and does not take portals into account. It just calculates world position to camera position. To change that I think we would have to track path length, not just current ray length in the integrator state. (which might be useful even without portals, maybe even exposed on the lightpath node?) - similarly the mist pass is weird at the moment All in all, as a user I think I would not be surprised that not all passes make sense through a portal, but of course it would be nice if we could do better.
david494 added 1 commit 2023-11-17 23:27:23 +01:00
david494 added 1 commit 2023-11-18 01:49:37 +01:00
david494 added 1 commit 2023-11-19 21:44:59 +01:00
947b127cdb reworked shadow logic again
MIS has to be off for correct light through a portal, but that means
we can't have transparent shadows for portal closures, so we turn them
off for any shader that contains a portal.

@LukasStockner, is this something you can review?

@LukasStockner, is this something you can review?
Brecht Van Lommel approved these changes 2024-01-29 18:24:26 +01:00
Dismissed
Brecht Van Lommel left a comment
Owner

Not a full review, just some quick comments from reading the code.

Not a full review, just some quick comments from reading the code.
@ -0,0 +27,4 @@
return;
}
if (sd->flag & SD_TRANSPARENT || sd->flag & SD_PORTAL) {

More efficient: (sd->flag & (SD_TRANSPARENT|SD_PORTAL))

More efficient: `(sd->flag & (SD_TRANSPARENT|SD_PORTAL)) `
Author
Contributor

Yep, thanks. Also reads better :)

Yep, thanks. Also reads better :)
david494 marked this conversation as resolved
@ -0,0 +28,4 @@
}
if (sd->flag & SD_TRANSPARENT || sd->flag & SD_PORTAL) {
sd->closure_transparent_extinction += weight;

The initialization of this is getting complicated.

We better set this to zero in surface_shader_eval next to sd->num_closure, and then always use +=.

The initialization of this is getting complicated. We better set this to zero in `surface_shader_eval` next to `sd->num_closure`, and then always use `+=`.
Author
Contributor

That simplifies things nicely, I agree.

That simplifies things nicely, I agree.
brecht marked this conversation as resolved
@ -69,3 +69,3 @@
}
if (!(sd->flag & SD_TRANSPARENT) || kernel_data.film.pass_alpha_threshold == 0.0f ||
if (!(sd->flag & SD_TRANSPARENT || sd->flag & SD_PORTAL) ||

(sd->flag & (SD_TRANSPARENT|SD_PORTAL))

`(sd->flag & (SD_TRANSPARENT|SD_PORTAL)) `
david494 marked this conversation as resolved
@ -108,3 +108,3 @@
/* ray through transparent keeps same flags from previous ray and is
* not counted as a regular bounce, transparent has separate max */
if (label & LABEL_TRANSPARENT) {
if (label & LABEL_TRANSPARENT || label & LABEL_PORTAL) {

(label & (LABEL_TRANSPARENT|LABEL_PORTAL))

`(label & (LABEL_TRANSPARENT|LABEL_PORTAL)) `
david494 marked this conversation as resolved
@ -52,2 +52,4 @@
# endif
/* Disable transparent shadows for portals */
if (shadow_sd->flag & SD_PORTAL) {

This makes it so the presence of a single portal BSDF affects the whole material, regardless of its weight, which might for example be near zero. That doesn't seem right?

This makes it so the presence of a single portal BSDF affects the whole material, regardless of its weight, which might for example be near zero. That doesn't seem right?
Author
Contributor

Yes, that one is a bit strange. I did not find a better place to disable transparent shadows for rays intersecting with portals, since I am reusing transparent_extinction, which is used in surface_shader_transparency(). I think we would either need to track portal transparency separately, or just treat portals as opaque (similar to refractions). The first option would add quite a bit of complexity I think, and the second one would get rid of one of my primary use cases (that passes and AOVs are visible through portals).

In my tests disabling transparent shadows for the whole material works out quite well, because with a weight=0 the shader is optimized out and we still get transparent shadows. If the transition is still problematic, one can still use "lightpath->is shadow ray" tricks to get transparent shadows (but this wasn't really necessary in my test scenes).

Yes, that one is a bit strange. I did not find a better place to disable transparent shadows for rays intersecting with portals, since I am reusing transparent_extinction, which is used in surface_shader_transparency(). I think we would either need to track portal transparency separately, or just treat portals as opaque (similar to refractions). The first option would add quite a bit of complexity I think, and the second one would get rid of one of my primary use cases (that passes and AOVs are visible through portals). In my tests disabling transparent shadows for the whole material works out quite well, because with a weight=0 the shader is optimized out and we still get transparent shadows. If the transition is still problematic, one can still use "lightpath->is shadow ray" tricks to get transparent shadows (but this wasn't really necessary in my test scenes).
Author
Contributor

I think I found an unrelated bug with transparent shadows during my tests: If there are any materials that contain transparent closures and have the "Transparent Shadows" checkbox set, all other materials in the scene that are transparent, but don't have "Transparent Shadows", are now completely opaque for shadows. I will report this as a separate bug.

edit: reported here #117652

I think I found an unrelated bug with transparent shadows during my tests: If there are any materials that contain transparent closures and have the "Transparent Shadows" checkbox set, all _other_ materials in the scene that are transparent, but don't have "Transparent Shadows", are now completely opaque for shadows. I will report this as a separate bug. edit: reported here https://projects.blender.org/blender/blender/issues/117652
@ -944,3 +944,3 @@
return one_spectrum();
}
else if (sd->flag & SD_TRANSPARENT) {
else if (sd->flag & SD_TRANSPARENT || sd->flag & SD_PORTAL) {

(sd->flag & (SD_TRANSPARENT|SD_PORTAL))

`(sd->flag & (SD_TRANSPARENT|SD_PORTAL)) `
david494 marked this conversation as resolved
Brecht Van Lommel dismissed brecht’s review 2024-01-29 18:24:53 +01:00
Reason:

Accidentally approved, did not mean to do it yet

david494 added 4 commits 2024-01-29 22:35:04 +01:00
david494 added 1 commit 2024-02-05 23:57:18 +01:00
david494 added 1 commit 2024-02-21 13:06:13 +01:00

@LukasStockner @weizhen is this something either of you could look at in the coming weeks, so we can get this into 4.2?

@LukasStockner @weizhen is this something either of you could look at in the coming weeks, so we can get this into 4.2?
Lukas Stockner requested changes 2024-03-02 01:43:47 +01:00
Dismissed
Lukas Stockner left a comment
Member

Code-wise, this seems mostly fine to me, just a few minor comments.

Also, OSL support is currently missing. I don't see any issues with adding it as a custom closure there - if you need any help with it, just let me know and I can take care of it.

There's also always the larger-picture view of whether the code complexity is warranted for a fairly niche feature, but I think in this case it's still reasonable.

Finally, one concern that I have is that we already have "light portals", so there's a danger of mixing things up (especially labels such as SD_PORTAL). I can't really think of a good alternative name for this BSDF though, so maybe we rename the existing Light Portals to e.g. Light Guides?

Code-wise, this seems mostly fine to me, just a few minor comments. Also, OSL support is currently missing. I don't see any issues with adding it as a custom closure there - if you need any help with it, just let me know and I can take care of it. There's also always the larger-picture view of whether the code complexity is warranted for a fairly niche feature, but I think in this case it's still reasonable. Finally, one concern that I have is that we already have "light portals", so there's a danger of mixing things up (especially labels such as `SD_PORTAL`). I can't really think of a good alternative name for this BSDF though, so maybe we rename the existing Light Portals to e.g. Light Guides?
@ -284,6 +285,7 @@ ccl_device_inline void bsdf_roughness_eta(const KernelGlobals kg,
*eta = 1.0f;
Member

(not specific to this line, but to the file):
Currently, CLOSURE_BSDF_PORTAL_ID isn't handled in the bsdf_sample switch statement. This works since it is handled by integrate_surface_portal instead, but we might want to make that explicit with a kernel_assert(false) case for it.

(not specific to this line, but to the file): Currently, `CLOSURE_BSDF_PORTAL_ID` isn't handled in the `bsdf_sample` switch statement. This works since it is handled by `integrate_surface_portal` instead, but we might want to make that explicit with a `kernel_assert(false)` case for it.
Author
Contributor

I agree. Added, with a small comment.

I agree. Added, with a small comment.
brecht marked this conversation as resolved
@ -482,3 +487,4 @@
break;
case CLOSURE_BSDF_TRANSPARENT_ID:
case CLOSURE_BSDF_PORTAL_ID:
eval = bsdf_transparent_eval(sc, sd->wi, wo, pdf);
Member

I'd prefer a separate bsdf_portal_eval just for consistency, even if it's the same stub as bsdf_transparent_eval.

I'd prefer a separate `bsdf_portal_eval` just for consistency, even if it's the same stub as `bsdf_transparent_eval`.
Author
Contributor

Yes, that makes it slightly more obvious if either has to change in the future.

Yes, that makes it slightly more obvious if either has to change in the future.
brecht marked this conversation as resolved
@ -0,0 +15,4 @@
static_assert(sizeof(ShaderClosure) >= sizeof(PortalClosure), "PortalClosure is too large!");
ccl_device void bsdf_portal_setup(ccl_private ShaderData *sd,
Member

I'm assuming that this is based on bsdf_transparent_setup.
That one is a bit of a special case, since all transparent closures are merged together.

In general, the usual pattern is that closure allocation and the sample weight check is handled in closure.h, and the setup function only handles parameter clamping etc.

That being said, the closures.h switch statement is massive, and I don't really see a problem with doing it this way...

I'm assuming that this is based on `bsdf_transparent_setup`. That one is a bit of a special case, since all transparent closures are merged together. In general, the usual pattern is that closure allocation and the sample weight check is handled in `closure.h`, and the setup function only handles parameter clamping etc. That being said, the `closures.h` switch statement is massive, and I don't really see a problem with doing it this way...
Author
Contributor

This is indeed based on the transparent BSDF code. While adding OSL support I also kept it close to the transparent code, which made the OSL changes very simple: osl_closure_portal_bsdf_setup just calls bsdf_portal_setup, so having this separated seems useful here.

This is indeed based on the transparent BSDF code. While adding OSL support I also kept it close to the transparent code, which made the OSL changes very simple: `osl_closure_portal_bsdf_setup` just calls `bsdf_portal_setup`, so having this separated seems useful here.
LukasStockner marked this conversation as resolved
@ -115,6 +115,10 @@ ccl_device_inline void path_state_next(KernelGlobals kg,
flag |= PATH_RAY_TERMINATE_ON_NEXT_SURFACE;
}
if (shader_flag & SD_PORTAL) {
Member

Shouldn't this check for label & LABEL_PORTAL instead? If an object is 50% diffuse and 50% portal, the diffuse bounces should still perform MIS.

Shouldn't this check for `label & LABEL_PORTAL` instead? If an object is 50% diffuse and 50% portal, the diffuse bounces should still perform MIS.
Author
Contributor

This is inside a check that already checks for either LABEL_PORTAL or LABEL_TRANSPARENT (so should not disable MIS on diffuse?), and based on my tests I believe we want to disable MIS for transparent as well if there is a portal closure in the mix. Otherwise the shadow behind a mix between portal and transparent does not converge to the correct result.

This is inside a check that already checks for either `LABEL_PORTAL` or `LABEL_TRANSPARENT` (so should not disable MIS on diffuse?), and based on my tests I believe we want to disable MIS for transparent as well if there is a portal closure in the mix. Otherwise the shadow behind a mix between portal and transparent does not converge to the correct result.
Member

Ah, right, I missed the previous check.

Ah, right, I missed the previous check.
LukasStockner marked this conversation as resolved
@ -160,0 +166,4 @@
float sum_sample_weight = 0.0f;
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *other_sc = &sd->closure[i];
Member

other_sc is misleading here, since unlike the MIS code the loop isn't skipping sc.

`other_sc` is misleading here, since unlike the MIS code the loop isn't skipping `sc`.
Author
Contributor

Yes, that is a little bit of unfortunate naming. I did not want to shadow the function parameter sc, but maybe that is fine as its immediately cast to pc and we can refer to pc everywhere after that.

Yes, that is a little bit of unfortunate naming. I did not want to shadow the function parameter `sc`, but maybe that is fine as its immediately cast to `pc` and we can refer to `pc` everywhere after that.
LukasStockner marked this conversation as resolved
@ -160,0 +192,4 @@
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
#endif
INTEGRATOR_STATE_WRITE(state, path, throughput) *= pc->weight / sc->sample_weight *
Member

Might be a bit clearer to explicitly write

const float pick_pdf = sc->sample_weight / sum_sample_weight;
INTEGRATOR_STATE_WRITE(state, path, throughput) *= pc->weight / pick_pdf;

even though it means an extra division.

Might be a bit clearer to explicitly write ``` const float pick_pdf = sc->sample_weight / sum_sample_weight; INTEGRATOR_STATE_WRITE(state, path, throughput) *= pc->weight / pick_pdf; ``` even though it means an extra division.
Author
Contributor

I agree, looks more readable.

I agree, looks more readable.
LukasStockner marked this conversation as resolved
david494 added 3 commits 2024-04-08 02:07:57 +02:00
david494 added 1 commit 2024-04-08 02:19:01 +02:00
Author
Contributor

Sorry, it took me a while to get back to this. Thanks for the review! All the code comments should be addressed.

Also, OSL support is currently missing. I don't see any issues with adding it as a custom closure there - if you need any help with it, just let me know and I can take care of it.

OSL should be working now as well. That was suprisingly straightforward! (If I indeed did it correctly)

There's also always the larger-picture view of whether the code complexity is warranted for a fairly niche feature, but I think in this case it's still reasonable.

I agree that it is niche, but seems to be fairly well isolated code-wise. At least there have been very few updates needed to keep the code up to date with main.

Finally, one concern that I have is that we already have "light portals", so there's a danger of mixing things up (especially labels such as SD_PORTAL). I can't really think of a good alternative name for this BSDF though, so maybe we rename the existing Light Portals to e.g. Light Guides?

Light portals are already named light portals in other renderers, so I think we should keep that name. I don't have a strong opinion on the name of this feature. This really just does a new raytrace anywhere, so maybe "Raytrace BSDF", or "Trace BSDF"? (Still ignoring that it is not really a BSDF). Having this as the user facing name might be okay, but in the code this seems very generic and might be even more confusing. I am open to other suggestions... maybe "Ray Portal"?

Sorry, it took me a while to get back to this. Thanks for the review! All the code comments should be addressed. > Also, OSL support is currently missing. I don't see any issues with adding it as a custom closure there - if you need any help with it, just let me know and I can take care of it. OSL should be working now as well. That was suprisingly straightforward! (If I indeed did it correctly) > There's also always the larger-picture view of whether the code complexity is warranted for a fairly niche feature, but I think in this case it's still reasonable. I agree that it is niche, but seems to be fairly well isolated code-wise. At least there have been very few updates needed to keep the code up to date with main. > Finally, one concern that I have is that we already have "light portals", so there's a danger of mixing things up (especially labels such as SD_PORTAL). I can't really think of a good alternative name for this BSDF though, so maybe we rename the existing Light Portals to e.g. Light Guides? Light portals are already named light portals in other renderers, so I think we should keep that name. I don't have a strong opinion on the name of this feature. This really just does a new raytrace anywhere, so maybe "Raytrace BSDF", or "Trace BSDF"? (Still ignoring that it is not really a BSDF). Having this as the user facing name might be okay, but in the code this seems very generic and might be even more confusing. I am open to other suggestions... maybe "Ray Portal"?
david494 requested review from Lukas Stockner 2024-04-09 10:40:34 +02:00
Member

Code looks good to me now.

In terms of name, I like "Ray Portal", that seems distinct enough from "Light Portal" and describes what is happening quite nicely.

Code looks good to me now. In terms of name, I like "Ray Portal", that seems distinct enough from "Light Portal" and describes what is happening quite nicely.
david494 added 1 commit 2024-04-21 00:34:28 +02:00
buildbot/vexp-code-patch-lint Build done. Details
buildbot/vexp-code-patch-darwin-x86_64 Build done. Details
buildbot/vexp-code-patch-darwin-arm64 Build done. Details
buildbot/vexp-code-patch-linux-x86_64 Build done. Details
buildbot/vexp-code-patch-windows-amd64 Build done. Details
buildbot/vexp-code-patch-coordinator Build done. Details
31bde20690
rename portal to ray portal
Author
Contributor

Thanks again, I now replaced all references to 'portal' with 'ray portal'.

(and updated the test files in the top post)

Thanks again, I now replaced all references to 'portal' with 'ray portal'. (and updated the test files in the top post)
Lukas Stockner reviewed 2024-04-21 18:32:02 +02:00
Lukas Stockner left a comment
Member

Mostly LGTM now, just two nitpicks and one detail that I'm not certain about.

Mostly LGTM now, just two nitpicks and one detail that I'm not certain about.
@ -160,0 +176,4 @@
return LABEL_NONE;
}
if (len_squared(sd->P - pc->P) > 1e-9f) {
Member

Nitpick: 1e-9f is really small, we usually use 1e-5f as an epsilon value.

Nitpick: `1e-9f` is really small, we usually use `1e-5f` as an epsilon value.
Author
Contributor

1e-9 is somewhat arbitrary, but my reasoning was that this is a squared distance. 1e-5 would still consider an offset of 0.001 the same point. I think a reasonable value here is dependent on scene scale.

1e-9 is somewhat arbitrary, but my reasoning was that this is a squared distance. 1e-5 would still consider an offset of 0.001 the same point. I think a reasonable value here is dependent on scene scale.
Member

Hm, yeah, for squared distance it's fine I guess.

Hm, yeah, for squared distance it's fine I guess.
LukasStockner marked this conversation as resolved
@ -160,0 +189,4 @@
INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f;
INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX;
#ifdef __RAY_DIFFERENTIALS__
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
Member

Not sure if this is the best way to handle it. This approach assumes that there's a 1:1 relationship between the shading position and the origin of the new ray, but that can't really be assumed.

The only realistic alternative I can think of is to set the differential to zero. Evaluating the real differential of the node input would be massive overkill.

@brecht, any opinions? I'm not 100% sure how important ray->dP currently is. Using sd->dP as an approximation is probably fine?

Not sure if this is the best way to handle it. This approach assumes that there's a 1:1 relationship between the shading position and the origin of the new ray, but that can't really be assumed. The only realistic alternative I can think of is to set the differential to zero. Evaluating the real differential of the node input would be massive overkill. @brecht, any opinions? I'm not 100% sure how important `ray->dP` currently is. Using `sd->dP` as an approximation is probably fine?

I think just copying like this is ok. It's correct if the portal is a flat window and there is no rotation, which I guess is close enough as an approximation.

I think just copying like this is ok. It's correct if the portal is a flat window and there is no rotation, which I guess is close enough as an approximation.
brecht marked this conversation as resolved
@ -0,0 +5,4 @@
#include "stdcycles.h"
shader node_ray_portal_bsdf(color Color = 0.8,
normal Normal = N,
Member

Nitpick: I don't think we need this input.

Nitpick: I don't think we need this input.
david494 marked this conversation as resolved
Member

@blender-bot build +gpu

@blender-bot build +gpu
david494 added 1 commit 2024-04-21 20:20:28 +02:00
david494 added 1 commit 2024-04-21 20:25:42 +02:00
buildbot/vexp-code-patch-lint Build done. Details
buildbot/vexp-code-patch-linux-x86_64 Build done. Details
buildbot/vexp-code-patch-darwin-x86_64 Build done. Details
buildbot/vexp-code-patch-windows-amd64 Build done. Details
buildbot/vexp-code-patch-darwin-arm64 Build done. Details
buildbot/vexp-code-patch-coordinator Build done. Details
36a0540af0
remove unneeded Normal input for ray portal
Lukas Stockner approved these changes 2024-04-22 01:57:49 +02:00
Lukas Stockner left a comment
Member

LGTM now. The only open question from my side is the differentials topic, but I'd be fine with keeping it as-is.

LGTM now. The only open question from my side is the differentials topic, but I'd be fine with keeping it as-is.

@blender-bot build

@blender-bot build
Brecht Van Lommel approved these changes 2024-04-22 13:53:20 +02:00
Brecht Van Lommel left a comment
Owner

We will need some simple blend files for automated tests, see the existing ones for a template:
https://projects.blender.org/blender/blender-test-data/src/branch/main/render

Mainly would like to see tested:

  • Mix of transparency and portal
  • Multiple portals
  • Bump map seen through portal (to test ray differentials)
We will need some simple blend files for automated tests, see the existing ones for a template: https://projects.blender.org/blender/blender-test-data/src/branch/main/render Mainly would like to see tested: * Mix of transparency and portal * Multiple portals * Bump map seen through portal (to test ray differentials)
Brecht Van Lommel added 2 commits 2024-04-29 10:57:34 +02:00
Brecht Van Lommel added 1 commit 2024-04-29 11:35:53 +02:00
Brecht Van Lommel added 1 commit 2024-04-29 12:31:24 +02:00
Brecht Van Lommel merged commit ee51f643b0 into main 2024-04-29 12:38:02 +02:00
Brecht Van Lommel deleted branch cycles-portal-bsdf 2024-04-29 12:38:05 +02:00
First-time contributor

For the future, since for most users this will be something they'll have to look at a guide every time they want to set it up, so I think a simple portal setup would be a good fit for a built-in asset.

For the future, since for most users this will be something they'll have to look at a guide every time they want to set it up, so I think a simple portal setup would be a good fit for a built-in asset.
First-time contributor

For the future, since for most users this will be something they'll have to look at a guide every time they want to set it up, so I think a simple portal setup would be a good fit for a built-in asset.

Yeah, I agree. It's really not easy to use. Maybe some easy-to-use nodes in the built-in essential would be nice.

Btw, while testing it I recognized the following:

  • It's awesome that we finally a way to create a mirror where we can mask the object in the mirror with Crytomatte!. But once you use a portal, there is now way to get the original object in the compositor in Crytomatteor. I think it would be handy to have an option, somehow also to get the original object.

  • Portal BSDF creats a Depth pass. That's cool. But it ignores Camera DOF. If somehow possible, a checkbox in PortalBSDF, to consider Camera DOF would be awesome. Currently, the only way is to use the Depth pass and create DOF in the compositor.

> For the future, since for most users this will be something they'll have to look at a guide every time they want to set it up, so I think a simple portal setup would be a good fit for a built-in asset. Yeah, I agree. It's really not easy to use. Maybe some easy-to-use nodes in the built-in essential would be nice. Btw, while testing it I recognized the following: - It's awesome that we finally a way to create a mirror where we can mask the object in the mirror with Crytomatte!. But once you use a portal, there is now way to get the original object in the compositor in Crytomatteor. I think it would be handy to have an option, somehow also to get the original object. - Portal BSDF creats a Depth pass. That's cool. But it ignores Camera DOF. If somehow possible, a checkbox in PortalBSDF, to consider Camera DOF would be awesome. Currently, the only way is to use the Depth pass and create DOF in the compositor.
Member

But once you use a portal, there is no way to get the original object in the compositor in Crytomatteor.

Shader AOVs can still be used to select the "Portal Object" rather than the objects visible through the portal

Portal BSDF ignores Camera DOF.

The Portal BSDF is interacting with DOF as expected in my testing. If you believe you have encountered a issue, please create a bug report.

> But once you use a portal, there is no way to get the original object in the compositor in Crytomatteor. Shader AOVs can still be used to select the "Portal Object" rather than the objects visible through the portal > Portal BSDF ignores Camera DOF. The Portal BSDF is interacting with DOF as expected in my testing. If you believe you have encountered a issue, please create a bug report.
First-time contributor

I think there might be a slightly weird interaction with Geometry Nodes points (which allow perfect spheres in Cycles) - this might not actually be relevant here, and rather something about the default rendering coordinate systems for points?
but just in principle:

image

image

For some reason, with points, the Direction ends up being incorrect

I think there might be a slightly weird interaction with Geometry Nodes points (which allow perfect spheres in Cycles) - this might not actually be relevant here, and rather something about the default rendering coordinate systems for points? but just in principle: ![image](/attachments/6bca9bf9-3a72-43de-b8fd-5943b68d1d2d) ![image](/attachments/ec08f957-8501-4c22-95ff-e5392a5891b9) For some reason, with points, the Direction ends up being incorrect
First-time contributor

Never mind, I think it's just generally the default. I thought the default for Direction is negative incoming, but apparently that's not the case

Never mind, I think it's just generally the default. I thought the default for Direction is negative incoming, but apparently that's not the case
First-time contributor

I was thinking this could be useful for screens, but then the DOF becomes an issue. Afaik the distance through the portal is added to the DOF, meaning it's gonna be problematic to focus on both the screen and what's through it. Is it possible to add a toggle to not add the distance through the portal?

I was thinking this could be useful for screens, but then the DOF becomes an issue. Afaik the distance through the portal is added to the DOF, meaning it's gonna be problematic to focus on both the screen and what's through it. Is it possible to add a toggle to not add the distance through the portal?
Member

@Calibrator , feature requests should be made on right click select.

As for your concern about DOF going through the portal, there are a few ways to work around it.

  1. If you're using the depth pass in the compositor to add DOF to your scene, then you can use Shader AOVs to mark out the area where the portal is and not apply DOF to it. Or use the shader AOV to get the deapth from camera and replace that section of the depth pass.
  2. If you're using in-camera DOF, then all you need to do is specify a direction the ray should go once it hits the portal. And as long as the surface the portal is on is in focus, then what's through it will look in focus (assuming you haven't done anything weird with the direction).
@Calibrator , feature requests should be made on [right click select](https://blender.community/c/rightclickselect/). As for your concern about DOF going through the portal, there are a few ways to work around it. 1. If you're using the depth pass in the compositor to add DOF to your scene, then you can use Shader AOVs to mark out the area where the portal is and not apply DOF to it. Or use the shader AOV to get the deapth from camera and replace that section of the depth pass. 2. If you're using in-camera DOF, then all you need to do is specify a direction the ray should go once it hits the portal. And as long as the surface the portal is on is in focus, then what's through it will look in focus (assuming you haven't done anything weird with the direction).
First-time contributor

@Calibrator , feature requests should be made on right click select.

As for your concern about DOF going through the portal, there are a few ways to work around it.

  1. If you're using the depth pass in the compositor to add DOF to your scene, then you can use Shader AOVs to mark out the area where the portal is and not apply DOF to it. Or use the shader AOV to get the deapth from camera and replace that section of the depth map.
  2. If you're using in-camera DOF, then all you need to do is specify a direction the ray should go once it hits the portal. And as long as the surface the portal is on is in focus, then what's through it will look in focus (assuming you haven't done anything weird with the direction).

Ah got it. I thought this would be the relevant place to mention it, but I see now. My bad.

As for the solutions I'll need time to figure this stuff out. This is the most complicated BSDF shader so far to be honest. In the meanwhile I hope the # 2 solution works. I believe that would be the method the average user would need to use the portal for a screen.

> @Calibrator , feature requests should be made on [right click select](https://blender.community/c/rightclickselect/). > > As for your concern about DOF going through the portal, there are a few ways to work around it. > > 1. If you're using the depth pass in the compositor to add DOF to your scene, then you can use Shader AOVs to mark out the area where the portal is and not apply DOF to it. Or use the shader AOV to get the deapth from camera and replace that section of the depth map. > 2. If you're using in-camera DOF, then all you need to do is specify a direction the ray should go once it hits the portal. And as long as the surface the portal is on is in focus, then what's through it will look in focus (assuming you haven't done anything weird with the direction). Ah got it. I thought this would be the relevant place to mention it, but I see now. My bad. As for the solutions I'll need time to figure this stuff out. This is the most complicated BSDF shader so far to be honest. In the meanwhile I hope the # 2 solution works. I believe that would be the method the average user would need to use the portal for a screen.

Depth of field works by modifying the initial ray position and direction, there is no other information that is tracked that could be modified. For a computer screen, you'd discard the ray direction and create a completely new one, since what's shown on the screen does not depend on the viewing angle.

Depth of field works by modifying the initial ray position and direction, there is no other information that is tracked that could be modified. For a computer screen, you'd discard the ray direction and create a completely new one, since what's shown on the screen does not depend on the viewing angle.
Sign in to join this conversation.
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
8 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#114386
No description provided.