Merge branch 'master' into refactor-mesh-selection-generic

This commit is contained in:
Hans Goudey 2022-08-15 09:03:53 -04:00
commit c4dc870a45
100 changed files with 3435 additions and 1119 deletions

View File

@ -105,7 +105,6 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private differential3 *domega_in,
ccl_private float *pdf)
{
/* For curves use the smooth normal, particularly for ribbons the geometric
@ -115,304 +114,80 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg,
switch (sc->type) {
case CLOSURE_BSDF_DIFFUSE_ID:
label = bsdf_diffuse_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_diffuse_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
#ifdef __SVM__
case CLOSURE_BSDF_OREN_NAYAR_ID:
label = bsdf_oren_nayar_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_oren_nayar_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
# ifdef __OSL__
case CLOSURE_BSDF_PHONG_RAMP_ID:
label = bsdf_phong_ramp_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_phong_ramp_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
case CLOSURE_BSDF_DIFFUSE_RAMP_ID:
label = bsdf_diffuse_ramp_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_diffuse_ramp_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
# endif
case CLOSURE_BSDF_TRANSLUCENT_ID:
label = bsdf_translucent_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_translucent_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
case CLOSURE_BSDF_REFLECTION_ID:
label = bsdf_reflection_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_reflection_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
case CLOSURE_BSDF_REFRACTION_ID:
label = bsdf_refraction_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_refraction_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
case CLOSURE_BSDF_TRANSPARENT_ID:
label = bsdf_transparent_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_transparent_sample(sc, Ng, sd->I, randu, randv, eval, 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:
label = bsdf_microfacet_ggx_sample(kg,
sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_microfacet_ggx_sample(kg, sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
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,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf,
&sd->lcg_state);
label = bsdf_microfacet_multi_ggx_sample(
kg, sc, Ng, sd->I, randu, randv, eval, omega_in, pdf, &sd->lcg_state);
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,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
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);
break;
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
label = bsdf_microfacet_beckmann_sample(kg,
sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_microfacet_beckmann_sample(
kg, sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
label = bsdf_ashikhmin_shirley_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_ashikhmin_shirley_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
label = bsdf_ashikhmin_velvet_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_ashikhmin_velvet_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
case CLOSURE_BSDF_DIFFUSE_TOON_ID:
label = bsdf_diffuse_toon_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_diffuse_toon_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
case CLOSURE_BSDF_GLOSSY_TOON_ID:
label = bsdf_glossy_toon_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_glossy_toon_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
label = bsdf_hair_reflection_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_hair_reflection_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID:
label = bsdf_hair_transmission_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_hair_transmission_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
case CLOSURE_BSDF_HAIR_PRINCIPLED_ID:
label = bsdf_principled_hair_sample(
kg, sc, sd, randu, randv, eval, omega_in, &domega_in->dx, &domega_in->dy, pdf);
label = bsdf_principled_hair_sample(kg, sc, sd, randu, randv, eval, omega_in, pdf);
break;
# ifdef __PRINCIPLED__
case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID:
label = bsdf_principled_diffuse_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_principled_diffuse_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID:
label = bsdf_principled_sheen_sample(sc,
Ng,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
label = bsdf_principled_sheen_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
break;
# endif /* __PRINCIPLED__ */
#endif

View File

@ -133,14 +133,10 @@ ccl_device_inline void bsdf_ashikhmin_shirley_sample_first_quadrant(float n_x,
ccl_device int bsdf_ashikhmin_shirley_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
@ -221,12 +217,6 @@ ccl_device int bsdf_ashikhmin_shirley_sample(ccl_private const ShaderClosure *sc
/* leave the rest to eval_reflect */
*eval = bsdf_ashikhmin_shirley_eval_reflect(sc, I, *omega_in, pdf);
}
#ifdef __RAY_DIFFERENTIALS__
/* just do the reflection thing for now */
*domega_in_dx = (2.0f * dot(N, dIdx)) * N - dIdx;
*domega_in_dy = (2.0f * dot(N, dIdy)) * N - dIdy;
#endif
}
return label;

View File

@ -87,14 +87,10 @@ ccl_device Spectrum bsdf_ashikhmin_velvet_eval_transmit(ccl_private const Shader
ccl_device int bsdf_ashikhmin_velvet_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const VelvetBsdf *bsdf = (ccl_private const VelvetBsdf *)sc;
@ -130,12 +126,6 @@ ccl_device int bsdf_ashikhmin_velvet_sample(ccl_private const ShaderClosure *sc,
float power = 0.25f * (D * G) / cosNO;
*eval = make_spectrum(power);
#ifdef __RAY_DIFFERENTIALS__
// TODO: find a better approximation for the retroreflective bounce
*domega_in_dx = (2 * dot(N, dIdx)) * N - dIdx;
*domega_in_dy = (2 * dot(N, dIdy)) * N - dIdy;
#endif
}
else {
*pdf = 0.0f;

View File

@ -51,14 +51,10 @@ ccl_device Spectrum bsdf_diffuse_eval_transmit(ccl_private const ShaderClosure *
ccl_device int bsdf_diffuse_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc;
@ -69,11 +65,6 @@ ccl_device int bsdf_diffuse_sample(ccl_private const ShaderClosure *sc,
if (dot(Ng, *omega_in) > 0.0f) {
*eval = make_spectrum(*pdf);
#ifdef __RAY_DIFFERENTIALS__
// TODO: find a better approximation for the diffuse bounce
*domega_in_dx = (2 * dot(N, dIdx)) * N - dIdx;
*domega_in_dy = (2 * dot(N, dIdy)) * N - dIdy;
#endif
}
else {
*pdf = 0.0f;
@ -115,14 +106,10 @@ ccl_device Spectrum bsdf_translucent_eval_transmit(ccl_private const ShaderClosu
ccl_device int bsdf_translucent_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc;
@ -133,11 +120,6 @@ ccl_device int bsdf_translucent_sample(ccl_private const ShaderClosure *sc,
sample_cos_hemisphere(-N, randu, randv, omega_in, pdf);
if (dot(Ng, *omega_in) < 0) {
*eval = make_spectrum(*pdf);
#ifdef __RAY_DIFFERENTIALS__
// TODO: find a better approximation for the diffuse bounce
*domega_in_dx = -((2 * dot(N, dIdx)) * N - dIdx);
*domega_in_dy = -((2 * dot(N, dIdy)) * N - dIdy);
#endif
}
else {
*pdf = 0;

View File

@ -71,14 +71,10 @@ ccl_device Spectrum bsdf_diffuse_ramp_eval_transmit(ccl_private const ShaderClos
ccl_device int bsdf_diffuse_ramp_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
const DiffuseRampBsdf *bsdf = (const DiffuseRampBsdf *)sc;
@ -89,10 +85,6 @@ ccl_device int bsdf_diffuse_ramp_sample(ccl_private const ShaderClosure *sc,
if (dot(Ng, *omega_in) > 0.0f) {
*eval = rgb_to_spectrum(bsdf_diffuse_ramp_get_color(bsdf->colors, *pdf * M_PI_F) * M_1_PI_F);
# ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = (2 * dot(N, dIdx)) * N - dIdx;
*domega_in_dy = (2 * dot(N, dIdy)) * N - dIdy;
# endif
}
else {
*pdf = 0.0f;

View File

@ -151,14 +151,10 @@ ccl_device Spectrum bsdf_hair_transmission_eval_transmit(ccl_private const Shade
ccl_device int bsdf_hair_reflection_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc;
@ -194,12 +190,6 @@ ccl_device int bsdf_hair_reflection_sample(ccl_private const ShaderClosure *sc,
fast_sincosf(phi, &sinphi, &cosphi);
*omega_in = (cosphi * costheta_i) * locy - (sinphi * costheta_i) * locx + (sintheta_i)*Tg;
// differentials - TODO: find a better approximation for the reflective bounce
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = 2 * dot(locy, dIdx) * locy - dIdx;
*domega_in_dy = 2 * dot(locy, dIdy) * locy - dIdy;
#endif
*pdf = fabsf(phi_pdf * theta_pdf);
if (M_PI_2_F - fabsf(theta_i) < 0.001f)
*pdf = 0.0f;
@ -212,14 +202,10 @@ ccl_device int bsdf_hair_reflection_sample(ccl_private const ShaderClosure *sc,
ccl_device int bsdf_hair_transmission_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc;
@ -255,12 +241,6 @@ ccl_device int bsdf_hair_transmission_sample(ccl_private const ShaderClosure *sc
fast_sincosf(phi, &sinphi, &cosphi);
*omega_in = (cosphi * costheta_i) * locy - (sinphi * costheta_i) * locx + (sintheta_i)*Tg;
// differentials - TODO: find a better approximation for the transmission bounce
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = 2 * dot(locy, dIdx) * locy - dIdx;
*domega_in_dy = 2 * dot(locy, dIdy) * locy - dIdy;
#endif
*pdf = fabsf(phi_pdf * theta_pdf);
if (M_PI_2_F - fabsf(theta_i) < 0.001f) {
*pdf = 0.0f;

View File

@ -354,8 +354,6 @@ ccl_device int bsdf_principled_hair_sample(KernelGlobals kg,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private PrincipledHairBSDF *bsdf = (ccl_private PrincipledHairBSDF *)sc;
@ -471,12 +469,6 @@ ccl_device int bsdf_principled_hair_sample(KernelGlobals kg,
*omega_in = X * sin_theta_i + Y * cos_theta_i * cosf(phi_i) + Z * cos_theta_i * sinf(phi_i);
#ifdef __RAY_DIFFERENTIALS__
float3 N = safe_normalize(sd->I + *omega_in);
*domega_in_dx = (2 * dot(N, sd->dI.dx)) * N - sd->dI.dx;
*domega_in_dy = (2 * dot(N, sd->dI.dy)) * N - sd->dI.dy;
#endif
return LABEL_GLOSSY | ((p == 0) ? LABEL_REFLECT : LABEL_TRANSMIT);
}

View File

@ -537,14 +537,10 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
@ -672,11 +668,6 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg,
if (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID) {
*eval *= 0.25f * bsdf->extra->clearcoat;
}
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = (2 * dot(m, dIdx)) * m - dIdx;
*domega_in_dy = (2 * dot(m, dIdy)) * m - dIdy;
#endif
}
else {
*eval = zero_spectrum();
@ -690,34 +681,13 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg,
/* CAUTION: the i and o variables are inverted relative to the paper
* eq. 39 - compute actual refractive direction */
float3 R, T;
#ifdef __RAY_DIFFERENTIALS__
float3 dRdx, dRdy, dTdx, dTdy;
#endif
float m_eta = bsdf->ior, fresnel;
bool inside;
fresnel = fresnel_dielectric(m_eta,
m,
I,
&R,
&T,
#ifdef __RAY_DIFFERENTIALS__
dIdx,
dIdy,
&dRdx,
&dRdy,
&dTdx,
&dTdy,
#endif
&inside);
fresnel = fresnel_dielectric(m_eta, m, I, &R, &T, &inside);
if (!inside && fresnel != 1.0f) {
*omega_in = T;
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = dTdx;
*domega_in_dy = dTdy;
#endif
if (alpha_x * alpha_y <= 1e-7f || fabsf(m_eta - 1.0f) < 1e-4f) {
/* some high number for MIS */
@ -978,14 +948,10 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
@ -1076,11 +1042,6 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg,
*eval = make_spectrum(out);
}
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = (2 * dot(m, dIdx)) * m - dIdx;
*domega_in_dy = (2 * dot(m, dIdy)) * m - dIdy;
#endif
}
else {
*eval = zero_spectrum();
@ -1094,35 +1055,14 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg,
/* CAUTION: the i and o variables are inverted relative to the paper
* eq. 39 - compute actual refractive direction */
float3 R, T;
#ifdef __RAY_DIFFERENTIALS__
float3 dRdx, dRdy, dTdx, dTdy;
#endif
float m_eta = bsdf->ior, fresnel;
bool inside;
fresnel = fresnel_dielectric(m_eta,
m,
I,
&R,
&T,
#ifdef __RAY_DIFFERENTIALS__
dIdx,
dIdy,
&dRdx,
&dRdy,
&dTdx,
&dTdy,
#endif
&inside);
fresnel = fresnel_dielectric(m_eta, m, I, &R, &T, &inside);
if (!inside && fresnel != 1.0f) {
*omega_in = T;
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = dTdx;
*domega_in_dy = dTdy;
#endif
if (alpha_x * alpha_y <= 1e-7f || fabsf(m_eta - 1.0f) < 1e-4f) {
/* some high number for MIS */
*pdf = 1e6f;

View File

@ -478,14 +478,10 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf,
ccl_private uint *lcg_state)
{
@ -510,10 +506,6 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals kg,
}
*pdf = 1e6f;
*eval = make_spectrum(1e6f);
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = (2 * dot(Z, dIdx)) * Z - dIdx;
*domega_in_dy = (2 * dot(Z, dIdy)) * Z - dIdy;
#endif
return LABEL_REFLECT | LABEL_SINGULAR;
}
@ -551,10 +543,6 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals kg,
*pdf = mf_ggx_pdf(localI, localO, bsdf->alpha_x);
*eval *= *pdf;
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = (2 * dot(Z, dIdx)) * Z - dIdx;
*domega_in_dy = (2 * dot(Z, dIdy)) * Z - dIdy;
#endif
return LABEL_REFLECT | LABEL_GLOSSY;
}
@ -662,14 +650,10 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf,
ccl_private uint *lcg_state)
{
@ -680,41 +664,17 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals kg,
if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) {
float3 R, T;
#ifdef __RAY_DIFFERENTIALS__
float3 dRdx, dRdy, dTdx, dTdy;
#endif
bool inside;
float fresnel = fresnel_dielectric(bsdf->ior,
Z,
I,
&R,
&T,
#ifdef __RAY_DIFFERENTIALS__
dIdx,
dIdy,
&dRdx,
&dRdy,
&dTdx,
&dTdy,
#endif
&inside);
float fresnel = fresnel_dielectric(bsdf->ior, Z, I, &R, &T, &inside);
*pdf = 1e6f;
*eval = make_spectrum(1e6f);
if (randu < fresnel) {
*omega_in = R;
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = dRdx;
*domega_in_dy = dRdy;
#endif
return LABEL_REFLECT | LABEL_SINGULAR;
}
else {
*omega_in = T;
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = dTdx;
*domega_in_dy = dTdy;
#endif
return LABEL_TRANSMIT | LABEL_SINGULAR;
}
}
@ -740,22 +700,9 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals kg,
*omega_in = X * localO.x + Y * localO.y + Z * localO.z;
if (localO.z * localI.z > 0.0f) {
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = (2 * dot(Z, dIdx)) * Z - dIdx;
*domega_in_dy = (2 * dot(Z, dIdy)) * Z - dIdy;
#endif
return LABEL_REFLECT | LABEL_GLOSSY;
}
else {
#ifdef __RAY_DIFFERENTIALS__
float cosI = dot(Z, I);
float dnp = max(sqrtf(1.0f - (bsdf->ior * bsdf->ior * (1.0f - cosI * cosI))), 1e-7f);
*domega_in_dx = -(bsdf->ior * dIdx) +
((bsdf->ior - bsdf->ior * bsdf->ior * cosI / dnp) * dot(dIdx, Z)) * Z;
*domega_in_dy = -(bsdf->ior * dIdy) +
((bsdf->ior - bsdf->ior * bsdf->ior * cosI / dnp) * dot(dIdy, Z)) * Z;
#endif
return LABEL_TRANSMIT | LABEL_GLOSSY;
}
}

View File

@ -75,14 +75,10 @@ ccl_device Spectrum bsdf_oren_nayar_eval_transmit(ccl_private const ShaderClosur
ccl_device int bsdf_oren_nayar_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const OrenNayarBsdf *bsdf = (ccl_private const OrenNayarBsdf *)sc;
@ -90,12 +86,6 @@ ccl_device int bsdf_oren_nayar_sample(ccl_private const ShaderClosure *sc,
if (dot(Ng, *omega_in) > 0.0f) {
*eval = bsdf_oren_nayar_get_intensity(sc, bsdf->N, I, *omega_in);
#ifdef __RAY_DIFFERENTIALS__
// TODO: find a better approximation for the bounce
*domega_in_dx = (2.0f * dot(bsdf->N, dIdx)) * bsdf->N - dIdx;
*domega_in_dy = (2.0f * dot(bsdf->N, dIdy)) * bsdf->N - dIdy;
#endif
}
else {
*pdf = 0.0f;

View File

@ -82,14 +82,10 @@ ccl_device float3 bsdf_phong_ramp_eval_transmit(ccl_private const ShaderClosure
ccl_device int bsdf_phong_ramp_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const PhongRampBsdf *bsdf = (ccl_private const PhongRampBsdf *)sc;
@ -99,12 +95,6 @@ ccl_device int bsdf_phong_ramp_sample(ccl_private const ShaderClosure *sc,
if (cosNO > 0) {
// reflect the view vector
float3 R = (2 * cosNO) * bsdf->N - I;
# ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = (2 * dot(bsdf->N, dIdx)) * bsdf->N - dIdx;
*domega_in_dy = (2 * dot(bsdf->N, dIdy)) * bsdf->N - dIdy;
# endif
float3 T, B;
make_orthonormals(R, &T, &B);
float phi = M_2PI_F * randu;

View File

@ -142,14 +142,10 @@ ccl_device Spectrum bsdf_principled_diffuse_eval_transmit(ccl_private const Shad
ccl_device int bsdf_principled_diffuse_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const PrincipledDiffuseBsdf *bsdf = (ccl_private const PrincipledDiffuseBsdf *)sc;
@ -160,12 +156,6 @@ ccl_device int bsdf_principled_diffuse_sample(ccl_private const ShaderClosure *s
if (dot(Ng, *omega_in) > 0) {
*eval = bsdf_principled_diffuse_compute_brdf(bsdf, N, I, *omega_in, pdf);
#ifdef __RAY_DIFFERENTIALS__
// TODO: find a better approximation for the diffuse bounce
*domega_in_dx = -((2 * dot(N, dIdx)) * N - dIdx);
*domega_in_dy = -((2 * dot(N, dIdy)) * N - dIdy);
#endif
}
else {
*pdf = 0.0f;

View File

@ -93,14 +93,10 @@ ccl_device Spectrum bsdf_principled_sheen_eval_transmit(ccl_private const Shader
ccl_device int bsdf_principled_sheen_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const PrincipledSheenBsdf *bsdf = (ccl_private const PrincipledSheenBsdf *)sc;
@ -113,12 +109,6 @@ ccl_device int bsdf_principled_sheen_sample(ccl_private const ShaderClosure *sc,
float3 H = normalize(I + *omega_in);
*eval = calculate_principled_sheen_brdf(N, I, *omega_in, H, pdf);
#ifdef __RAY_DIFFERENTIALS__
// TODO: find a better approximation for the diffuse bounce
*domega_in_dx = -((2 * dot(N, dIdx)) * N - dIdx);
*domega_in_dy = -((2 * dot(N, dIdy)) * N - dIdy);
#endif
}
else {
*eval = zero_spectrum();

View File

@ -39,14 +39,10 @@ ccl_device Spectrum bsdf_reflection_eval_transmit(ccl_private const ShaderClosur
ccl_device int bsdf_reflection_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
@ -57,10 +53,6 @@ ccl_device int bsdf_reflection_sample(ccl_private const ShaderClosure *sc,
if (cosNO > 0) {
*omega_in = (2 * cosNO) * N - I;
if (dot(Ng, *omega_in) > 0) {
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = 2 * dot(N, dIdx) * N - dIdx;
*domega_in_dy = 2 * dot(N, dIdy) * N - dIdy;
#endif
/* Some high number for MIS. */
*pdf = 1e6f;
*eval = make_spectrum(1e6f);

View File

@ -39,14 +39,10 @@ ccl_device Spectrum bsdf_refraction_eval_transmit(ccl_private const ShaderClosur
ccl_device int bsdf_refraction_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
@ -54,35 +50,15 @@ ccl_device int bsdf_refraction_sample(ccl_private const ShaderClosure *sc,
float3 N = bsdf->N;
float3 R, T;
#ifdef __RAY_DIFFERENTIALS__
float3 dRdx, dRdy, dTdx, dTdy;
#endif
bool inside;
float fresnel;
fresnel = fresnel_dielectric(m_eta,
N,
I,
&R,
&T,
#ifdef __RAY_DIFFERENTIALS__
dIdx,
dIdy,
&dRdx,
&dRdy,
&dTdx,
&dTdy,
#endif
&inside);
fresnel = fresnel_dielectric(m_eta, N, I, &R, &T, &inside);
if (!inside && fresnel != 1.0f) {
/* Some high number for MIS. */
*pdf = 1e6f;
*eval = make_spectrum(1e6f);
*omega_in = T;
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = dTdx;
*domega_in_dy = dTdy;
#endif
}
else {
*pdf = 0.0f;

View File

@ -83,14 +83,10 @@ ccl_device Spectrum bsdf_diffuse_toon_eval_transmit(ccl_private const ShaderClos
ccl_device int bsdf_diffuse_toon_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc;
@ -104,12 +100,6 @@ ccl_device int bsdf_diffuse_toon_sample(ccl_private const ShaderClosure *sc,
if (dot(Ng, *omega_in) > 0.0f) {
*eval = make_spectrum(*pdf * bsdf_toon_get_intensity(max_angle, smooth, angle));
#ifdef __RAY_DIFFERENTIALS__
// TODO: find a better approximation for the bounce
*domega_in_dx = (2.0f * dot(bsdf->N, dIdx)) * bsdf->N - dIdx;
*domega_in_dy = (2.0f * dot(bsdf->N, dIdy)) * bsdf->N - dIdy;
#endif
}
else {
*eval = zero_spectrum();
@ -175,14 +165,10 @@ ccl_device Spectrum bsdf_glossy_toon_eval_transmit(ccl_private const ShaderClosu
ccl_device int bsdf_glossy_toon_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc;
@ -205,11 +191,6 @@ ccl_device int bsdf_glossy_toon_sample(ccl_private const ShaderClosure *sc,
/* make sure the direction we chose is still in the right hemisphere */
if (cosNI > 0) {
*eval = make_spectrum(*pdf * bsdf_toon_get_intensity(max_angle, smooth, angle));
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = (2 * dot(bsdf->N, dIdx)) * bsdf->N - dIdx;
*domega_in_dy = (2 * dot(bsdf->N, dIdy)) * bsdf->N - dIdy;
#endif
}
else {
*pdf = 0.0f;

View File

@ -80,22 +80,14 @@ ccl_device Spectrum bsdf_transparent_eval_transmit(ccl_private const ShaderClosu
ccl_device int bsdf_transparent_sample(ccl_private const ShaderClosure *sc,
float3 Ng,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
// only one direction is possible
*omega_in = -I;
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = -dIdx;
*domega_in_dy = -dIdy;
#endif
*pdf = 1;
*eval = one_spectrum();
return LABEL_TRANSMIT | LABEL_TRANSPARENT;

View File

@ -15,14 +15,6 @@ ccl_device float fresnel_dielectric(float eta,
const float3 I,
ccl_private float3 *R,
ccl_private float3 *T,
#ifdef __RAY_DIFFERENTIALS__
const float3 dIdx,
const float3 dIdy,
ccl_private float3 *dRdx,
ccl_private float3 *dRdy,
ccl_private float3 *dTdx,
ccl_private float3 *dTdy,
#endif
ccl_private bool *is_inside)
{
float cos = dot(N, I), neta;
@ -45,28 +37,16 @@ ccl_device float fresnel_dielectric(float eta,
// compute reflection
*R = (2 * cos) * Nn - I;
#ifdef __RAY_DIFFERENTIALS__
*dRdx = (2 * dot(Nn, dIdx)) * Nn - dIdx;
*dRdy = (2 * dot(Nn, dIdy)) * Nn - dIdy;
#endif
float arg = 1 - (neta * neta * (1 - (cos * cos)));
if (arg < 0) {
*T = make_float3(0.0f, 0.0f, 0.0f);
#ifdef __RAY_DIFFERENTIALS__
*dTdx = make_float3(0.0f, 0.0f, 0.0f);
*dTdy = make_float3(0.0f, 0.0f, 0.0f);
#endif
return 1; // total internal reflection
}
else {
float dnp = max(sqrtf(arg), 1e-7f);
float nK = (neta * cos) - dnp;
*T = -(neta * I) + (nK * Nn);
#ifdef __RAY_DIFFERENTIALS__
*dTdx = -(neta * dIdx) + ((neta - neta * neta * cos / dnp) * dot(dIdx, Nn)) * Nn;
*dTdy = -(neta * dIdy) + ((neta - neta * neta * cos / dnp) * dot(dIdy, Nn)) * Nn;
#endif
// compute Fresnel terms
float cosTheta1 = cos; // N.R
float cosTheta2 = -dot(Nn, *T);

View File

@ -101,14 +101,10 @@ henyey_greenstrein_sample(float3 D, float g, float randu, float randv, ccl_priva
ccl_device int volume_henyey_greenstein_sample(ccl_private const ShaderVolumeClosure *svc,
float3 I,
float3 dIdx,
float3 dIdy,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private float3 *domega_in_dx,
ccl_private float3 *domega_in_dy,
ccl_private float *pdf)
{
float g = svc->g;
@ -117,12 +113,6 @@ ccl_device int volume_henyey_greenstein_sample(ccl_private const ShaderVolumeClo
*omega_in = henyey_greenstrein_sample(-I, g, randu, randv, pdf);
*eval = make_spectrum(*pdf); /* perfect importance sampling */
#ifdef __RAY_DIFFERENTIALS__
/* todo: implement ray differential estimation */
*domega_in_dx = make_float3(0.0f, 0.0f, 0.0f);
*domega_in_dy = make_float3(0.0f, 0.0f, 0.0f);
#endif
return LABEL_VOLUME_SCATTER;
}
@ -142,20 +132,9 @@ ccl_device int volume_phase_sample(ccl_private const ShaderData *sd,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *omega_in,
ccl_private differential3 *domega_in,
ccl_private float *pdf)
{
return volume_henyey_greenstein_sample(svc,
sd->I,
sd->dI.dx,
sd->dI.dy,
randu,
randv,
eval,
omega_in,
&domega_in->dx,
&domega_in->dy,
pdf);
return volume_henyey_greenstein_sample(svc, sd->I, randu, randv, eval, omega_in, pdf);
}
/* Volume sampling utilities. */

View File

@ -321,7 +321,7 @@ inline TReturnType metalrt_visibility_test(
constant KernelParamsMetal &launch_params_metal,
ray_data MetalKernelContext::MetalRTIntersectionPayload &payload,
const uint object,
const uint prim,
uint prim,
const float u)
{
TReturnType result;

View File

@ -123,9 +123,9 @@ ccl_device_inline void shader_setup_from_ray(KernelGlobals kg,
#ifdef __RAY_DIFFERENTIALS__
/* differentials */
differential_transfer_compact(&sd->dP, ray->dP, ray->D, ray->dD, sd->Ng, sd->ray_length);
differential_incoming_compact(&sd->dI, ray->D, ray->dD);
differential_dudv(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng);
sd->dP = differential_transfer_compact(ray->dP, ray->D, ray->dD, sd->ray_length);
sd->dI = differential_incoming_compact(ray->dD);
differential_dudv_compact(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng);
#endif
}
@ -240,8 +240,8 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg,
#ifdef __RAY_DIFFERENTIALS__
/* no ray differentials here yet */
sd->dP = differential3_zero();
sd->dI = differential3_zero();
sd->dP = differential_zero_compact();
sd->dI = differential_zero_compact();
sd->du = differential_zero();
sd->dv = differential_zero();
#endif
@ -348,8 +348,8 @@ ccl_device void shader_setup_from_curve(KernelGlobals kg,
/* No ray differentials currently. */
#ifdef __RAY_DIFFERENTIALS__
sd->dP = differential3_zero();
sd->dI = differential3_zero();
sd->dP = differential_zero_compact();
sd->dI = differential_zero_compact();
sd->du = differential_zero();
sd->dv = differential_zero();
#endif
@ -391,8 +391,8 @@ ccl_device_inline void shader_setup_from_background(KernelGlobals kg,
#ifdef __RAY_DIFFERENTIALS__
/* differentials */
sd->dP = differential3_zero(); /* TODO: ray->dP */
differential_incoming(&sd->dI, sd->dP);
sd->dP = differential_zero_compact(); /* TODO: ray->dP */
sd->dI = differential_zero_compact();
sd->du = differential_zero();
sd->dv = differential_zero();
#endif
@ -433,8 +433,8 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals kg,
# ifdef __RAY_DIFFERENTIALS__
/* differentials */
sd->dP = differential3_zero(); /* TODO ray->dD */
differential_incoming(&sd->dI, sd->dP);
sd->dP = differential_zero_compact(); /* TODO ray->dD */
sd->dI = differential_zero_compact();
sd->du = differential_zero();
sd->dv = differential_zero();
# endif

View File

@ -362,11 +362,10 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
float bsdf_pdf;
BsdfEval bsdf_eval ccl_optional_struct_init;
float3 bsdf_omega_in ccl_optional_struct_init;
differential3 bsdf_domega_in ccl_optional_struct_init;
int label;
label = shader_bsdf_sample_closure(
kg, sd, sc, bsdf_u, bsdf_v, &bsdf_eval, &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
kg, sd, sc, bsdf_u, bsdf_v, &bsdf_eval, &bsdf_omega_in, &bsdf_pdf);
if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) {
return LABEL_NONE;
@ -385,7 +384,6 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX;
#ifdef __RAY_DIFFERENTIALS__
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(bsdf_domega_in);
#endif
}

View File

@ -871,17 +871,9 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
float phase_pdf;
BsdfEval phase_eval ccl_optional_struct_init;
float3 phase_omega_in ccl_optional_struct_init;
differential3 phase_domega_in ccl_optional_struct_init;
const int label = shader_volume_phase_sample(kg,
sd,
phases,
phase_u,
phase_v,
&phase_eval,
&phase_omega_in,
&phase_domega_in,
&phase_pdf);
const int label = shader_volume_phase_sample(
kg, sd, phases, phase_u, phase_v, &phase_eval, &phase_omega_in, &phase_pdf);
if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) {
return false;
@ -894,7 +886,6 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX;
# ifdef __RAY_DIFFERENTIALS__
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(phase_domega_in);
# endif
// Save memory by storing last hit prim and object in isect
INTEGRATOR_STATE_WRITE(state, isect, prim) = sd->prim;

View File

@ -339,7 +339,6 @@ ccl_device int shader_bsdf_sample_closure(KernelGlobals kg,
float randv,
ccl_private BsdfEval *bsdf_eval,
ccl_private float3 *omega_in,
ccl_private differential3 *domega_in,
ccl_private float *pdf)
{
/* BSSRDF should already have been handled elsewhere. */
@ -349,7 +348,7 @@ ccl_device int shader_bsdf_sample_closure(KernelGlobals kg,
Spectrum eval = zero_spectrum();
*pdf = 0.0f;
label = bsdf_sample(kg, sd, sc, randu, randv, &eval, omega_in, domega_in, pdf);
label = bsdf_sample(kg, sd, sc, randu, randv, &eval, omega_in, pdf);
if (*pdf != 0.0f) {
bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight);
@ -708,7 +707,6 @@ ccl_device int shader_volume_phase_sample(KernelGlobals kg,
float randv,
ccl_private BsdfEval *phase_eval,
ccl_private float3 *omega_in,
ccl_private differential3 *domega_in,
ccl_private float *pdf)
{
int sampled = 0;
@ -751,7 +749,7 @@ ccl_device int shader_volume_phase_sample(KernelGlobals kg,
Spectrum eval = zero_spectrum();
*pdf = 0.0f;
label = volume_phase_sample(sd, svc, randu, randv, &eval, omega_in, domega_in, pdf);
label = volume_phase_sample(sd, svc, randu, randv, &eval, omega_in, pdf);
if (*pdf != 0.0f) {
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
@ -767,14 +765,13 @@ ccl_device int shader_phase_sample_closure(KernelGlobals kg,
float randv,
ccl_private BsdfEval *phase_eval,
ccl_private float3 *omega_in,
ccl_private differential3 *domega_in,
ccl_private float *pdf)
{
int label;
Spectrum eval = zero_spectrum();
*pdf = 0.0f;
label = volume_phase_sample(sd, sc, randu, randv, &eval, omega_in, domega_in, pdf);
label = volume_phase_sample(sd, sc, randu, randv, &eval, omega_in, pdf);
if (*pdf != 0.0f)
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);

View File

@ -1102,8 +1102,9 @@ bool OSLRenderServices::get_background_attribute(const KernelGlobalsCPU *kg,
ndc[0] = camera_world_to_ndc(kg, sd, sd->P);
if (derivatives) {
ndc[1] = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dx) - ndc[0];
ndc[2] = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dy) - ndc[0];
const differential3 dP = differential_from_compact(sd->Ng, sd->dP);
ndc[1] = camera_world_to_ndc(kg, sd, sd->P + dP.dx) - ndc[0];
ndc[2] = camera_world_to_ndc(kg, sd, sd->P + dP.dy) - ndc[0];
}
}
@ -1755,11 +1756,13 @@ bool OSLRenderServices::getmessage(OSL::ShaderGlobals *sg,
return set_attribute_float3(sd->Ng, type, derivatives, val);
}
else if (name == u_P) {
float3 f[3] = {sd->P, sd->dP.dx, sd->dP.dy};
const differential3 dP = differential_from_compact(sd->Ng, sd->dP);
float3 f[3] = {sd->P, dP.dx, dP.dy};
return set_attribute_float3(f, type, derivatives, val);
}
else if (name == u_I) {
float3 f[3] = {sd->I, sd->dI.dx, sd->dI.dy};
const differential3 dI = differential_from_compact(sd->I, sd->dI);
float3 f[3] = {sd->I, dI.dx, dI.dy};
return set_attribute_float3(f, type, derivatives, val);
}
else if (name == u_u) {

View File

@ -17,6 +17,8 @@
#include "kernel/osl/globals.h"
#include "kernel/osl/services.h"
#include "kernel/osl/shader.h"
#include "kernel/util/differential.h"
// clang-format on
#include "scene/attribute.h"
@ -79,13 +81,16 @@ static void shaderdata_to_shaderglobals(const KernelGlobalsCPU *kg,
{
OSL::ShaderGlobals *globals = &tdata->globals;
const differential3 dP = differential_from_compact(sd->Ng, sd->dP);
const differential3 dI = differential_from_compact(sd->I, sd->dI);
/* copy from shader data to shader globals */
globals->P = TO_VEC3(sd->P);
globals->dPdx = TO_VEC3(sd->dP.dx);
globals->dPdy = TO_VEC3(sd->dP.dy);
globals->dPdx = TO_VEC3(dP.dx);
globals->dPdy = TO_VEC3(dP.dy);
globals->I = TO_VEC3(sd->I);
globals->dIdx = TO_VEC3(sd->dI.dx);
globals->dIdy = TO_VEC3(sd->dI.dy);
globals->dIdx = TO_VEC3(dI.dx);
globals->dIdy = TO_VEC3(dI.dy);
globals->N = TO_VEC3(sd->N);
globals->Ng = TO_VEC3(sd->Ng);
globals->u = sd->u;
@ -183,9 +188,10 @@ void OSLShader::eval_surface(const KernelGlobalsCPU *kg,
/* automatic bump shader */
if (kg->osl->bump_state[shader]) {
/* save state */
float3 P = sd->P;
float3 dPdx = sd->dP.dx;
float3 dPdy = sd->dP.dy;
const float3 P = sd->P;
const float dP = sd->dP;
const OSL::Vec3 dPdx = globals->dPdx;
const OSL::Vec3 dPdy = globals->dPdy;
/* set state as if undisplaced */
if (sd->flag & SD_HAS_DISPLACEMENT) {
@ -199,17 +205,20 @@ void OSLShader::eval_surface(const KernelGlobalsCPU *kg,
(void)found;
assert(found);
differential3 tmp_dP;
memcpy(&sd->P, data, sizeof(float) * 3);
memcpy(&sd->dP.dx, data + 3, sizeof(float) * 3);
memcpy(&sd->dP.dy, data + 6, sizeof(float) * 3);
memcpy(&tmp_dP.dx, data + 3, sizeof(float) * 3);
memcpy(&tmp_dP.dy, data + 6, sizeof(float) * 3);
object_position_transform(kg, sd, &sd->P);
object_dir_transform(kg, sd, &sd->dP.dx);
object_dir_transform(kg, sd, &sd->dP.dy);
object_dir_transform(kg, sd, &tmp_dP.dx);
object_dir_transform(kg, sd, &tmp_dP.dy);
sd->dP = differential_make_compact(tmp_dP);
globals->P = TO_VEC3(sd->P);
globals->dPdx = TO_VEC3(sd->dP.dx);
globals->dPdy = TO_VEC3(sd->dP.dy);
globals->dPdx = TO_VEC3(tmp_dP.dx);
globals->dPdy = TO_VEC3(tmp_dP.dy);
}
/* execute bump shader */
@ -217,8 +226,7 @@ void OSLShader::eval_surface(const KernelGlobalsCPU *kg,
/* reset state */
sd->P = P;
sd->dP.dx = dPdx;
sd->dP.dy = dPdy;
sd->dP = dP;
globals->P = TO_VEC3(P);
globals->dPdx = TO_VEC3(dPdx);

View File

@ -140,6 +140,16 @@ ccl_device_noinline void svm_node_attr(KernelGlobals kg,
}
}
ccl_device_forceinline float3 svm_node_bump_P_dx(const ccl_private ShaderData *sd)
{
return sd->P + differential_from_compact(sd->Ng, sd->dP).dx;
}
ccl_device_forceinline float3 svm_node_bump_P_dy(const ccl_private ShaderData *sd)
{
return sd->P + differential_from_compact(sd->Ng, sd->dP).dy;
}
ccl_device_noinline void svm_node_attr_bump_dx(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *stack,
@ -167,7 +177,7 @@ ccl_device_noinline void svm_node_attr_bump_dx(KernelGlobals kg,
if (node.y == ATTR_STD_GENERATED && desc.element == ATTR_ELEMENT_NONE) {
/* No generated attribute, fall back to object coordinates. */
float3 f = sd->P + sd->dP.dx;
float3 f = svm_node_bump_P_dx(sd);
if (sd->object != OBJECT_NONE) {
object_inverse_position_transform(kg, sd, &f);
}
@ -265,7 +275,7 @@ ccl_device_noinline void svm_node_attr_bump_dy(KernelGlobals kg,
if (node.y == ATTR_STD_GENERATED && desc.element == ATTR_ELEMENT_NONE) {
/* No generated attribute, fall back to object coordinates. */
float3 f = sd->P + sd->dP.dy;
float3 f = svm_node_bump_P_dy(sd);
if (sd->object != OBJECT_NONE) {
object_inverse_position_transform(kg, sd, &f);
}

View File

@ -14,23 +14,21 @@ ccl_device_noinline void svm_node_enter_bump_eval(KernelGlobals kg,
{
/* save state */
stack_store_float3(stack, offset + 0, sd->P);
stack_store_float3(stack, offset + 3, sd->dP.dx);
stack_store_float3(stack, offset + 6, sd->dP.dy);
stack_store_float(stack, offset + 3, sd->dP);
/* set state as if undisplaced */
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POSITION_UNDISPLACED);
if (desc.offset != ATTR_STD_NOT_FOUND) {
float3 P, dPdx, dPdy;
P = primitive_surface_attribute_float3(kg, sd, desc, &dPdx, &dPdy);
differential3 dP;
float3 P = primitive_surface_attribute_float3(kg, sd, desc, &dP.dx, &dP.dy);
object_position_transform(kg, sd, &P);
object_dir_transform(kg, sd, &dPdx);
object_dir_transform(kg, sd, &dPdy);
object_dir_transform(kg, sd, &dP.dx);
object_dir_transform(kg, sd, &dP.dy);
sd->P = P;
sd->dP.dx = dPdx;
sd->dP.dy = dPdy;
sd->dP = differential_make_compact(dP);
}
}
@ -41,8 +39,7 @@ ccl_device_noinline void svm_node_leave_bump_eval(KernelGlobals kg,
{
/* restore state */
sd->P = stack_load_float3(stack, offset + 0);
sd->dP.dx = stack_load_float3(stack, offset + 3);
sd->dP.dy = stack_load_float3(stack, offset + 6);
sd->dP = stack_load_float(stack, offset + 3);
}
CCL_NAMESPACE_END

View File

@ -24,18 +24,17 @@ ccl_device_noinline void svm_node_set_bump(KernelGlobals kg,
float3 normal_in = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) :
sd->N;
float3 dPdx = sd->dP.dx;
float3 dPdy = sd->dP.dy;
differential3 dP = differential_from_compact(sd->Ng, sd->dP);
if (use_object_space) {
object_inverse_normal_transform(kg, sd, &normal_in);
object_inverse_dir_transform(kg, sd, &dPdx);
object_inverse_dir_transform(kg, sd, &dPdy);
object_inverse_dir_transform(kg, sd, &dP.dx);
object_inverse_dir_transform(kg, sd, &dP.dy);
}
/* get surface tangents from normal */
float3 Rx = cross(dPdy, normal_in);
float3 Ry = cross(normal_in, dPdx);
float3 Rx = cross(dP.dy, normal_in);
float3 Ry = cross(normal_in, dP.dx);
/* get bump values */
uint c_offset, x_offset, y_offset, strength_offset;
@ -46,7 +45,7 @@ ccl_device_noinline void svm_node_set_bump(KernelGlobals kg,
float h_y = stack_load_float(stack, y_offset);
/* compute surface gradient and determinant */
float det = dot(dPdx, Rx);
float det = dot(dP.dx, Rx);
float3 surfgrad = (h_x - h_c) * Rx + (h_y - h_c) * Ry;
float absdet = fabsf(det);

View File

@ -54,7 +54,7 @@ ccl_device_noinline void svm_node_geometry_bump_dx(KernelGlobals kg,
switch (type) {
case NODE_GEOM_P:
data = sd->P + sd->dP.dx;
data = svm_node_bump_P_dx(sd);
break;
case NODE_GEOM_uv:
data = make_float3(1.0f - sd->u - sd->du.dx - sd->v - sd->dv.dx, sd->u + sd->du.dx, 0.0f);
@ -81,7 +81,7 @@ ccl_device_noinline void svm_node_geometry_bump_dy(KernelGlobals kg,
switch (type) {
case NODE_GEOM_P:
data = sd->P + sd->dP.dy;
data = svm_node_bump_P_dy(sd);
break;
case NODE_GEOM_uv:
data = make_float3(1.0f - sd->u - sd->du.dy - sd->v - sd->dv.dy, sd->u + sd->du.dy, 0.0f);

View File

@ -106,7 +106,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dx(KernelGlobals kg,
switch (type) {
case NODE_TEXCO_OBJECT: {
data = sd->P + sd->dP.dx;
data = svm_node_bump_P_dx(sd);
if (node.w == 0) {
if (sd->object != OBJECT_NONE) {
object_inverse_position_transform(kg, sd, &data);
@ -130,9 +130,9 @@ ccl_device_noinline int svm_node_tex_coord_bump_dx(KernelGlobals kg,
Transform tfm = kernel_data.cam.worldtocamera;
if (sd->object != OBJECT_NONE)
data = transform_point(&tfm, sd->P + sd->dP.dx);
data = transform_point(&tfm, svm_node_bump_P_dx(sd));
else
data = transform_point(&tfm, sd->P + sd->dP.dx + camera_position(kg));
data = transform_point(&tfm, svm_node_bump_P_dx(sd) + camera_position(kg));
break;
}
case NODE_TEXCO_WINDOW: {
@ -140,7 +140,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dx(KernelGlobals kg,
kernel_data.cam.type == CAMERA_ORTHOGRAPHIC)
data = camera_world_to_ndc(kg, sd, sd->ray_P);
else
data = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dx);
data = camera_world_to_ndc(kg, sd, svm_node_bump_P_dx(sd));
data.z = 0.0f;
break;
}
@ -160,7 +160,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dx(KernelGlobals kg,
break;
}
case NODE_TEXCO_VOLUME_GENERATED: {
data = sd->P + sd->dP.dx;
data = svm_node_bump_P_dx(sd);
# ifdef __VOLUME__
if (sd->object != OBJECT_NONE)
@ -191,7 +191,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dy(KernelGlobals kg,
switch (type) {
case NODE_TEXCO_OBJECT: {
data = sd->P + sd->dP.dy;
data = svm_node_bump_P_dy(sd);
if (node.w == 0) {
if (sd->object != OBJECT_NONE) {
object_inverse_position_transform(kg, sd, &data);
@ -215,9 +215,9 @@ ccl_device_noinline int svm_node_tex_coord_bump_dy(KernelGlobals kg,
Transform tfm = kernel_data.cam.worldtocamera;
if (sd->object != OBJECT_NONE)
data = transform_point(&tfm, sd->P + sd->dP.dy);
data = transform_point(&tfm, svm_node_bump_P_dy(sd));
else
data = transform_point(&tfm, sd->P + sd->dP.dy + camera_position(kg));
data = transform_point(&tfm, svm_node_bump_P_dy(sd) + camera_position(kg));
break;
}
case NODE_TEXCO_WINDOW: {
@ -225,7 +225,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dy(KernelGlobals kg,
kernel_data.cam.type == CAMERA_ORTHOGRAPHIC)
data = camera_world_to_ndc(kg, sd, sd->ray_P);
else
data = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dy);
data = camera_world_to_ndc(kg, sd, svm_node_bump_P_dy(sd));
data.z = 0.0f;
break;
}
@ -245,7 +245,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dy(KernelGlobals kg,
break;
}
case NODE_TEXCO_VOLUME_GENERATED: {
data = sd->P + sd->dP.dy;
data = svm_node_bump_P_dy(sd);
# ifdef __VOLUME__
if (sd->object != OBJECT_NONE)

View File

@ -12,7 +12,7 @@ CCL_NAMESPACE_BEGIN
/* SVM stack offsets with this value indicate that it's not on the stack */
#define SVM_STACK_INVALID 255
#define SVM_BUMP_EVAL_STATE_SIZE 9
#define SVM_BUMP_EVAL_STATE_SIZE 4
/* Nodes */

View File

@ -14,6 +14,7 @@ CCL_NAMESPACE_BEGIN
ccl_device_inline float wireframe(KernelGlobals kg,
ccl_private ShaderData *sd,
const differential3 dP,
float size,
int pixel_size,
ccl_private float3 *P)
@ -46,8 +47,8 @@ ccl_device_inline float wireframe(KernelGlobals kg,
if (pixel_size) {
// Project the derivatives of P to the viewing plane defined
// by I so we have a measure of how big is a pixel at this point
float pixelwidth_x = len(sd->dP.dx - dot(sd->dP.dx, sd->I) * sd->I);
float pixelwidth_y = len(sd->dP.dy - dot(sd->dP.dy, sd->I) * sd->I);
float pixelwidth_x = len(dP.dx - dot(dP.dx, sd->I) * sd->I);
float pixelwidth_y = len(dP.dy - dot(dP.dy, sd->I) * sd->I);
// Take the average of both axis' length
pixelwidth = (pixelwidth_x + pixelwidth_y) * 0.5f;
}
@ -86,16 +87,17 @@ ccl_device_noinline void svm_node_wireframe(KernelGlobals kg,
int pixel_size = (int)use_pixel_size;
/* Calculate wireframe */
float f = wireframe(kg, sd, size, pixel_size, &sd->P);
const differential3 dP = differential_from_compact(sd->Ng, sd->dP);
float f = wireframe(kg, sd, dP, size, pixel_size, &sd->P);
/* TODO(sergey): Think of faster way to calculate derivatives. */
if (bump_offset == NODE_BUMP_OFFSET_DX) {
float3 Px = sd->P - sd->dP.dx;
f += (f - wireframe(kg, sd, size, pixel_size, &Px)) / len(sd->dP.dx);
float3 Px = sd->P - dP.dx;
f += (f - wireframe(kg, sd, dP, size, pixel_size, &Px)) / len(dP.dx);
}
else if (bump_offset == NODE_BUMP_OFFSET_DY) {
float3 Py = sd->P - sd->dP.dy;
f += (f - wireframe(kg, sd, size, pixel_size, &Py)) / len(sd->dP.dy);
float3 Py = sd->P - dP.dy;
f += (f - wireframe(kg, sd, dP, size, pixel_size, &Py)) / len(dP.dy);
}
if (stack_valid(out_fac))

View File

@ -873,10 +873,10 @@ typedef struct ccl_align(16) ShaderData
float ray_length;
#ifdef __RAY_DIFFERENTIALS__
/* differential of P. these are orthogonal to Ng, not N */
differential3 dP;
/* differential of I */
differential3 dI;
/* Radius of differential of P. */
float dP;
/* Radius of differential of I. */
float dI;
/* differential of u, v */
differential du;
differential dv;

View File

@ -101,53 +101,59 @@ ccl_device differential3 differential3_zero()
return d;
}
/* Compact ray differentials that are just a scale to reduce memory usage and
* access cost in GPU.
/* Compact ray differentials that are just a radius to reduce memory usage and access cost
* on GPUs, basically cone tracing.
*
* See above for more accurate reference implementations.
*
* TODO: also store the more compact version in ShaderData and recompute where
* needed? */
* See above for more accurate reference implementations of ray differentials. */
ccl_device_forceinline float differential_zero_compact()
{
return 0.0f;
}
ccl_device_forceinline float differential_make_compact(const differential3 D)
ccl_device_forceinline float differential_make_compact(const float dD)
{
return 0.5f * (len(D.dx) + len(D.dy));
return dD;
}
ccl_device_forceinline void differential_transfer_compact(ccl_private differential3 *surface_dP,
const float ray_dP,
const float3 /* ray_D */,
const float ray_dD,
const float3 surface_Ng,
const float ray_t)
ccl_device_forceinline float differential_make_compact(const differential3 dD)
{
/* ray differential transfer through homogeneous medium, to
* compute dPdx/dy at a shading point from the incoming ray */
float scale = ray_dP + ray_t * ray_dD;
float3 dx, dy;
make_orthonormals(surface_Ng, &dx, &dy);
surface_dP->dx = dx * scale;
surface_dP->dy = dy * scale;
return 0.5f * (len(dD.dx) + len(dD.dy));
}
ccl_device_forceinline void differential_incoming_compact(ccl_private differential3 *dI,
const float3 D,
const float dD)
ccl_device_forceinline float differential_incoming_compact(const float dD)
{
/* compute dIdx/dy at a shading point, we just need to negate the
* differential of the ray direction */
return dD;
}
ccl_device_forceinline float differential_transfer_compact(const float ray_dP,
const float3 /* ray_D */,
const float ray_dD,
const float ray_t)
{
return ray_dP + ray_t * ray_dD;
}
ccl_device_forceinline differential3 differential_from_compact(const float3 D, const float dD)
{
float3 dx, dy;
make_orthonormals(D, &dx, &dy);
dI->dx = dD * dx;
dI->dy = dD * dy;
differential3 d;
d.dx = dD * dx;
d.dy = dD * dy;
return d;
}
ccl_device void differential_dudv_compact(ccl_private differential *du,
ccl_private differential *dv,
float3 dPdu,
float3 dPdv,
float dP,
float3 Ng)
{
/* TODO: can we speed this up? */
differential_dudv(du, dv, dPdu, dPdv, differential_from_compact(Ng, dP), Ng);
}
CCL_NAMESPACE_END

View File

@ -772,10 +772,7 @@ float Camera::world_to_raster_size(float3 P)
#endif
/* TODO: would it help to use more accurate differentials here? */
differential3 dP;
differential_transfer_compact(&dP, ray.dP, ray.D, ray.dD, ray.D, dist);
return max(len(dP.dx), len(dP.dy));
return differential_transfer_compact(ray.dP, ray.D, ray.dD, dist);
}
return res;

View File

@ -13,7 +13,8 @@
# include <features.h>
# include <math.h>
# if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 31)
# if defined(__GLIBC_PREREQ)
# if __GLIBC_PREREQ(2, 31)
double __exp_finite(double x);
double __exp2_finite(double x);
@ -112,5 +113,6 @@ float __powf_finite(float x, float y)
return powf(x, y);
}
# endif /* __GLIBC_PREREQ */
#endif /* __linux__ */
# endif /* __GLIBC_PREREQ(2, 31) */
# endif /* __GLIBC_PREREQ */
#endif /* __linux__ */

View File

@ -18,7 +18,7 @@ class DataButtonsPanel:
class DATA_PT_context_light(DataButtonsPanel, Panel):
bl_label = ""
bl_options = {'HIDE_HEADER'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE_NEXT', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
def draw(self, context):
layout = self.layout
@ -36,7 +36,7 @@ class DATA_PT_context_light(DataButtonsPanel, Panel):
class DATA_PT_preview(DataButtonsPanel, Panel):
bl_label = "Preview"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE_NEXT', 'BLENDER_EEVEE'}
def draw(self, context):
self.layout.template_preview(context.light)
@ -62,7 +62,7 @@ class DATA_PT_light(DataButtonsPanel, Panel):
class DATA_PT_EEVEE_light(DataButtonsPanel, Panel):
bl_label = "Light"
COMPAT_ENGINES = {'BLENDER_EEVEE'}
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT', 'BLENDER_EEVEE'}
def draw(self, context):
layout = self.layout
@ -108,7 +108,7 @@ class DATA_PT_EEVEE_light_distance(DataButtonsPanel, Panel):
bl_label = "Custom Distance"
bl_parent_id = "DATA_PT_EEVEE_light"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_EEVEE'}
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT', 'BLENDER_EEVEE'}
@classmethod
def poll(cls, context):
@ -256,7 +256,7 @@ class DATA_PT_area(DataButtonsPanel, Panel):
class DATA_PT_spot(DataButtonsPanel, Panel):
bl_label = "Spot Shape"
bl_parent_id = "DATA_PT_EEVEE_light"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE_NEXT', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
@ -301,7 +301,7 @@ class DATA_PT_falloff_curve(DataButtonsPanel, Panel):
class DATA_PT_custom_props_light(DataButtonsPanel, PropertyPanel, Panel):
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE_NEXT', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
_context_path = "object.data"
_property_type = bpy.types.Light

View File

@ -330,11 +330,17 @@ class OUTLINER_MT_liboverride(Menu):
def draw(self, _context):
layout = self.layout
layout.operator_menu_enum("outliner.liboverride_operation", "selection_set", text="Create").type = 'OVERRIDE_LIBRARY_CREATE_HIERARCHY'
layout.operator_menu_enum("outliner.liboverride_operation", "selection_set", text="Reset").type = 'OVERRIDE_LIBRARY_RESET'
layout.operator_menu_enum("outliner.liboverride_operation", "selection_set", text="Clear").type = 'OVERRIDE_LIBRARY_CLEAR_SINGLE'
layout.operator_menu_enum("outliner.liboverride_operation", "selection_set",
text="Create").type = 'OVERRIDE_LIBRARY_CREATE_HIERARCHY'
layout.operator_menu_enum(
"outliner.liboverride_operation",
"selection_set",
text="Reset").type = 'OVERRIDE_LIBRARY_RESET'
layout.operator_menu_enum("outliner.liboverride_operation", "selection_set",
text="Clear").type = 'OVERRIDE_LIBRARY_CLEAR_SINGLE'
layout.operator_menu_enum("outliner.liboverride_troubleshoot_operation", "type", text="Troubleshoot Hierarchy").selection_set = 'SELECTED'
layout.operator_menu_enum("outliner.liboverride_troubleshoot_operation", "type",
text="Troubleshoot Hierarchy").selection_set = 'SELECTED'
class OUTLINER_PT_filter(Panel):

View File

@ -926,7 +926,7 @@ int BKE_image_find_nearest_tile_with_offset(const Image *image,
zero_v2(r_uv_offset);
int tile_number_best = -1;
if (image->source != IMA_SRC_TILED) {
if (!image || image->source != IMA_SRC_TILED) {
return tile_number_best;
}

View File

@ -128,7 +128,7 @@ typedef struct NlaEvalData {
int num_channels;
NlaEvalSnapshot base_snapshot;
/* Evaluation result shapshot. */
/* Evaluation result snapshot. */
NlaEvalSnapshot eval_snapshot;
} NlaEvalData;

View File

@ -915,107 +915,63 @@ float tri_to_quat(float q[4], const float a[3], const float b[3], const float c[
return len;
}
void sin_cos_from_fraction(int numerator, const int denominator, float *r_sin, float *r_cos)
void sin_cos_from_fraction(int numerator, int denominator, float *r_sin, float *r_cos)
{
/* By default, creating an circle from an integer: calling #sinf & #cosf on the fraction doesn't
* create symmetrical values (because of float imprecision).
/* By default, creating a circle from an integer: calling #sinf & #cosf on the fraction doesn't
* create symmetrical values (because floats can't represent Pi exactly).
* Resolve this when the rotation is calculated from a fraction by mapping the `numerator`
* to lower values so X/Y values for points around a circle are exactly symmetrical, see T87779.
*
* - Numbers divisible by 4 are mapped to the lower 8th (8 axis symmetry).
* - Even numbers are mapped to the lower quarter (4 axis symmetry).
* - Odd numbers are mapped to the lower half (1 axis symmetry).
* Multiply both the `numerator` and `denominator` by eight to ensure we can divide the circle
* into 8 octants. For each octant, we then use symmetry and negation to bring the `numerator`
* closer to the origin where precision is highest.
*
* Once the values are calculated, the are mapped back to their position in the circle
* using negation & swapping values. */
* Cases 2, 4, 5 and 7, use the trigonometric identity sin(-x) == -sin(x).
* Cases 1, 2, 5 and 6, swap the pointers `r_sin` and `r_cos`.
*/
BLI_assert(0 <= numerator);
BLI_assert(numerator <= denominator);
BLI_assert(denominator > 0);
BLI_assert((numerator <= denominator) && (denominator > 0));
enum { NEGATE_SIN_BIT = 0, NEGATE_COS_BIT = 1, SWAP_SIN_COS_BIT = 2 };
enum {
NEGATE_SIN = (1 << NEGATE_SIN_BIT),
NEGATE_COS = (1 << NEGATE_COS_BIT),
SWAP_SIN_COS = (1 << SWAP_SIN_COS_BIT),
} xform = 0;
if ((denominator & 3) == 0) {
/* The denominator divides by 4, determine the quadrant then further refine the upper 8th. */
const int denominator_4 = denominator / 4;
if (numerator < denominator_4) {
/* Fall through. */
}
else {
if (numerator < denominator_4 * 2) {
numerator -= denominator_4;
xform = NEGATE_SIN | SWAP_SIN_COS;
}
else if (numerator == denominator_4 * 2) {
numerator = 0;
xform = NEGATE_COS;
}
else if (numerator < denominator_4 * 3) {
numerator -= denominator_4 * 2;
xform = NEGATE_SIN | NEGATE_COS;
}
else if (numerator == denominator_4 * 3) {
numerator = 0;
xform = NEGATE_COS | SWAP_SIN_COS;
}
else {
numerator -= denominator_4 * 3;
xform = NEGATE_COS | SWAP_SIN_COS;
}
}
/* Further increase accuracy by using the range of the upper 8th. */
const int numerator_test = denominator_4 - numerator;
if (numerator_test < numerator) {
numerator = numerator_test;
xform ^= SWAP_SIN_COS;
/* Swap #NEGATE_SIN, #NEGATE_COS flags. */
xform = (xform & (uint)(~(NEGATE_SIN | NEGATE_COS))) |
(((xform & NEGATE_SIN) >> NEGATE_SIN_BIT) << NEGATE_COS_BIT) |
(((xform & NEGATE_COS) >> NEGATE_COS_BIT) << NEGATE_SIN_BIT);
}
}
else if ((denominator & 1) == 0) {
/* The denominator divides by 2, determine the quadrant then further refine the upper 4th. */
const int denominator_2 = denominator / 2;
if (numerator < denominator_2) {
/* Fall through. */
}
else if (numerator == denominator_2) {
numerator = 0;
xform = NEGATE_COS;
}
else {
numerator -= denominator_2;
xform = NEGATE_SIN | NEGATE_COS;
}
/* Further increase accuracy by using the range of the upper 4th. */
const int numerator_test = denominator_2 - numerator;
if (numerator_test < numerator) {
numerator = numerator_test;
xform ^= NEGATE_COS;
}
}
else {
/* The denominator is an odd number, only refine the upper half. */
const int numerator_test = denominator - numerator;
if (numerator_test < numerator) {
numerator = numerator_test;
xform ^= NEGATE_SIN;
}
numerator *= 8; /* Multiply numerator the same as denominator. */
const int octant = numerator / denominator; /* Determine the octant. */
denominator *= 8; /* Ensure denominator is a multiple of eight. */
float cos_sign = 1.0f; /* Either 1.0f or -1.0f. */
switch (octant) {
case 0:
/* Primary octant, nothing to do. */
break;
case 1:
case 2:
numerator = (denominator / 4) - numerator;
SWAP(float *, r_sin, r_cos);
break;
case 3:
case 4:
numerator = (denominator / 2) - numerator;
cos_sign = -1.0f;
break;
case 5:
case 6:
numerator = numerator - (denominator * 3 / 4);
SWAP(float *, r_sin, r_cos);
cos_sign = -1.0f;
break;
case 7:
numerator = numerator - denominator;
break;
default:
BLI_assert_unreachable();
}
const float phi = (float)(2.0 * M_PI) * ((float)numerator / (float)denominator);
const float sin_phi = sinf(phi) * ((xform & NEGATE_SIN) ? -1.0f : 1.0f);
const float cos_phi = cosf(phi) * ((xform & NEGATE_COS) ? -1.0f : 1.0f);
if ((xform & SWAP_SIN_COS) == 0) {
*r_sin = sin_phi;
*r_cos = cos_phi;
}
else {
*r_sin = cos_phi;
*r_cos = sin_phi;
}
BLI_assert(-denominator / 4 <= numerator); /* Numerator may be negative. */
BLI_assert(numerator <= denominator / 4);
BLI_assert(cos_sign == -1.0f || cos_sign == 1.0f);
const float angle = (float)(2.0 * M_PI) * ((float)numerator / (float)denominator);
*r_sin = sinf(angle);
*r_cos = cosf(angle) * cos_sign;
}
void print_qt(const char *str, const float q[4])

View File

@ -69,6 +69,10 @@ void deg_evaluate_object_modifiers_mode_node_visibility(::Depsgraph *depsgraph,
OperationNode *modifier_node = geometry_component->find_operation(OperationCode::MODIFIER,
modifier->name);
BLI_assert_msg(modifier_node != nullptr,
"Modifier node in depsgraph is not found. Likely due to missing "
"DEG_relations_tag_update().");
const bool modifier_enabled = modifier->mode & modifier_mode;
const int mute_flag = modifier_enabled ? 0 : DEPSOP_FLAG_MUTE;
if ((modifier_node->flag & DEPSOP_FLAG_MUTE) != mute_flag) {

View File

@ -140,6 +140,7 @@ set(SRC
engines/eevee_next/eevee_engine.cc
engines/eevee_next/eevee_film.cc
engines/eevee_next/eevee_instance.cc
engines/eevee_next/eevee_light.cc
engines/eevee_next/eevee_material.cc
engines/eevee_next/eevee_motion_blur.cc
engines/eevee_next/eevee_pipeline.cc
@ -391,6 +392,15 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl
engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl
engines/eevee_next/shaders/eevee_geom_world_vert.glsl
engines/eevee_next/shaders/eevee_light_culling_debug_frag.glsl
engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl
engines/eevee_next/shaders/eevee_light_culling_sort_comp.glsl
engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl
engines/eevee_next/shaders/eevee_light_culling_zbin_comp.glsl
engines/eevee_next/shaders/eevee_light_eval_lib.glsl
engines/eevee_next/shaders/eevee_light_iter_lib.glsl
engines/eevee_next/shaders/eevee_light_lib.glsl
engines/eevee_next/shaders/eevee_ltc_lib.glsl
engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl
engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl
engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl
@ -437,10 +447,12 @@ set(GLSL_SRC
engines/workbench/workbench_shader_shared.h
intern/shaders/common_aabb_lib.glsl
intern/shaders/common_attribute_lib.glsl
intern/shaders/common_colormanagement_lib.glsl
intern/shaders/common_debug_draw_lib.glsl
intern/shaders/common_debug_print_lib.glsl
intern/shaders/common_debug_shape_lib.glsl
intern/shaders/common_fullscreen_vert.glsl
intern/shaders/common_fxaa_lib.glsl
intern/shaders/common_globals_lib.glsl
@ -448,9 +460,11 @@ set(GLSL_SRC
intern/shaders/common_hair_lib.glsl
intern/shaders/common_hair_refine_comp.glsl
intern/shaders/common_hair_refine_vert.glsl
intern/shaders/common_intersect_lib.glsl
intern/shaders/common_math_geom_lib.glsl
intern/shaders/common_math_lib.glsl
intern/shaders/common_pointcloud_lib.glsl
intern/shaders/common_shape_lib.glsl
intern/shaders/common_smaa_lib.glsl
intern/shaders/common_subdiv_custom_data_interp_comp.glsl
intern/shaders/common_subdiv_ibo_lines_comp.glsl

View File

@ -124,7 +124,7 @@ void dof_slight_focus_gather(float radius, out vec4 out_color, out float out_wei
dof_gather_accumulate_resolve(total_sample_count, bg_accum, bg_col, bg_weight, unused_occlusion);
dof_gather_accumulate_resolve(total_sample_count, fg_accum, fg_col, fg_weight, unused_occlusion);
/* Fix weighting issues on perfectly focus > slight focus transitionning areas. */
/* Fix weighting issues on perfectly focus > slight focus transitioning areas. */
if (abs(center_data.coc) < 0.5) {
bg_col = center_data.color;
bg_weight = 1.0;

View File

@ -11,12 +11,13 @@
#pragma once
/**
* Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536.
* Current limiting factor is the sorting phase which is single pass and only sort within a
* thread-group which maximum size is 1024.
*/
#define CULLING_BATCH_SIZE 1024
/* Avoid too much overhead caused by resizing the light buffers too many time. */
#define LIGHT_CHUNK 256
#define CULLING_SELECT_GROUP_SIZE 256
#define CULLING_SORT_GROUP_SIZE 256
#define CULLING_ZBIN_GROUP_SIZE 1024
#define CULLING_TILE_GROUP_SIZE 1024
/**
* IMPORTANT: Some data packing are tweaked for these values.

View File

@ -53,6 +53,10 @@ void Instance::init(const int2 &output_res,
v3d = v3d_;
rv3d = rv3d_;
if (assign_if_different(debug_mode, (eDebugMode)G.debug_value)) {
sampling.reset();
}
info = "";
update_eval_members();
@ -96,6 +100,7 @@ void Instance::begin_sync()
{
materials.begin_sync();
velocity.begin_sync(); /* NOTE: Also syncs camera. */
lights.begin_sync();
gpencil_engine_enabled = false;
@ -109,7 +114,7 @@ void Instance::begin_sync()
void Instance::object_sync(Object *ob)
{
const bool is_renderable_type = ELEM(ob->type, OB_CURVES, OB_GPENCIL, OB_MESH);
const bool is_renderable_type = ELEM(ob->type, OB_CURVES, OB_GPENCIL, OB_MESH, OB_LAMP);
const int ob_visibility = DRW_object_visibility_in_active_context(ob);
const bool partsys_is_visible = (ob_visibility & OB_VISIBLE_PARTICLES) != 0 &&
(ob->type == OB_MESH);
@ -133,6 +138,7 @@ void Instance::object_sync(Object *ob)
if (object_is_visible) {
switch (ob->type) {
case OB_LAMP:
lights.sync_light(ob, ob_handle);
break;
case OB_MESH:
case OB_CURVES_LEGACY:
@ -172,6 +178,7 @@ void Instance::object_sync_render(void *instance_,
void Instance::end_sync()
{
velocity.end_sync();
lights.end_sync();
sampling.end_sync();
film.end_sync();
}

View File

@ -18,6 +18,7 @@
#include "eevee_camera.hh"
#include "eevee_depth_of_field.hh"
#include "eevee_film.hh"
#include "eevee_light.hh"
#include "eevee_material.hh"
#include "eevee_motion_blur.hh"
#include "eevee_pipeline.hh"
@ -43,6 +44,7 @@ class Instance {
SyncModule sync;
MaterialModule materials;
PipelineModule pipelines;
LightModule lights;
VelocityModule velocity;
MotionBlurModule motion_blur;
DepthOfField depth_of_field;
@ -71,8 +73,10 @@ class Instance {
/** True if the grease pencil engine might be running. */
bool gpencil_engine_enabled;
/* Info string displayed at the top of the render / viewport. */
/** Info string displayed at the top of the render / viewport. */
std::string info = "";
/** Debug mode from debug value. */
eDebugMode debug_mode = eDebugMode::DEBUG_NONE;
public:
Instance()
@ -80,6 +84,7 @@ class Instance {
sync(*this),
materials(*this),
pipelines(*this),
lights(*this),
velocity(*this),
motion_blur(*this),
depth_of_field(*this),

View File

@ -0,0 +1,499 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2021 Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* The light module manages light data buffers and light culling system.
*/
#include "draw_debug.hh"
#include "eevee_instance.hh"
#include "eevee_light.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name LightData
* \{ */
static eLightType to_light_type(short blender_light_type, short blender_area_type)
{
switch (blender_light_type) {
default:
case LA_LOCAL:
return LIGHT_POINT;
case LA_SUN:
return LIGHT_SUN;
case LA_SPOT:
return LIGHT_SPOT;
case LA_AREA:
return ELEM(blender_area_type, LA_AREA_DISK, LA_AREA_ELLIPSE) ? LIGHT_ELLIPSE : LIGHT_RECT;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Light Object
* \{ */
void Light::sync(/* ShadowModule &shadows , */ const Object *ob, float threshold)
{
const ::Light *la = (const ::Light *)ob->data;
float scale[3];
float max_power = max_fff(la->r, la->g, la->b) * fabsf(la->energy / 100.0f);
float surface_max_power = max_ff(la->diff_fac, la->spec_fac) * max_power;
float volume_max_power = la->volume_fac * max_power;
float influence_radius_surface = attenuation_radius_get(la, threshold, surface_max_power);
float influence_radius_volume = attenuation_radius_get(la, threshold, volume_max_power);
this->influence_radius_max = max_ff(influence_radius_surface, influence_radius_volume);
this->influence_radius_invsqr_surface = 1.0f / square_f(max_ff(influence_radius_surface, 1e-8f));
this->influence_radius_invsqr_volume = 1.0f / square_f(max_ff(influence_radius_volume, 1e-8f));
this->color = float3(&la->r) * la->energy;
normalize_m4_m4_ex(this->object_mat.ptr(), ob->obmat, scale);
/* Make sure we have consistent handedness (in case of negatively scaled Z axis). */
float3 cross = math::cross(float3(this->_right), float3(this->_up));
if (math::dot(cross, float3(this->_back)) < 0.0f) {
negate_v3(this->_up);
}
shape_parameters_set(la, scale);
float shape_power = shape_power_get(la);
float point_power = point_power_get(la);
this->diffuse_power = la->diff_fac * shape_power;
this->transmit_power = la->diff_fac * point_power;
this->specular_power = la->spec_fac * shape_power;
this->volume_power = la->volume_fac * point_power;
eLightType new_type = to_light_type(la->type, la->area_shape);
if (this->type != new_type) {
/* shadow_discard_safe(shadows); */
this->type = new_type;
}
#if 0
if (la->mode & LA_SHADOW) {
if (la->type == LA_SUN) {
if (this->shadow_id == LIGHT_NO_SHADOW) {
this->shadow_id = shadows.directionals.alloc();
}
ShadowDirectional &shadow = shadows.directionals[this->shadow_id];
shadow.sync(this->object_mat, la->bias * 0.05f, 1.0f);
}
else {
float cone_aperture = DEG2RAD(360.0);
if (la->type == LA_SPOT) {
cone_aperture = min_ff(DEG2RAD(179.9), la->spotsize);
}
else if (la->type == LA_AREA) {
cone_aperture = DEG2RAD(179.9);
}
if (this->shadow_id == LIGHT_NO_SHADOW) {
this->shadow_id = shadows.punctuals.alloc();
}
ShadowPunctual &shadow = shadows.punctuals[this->shadow_id];
shadow.sync(this->type,
this->object_mat,
cone_aperture,
la->clipsta,
this->influence_radius_max,
la->bias * 0.05f);
}
}
else {
shadow_discard_safe(shadows);
}
#endif
this->initialized = true;
}
#if 0
void Light::shadow_discard_safe(ShadowModule &shadows)
{
if (shadow_id != LIGHT_NO_SHADOW) {
if (this->type != LIGHT_SUN) {
shadows.punctuals.free(shadow_id);
}
else {
shadows.directionals.free(shadow_id);
}
shadow_id = LIGHT_NO_SHADOW;
}
}
#endif
/* Returns attenuation radius inverted & squared for easy bound checking inside the shader. */
float Light::attenuation_radius_get(const ::Light *la, float light_threshold, float light_power)
{
if (la->type == LA_SUN) {
return (light_power > 1e-5f) ? 1e16f : 0.0f;
}
if (la->mode & LA_CUSTOM_ATTENUATION) {
return la->att_dist;
}
/* Compute the distance (using the inverse square law)
* at which the light power reaches the light_threshold. */
/* TODO take area light scale into account. */
return sqrtf(light_power / light_threshold);
}
void Light::shape_parameters_set(const ::Light *la, const float scale[3])
{
if (la->type == LA_AREA) {
float area_size_y = (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE)) ? la->area_sizey :
la->area_size;
_area_size_x = max_ff(0.003f, la->area_size * scale[0] * 0.5f);
_area_size_y = max_ff(0.003f, area_size_y * scale[1] * 0.5f);
/* For volume point lighting. */
radius_squared = max_ff(0.001f, hypotf(_area_size_x, _area_size_y) * 0.5f);
radius_squared = square_f(radius_squared);
}
else {
if (la->type == LA_SPOT) {
/* Spot size & blend */
spot_size_inv[0] = scale[2] / scale[0];
spot_size_inv[1] = scale[2] / scale[1];
float spot_size = cosf(la->spotsize * 0.5f);
float spot_blend = (1.0f - spot_size) * la->spotblend;
_spot_mul = 1.0f / max_ff(1e-8f, spot_blend);
_spot_bias = -spot_size * _spot_mul;
spot_tan = tanf(min_ff(la->spotsize * 0.5f, M_PI_2 - 0.0001f));
}
if (la->type == LA_SUN) {
_area_size_x = tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f);
}
else {
_area_size_x = la->area_size;
}
_area_size_y = _area_size_x = max_ff(0.001f, _area_size_x);
radius_squared = square_f(_area_size_x);
}
}
float Light::shape_power_get(const ::Light *la)
{
/* Make illumination power constant */
switch (la->type) {
case LA_AREA: {
float area = _area_size_x * _area_size_y;
float power = 1.0f / (area * 4.0f * float(M_PI));
/* FIXME : Empirical, Fit cycles power */
power *= 0.8f;
if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
/* Scale power to account for the lower area of the ellipse compared to the surrounding
* rectangle. */
power *= 4.0f / M_PI;
}
return power;
}
case LA_SPOT:
case LA_LOCAL: {
return 1.0f / (4.0f * square_f(_radius) * float(M_PI * M_PI));
}
default:
case LA_SUN: {
float power = 1.0f / (square_f(_radius) * float(M_PI));
/* Make illumination power closer to cycles for bigger radii. Cycles uses a cos^3 term that
* we cannot reproduce so we account for that by scaling the light power. This function is
* the result of a rough manual fitting. */
/* Simplification of: power *= 1 + r²/2 */
power += 1.0f / (2.0f * M_PI);
return power;
}
}
}
float Light::point_power_get(const ::Light *la)
{
/* Volume light is evaluated as point lights. Remove the shape power. */
switch (la->type) {
case LA_AREA: {
/* Match cycles. Empirical fit... must correspond to some constant. */
float power = 0.0792f * M_PI;
/* This corrects for area light most representative point trick. The fit was found by
* reducing the average error compared to cycles. */
float area = _area_size_x * _area_size_y;
float tmp = M_PI_2 / (M_PI_2 + sqrtf(area));
/* Lerp between 1.0 and the limit (1 / pi). */
power *= tmp + (1.0f - tmp) * M_1_PI;
return power;
}
case LA_SPOT:
case LA_LOCAL: {
/* Match cycles. Empirical fit... must correspond to some constant. */
return 0.0792f;
}
default:
case LA_SUN: {
return 1.0f;
}
}
}
void Light::debug_draw()
{
#ifdef DEBUG
drw_debug_sphere(_position, influence_radius_max, float4(0.8f, 0.3f, 0.0f, 1.0f));
#endif
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name LightModule
* \{ */
void LightModule::begin_sync()
{
use_scene_lights_ = inst_.use_scene_lights();
/* In begin_sync so it can be animated. */
if (assign_if_different(light_threshold_, max_ff(1e-16f, inst_.scene->eevee.light_threshold))) {
inst_.sampling.reset();
}
sun_lights_len_ = 0;
local_lights_len_ = 0;
}
void LightModule::sync_light(const Object *ob, ObjectHandle &handle)
{
if (use_scene_lights_ == false) {
return;
}
Light &light = light_map_.lookup_or_add_default(handle.object_key);
light.used = true;
if (handle.recalc != 0 || !light.initialized) {
light.sync(/* inst_.shadows, */ ob, light_threshold_);
}
sun_lights_len_ += int(light.type == LIGHT_SUN);
local_lights_len_ += int(light.type != LIGHT_SUN);
}
void LightModule::end_sync()
{
// ShadowModule &shadows = inst_.shadows;
/* NOTE: We resize this buffer before removing deleted lights. */
int lights_allocated = ceil_to_multiple_u(max_ii(light_map_.size(), 1), LIGHT_CHUNK);
light_buf_.resize(lights_allocated);
/* Track light deletion. */
Vector<ObjectKey, 0> deleted_keys;
/* Indices inside GPU data array. */
int sun_lights_idx = 0;
int local_lights_idx = sun_lights_len_;
/* Fill GPU data with scene data. */
for (auto item : light_map_.items()) {
Light &light = item.value;
if (!light.used) {
/* Deleted light. */
deleted_keys.append(item.key);
// light.shadow_discard_safe(shadows);
continue;
}
int dst_idx = (light.type == LIGHT_SUN) ? sun_lights_idx++ : local_lights_idx++;
/* Put all light data into global data SSBO. */
light_buf_[dst_idx] = light;
#if 0
if (light.shadow_id != LIGHT_NO_SHADOW) {
if (light.type == LIGHT_SUN) {
light_buf_[dst_idx].shadow_data = shadows.directionals[light.shadow_id];
}
else {
light_buf_[dst_idx].shadow_data = shadows.punctuals[light.shadow_id];
}
}
#endif
/* Untag for next sync. */
light.used = false;
}
/* This scene data buffer is then immutable after this point. */
light_buf_.push_update();
for (auto key : deleted_keys) {
light_map_.remove(key);
}
/* Update sampling on deletion or un-hidding (use_scene_lights). */
if (assign_if_different(light_map_size_, light_map_.size())) {
inst_.sampling.reset();
}
/* If exceeding the limit, just trim off the excess to avoid glitchy rendering. */
if (sun_lights_len_ + local_lights_len_ > CULLING_MAX_ITEM) {
sun_lights_len_ = min_ii(sun_lights_len_, CULLING_MAX_ITEM);
local_lights_len_ = min_ii(local_lights_len_, CULLING_MAX_ITEM - sun_lights_len_);
inst_.info = "Error: Too many lights in the scene.";
}
lights_len_ = sun_lights_len_ + local_lights_len_;
/* Resize to the actual number of lights after pruning. */
lights_allocated = ceil_to_multiple_u(max_ii(lights_len_, 1), LIGHT_CHUNK);
culling_key_buf_.resize(lights_allocated);
culling_zdist_buf_.resize(lights_allocated);
culling_light_buf_.resize(lights_allocated);
{
/* Compute tile size and total word count. */
uint word_per_tile = divide_ceil_u(max_ii(lights_len_, 1), 32);
int2 render_extent = inst_.film.render_extent_get();
int2 tiles_extent;
/* Default to 32 as this is likely to be the maximum
* tile size used by hardware or compute shading. */
uint tile_size = 16;
do {
tile_size *= 2;
tiles_extent = math::divide_ceil(render_extent, int2(tile_size));
uint tile_count = tiles_extent.x * tiles_extent.y;
if (tile_count > max_tile_count_threshold) {
continue;
}
total_word_count_ = tile_count * word_per_tile;
} while (total_word_count_ > max_word_count_threshold);
/* Keep aligned with storage buffer requirements. */
total_word_count_ = ceil_to_multiple_u(total_word_count_, 32);
culling_data_buf_.tile_word_len = word_per_tile;
culling_data_buf_.tile_size = tile_size;
culling_data_buf_.tile_x_len = tiles_extent.x;
culling_data_buf_.tile_y_len = tiles_extent.y;
culling_data_buf_.items_count = lights_len_;
culling_data_buf_.local_lights_len = local_lights_len_;
culling_data_buf_.sun_lights_len = sun_lights_len_;
}
culling_tile_buf_.resize(total_word_count_);
culling_pass_sync();
debug_pass_sync();
}
void LightModule::culling_pass_sync()
{
uint safe_lights_len = max_ii(lights_len_, 1);
uint culling_select_dispatch_size = divide_ceil_u(safe_lights_len, CULLING_SELECT_GROUP_SIZE);
uint culling_sort_dispatch_size = divide_ceil_u(safe_lights_len, CULLING_SORT_GROUP_SIZE);
uint culling_tile_dispatch_size = divide_ceil_u(total_word_count_, CULLING_TILE_GROUP_SIZE);
/* NOTE: We reference the buffers that may be resized or updated later. */
{
DRW_PASS_CREATE(culling_select_ps_, DRW_STATE_NO_DRAW);
GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_SELECT);
DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_select_ps_);
DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_);
DRW_shgroup_storage_block(grp, "in_light_buf", light_buf_);
DRW_shgroup_storage_block(grp, "out_light_buf", culling_light_buf_);
DRW_shgroup_storage_block(grp, "out_zdist_buf", culling_zdist_buf_);
DRW_shgroup_storage_block(grp, "out_key_buf", culling_key_buf_);
DRW_shgroup_call_compute(grp, culling_select_dispatch_size, 1, 1);
DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE);
}
{
DRW_PASS_CREATE(culling_sort_ps_, DRW_STATE_NO_DRAW);
GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_SORT);
DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_sort_ps_);
DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_);
DRW_shgroup_storage_block(grp, "in_light_buf", light_buf_);
DRW_shgroup_storage_block(grp, "out_light_buf", culling_light_buf_);
DRW_shgroup_storage_block(grp, "in_zdist_buf", culling_zdist_buf_);
DRW_shgroup_storage_block(grp, "in_key_buf", culling_key_buf_);
DRW_shgroup_call_compute(grp, culling_sort_dispatch_size, 1, 1);
DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE);
}
{
DRW_PASS_CREATE(culling_zbin_ps_, DRW_STATE_NO_DRAW);
GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_ZBIN);
DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_zbin_ps_);
DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_);
DRW_shgroup_storage_block(grp, "light_buf", culling_light_buf_);
DRW_shgroup_storage_block(grp, "out_zbin_buf", culling_zbin_buf_);
DRW_shgroup_call_compute(grp, 1, 1, 1);
DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE);
}
{
DRW_PASS_CREATE(culling_tile_ps_, DRW_STATE_NO_DRAW);
GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_TILE);
DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_tile_ps_);
DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_);
DRW_shgroup_storage_block(grp, "light_buf", culling_light_buf_);
DRW_shgroup_storage_block(grp, "out_light_tile_buf", culling_tile_buf_);
DRW_shgroup_call_compute(grp, culling_tile_dispatch_size, 1, 1);
DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE);
}
}
void LightModule::debug_pass_sync()
{
if (inst_.debug_mode != eDebugMode::DEBUG_LIGHT_CULLING) {
debug_draw_ps_ = nullptr;
return;
}
debug_draw_ps_ = DRW_pass_create("LightCulling.Debug", DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_DEBUG);
DRWShadingGroup *grp = DRW_shgroup_create(sh, debug_draw_ps_);
DRW_shgroup_storage_block_ref(grp, "light_buf", &culling_light_buf_);
DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_);
DRW_shgroup_storage_block_ref(grp, "light_zbin_buf", &culling_zbin_buf_);
DRW_shgroup_storage_block_ref(grp, "light_tile_buf", &culling_tile_buf_);
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &inst_.render_buffers.depth_tx);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
void LightModule::set_view(const DRWView *view, const int2 extent)
{
float far_z = DRW_view_far_distance_get(view);
float near_z = DRW_view_near_distance_get(view);
culling_data_buf_.zbin_scale = -CULLING_ZBIN_COUNT / fabsf(far_z - near_z);
culling_data_buf_.zbin_bias = -near_z * culling_data_buf_.zbin_scale;
culling_data_buf_.tile_to_uv_fac = (culling_data_buf_.tile_size / float2(extent));
culling_data_buf_.visible_count = 0;
culling_data_buf_.push_update();
DRW_stats_group_start("Light Culling");
DRW_view_set_active(view);
DRW_draw_pass(culling_select_ps_);
DRW_draw_pass(culling_sort_ps_);
DRW_draw_pass(culling_zbin_ps_);
DRW_draw_pass(culling_tile_ps_);
DRW_stats_group_end();
}
void LightModule::debug_draw(GPUFrameBuffer *view_fb)
{
if (debug_draw_ps_ == nullptr) {
return;
}
GPU_framebuffer_bind(view_fb);
DRW_draw_pass(debug_draw_ps_);
}
/** \} */
} // namespace blender::eevee

View File

@ -0,0 +1,164 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2021 Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* The light module manages light data buffers and light culling system.
*
* The culling follows the principles of Tiled Culling + Z binning from:
* "Improved Culling for Tiled and Clustered Rendering"
* by Michal Drobot
* http://advances.realtimerendering.com/s2017/2017_Sig_Improved_Culling_final.pdf
*
* The culling is separated in 4 compute phases:
* - View Culling (select pass): Create a z distance and a index buffer of visible lights.
* - Light sorting: Outputs visible lights sorted by Z distance.
* - Z binning: Compute the Z bins min/max light indices.
* - Tile intersection: Fine grained 2D culling of each lights outputting a bitmap per tile.
*/
#pragma once
#include "BLI_bitmap.h"
#include "BLI_vector.hh"
#include "DNA_light_types.h"
#include "eevee_camera.hh"
#include "eevee_sampling.hh"
#include "eevee_shader.hh"
#include "eevee_shader_shared.hh"
#include "eevee_sync.hh"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name Light Object
* \{ */
struct Light : public LightData {
public:
bool initialized = false;
bool used = false;
public:
Light()
{
shadow_id = LIGHT_NO_SHADOW;
}
void sync(/* ShadowModule &shadows, */ const Object *ob, float threshold);
// void shadow_discard_safe(ShadowModule &shadows);
void debug_draw();
private:
float attenuation_radius_get(const ::Light *la, float light_threshold, float light_power);
void shape_parameters_set(const ::Light *la, const float scale[3]);
float shape_power_get(const ::Light *la);
float point_power_get(const ::Light *la);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name LightModule
* \{ */
/**
* The light module manages light data buffers and light culling system.
*/
class LightModule {
// friend ShadowModule;
private:
/* Keep tile count reasonable for memory usage and 2D culling performance. */
static constexpr uint max_memory_threshold = 32 * 1024 * 1024; /* 32 MiB */
static constexpr uint max_word_count_threshold = max_memory_threshold / sizeof(uint);
static constexpr uint max_tile_count_threshold = 8192;
Instance &inst_;
/** Map of light objects data. Converted to flat array each frame. */
Map<ObjectKey, Light> light_map_;
/** Flat array sent to GPU, populated from light_map_. Source buffer for light culling. */
LightDataBuf light_buf_ = {"Lights_no_cull"};
/** Recorded size of light_map_ (after pruning) to detect deletion. */
int64_t light_map_size_ = 0;
/** Luminous intensity to consider the light boundary at. Used for culling. */
float light_threshold_ = 0.01f;
/** If false, will prevent all scene light from being synced. */
bool use_scene_lights_ = false;
/** Number of sun lights synced during the last sync. Used as offset. */
int sun_lights_len_ = 0;
int local_lights_len_ = 0;
/** Sun plus local lights count for convenience. */
int lights_len_ = 0;
/**
* Light Culling
*/
/** LightData buffer used for rendering. Filled by the culling pass. */
LightDataBuf culling_light_buf_ = {"Lights_culled"};
/** Culling infos. */
LightCullingDataBuf culling_data_buf_ = {"LightCull_data"};
/** Z-distance matching the key for each visible lights. Used for sorting. */
LightCullingZdistBuf culling_zdist_buf_ = {"LightCull_zdist"};
/** Key buffer containing only visible lights indices. Used for sorting. */
LightCullingKeyBuf culling_key_buf_ = {"LightCull_key"};
/** Zbins containing min and max light index for each Z bin. */
LightCullingZbinBuf culling_zbin_buf_ = {"LightCull_zbin"};
/** Bitmap of lights touching each tiles. */
LightCullingTileBuf culling_tile_buf_ = {"LightCull_tile"};
/** Culling compute passes. */
DRWPass *culling_select_ps_ = nullptr;
DRWPass *culling_sort_ps_ = nullptr;
DRWPass *culling_zbin_ps_ = nullptr;
DRWPass *culling_tile_ps_ = nullptr;
/** Total number of words the tile buffer needs to contain for the render resolution. */
uint total_word_count_ = 0;
/** Debug Culling visualization. */
DRWPass *debug_draw_ps_ = nullptr;
/* GPUTexture *input_depth_tx_ = nullptr; */
public:
LightModule(Instance &inst) : inst_(inst){};
~LightModule(){};
void begin_sync();
void sync_light(const Object *ob, ObjectHandle &handle);
void end_sync();
/**
* Update acceleration structure for the given view.
*/
void set_view(const DRWView *view, const int2 extent);
void debug_draw(GPUFrameBuffer *view_fb);
void bind_resources(DRWShadingGroup *grp)
{
DRW_shgroup_storage_block_ref(grp, "light_buf", &culling_light_buf_);
DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_);
DRW_shgroup_storage_block_ref(grp, "light_zbin_buf", &culling_zbin_buf_);
DRW_shgroup_storage_block_ref(grp, "light_tile_buf", &culling_tile_buf_);
#if 0
DRW_shgroup_uniform_texture(grp, "shadow_atlas_tx", inst_.shadows.atlas_tx_get());
DRW_shgroup_uniform_texture(grp, "shadow_tilemaps_tx", inst_.shadows.tilemap_tx_get());
#endif
}
private:
void culling_pass_sync();
void debug_pass_sync();
};
/** \} */
} // namespace blender::eevee

View File

@ -101,12 +101,14 @@ DRWShadingGroup *ForwardPipeline::material_opaque_add(::Material *blender_mat, G
{
RenderBuffers &rbufs = inst_.render_buffers;
DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? opaque_culled_ps_ : opaque_ps_;
// LightModule &lights = inst_.lights;
LightModule &lights = inst_.lights;
Sampling &sampling = inst_.sampling;
// LightProbeModule &lightprobes = inst_.lightprobes;
// RaytracingModule &raytracing = inst_.raytracing;
// eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
// lights.shgroup_resources(grp);
lights.bind_resources(grp);
sampling.bind_resources(grp);
// DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
// DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get());
// DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
@ -163,19 +165,21 @@ DRWShadingGroup *ForwardPipeline::material_transparent_add(::Material *blender_m
GPUMaterial *gpumat)
{
RenderBuffers &rbufs = inst_.render_buffers;
// LightModule &lights = inst_.lights;
LightModule &lights = inst_.lights;
Sampling &sampling = inst_.sampling;
// LightProbeModule &lightprobes = inst_.lightprobes;
// RaytracingModule &raytracing = inst_.raytracing;
// eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_);
// lights.shgroup_resources(grp);
lights.bind_resources(grp);
sampling.bind_resources(grp);
// DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
// DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get());
// DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
// DRW_shgroup_uniform_block(grp, "probes_buf", lightprobes.info_ubo_get());
// DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
// DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
// DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.pipelines.utility_tx);
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.pipelines.utility_tx);
/* TODO(fclem): Make this only needed if material uses it ... somehow. */
// if (true) {
// DRW_shgroup_uniform_texture_ref(

View File

@ -124,6 +124,16 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_depth_of_field_tiles_dilate_minmax";
case DOF_TILES_FLATTEN:
return "eevee_depth_of_field_tiles_flatten";
case LIGHT_CULLING_DEBUG:
return "eevee_light_culling_debug";
case LIGHT_CULLING_SELECT:
return "eevee_light_culling_select";
case LIGHT_CULLING_SORT:
return "eevee_light_culling_sort";
case LIGHT_CULLING_TILE:
return "eevee_light_culling_tile";
case LIGHT_CULLING_ZBIN:
return "eevee_light_culling_zbin";
/* To avoid compiler warning about missing case. */
case MAX_SHADER_TYPE:
return "";

View File

@ -47,6 +47,12 @@ enum eShaderType {
DOF_TILES_DILATE_MINMAX,
DOF_TILES_FLATTEN,
LIGHT_CULLING_DEBUG,
LIGHT_CULLING_SELECT,
LIGHT_CULLING_SORT,
LIGHT_CULLING_TILE,
LIGHT_CULLING_ZBIN,
MOTION_BLUR_GATHER,
MOTION_BLUR_TILE_DILATE,
MOTION_BLUR_TILE_FLATTEN_RENDER,

View File

@ -30,6 +30,52 @@ constexpr eGPUSamplerState with_filter = GPU_SAMPLER_FILTER;
#define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14
/* -------------------------------------------------------------------- */
/** \name Debug Mode
* \{ */
/** These are just to make more sense of G.debug_value's values. Reserved range is 1-30. */
enum eDebugMode : uint32_t {
DEBUG_NONE = 0u,
/**
* Gradient showing light evaluation hotspots.
*/
DEBUG_LIGHT_CULLING = 1u,
/**
* Tilemaps to screen. Is also present in other modes.
* - Black pixels, no pages allocated.
* - Green pixels, pages cached.
* - Red pixels, pages allocated.
*/
DEBUG_SHADOW_TILEMAPS = 2u,
/**
* Random color per pages. Validates page density allocation and sampling.
*/
DEBUG_SHADOW_PAGES = 3u,
/**
* Outputs random color per tilemap (or tilemap level). Validates tilemaps coverage.
* Black means not covered by any tilemaps LOD of the shadow.
*/
DEBUG_SHADOW_LOD = 4u,
/**
* Outputs white pixels for pages allocated and black pixels for unused pages.
* This needs DEBUG_SHADOW_PAGE_ALLOCATION_ENABLED defined in order to work.
*/
DEBUG_SHADOW_PAGE_ALLOCATION = 5u,
/**
* Outputs the tilemap atlas. Default tilemap is too big for the usual screen resolution.
* Try lowering SHADOW_TILEMAP_PER_ROW and SHADOW_MAX_TILEMAP before using this option.
*/
DEBUG_SHADOW_TILE_ALLOCATION = 6u,
/**
* Visualize linear depth stored in the atlas regions of the active light.
* This way, one can check if the rendering, the copying and the shadow sampling functions works.
*/
DEBUG_SHADOW_SHADOW_DEPTH = 7u
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Sampling
* \{ */
@ -459,6 +505,113 @@ static inline float circle_to_polygon_angle(float sides_count, float theta)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Light Culling
* \{ */
/* Number of items we can cull. Limited by how we store CullingZBin. */
#define CULLING_MAX_ITEM 65536
/* Fine grained subdivision in the Z direction. Limited by the LDS in z-binning compute shader. */
#define CULLING_ZBIN_COUNT 4096
/* Max tile map resolution per axes. */
#define CULLING_TILE_RES 16
struct LightCullingData {
/** Scale applied to tile pixel coordinates to get target UV coordinate. */
float2 tile_to_uv_fac;
/** Scale and bias applied to linear Z to get zbin. */
float zbin_scale;
float zbin_bias;
/** Valid item count in the source data array. */
uint items_count;
/** Items that are processed by the 2.5D culling. */
uint local_lights_len;
/** Items that are **NOT** processed by the 2.5D culling (i.e: Sun Lights). */
uint sun_lights_len;
/** Number of items that passes the first culling test. */
uint visible_count;
/** Extent of one square tile in pixels. */
float tile_size;
/** Number of tiles on the X/Y axis. */
uint tile_x_len;
uint tile_y_len;
/** Number of word per tile. Depends on the maximum number of lights. */
uint tile_word_len;
};
BLI_STATIC_ASSERT_ALIGN(LightCullingData, 16)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Lights
* \{ */
#define LIGHT_NO_SHADOW -1
enum eLightType : uint32_t {
LIGHT_SUN = 0u,
LIGHT_POINT = 1u,
LIGHT_SPOT = 2u,
LIGHT_RECT = 3u,
LIGHT_ELLIPSE = 4u
};
static inline bool is_area_light(eLightType type)
{
return type >= LIGHT_RECT;
}
struct LightData {
/** Normalized object matrix. Last column contains data accessible using the following macros. */
float4x4 object_mat;
/** Packed data in the last column of the object_mat. */
#define _area_size_x object_mat[0][3]
#define _area_size_y object_mat[1][3]
#define _radius _area_size_x
#define _spot_mul object_mat[2][3]
#define _spot_bias object_mat[3][3]
/** Aliases for axes. */
#ifndef USE_GPU_SHADER_CREATE_INFO
# define _right object_mat[0]
# define _up object_mat[1]
# define _back object_mat[2]
# define _position object_mat[3]
#else
# define _right object_mat[0].xyz
# define _up object_mat[1].xyz
# define _back object_mat[2].xyz
# define _position object_mat[3].xyz
#endif
/** Influence radius (inverted and squared) adjusted for Surface / Volume power. */
float influence_radius_invsqr_surface;
float influence_radius_invsqr_volume;
/** Maximum influence radius. Used for culling. */
float influence_radius_max;
/** Index of the shadow struct on CPU. -1 means no shadow. */
int shadow_id;
/** NOTE: It is ok to use float3 here. A float is declared right after it.
* float3 is also aligned to 16 bytes. */
float3 color;
/** Power depending on shader type. */
float diffuse_power;
float specular_power;
float volume_power;
float transmit_power;
/** Special radius factor for point lighting. */
float radius_squared;
/** Light Type. */
eLightType type;
/** Spot angle tangent. */
float spot_tan;
/** Spot size. Aligned to size of float2. */
float2 spot_size_inv;
/** Associated shadow data. Only valid if shadow_id is not LIGHT_NO_SHADOW. */
// ShadowData shadow_data;
};
BLI_STATIC_ASSERT_ALIGN(LightData, 16)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Ray-Tracing
* \{ */
@ -479,6 +632,34 @@ enum eClosureBits : uint32_t {
/** \} */
/* -------------------------------------------------------------------- */
/** \name Subsurface
* \{ */
#define SSS_SAMPLE_MAX 64
#define SSS_BURLEY_TRUNCATE 16.0
#define SSS_BURLEY_TRUNCATE_CDF 0.9963790093708328
#define SSS_TRANSMIT_LUT_SIZE 64.0
#define SSS_TRANSMIT_LUT_RADIUS 1.218
#define SSS_TRANSMIT_LUT_SCALE ((SSS_TRANSMIT_LUT_SIZE - 1.0) / float(SSS_TRANSMIT_LUT_SIZE))
#define SSS_TRANSMIT_LUT_BIAS (0.5 / float(SSS_TRANSMIT_LUT_SIZE))
#define SSS_TRANSMIT_LUT_STEP_RES 64.0
struct SubsurfaceData {
/** xy: 2D sample position [-1..1], zw: sample_bounds. */
/* NOTE(fclem) Using float4 for alignment. */
float4 samples[SSS_SAMPLE_MAX];
/** Sample index after which samples are not randomly rotated anymore. */
int jitter_threshold;
/** Number of samples precomputed in the set. */
int sample_len;
int _pad0;
int _pad1;
};
BLI_STATIC_ASSERT_ALIGN(SubsurfaceData, 16)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Utility Texture
* \{ */
@ -518,6 +699,12 @@ float4 utility_tx_sample(sampler2DArray util_tx, float2 uv, float layer)
using AOVsInfoDataBuf = draw::StorageBuffer<AOVsInfoData>;
using CameraDataBuf = draw::UniformBuffer<CameraData>;
using LightDataBuf = draw::StorageArrayBuffer<LightData, LIGHT_CHUNK>;
using LightCullingDataBuf = draw::StorageBuffer<LightCullingData>;
using LightCullingKeyBuf = draw::StorageArrayBuffer<uint, LIGHT_CHUNK, true>;
using LightCullingTileBuf = draw::StorageArrayBuffer<uint, LIGHT_CHUNK, true>;
using LightCullingZbinBuf = draw::StorageArrayBuffer<uint, CULLING_ZBIN_COUNT, true>;
using LightCullingZdistBuf = draw::StorageArrayBuffer<float, LIGHT_CHUNK, true>;
using DepthOfFieldDataBuf = draw::UniformBuffer<DepthOfFieldData>;
using DepthOfFieldScatterListBuf = draw::StorageArrayBuffer<ScatterRect, 16, true>;
using DrawIndirectBuf = draw::StorageBuffer<DrawCommand, true>;

View File

@ -118,6 +118,9 @@ void ShadingView::render()
inst_.pipelines.world.render();
/* TODO(fclem): Move it after the first prepass (and hiz update) once pipeline is stabilized. */
inst_.lights.set_view(render_view_, extent_);
// inst_.pipelines.deferred.render(
// render_view_, rt_buffer_opaque_, rt_buffer_refract_, depth_tx_, combined_tx_);
@ -128,13 +131,14 @@ void ShadingView::render()
inst_.pipelines.forward.render(
render_view_, prepass_fb_, combined_fb_, rbufs.depth_tx, rbufs.combined_tx);
// inst_.lights.debug_draw(view_fb_);
// inst_.shadows.debug_draw(view_fb_);
inst_.lights.debug_draw(combined_fb_);
GPUTexture *combined_final_tx = render_postfx(rbufs.combined_tx);
inst_.film.accumulate(sub_view_, combined_final_tx);
// inst_.shadows.debug_draw();
rbufs.release();
postfx_tx_.release();
@ -176,13 +180,10 @@ void ShadingView::update_view()
window_translate_m4(winmat.ptr(), winmat.ptr(), UNPACK2(jitter));
DRW_view_update_sub(sub_view_, viewmat.ptr(), winmat.ptr());
/* FIXME(fclem): The offset may be is noticeably large and the culling might make object pop
/* FIXME(fclem): The offset may be noticeably large and the culling might make object pop
* out of the blurring radius. To fix this, use custom enlarged culling matrix. */
inst_.depth_of_field.jitter_apply(winmat, viewmat);
DRW_view_update_sub(render_view_, viewmat.ptr(), winmat.ptr());
// inst_.lightprobes.set_view(render_view_, extent_);
// inst_.lights.set_view(render_view_, extent_, !inst_.use_scene_lights());
}
/** \} */

View File

@ -665,7 +665,7 @@ void dof_slight_focus_gather(sampler2D depth_tx,
dof_gather_accumulate_resolve(total_sample_count, bg_accum, bg_col, bg_weight, unused_occlusion);
dof_gather_accumulate_resolve(total_sample_count, fg_accum, fg_col, fg_weight, unused_occlusion);
/* Fix weighting issues on perfectly focus to slight focus transitionning areas. */
/* Fix weighting issues on perfectly focus to slight focus transitioning areas. */
if (abs(center_data.coc) < 0.5) {
bg_col = center_data.color;
bg_weight = 1.0;

View File

@ -134,7 +134,7 @@ void main()
{
/**
* NOTE: We can **NOT** optimize by discarding some tiles as the result is sampled using bilinear
* filtering in the resolve pass. Not outputing to a tile means that border texels have undefined
* filtering in the resolve pass. Not outputting to a tile means that border texels have undefined
* value and tile border will be noticeable in the final image.
*/

View File

@ -2,7 +2,7 @@
/**
* Gather pass: Convolve foreground and background parts in separate passes.
*
* Using the min&max CoC tile buffer, we select the best apropriate method to blur the scene color.
* Using the min&max CoC tile buffer, we select the best appropriate method to blur the scene color.
* A fast gather path is taken if there is not many CoC variation inside the tile.
*
* We sample using an octaweb sampling pattern. We randomize the kernel center and each ring

View File

@ -2,7 +2,7 @@
/**
* Holefill pass: Gather background parts where foreground is present.
*
* Using the min&max CoC tile buffer, we select the best apropriate method to blur the scene color.
* Using the min&max CoC tile buffer, we select the best appropriate method to blur the scene color.
* A fast gather path is taken if there is not many CoC variation inside the tile.
*
* We sample using an octaweb sampling pattern. We randomize the kernel center and each ring

View File

@ -8,7 +8,7 @@
* Inputs:
* - Output of setup pass (halfres) and reduce downsample pass (quarter res).
* Outputs:
* - Halfres padded to avoid mipmap mis-alignment (so possibly not matching input size).
* - Halfres padded to avoid mipmap misalignment (so possibly not matching input size).
* - Gather input color (whole mip chain), Scatter rect list, Signed CoC (whole mip chain).
**/
@ -98,7 +98,7 @@ void main()
do_scatter[LOCAL_INDEX] *= dof_scatter_screen_border_rejection(coc_cache[LOCAL_INDEX], texel);
/* Only scatter if neighborhood is different enough. */
do_scatter[LOCAL_INDEX] *= dof_scatter_neighborhood_rejection(color_cache[LOCAL_INDEX].rgb);
/* For debuging. */
/* For debugging. */
if (no_scatter_pass) {
do_scatter[LOCAL_INDEX] = 0.0;
}

View File

@ -36,7 +36,7 @@ float dof_slight_focus_coc_tile_get(vec2 frag_coord)
}
/* Use atomic reduce operation. */
atomicMax(shared_max_slight_focus_abs_coc, floatBitsToUint(local_abs_max));
/* "Broadcast" result accross all threads. */
/* "Broadcast" result across all threads. */
barrier();
return uintBitsToFloat(shared_max_slight_focus_abs_coc);
@ -44,7 +44,7 @@ float dof_slight_focus_coc_tile_get(vec2 frag_coord)
vec3 dof_neighborhood_clamp(vec2 frag_coord, vec3 color, float center_coc, float weight)
{
/* Stabilize color by clamping with the stable half res neighboorhood. */
/* Stabilize color by clamping with the stable half res neighborhood. */
vec3 neighbor_min, neighbor_max;
const vec2 corners[4] = vec2[4](vec2(-1, -1), vec2(1, -1), vec2(-1, 1), vec2(1, 1));
for (int i = 0; i < 4; i++) {

View File

@ -211,7 +211,7 @@ vec2 dof_pixel_history_motion_vector(ivec2 texel_sample)
return vector.xy * vec2(textureSize(color_tx, 0));
}
/* Load color using a special filter to avoid loosing detail.
/* Load color using a special filter to avoid losing detail.
* \a texel is sample position with subpixel accuracy. */
DofSample dof_sample_history(vec2 input_texel)
{
@ -285,17 +285,17 @@ float dof_history_blend_factor(
/* 5% of incoming color by default. */
float blend = 0.05;
/* Blend less history if the pixel has substential velocity. */
/* Blend less history if the pixel has substantial velocity. */
/* NOTE(fclem): velocity threshold multiplied by 2 because of half resolution. */
blend = mix(blend, 0.20, saturate(velocity * 0.02 * 2.0));
/**
* "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014 (Slide 43)
* Bias towards history if incomming pixel is near clamping. Reduces flicker.
* Bias towards history if incoming pixel is near clamping. Reduces flicker.
*/
float distance_to_luma_clip = min_v2(vec2(luma_history - luma_min, luma_max - luma_history));
/* Divide by bbox size to get a factor. 2 factor to compensate the line above. */
distance_to_luma_clip *= 2.0 * safe_rcp(luma_max - luma_min);
/* Linearly blend when history gets bellow to 25% of the bbox size. */
/* Linearly blend when history gets below to 25% of the bbox size. */
blend *= saturate(distance_to_luma_clip * 4.0 + 0.1);
/* Progressively discard history until history CoC is twice as big as the filtered CoC.
* Note we use absolute diff here because we are not comparing neighbors and thus do not risk to
@ -335,7 +335,7 @@ void main()
DofSample dst = dof_sample_history(history_texel);
/* Get local color bounding box of source neighboorhood. */
/* Get local color bounding box of source neighborhood. */
DofNeighborhoodMinMax bbox = dof_neighbor_boundbox();
float blend = dof_history_blend_factor(velocity, history_texel, bbox, src, dst);

View File

@ -0,0 +1,52 @@
/**
* Debug Shader outputing a gradient of orange - white - blue to mark culling hotspots.
* Green pixels are error pixels that are missing lights from the culling pass (i.e: when culling
* pass is not conservative enough).
*/
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
void main()
{
ivec2 texel = ivec2(gl_FragCoord.xy);
float depth = texelFetch(depth_tx, texel, 0).r;
float vP_z = get_view_z_from_depth(depth);
vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth);
float light_count = 0.0;
uint light_cull = 0u;
vec2 px = gl_FragCoord.xy;
LIGHT_FOREACH_BEGIN_LOCAL(light_cull_buf, light_zbin_buf, light_tile_buf, px, vP_z, l_idx)
{
LightData light = light_buf[l_idx];
light_cull |= 1u << l_idx;
light_count += 1.0;
}
LIGHT_FOREACH_END
uint light_nocull = 0u;
LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(light_cull_buf, l_idx)
{
LightData light = light_buf[l_idx];
vec3 L;
float dist;
light_vector_get(light, P, L, dist);
if (light_attenuation(light_buf[l_idx], L, dist) > 0.0) {
light_nocull |= 1u << l_idx;
}
}
LIGHT_FOREACH_END
if ((light_cull & light_nocull) != light_nocull) {
/* ERROR. Some lights were culled incorrectly. */
out_debug_color = vec4(0.0, 1.0, 0.0, 1.0);
}
else {
out_debug_color = vec4(heatmap_gradient(light_count / 4.0), 1.0);
}
}

View File

@ -0,0 +1,62 @@
/**
* Select the visible items inside the active view and put them inside the sorting buffer.
*/
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
#pragma BLENDER_REQUIRE(common_intersect_lib.glsl)
void main()
{
uint l_idx = gl_GlobalInvocationID.x;
if (l_idx >= light_cull_buf.items_count) {
return;
}
LightData light = in_light_buf[l_idx];
/* Do not select 0 power lights. */
if (light.influence_radius_max < 1e-8) {
return;
}
/* Sun lights are packed at the end of the array. Perform early copy. */
if (light.type == LIGHT_SUN) {
/* NOTE: We know the index because sun lights are packed at the start of the input buffer. */
out_light_buf[light_cull_buf.local_lights_len + l_idx] = light;
return;
}
Sphere sphere;
switch (light.type) {
case LIGHT_SPOT:
/* Only for < ~170° Cone due to plane extraction precision. */
if (light.spot_tan < 10.0) {
Pyramid pyramid = shape_pyramid_non_oblique(
light._position,
light._position - light._back * light.influence_radius_max,
light._right * light.influence_radius_max * light.spot_tan / light.spot_size_inv.x,
light._up * light.influence_radius_max * light.spot_tan / light.spot_size_inv.y);
if (!intersect_view(pyramid)) {
return;
}
}
case LIGHT_RECT:
case LIGHT_ELLIPSE:
case LIGHT_POINT:
sphere = Sphere(light._position, light.influence_radius_max);
break;
}
/* TODO(fclem): HiZ culling? Could be quite beneficial given the nature of the 2.5D culling. */
/* TODO(fclem): Small light culling / fading? */
if (intersect_view(sphere)) {
uint index = atomicAdd(light_cull_buf.visible_count, 1u);
out_zdist_buf[index] = dot(cameraForward, light._position) - dot(cameraForward, cameraPos);
out_key_buf[index] = l_idx;
}
}

View File

@ -0,0 +1,57 @@
/**
* Sort the lights by their Z distance to the camera.
* Outputs ordered light buffer.
* One thread processes one Light entity.
*/
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
shared float zdists_cache[gl_WorkGroupSize.x];
void main()
{
uint src_index = gl_GlobalInvocationID.x;
bool valid_thread = true;
if (src_index >= light_cull_buf.visible_count) {
/* Do not return because we use barriers later on (which need uniform control flow).
* Just process the same last item but avoid insertion. */
src_index = light_cull_buf.visible_count - 1;
valid_thread = false;
}
float local_zdist = in_zdist_buf[src_index];
int prefix_sum = 0;
/* Iterate over the whole key buffer. */
uint iter = divide_ceil_u(light_cull_buf.visible_count, gl_WorkGroupSize.x);
for (uint i = 0u; i < iter; i++) {
uint index = gl_WorkGroupSize.x * i + gl_LocalInvocationID.x;
/* NOTE: This will load duplicated values, but they will be discarded. */
index = min(index, light_cull_buf.visible_count - 1);
zdists_cache[gl_LocalInvocationID.x] = in_zdist_buf[index];
barrier();
/* Iterate over the cache line. */
uint line_end = min(gl_WorkGroupSize.x, light_cull_buf.visible_count - gl_WorkGroupSize.x * i);
for (uint j = 0u; j < line_end; j++) {
if (zdists_cache[j] < local_zdist) {
prefix_sum++;
}
else if (zdists_cache[j] == local_zdist) {
/* Same depth, use index to order and avoid same prefix for 2 different lights. */
if ((gl_WorkGroupSize.x * i + j) < src_index) {
prefix_sum++;
}
}
}
}
if (valid_thread) {
/* Copy sorted light to render light buffer. */
uint input_index = in_key_buf[src_index];
out_light_buf[prefix_sum] = in_light_buf[input_index];
}
}

View File

@ -0,0 +1,188 @@
/**
* 2D Culling pass for lights.
* We iterate over all items and check if they intersect with the tile frustum.
* Dispatch one thread per word.
*/
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_intersect_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
/* ---------------------------------------------------------------------- */
/** \name Culling shapes extraction
* \{ */
struct CullingTile {
IsectFrustum frustum;
vec4 bounds;
};
/* Corners are expected to be in viewspace so that the cone is starting from the origin.
* Corner order does not matter. */
vec4 tile_bound_cone(vec3 v00, vec3 v01, vec3 v10, vec3 v11)
{
v00 = normalize(v00);
v01 = normalize(v01);
v10 = normalize(v10);
v11 = normalize(v11);
vec3 center = normalize(v00 + v01 + v10 + v11);
float angle_cosine = dot(center, v00);
angle_cosine = max(angle_cosine, dot(center, v01));
angle_cosine = max(angle_cosine, dot(center, v10));
angle_cosine = max(angle_cosine, dot(center, v11));
return vec4(center, angle_cosine);
}
/* Corners are expected to be in viewspace. Returns Z-aligned bounding cylinder.
* Corner order does not matter. */
vec4 tile_bound_cylinder(vec3 v00, vec3 v01, vec3 v10, vec3 v11)
{
vec3 center = (v00 + v01 + v10 + v11) * 0.25;
vec4 corners_dist;
float dist_sqr = distance_squared(center, v00);
dist_sqr = max(dist_sqr, distance_squared(center, v01));
dist_sqr = max(dist_sqr, distance_squared(center, v10));
dist_sqr = max(dist_sqr, distance_squared(center, v11));
/* Return a cone. Later converted to cylinder. */
return vec4(center, sqrt(dist_sqr));
}
vec2 tile_to_ndc(vec2 tile_co, vec2 offset)
{
/* Add a margin to prevent culling too much if the frustum becomes too much unstable. */
const float margin = 0.02;
tile_co += margin * (offset * 2.0 - 1.0);
tile_co += offset;
return tile_co * light_cull_buf.tile_to_uv_fac * 2.0 - 1.0;
}
CullingTile tile_culling_get(uvec2 tile_co)
{
vec2 ftile = vec2(tile_co);
/* Culling frustum corners for this tile. */
vec3 corners[8];
/* Follow same corners order as view frustum. */
corners[1].xy = corners[0].xy = tile_to_ndc(ftile, vec2(0, 0));
corners[5].xy = corners[4].xy = tile_to_ndc(ftile, vec2(1, 0));
corners[6].xy = corners[7].xy = tile_to_ndc(ftile, vec2(1, 1));
corners[2].xy = corners[3].xy = tile_to_ndc(ftile, vec2(0, 1));
corners[1].z = corners[5].z = corners[6].z = corners[2].z = -1.0;
corners[0].z = corners[4].z = corners[7].z = corners[3].z = 1.0;
for (int i = 0; i < 8; i++) {
/* Culling in view space for precision. */
corners[i] = project_point(ProjectionMatrixInverse, corners[i]);
}
bool is_persp = ProjectionMatrix[3][3] == 0.0;
CullingTile tile;
tile.bounds = (is_persp) ? tile_bound_cone(corners[0], corners[4], corners[7], corners[3]) :
tile_bound_cylinder(corners[0], corners[4], corners[7], corners[3]);
tile.frustum = isect_data_setup(shape_frustum(corners));
return tile;
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Intersection Tests
* \{ */
bool intersect(CullingTile tile, Sphere sphere)
{
bool isect = true;
/* Test tile intersection using bounding cone or bounding cylinder.
* This has less false positive cases when the sphere is large. */
if (ProjectionMatrix[3][3] == 0.0) {
isect = intersect(shape_cone(tile.bounds.xyz, tile.bounds.w), sphere);
}
else {
/* Simplify to a 2D circle test on the view Z axis plane. */
isect = intersect(shape_circle(tile.bounds.xy, tile.bounds.w),
shape_circle(sphere.center.xy, sphere.radius));
}
/* Refine using frustum test. If the sphere is small it avoids intersection
* with a neighbor tile. */
if (isect) {
isect = intersect(tile.frustum, sphere);
}
return isect;
}
bool intersect(CullingTile tile, Box bbox)
{
return intersect(tile.frustum, bbox);
}
bool intersect(CullingTile tile, Pyramid pyramid)
{
return intersect(tile.frustum, pyramid);
}
/** \} */
void main()
{
uint word_idx = gl_GlobalInvocationID.x % light_cull_buf.tile_word_len;
uint tile_idx = gl_GlobalInvocationID.x / light_cull_buf.tile_word_len;
uvec2 tile_co = uvec2(tile_idx % light_cull_buf.tile_x_len,
tile_idx / light_cull_buf.tile_x_len);
if (tile_co.y >= light_cull_buf.tile_y_len) {
return;
}
/* TODO(fclem): We could stop the tile at the HiZ depth. */
CullingTile tile = tile_culling_get(tile_co);
uint l_idx = word_idx * 32u;
uint l_end = min(l_idx + 32u, light_cull_buf.visible_count);
uint word = 0u;
for (; l_idx < l_end; l_idx++) {
LightData light = light_buf[l_idx];
/* Culling in view space for precision and simplicity. */
vec3 vP = transform_point(ViewMatrix, light._position);
vec3 v_right = transform_direction(ViewMatrix, light._right);
vec3 v_up = transform_direction(ViewMatrix, light._up);
vec3 v_back = transform_direction(ViewMatrix, light._back);
float radius = light.influence_radius_max;
Sphere sphere = shape_sphere(vP, radius);
bool intersect_tile = intersect(tile, sphere);
switch (light.type) {
case LIGHT_SPOT:
/* Only for < ~170° Cone due to plane extraction precision. */
if (light.spot_tan < 10.0) {
Pyramid pyramid = shape_pyramid_non_oblique(
vP,
vP - v_back * radius,
v_right * radius * light.spot_tan / light.spot_size_inv.x,
v_up * radius * light.spot_tan / light.spot_size_inv.y);
intersect_tile = intersect_tile && intersect(tile, pyramid);
break;
}
/* Fallthrough to the hemispheric case. */
case LIGHT_RECT:
case LIGHT_ELLIPSE:
vec3 v000 = vP - v_right * radius - v_up * radius;
vec3 v100 = v000 + v_right * (radius * 2.0);
vec3 v010 = v000 + v_up * (radius * 2.0);
vec3 v001 = v000 - v_back * radius;
Box bbox = shape_box(v000, v100, v010, v001);
intersect_tile = intersect_tile && intersect(tile, bbox);
default:
break;
}
if (intersect_tile) {
word |= 1u << (l_idx % 32u);
}
}
out_light_tile_buf[gl_GlobalInvocationID.x] = word;
}

View File

@ -0,0 +1,56 @@
/**
* Create the Zbins from Z-sorted lights.
* Perform min-max operation in LDS memory for speed.
* For this reason, we only dispatch 1 thread group.
*/
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
/* Fits the limit of 32KB. */
shared uint zbin_max[CULLING_ZBIN_COUNT];
shared uint zbin_min[CULLING_ZBIN_COUNT];
void main()
{
const uint zbin_iter = CULLING_ZBIN_COUNT / gl_WorkGroupSize.x;
const uint zbin_local = gl_LocalInvocationID.x * zbin_iter;
uint src_index = gl_GlobalInvocationID.x;
for (uint i = 0u, l = zbin_local; i < zbin_iter; i++, l++) {
zbin_max[l] = 0x0u;
zbin_min[l] = ~0x0u;
}
barrier();
uint light_iter = divide_ceil_u(light_cull_buf.visible_count, gl_WorkGroupSize.x);
for (uint i = 0u; i < light_iter; i++) {
uint index = i * gl_WorkGroupSize.x + gl_LocalInvocationID.x;
if (index >= light_cull_buf.visible_count) {
continue;
}
vec3 P = light_buf[index]._position;
/* TODO(fclem): Could have better bounds for spot and area lights. */
float radius = light_buf[index].influence_radius_max;
float z_dist = dot(cameraForward, P) - dot(cameraForward, cameraPos);
int z_min = culling_z_to_zbin(
light_cull_buf.zbin_scale, light_cull_buf.zbin_bias, z_dist + radius);
int z_max = culling_z_to_zbin(
light_cull_buf.zbin_scale, light_cull_buf.zbin_bias, z_dist - radius);
z_min = clamp(z_min, 0, CULLING_ZBIN_COUNT - 1);
z_max = clamp(z_max, 0, CULLING_ZBIN_COUNT - 1);
/* Register to Z bins. */
for (int z = z_min; z <= z_max; z++) {
atomicMin(zbin_min[z], index);
atomicMax(zbin_max[z], index);
}
}
barrier();
/* Write result to zbins buffer. Pack min & max into 1 uint. */
for (uint i = 0u, l = zbin_local; i < zbin_iter; i++, l++) {
out_zbin_buf[l] = (zbin_max[l] << 16u) | (zbin_min[l] & 0xFFFFu);
}
}

View File

@ -0,0 +1,129 @@
/**
* The resources expected to be defined are:
* - light_buf
* - light_zbin_buf
* - light_cull_buf
* - light_tile_buf
* - shadow_atlas_tx
* - shadow_tilemaps_tx
* - sss_transmittance_tx
* - utility_tx
*/
#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
/* TODO(fclem): We could reduce register pressure by only having static branches for sun lights. */
void light_eval_ex(ClosureDiffuse diffuse,
ClosureReflection reflection,
const bool is_directional,
vec3 P,
vec3 V,
float vP_z,
float thickness,
vec4 ltc_mat,
uint l_idx,
inout vec3 out_diffuse,
inout vec3 out_specular)
{
LightData light = light_buf[l_idx];
vec3 L;
float dist;
light_vector_get(light, P, L, dist);
float visibility = light_attenuation(light, L, dist);
#if 0 /* TODO(fclem): Shadows */
if ((light.shadow_id != LIGHT_NO_SHADOW) && (visibility > 0.0)) {
vec3 lL = light_world_to_local(light, -L) * dist;
float shadow_delta = shadow_delta_get(
shadow_atlas_tx, shadow_tilemaps_tx, light, light.shadow_data, lL, dist, P);
# ifdef SSS_TRANSMITTANCE
/* Transmittance evaluation first to use initial visibility. */
if (diffuse.sss_id != 0u && light.diffuse_power > 0.0) {
float delta = max(thickness, shadow_delta);
vec3 intensity = visibility * light.transmit_power *
light_translucent(sss_transmittance_tx,
is_directional,
light,
diffuse.N,
L,
dist,
diffuse.sss_radius,
delta);
out_diffuse += light.color * intensity;
}
# endif
visibility *= float(shadow_delta - light.shadow_data.bias <= 0.0);
}
#endif
if (visibility < 1e-6) {
return;
}
if (light.diffuse_power > 0.0) {
float intensity = visibility * light.diffuse_power *
light_diffuse(utility_tx, is_directional, light, diffuse.N, V, L, dist);
out_diffuse += light.color * intensity;
}
if (light.specular_power > 0.0) {
float intensity = visibility * light.specular_power *
light_ltc(
utility_tx, is_directional, light, reflection.N, V, L, dist, ltc_mat);
out_specular += light.color * intensity;
}
}
void light_eval(ClosureDiffuse diffuse,
ClosureReflection reflection,
vec3 P,
vec3 V,
float vP_z,
float thickness,
inout vec3 out_diffuse,
inout vec3 out_specular)
{
vec2 uv = vec2(reflection.roughness, safe_sqrt(1.0 - dot(reflection.N, V)));
uv = uv * UTIL_TEX_UV_SCALE + UTIL_TEX_UV_BIAS;
vec4 ltc_mat = utility_tx_sample(utility_tx, uv, UTIL_LTC_MAT_LAYER);
LIGHT_FOREACH_BEGIN_DIRECTIONAL(light_cull_buf, l_idx)
{
light_eval_ex(diffuse,
reflection,
true,
P,
V,
vP_z,
thickness,
ltc_mat,
l_idx,
out_diffuse,
out_specular);
}
LIGHT_FOREACH_END
vec2 px = gl_FragCoord.xy;
LIGHT_FOREACH_BEGIN_LOCAL(light_cull_buf, light_zbin_buf, light_tile_buf, px, vP_z, l_idx)
{
light_eval_ex(diffuse,
reflection,
false,
P,
V,
vP_z,
thickness,
ltc_mat,
l_idx,
out_diffuse,
out_specular);
}
LIGHT_FOREACH_END
}

View File

@ -0,0 +1,72 @@
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
uint zbin_mask(uint word_index, uint zbin_min, uint zbin_max)
{
uint word_start = word_index * 32u;
uint word_end = word_start + 31u;
uint local_min = max(zbin_min, word_start);
uint local_max = min(zbin_max, word_end);
uint mask_width = local_max - local_min + 1;
return bit_field_mask(mask_width, local_min);
}
int culling_z_to_zbin(float scale, float bias, float z)
{
return int(z * scale + bias);
}
/* Waiting to implement extensions support. We need:
* - GL_KHR_shader_subgroup_ballot
* - GL_KHR_shader_subgroup_arithmetic
* or
* - Vulkan 1.1
*/
#if 1
# define subgroupMin(a) a
# define subgroupMax(a) a
# define subgroupOr(a) a
# define subgroupBroadcastFirst(a) a
#endif
#define LIGHT_FOREACH_BEGIN_DIRECTIONAL(_culling, _index) \
{ \
{ \
for (uint _index = _culling.local_lights_len; _index < _culling.items_count; _index++) {
#define LIGHT_FOREACH_BEGIN_LOCAL(_culling, _zbins, _words, _pixel, _linearz, _item_index) \
{ \
uvec2 tile_co = uvec2(_pixel / _culling.tile_size); \
uint tile_word_offset = (tile_co.x + tile_co.y * _culling.tile_x_len) * \
_culling.tile_word_len; \
int zbin_index = culling_z_to_zbin(_culling.zbin_scale, _culling.zbin_bias, _linearz); \
zbin_index = clamp(zbin_index, 0, CULLING_ZBIN_COUNT - 1); \
uint zbin_data = _zbins[zbin_index]; \
uint min_index = zbin_data & 0xFFFFu; \
uint max_index = zbin_data >> 16u; \
/* Ensure all threads inside a subgroup get the same value to reduce VGPR usage. */ \
min_index = subgroupBroadcastFirst(subgroupMin(min_index)); \
max_index = subgroupBroadcastFirst(subgroupMax(max_index)); \
/* Same as divide by 32 but avoid interger division. */ \
uint word_min = min_index >> 5u; \
uint word_max = max_index >> 5u; \
for (uint word_idx = word_min; word_idx <= word_max; word_idx++) { \
uint word = _words[tile_word_offset + word_idx]; \
word &= zbin_mask(word_idx, min_index, max_index); \
/* Ensure all threads inside a subgroup get the same value to reduce VGPR usage. */ \
word = subgroupBroadcastFirst(subgroupOr(word)); \
int bit_index; \
while ((bit_index = findLSB(word)) != -1) { \
word &= ~1u << uint(bit_index); \
uint _item_index = word_idx * 32u + bit_index;
/* No culling. Iterate over all items. */
#define LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(_culling, _item_index) \
{ \
{ \
for (uint _item_index = 0; _item_index < _culling.visible_count; _item_index++) {
#define LIGHT_FOREACH_END \
} \
} \
}

View File

@ -0,0 +1,209 @@
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_ltc_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
/* ---------------------------------------------------------------------- */
/** \name Light Functions
* \{ */
void light_vector_get(LightData ld, vec3 P, out vec3 L, out float dist)
{
if (ld.type == LIGHT_SUN) {
L = ld._back;
dist = 1.0;
}
else {
L = ld._position - P;
dist = inversesqrt(len_squared(L));
L *= dist;
dist = 1.0 / dist;
}
}
/* Rotate vector to light's local space. Does not translate. */
vec3 light_world_to_local(LightData ld, vec3 L)
{
/* Avoid relying on compiler to optimize this.
* vec3 lL = transpose(mat3(ld.object_mat)) * L; */
vec3 lL;
lL.x = dot(ld.object_mat[0].xyz, L);
lL.y = dot(ld.object_mat[1].xyz, L);
lL.z = dot(ld.object_mat[2].xyz, L);
return lL;
}
/* From Frostbite PBR Course
* Distance based attenuation
* http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf */
float light_influence_attenuation(float dist, float inv_sqr_influence)
{
float factor = sqr(dist) * inv_sqr_influence;
float fac = saturate(1.0 - sqr(factor));
return sqr(fac);
}
float light_spot_attenuation(LightData ld, vec3 L)
{
vec3 lL = light_world_to_local(ld, L);
float ellipse = inversesqrt(1.0 + len_squared(lL.xy * ld.spot_size_inv / lL.z));
float spotmask = smoothstep(0.0, 1.0, ellipse * ld._spot_mul + ld._spot_bias);
return spotmask;
}
float light_attenuation(LightData ld, vec3 L, float dist)
{
float vis = 1.0;
if (ld.type == LIGHT_SPOT) {
vis *= light_spot_attenuation(ld, L);
}
if (ld.type >= LIGHT_SPOT) {
vis *= step(0.0, -dot(L, -ld._back));
}
if (ld.type != LIGHT_SUN) {
#ifdef VOLUME_LIGHTING
vis *= light_influence_attenuation(dist, ld.influence_radius_invsqr_volume);
#else
vis *= light_influence_attenuation(dist, ld.influence_radius_invsqr_surface);
#endif
}
return vis;
}
/* Cheaper alternative than evaluating the LTC.
* The result needs to be multiplied by BSDF or Phase Function. */
float light_point_light(LightData ld, const bool is_directional, vec3 L, float dist)
{
if (is_directional) {
return 1.0;
}
/**
* Using "Point Light Attenuation Without Singularity" from Cem Yuksel
* http://www.cemyuksel.com/research/pointlightattenuation/pointlightattenuation.pdf
* http://www.cemyuksel.com/research/pointlightattenuation/
**/
float d_sqr = sqr(dist);
float r_sqr = ld.radius_squared;
/* Using reformulation that has better numerical percision. */
float power = 2.0 / (d_sqr + r_sqr + dist * sqrt(d_sqr + r_sqr));
if (is_area_light(ld.type)) {
/* Modulate by light plane orientation / solid angle. */
power *= saturate(dot(ld._back, L));
}
return power;
}
float light_diffuse(sampler2DArray utility_tx,
const bool is_directional,
LightData ld,
vec3 N,
vec3 V,
vec3 L,
float dist)
{
if (is_directional || !is_area_light(ld.type)) {
float radius = ld._radius / dist;
return ltc_evaluate_disk_simple(utility_tx, radius, dot(N, L));
}
else if (ld.type == LIGHT_RECT) {
vec3 corners[4];
corners[0] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y;
corners[1] = ld._right * ld._area_size_x + ld._up * ld._area_size_y;
corners[2] = -corners[0];
corners[3] = -corners[1];
corners[0] = normalize(L * dist + corners[0]);
corners[1] = normalize(L * dist + corners[1]);
corners[2] = normalize(L * dist + corners[2]);
corners[3] = normalize(L * dist + corners[3]);
return ltc_evaluate_quad(utility_tx, corners, N);
}
else /* (ld.type == LIGHT_ELLIPSE) */ {
vec3 points[3];
points[0] = ld._right * -ld._area_size_x + ld._up * -ld._area_size_y;
points[1] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y;
points[2] = -points[0];
points[0] += L * dist;
points[1] += L * dist;
points[2] += L * dist;
return ltc_evaluate_disk(utility_tx, N, V, mat3(1.0), points);
}
}
float light_ltc(sampler2DArray utility_tx,
const bool is_directional,
LightData ld,
vec3 N,
vec3 V,
vec3 L,
float dist,
vec4 ltc_mat)
{
if (is_directional || ld.type != LIGHT_RECT) {
vec3 Px = ld._right;
vec3 Py = ld._up;
if (is_directional || !is_area_light(ld.type)) {
make_orthonormal_basis(L, Px, Py);
}
vec3 points[3];
points[0] = Px * -ld._area_size_x + Py * -ld._area_size_y;
points[1] = Px * ld._area_size_x + Py * -ld._area_size_y;
points[2] = -points[0];
points[0] += L * dist;
points[1] += L * dist;
points[2] += L * dist;
return ltc_evaluate_disk(utility_tx, N, V, ltc_matrix(ltc_mat), points);
}
else {
vec3 corners[4];
corners[0] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y;
corners[1] = ld._right * ld._area_size_x + ld._up * ld._area_size_y;
corners[2] = -corners[0];
corners[3] = -corners[1];
corners[0] += L * dist;
corners[1] += L * dist;
corners[2] += L * dist;
corners[3] += L * dist;
ltc_transform_quad(N, V, ltc_matrix(ltc_mat), corners);
return ltc_evaluate_quad(utility_tx, corners, vec3(0.0, 0.0, 1.0));
}
}
vec3 light_translucent(sampler1D transmittance_tx,
const bool is_directional,
LightData ld,
vec3 N,
vec3 L,
float dist,
vec3 sss_radius,
float delta)
{
/* TODO(fclem): We should compute the power at the entry point. */
/* NOTE(fclem): we compute the light attenuation using the light vector but the transmittance
* using the shadow depth delta. */
float power = light_point_light(ld, is_directional, L, dist);
/* Do not add more energy on front faces. Also apply lambertian BSDF. */
power *= max(0.0, dot(-N, L)) * M_1_PI;
sss_radius *= SSS_TRANSMIT_LUT_RADIUS;
vec3 channels_co = saturate(delta / sss_radius) * SSS_TRANSMIT_LUT_SCALE + SSS_TRANSMIT_LUT_BIAS;
vec3 translucency;
translucency.x = (sss_radius.x > 0.0) ? texture(transmittance_tx, channels_co.x).r : 0.0;
translucency.y = (sss_radius.y > 0.0) ? texture(transmittance_tx, channels_co.y).r : 0.0;
translucency.z = (sss_radius.z > 0.0) ? texture(transmittance_tx, channels_co.z).r : 0.0;
return translucency * power;
}
/** \} */

View File

@ -0,0 +1,299 @@
/**
* Adapted from :
* Real-Time Polygonal-Light Shading with Linearly Transformed Cosines.
* Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt.
* ACM Transactions on Graphics (Proceedings of ACM SIGGRAPH 2016) 35(4), 2016.
* Project page: https://eheitzresearch.wordpress.com/415-2/
*/
/* Diffuse *clipped* sphere integral. */
float ltc_diffuse_sphere_integral(sampler2DArray utility_tx, float avg_dir_z, float form_factor)
{
#if 1
/* use tabulated horizon-clipped sphere */
vec2 uv = vec2(avg_dir_z * 0.5 + 0.5, form_factor);
uv = uv * UTIL_TEX_UV_SCALE + UTIL_TEX_UV_BIAS;
return texture(utility_tx, vec3(uv, UTIL_DISK_INTEGRAL_LAYER))[UTIL_DISK_INTEGRAL_COMP];
#else
/* Cheap approximation. Less smooth and have energy issues. */
return max((form_factor * form_factor + avg_dir_z) / (form_factor + 1.0), 0.0);
#endif
}
/**
* An extended version of the implementation from
* "How to solve a cubic equation, revisited"
* http://momentsingraphics.de/?p=105
*/
vec3 ltc_solve_cubic(vec4 coefs)
{
/* Normalize the polynomial */
coefs.xyz /= coefs.w;
/* Divide middle coefficients by three */
coefs.yz /= 3.0;
float A = coefs.w;
float B = coefs.z;
float C = coefs.y;
float D = coefs.x;
/* Compute the Hessian and the discriminant */
vec3 delta = vec3(-coefs.zy * coefs.zz + coefs.yx, dot(vec2(coefs.z, -coefs.y), coefs.xy));
/* Discriminant */
float discr = dot(vec2(4.0 * delta.x, -delta.y), delta.zy);
/* Clamping avoid NaN output on some platform. (see T67060) */
float sqrt_discr = sqrt(clamp(discr, 0.0, FLT_MAX));
vec2 xlc, xsc;
/* Algorithm A */
{
float A_a = 1.0;
float C_a = delta.x;
float D_a = -2.0 * B * delta.x + delta.y;
/* Take the cubic root of a normalized complex number */
float theta = atan(sqrt_discr, -D_a) / 3.0;
float _2_sqrt_C_a = 2.0 * sqrt(-C_a);
float x_1a = _2_sqrt_C_a * cos(theta);
float x_3a = _2_sqrt_C_a * cos(theta + (2.0 / 3.0) * M_PI);
float xl;
if ((x_1a + x_3a) > 2.0 * B) {
xl = x_1a;
}
else {
xl = x_3a;
}
xlc = vec2(xl - B, A);
}
/* Algorithm D */
{
float A_d = D;
float C_d = delta.z;
float D_d = -D * delta.y + 2.0 * C * delta.z;
/* Take the cubic root of a normalized complex number */
float theta = atan(D * sqrt_discr, -D_d) / 3.0;
float _2_sqrt_C_d = 2.0 * sqrt(-C_d);
float x_1d = _2_sqrt_C_d * cos(theta);
float x_3d = _2_sqrt_C_d * cos(theta + (2.0 / 3.0) * M_PI);
float xs;
if (x_1d + x_3d < 2.0 * C) {
xs = x_1d;
}
else {
xs = x_3d;
}
xsc = vec2(-D, xs + C);
}
float E = xlc.y * xsc.y;
float F = -xlc.x * xsc.y - xlc.y * xsc.x;
float G = xlc.x * xsc.x;
vec2 xmc = vec2(C * F - B * G, -B * F + C * E);
vec3 root = vec3(xsc.x / xsc.y, xmc.x / xmc.y, xlc.x / xlc.y);
if (root.x < root.y && root.x < root.z) {
root.xyz = root.yxz;
}
else if (root.z < root.x && root.z < root.y) {
root.xyz = root.xzy;
}
return root;
}
/* from Real-Time Area Lighting: a Journey from Research to Production
* Stephen Hill and Eric Heitz */
vec3 ltc_edge_integral_vec(vec3 v1, vec3 v2)
{
float x = dot(v1, v2);
float y = abs(x);
float a = 0.8543985 + (0.4965155 + 0.0145206 * y) * y;
float b = 3.4175940 + (4.1616724 + y) * y;
float v = a / b;
float theta_sintheta = (x > 0.0) ? v : 0.5 * inversesqrt(max(1.0 - x * x, 1e-7)) - v;
return cross(v1, v2) * theta_sintheta;
}
mat3 ltc_matrix(vec4 lut)
{
/* Load inverse matrix. */
return mat3(vec3(lut.x, 0, lut.y), vec3(0, 1, 0), vec3(lut.z, 0, lut.w));
}
void ltc_transform_quad(vec3 N, vec3 V, mat3 Minv, inout vec3 corners[4])
{
/* Avoid dot(N, V) == 1 in ortho mode, leading T1 normalize to fail. */
V = normalize(V + 1e-8);
/* Construct orthonormal basis around N. */
vec3 T1, T2;
T1 = normalize(V - N * dot(N, V));
T2 = cross(N, T1);
/* Rotate area light in (T1, T2, R) basis. */
Minv = Minv * transpose(mat3(T1, T2, N));
/* Apply LTC inverse matrix. */
corners[0] = normalize(Minv * corners[0]);
corners[1] = normalize(Minv * corners[1]);
corners[2] = normalize(Minv * corners[2]);
corners[3] = normalize(Minv * corners[3]);
}
/* If corners have already pass through ltc_transform_quad(),
* then N **MUST** be vec3(0.0, 0.0, 1.0), corresponding to the Up axis of the shading basis. */
float ltc_evaluate_quad(sampler2DArray utility_tx, vec3 corners[4], vec3 N)
{
/* Approximation using a sphere of the same solid angle than the quad.
* Finding the clipped sphere diffuse integral is easier than clipping the quad. */
vec3 avg_dir;
avg_dir = ltc_edge_integral_vec(corners[0], corners[1]);
avg_dir += ltc_edge_integral_vec(corners[1], corners[2]);
avg_dir += ltc_edge_integral_vec(corners[2], corners[3]);
avg_dir += ltc_edge_integral_vec(corners[3], corners[0]);
float form_factor = length(avg_dir);
float avg_dir_z = dot(N, avg_dir / form_factor);
return form_factor * ltc_diffuse_sphere_integral(utility_tx, avg_dir_z, form_factor);
}
/* If disk does not need to be transformed and is already front facing. */
float ltc_evaluate_disk_simple(sampler2DArray utility_tx, float disk_radius, float NL)
{
float r_sqr = disk_radius * disk_radius;
float one_r_sqr = 1.0 + r_sqr;
float form_factor = r_sqr * inversesqrt(one_r_sqr * one_r_sqr);
return form_factor * ltc_diffuse_sphere_integral(utility_tx, NL, form_factor);
}
/* disk_points are WS vectors from the shading point to the disk "bounding domain" */
float ltc_evaluate_disk(sampler2DArray utility_tx, vec3 N, vec3 V, mat3 Minv, vec3 disk_points[3])
{
/* Avoid dot(N, V) == 1 in ortho mode, leading T1 normalize to fail. */
V = normalize(V + 1e-8);
/* construct orthonormal basis around N */
vec3 T1, T2;
T1 = normalize(V - N * dot(V, N));
T2 = cross(N, T1);
/* rotate area light in (T1, T2, R) basis */
mat3 R = transpose(mat3(T1, T2, N));
/* Intermediate step: init ellipse. */
vec3 L_[3];
L_[0] = mul(R, disk_points[0]);
L_[1] = mul(R, disk_points[1]);
L_[2] = mul(R, disk_points[2]);
vec3 C = 0.5 * (L_[0] + L_[2]);
vec3 V1 = 0.5 * (L_[1] - L_[2]);
vec3 V2 = 0.5 * (L_[1] - L_[0]);
/* Transform ellipse by Minv. */
C = Minv * C;
V1 = Minv * V1;
V2 = Minv * V2;
/* Compute eigenvectors of new ellipse. */
float d11 = dot(V1, V1);
float d22 = dot(V2, V2);
float d12 = dot(V1, V2);
float a, b; /* Eigenvalues */
const float threshold = 0.0007; /* Can be adjusted. Fix artifacts. */
if (abs(d12) / sqrt(d11 * d22) > threshold) {
float tr = d11 + d22;
float det = -d12 * d12 + d11 * d22;
/* use sqrt matrix to solve for eigenvalues */
det = sqrt(det);
float u = 0.5 * sqrt(tr - 2.0 * det);
float v = 0.5 * sqrt(tr + 2.0 * det);
float e_max = (u + v);
float e_min = (u - v);
e_max *= e_max;
e_min *= e_min;
vec3 V1_, V2_;
if (d11 > d22) {
V1_ = d12 * V1 + (e_max - d11) * V2;
V2_ = d12 * V1 + (e_min - d11) * V2;
}
else {
V1_ = d12 * V2 + (e_max - d22) * V1;
V2_ = d12 * V2 + (e_min - d22) * V1;
}
a = 1.0 / e_max;
b = 1.0 / e_min;
V1 = normalize(V1_);
V2 = normalize(V2_);
}
else {
a = 1.0 / d11;
b = 1.0 / d22;
V1 *= sqrt(a);
V2 *= sqrt(b);
}
/* Now find front facing ellipse with same solid angle. */
vec3 V3 = normalize(cross(V1, V2));
if (dot(C, V3) < 0.0) {
V3 *= -1.0;
}
float L = dot(V3, C);
float inv_L = 1.0 / L;
float x0 = dot(V1, C) * inv_L;
float y0 = dot(V2, C) * inv_L;
float L_sqr = L * L;
a *= L_sqr;
b *= L_sqr;
float t = 1.0 + x0 * x0;
float c0 = a * b;
float c1 = c0 * (t + y0 * y0) - a - b;
float c2 = (1.0 - a * t) - b * (1.0 + y0 * y0);
float c3 = 1.0;
vec3 roots = ltc_solve_cubic(vec4(c0, c1, c2, c3));
float e1 = roots.x;
float e2 = roots.y;
float e3 = roots.z;
vec3 avg_dir = vec3(a * x0 / (a - e2), b * y0 / (b - e2), 1.0);
mat3 rotate = mat3(V1, V2, V3);
avg_dir = rotate * avg_dir;
avg_dir = normalize(avg_dir);
/* L1, L2 are the extends of the front facing ellipse. */
float L1 = sqrt(-e2 / e3);
float L2 = sqrt(-e2 / e1);
/* Find the sphere and compute lighting. */
float form_factor = max(0.0, L1 * L2 * inversesqrt((1.0 + L1 * L1) * (1.0 + L2 * L2)));
return form_factor * ltc_diffuse_sphere_integral(utility_tx, avg_dir.z, form_factor);
}

View File

@ -39,6 +39,8 @@ bool closure_select(float weight, inout float total_weight, inout float r)
destination = candidate; \
}
float g_closure_rand;
void closure_weights_reset()
{
g_diffuse_data.weight = 0.0;
@ -58,18 +60,8 @@ void closure_weights_reset()
g_refraction_data.roughness = 0.0;
g_refraction_data.ior = 0.0;
/* TEMP */
#define P(x) ((x + 0.5) / 16.0)
const vec4 dither_mat4x4[4] = vec4[4](vec4(P(0.0), P(8.0), P(2.0), P(10.0)),
vec4(P(12.0), P(4.0), P(14.0), P(6.0)),
vec4(P(3.0), P(11.0), P(1.0), P(9.0)),
vec4(P(15.0), P(7.0), P(13.0), P(5.0)));
#undef P
#if defined(GPU_FRAGMENT_SHADER)
ivec2 pix = ivec2(gl_FragCoord.xy) % ivec2(4);
g_diffuse_rand = dither_mat4x4[pix.x][pix.y];
g_reflection_rand = dither_mat4x4[pix.x][pix.y];
g_refraction_rand = dither_mat4x4[pix.x][pix.y];
g_diffuse_rand = g_reflection_rand = g_refraction_rand = g_closure_rand;
#else
g_diffuse_rand = 0.0;
g_reflection_rand = 0.0;

View File

@ -5,35 +5,36 @@
* This is used by alpha blended materials and materials using Shader to RGB nodes.
**/
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
float spec_light(ClosureReflection ref)
{
float gloss = saturate(1.0 - ref.roughness);
float shininess = exp2(10.0 * gloss + 1.0);
vec3 N = ref.N;
vec3 L = vec3(0.0, 0.0, 1.0);
vec3 H = normalize(L + cameraVec(g_data.P));
float spec_angle = saturate(dot(N, H));
float normalization_factor = shininess * 0.125 + 1.0;
float spec_light = pow(spec_angle, shininess) * saturate(dot(N, L)) * normalization_factor;
return spec_light;
}
vec4 closure_to_rgba(Closure cl)
{
vec3 diffuse_light = vec3(0.0);
vec3 reflection_light = vec3(0.0);
vec3 refraction_light = vec3(0.0);
float vP_z = dot(cameraForward, g_data.P) - dot(cameraForward, cameraPos);
light_eval(g_diffuse_data,
g_reflection_data,
g_data.P,
cameraVec(g_data.P),
vP_z,
0.01 /* TODO(fclem) thickness. */,
diffuse_light,
reflection_light);
vec4 out_color;
out_color.rgb = g_emission;
out_color.rgb += g_diffuse_data.color * g_diffuse_data.weight *
saturate(g_diffuse_data.N.z * 0.5 + 0.5);
out_color.rgb += g_reflection_data.color * g_reflection_data.weight *
spec_light(g_reflection_data);
out_color.rgb += g_refraction_data.color * g_refraction_data.weight *
saturate(g_refraction_data.N.z * 0.5 + 0.5);
out_color.rgb += g_diffuse_data.color * g_diffuse_data.weight * diffuse_light;
out_color.rgb += g_reflection_data.color * g_reflection_data.weight * reflection_light;
out_color.rgb += g_refraction_data.color * g_refraction_data.weight * refraction_light;
out_color.a = saturate(1.0 - avg(g_transmittance));
@ -47,15 +48,29 @@ void main()
{
init_globals();
float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r;
g_closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE));
fragment_displacement();
nodetree_surface();
g_holdout = saturate(g_holdout);
vec3 diffuse_light = vec3(saturate(g_diffuse_data.N.z * 0.5 + 0.5));
vec3 reflection_light = vec3(spec_light(g_reflection_data));
vec3 refraction_light = vec3(saturate(g_refraction_data.N.z * 0.5 + 0.5));
vec3 diffuse_light = vec3(0.0);
vec3 reflection_light = vec3(0.0);
vec3 refraction_light = vec3(0.0);
float vP_z = dot(cameraForward, g_data.P) - dot(cameraForward, cameraPos);
light_eval(g_diffuse_data,
g_reflection_data,
g_data.P,
cameraVec(g_data.P),
vP_z,
0.01 /* TODO(fclem) thickness. */,
diffuse_light,
reflection_light);
g_diffuse_data.color *= g_diffuse_data.weight;
g_reflection_data.color *= g_reflection_data.weight;

View File

@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "eevee_defines.hh"
#include "gpu_shader_create_info.hh"
/* -------------------------------------------------------------------- */
/** \name Shared
* \{ */
GPU_SHADER_CREATE_INFO(eevee_light_data)
.storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf")
.storage_buf(1, Qualifier::READ, "LightData", "light_buf[]")
.storage_buf(2, Qualifier::READ, "uint", "light_zbin_buf[]")
.storage_buf(3, Qualifier::READ, "uint", "light_tile_buf[]");
/** \} */
/* -------------------------------------------------------------------- */
/** \name Culling
* \{ */
GPU_SHADER_CREATE_INFO(eevee_light_culling_select)
.do_static_compilation(true)
.additional_info("eevee_shared", "draw_view")
.local_group_size(CULLING_SELECT_GROUP_SIZE)
.storage_buf(0, Qualifier::READ_WRITE, "LightCullingData", "light_cull_buf")
.storage_buf(1, Qualifier::READ, "LightData", "in_light_buf[]")
.storage_buf(2, Qualifier::WRITE, "LightData", "out_light_buf[]")
.storage_buf(3, Qualifier::WRITE, "float", "out_zdist_buf[]")
.storage_buf(4, Qualifier::WRITE, "uint", "out_key_buf[]")
.compute_source("eevee_light_culling_select_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_light_culling_sort)
.do_static_compilation(true)
.additional_info("eevee_shared", "draw_view")
.storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf")
.storage_buf(1, Qualifier::READ, "LightData", "in_light_buf[]")
.storage_buf(2, Qualifier::WRITE, "LightData", "out_light_buf[]")
.storage_buf(3, Qualifier::READ, "float", "in_zdist_buf[]")
.storage_buf(4, Qualifier::READ, "uint", "in_key_buf[]")
.local_group_size(CULLING_SORT_GROUP_SIZE)
.compute_source("eevee_light_culling_sort_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_light_culling_zbin)
.do_static_compilation(true)
.additional_info("eevee_shared", "draw_view")
.local_group_size(CULLING_ZBIN_GROUP_SIZE)
.storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf")
.storage_buf(1, Qualifier::READ, "LightData", "light_buf[]")
.storage_buf(2, Qualifier::WRITE, "uint", "out_zbin_buf[]")
.compute_source("eevee_light_culling_zbin_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_light_culling_tile)
.do_static_compilation(true)
.additional_info("eevee_shared", "draw_view")
.local_group_size(CULLING_TILE_GROUP_SIZE)
.storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf")
.storage_buf(1, Qualifier::READ, "LightData", "light_buf[]")
.storage_buf(2, Qualifier::WRITE, "uint", "out_light_tile_buf[]")
.compute_source("eevee_light_culling_tile_comp.glsl");
/** \} */
/* -------------------------------------------------------------------- */
/** \name Debug
* \{ */
GPU_SHADER_CREATE_INFO(eevee_light_culling_debug)
.do_static_compilation(true)
.sampler(0, ImageType::DEPTH_2D, "depth_tx")
.fragment_out(0, Type::VEC4, "out_debug_color")
.additional_info("eevee_shared", "draw_view")
.fragment_source("eevee_light_culling_debug_frag.glsl")
.additional_info("draw_fullscreen", "eevee_light_data");
/** \} */

View File

@ -16,6 +16,8 @@ GPU_SHADER_CREATE_INFO(eevee_sampling_data)
.additional_info("eevee_shared")
.storage_buf(14, Qualifier::READ, "SamplingData", "sampling_buf");
GPU_SHADER_CREATE_INFO(eevee_utility_texture).sampler(8, ImageType::FLOAT_2D_ARRAY, "utility_tx");
/** \} */
/* -------------------------------------------------------------------- */
@ -116,14 +118,14 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward)
.image_out(3, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_light_img")
.image_out(4, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_color_img")
.image_out(5, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_emission_img")
.additional_info("eevee_aov_out"
// "eevee_sampling_data",
.additional_info("eevee_aov_out",
"eevee_light_data",
"eevee_utility_texture",
"eevee_sampling_data"
// "eevee_lightprobe_data",
/* Optionally added depending on the material. */
// "eevee_raytrace_data",
// "eevee_transmittance_data",
// "eevee_utility_texture",
// "eevee_light_data",
// "eevee_shadow_data"
);

View File

@ -19,6 +19,8 @@
extern "C" {
#endif
#define GP_LIGHT
#include "gpencil_defines.h"
#include "gpencil_shader_shared.h"

View File

@ -7,7 +7,9 @@
typedef struct gpMaterial gpMaterial;
typedef struct gpLight gpLight;
typedef enum gpMaterialFlag gpMaterialFlag;
# ifdef GP_LIGHT
typedef enum gpLightType gpLightType;
# endif
# endif
#endif
@ -75,8 +77,9 @@ struct gpMaterial {
};
BLI_STATIC_ASSERT_ALIGN(gpMaterial, 16)
#ifdef GP_LIGHT
struct gpLight {
#ifndef GPU_SHADER
# ifndef GPU_SHADER
float3 color;
gpLightType type;
float3 right;
@ -87,7 +90,7 @@ struct gpLight {
float _pad0;
float3 position;
float _pad1;
#else
# else
/* Some drivers are completely messing the alignment or the fetches here.
* We are forced to pack these into vec4 otherwise we only get 0.0 as value. */
/* NOTE(@fclem): This was the case on MacOS OpenGL implementation.
@ -97,17 +100,18 @@ struct gpLight {
float4 packed2;
float4 packed3;
float4 packed4;
# define _color packed0.xyz
# define _type packed0.w
# define _right packed1.xyz
# define _spot_size packed1.w
# define _up packed2.xyz
# define _spot_blend packed2.w
# define _forward packed3.xyz
# define _position packed4.xyz
#endif
# define _color packed0.xyz
# define _type packed0.w
# define _right packed1.xyz
# define _spot_size packed1.w
# define _up packed2.xyz
# define _spot_blend packed2.w
# define _forward packed3.xyz
# define _position packed4.xyz
# endif
};
BLI_STATIC_ASSERT_ALIGN(gpLight, 16)
#endif
#ifndef GPU_SHADER
# undef gpMaterialFlag

View File

@ -20,8 +20,8 @@ GPU_SHADER_INTERFACE_INFO(gpencil_geometry_iface, "gp_interp")
GPU_SHADER_CREATE_INFO(gpencil_geometry)
.do_static_compilation(true)
.define("GP_LIGHT")
.typedef_source("gpencil_defines.h")
.typedef_source("gpencil_shader_shared.h")
.sampler(0, ImageType::FLOAT_2D, "gpFillTexture")
.sampler(1, ImageType::FLOAT_2D, "gpStrokeTexture")
.sampler(2, ImageType::DEPTH_2D, "gpSceneDepthTexture")

View File

@ -130,7 +130,7 @@ void main()
gl_Position = p1;
/* compute position from 3 vertex because the change in direction
* can happen very quicky and lead to very thin edges. */
* can happen very quickly and lead to very thin edges. */
vec2 ss0 = proj(p0);
vec2 ss1 = proj(p1);
vec2 ss2 = proj(p2);

View File

@ -113,17 +113,17 @@ class DebugDraw {
print_no_endl(str, args...);
print_newline();
}
template<typename T> void print(const T &&value)
template<typename T> void print(const T &value)
{
print_value(value);
print_newline();
}
template<typename T> void print_hex(const T &&value)
template<typename T> void print_hex(const T &value)
{
print_value_hex(value);
print_newline();
}
template<typename T> void print_binary(const T &&value)
template<typename T> void print_binary(const T &value)
{
print_value_binary(value);
print_newline();

View File

@ -2128,6 +2128,14 @@ void DRW_view_update(DRWView *view,
draw_frustum_bound_sphere_calc(
&view->frustum_corners, viewinv, winmat, wininv, &view->frustum_bsphere);
/* TODO(fclem): Deduplicate. */
for (int i = 0; i < 8; i++) {
copy_v3_v3(view->storage.frustum_corners[i], view->frustum_corners.vec[i]);
}
for (int i = 0; i < 6; i++) {
copy_v4_v4(view->storage.frustum_planes[i], view->frustum_planes[i]);
}
#ifdef DRW_DEBUG_CULLING
if (G.debug_value != 0) {
DRW_debug_sphere(

View File

@ -0,0 +1,59 @@
#pragma BLENDER_REQUIRE(common_shape_lib.glsl)
/* ---------------------------------------------------------------------- */
/** \name Axis Aligned Bound Box
* \{ */
struct AABB {
vec3 min, max;
};
AABB aabb_init_min_max()
{
AABB aabb;
aabb.min = vec3(1.0e30);
aabb.max = vec3(-1.0e30);
return aabb;
}
void aabb_merge(inout AABB aabb, vec3 v)
{
aabb.min = min(aabb.min, v);
aabb.max = max(aabb.max, v);
}
/**
* Return true if there is any intersection.
*/
bool aabb_intersect(AABB a, AABB b)
{
return all(greaterThanEqual(min(a.max, b.max), max(a.min, b.min)));
}
/**
* Compute intersect intersection volume of \a a and \a b.
* Return true if the resulting volume is not empty.
*/
bool aabb_clip(AABB a, AABB b, out AABB c)
{
c.min = max(a.min, b.min);
c.max = min(a.max, b.max);
return all(greaterThanEqual(c.max, c.min));
}
Box aabb_to_box(AABB aabb)
{
Box box;
box.corners[0] = aabb.min;
box.corners[1] = vec3(aabb.max.x, aabb.min.y, aabb.min.z);
box.corners[2] = vec3(aabb.max.x, aabb.max.y, aabb.min.z);
box.corners[3] = vec3(aabb.min.x, aabb.max.y, aabb.min.z);
box.corners[4] = vec3(aabb.min.x, aabb.min.y, aabb.max.z);
box.corners[5] = vec3(aabb.max.x, aabb.min.y, aabb.max.z);
box.corners[6] = aabb.max;
box.corners[7] = vec3(aabb.min.x, aabb.max.y, aabb.max.z);
return box;
}
/** \} */

View File

@ -11,7 +11,7 @@ bool drw_debug_draw_enable = true;
const vec4 drw_debug_default_color = vec4(1.0, 0.0, 0.0, 1.0);
/* -------------------------------------------------------------------- */
/** \name Interals
/** \name Internals
* \{ */
uint drw_debug_start_draw(uint v_needed)
@ -148,14 +148,14 @@ void drw_debug_sphere(vec3 p, float radius, vec4 color)
for (int axis = 0; axis < 3; axis++) {
for (int edge = 0; edge < circle_resolution; edge++) {
float angle1 = (2.0 * 3.141592) * float(edge + 0) / float(circle_resolution);
vec3 p1 = vec3(cos(angle1), sin(angle1), 0.0);
vec3 p1 = vec3(cos(angle1), sin(angle1), 0.0) * radius;
p1 = vec3(p1[(0 + axis) % 3], p1[(1 + axis) % 3], p1[(2 + axis) % 3]);
float angle2 = (2.0 * 3.141592) * float(edge + 1) / float(circle_resolution);
vec3 p2 = vec3(cos(angle2), sin(angle2), 0.0);
vec3 p2 = vec3(cos(angle2), sin(angle2), 0.0) * radius;
p2 = vec3(p2[(0 + axis) % 3], p2[(1 + axis) % 3], p2[(2 + axis) % 3]);
drw_debug_line(vertid, p1, p2, pcolor);
drw_debug_line(vertid, p + p1, p + p2, pcolor);
}
}
}

View File

@ -0,0 +1,57 @@
/**
* Debug drawing of shapes.
*/
#pragma BLENDER_REQUIRE(common_debug_draw_lib.glsl)
#pragma BLENDER_REQUIRE(common_shape_lib.glsl)
void drw_debug(Box shape, vec4 color)
{
drw_debug_quad(shape.corners[0], shape.corners[1], shape.corners[2], shape.corners[3], color);
drw_debug_line(shape.corners[0], shape.corners[4], color);
drw_debug_line(shape.corners[1], shape.corners[5], color);
drw_debug_line(shape.corners[2], shape.corners[6], color);
drw_debug_line(shape.corners[3], shape.corners[7], color);
drw_debug_quad(shape.corners[4], shape.corners[5], shape.corners[6], shape.corners[7], color);
}
void drw_debug(Box shape)
{
drw_debug(shape, drw_debug_default_color);
}
void drw_debug(Frustum shape, vec4 color)
{
drw_debug_quad(shape.corners[0], shape.corners[1], shape.corners[2], shape.corners[3], color);
drw_debug_line(shape.corners[0], shape.corners[4], color);
drw_debug_line(shape.corners[1], shape.corners[5], color);
drw_debug_line(shape.corners[2], shape.corners[6], color);
drw_debug_line(shape.corners[3], shape.corners[7], color);
drw_debug_quad(shape.corners[4], shape.corners[5], shape.corners[6], shape.corners[7], color);
}
void drw_debug(Frustum shape)
{
drw_debug(shape, drw_debug_default_color);
}
void drw_debug(Pyramid shape, vec4 color)
{
drw_debug_line(shape.corners[0], shape.corners[1], color);
drw_debug_line(shape.corners[0], shape.corners[2], color);
drw_debug_line(shape.corners[0], shape.corners[3], color);
drw_debug_line(shape.corners[0], shape.corners[4], color);
drw_debug_quad(shape.corners[1], shape.corners[2], shape.corners[3], shape.corners[4], color);
}
void drw_debug(Pyramid shape)
{
drw_debug(shape, drw_debug_default_color);
}
void drw_debug(Sphere shape, vec4 color)
{
drw_debug_sphere(shape.center, shape.radius, color);
}
void drw_debug(Sphere shape)
{
drw_debug(shape, drw_debug_default_color);
}

View File

@ -0,0 +1,399 @@
/**
* Intersection library used for culling.
* Results are meant to be conservative.
*/
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
#pragma BLENDER_REQUIRE(common_shape_lib.glsl)
/* ---------------------------------------------------------------------- */
/** \name Plane extraction functions.
* \{ */
/** \a v1 and \a v2 are vectors on the plane. \a p is a point on the plane. */
vec4 isect_plane_setup(vec3 p, vec3 v1, vec3 v2)
{
vec3 normal_to_plane = normalize(cross(v1, v2));
return vec4(normal_to_plane, -dot(normal_to_plane, p));
}
struct IsectPyramid {
vec3 corners[5];
vec4 planes[5];
};
IsectPyramid isect_data_setup(Pyramid shape)
{
vec3 A1 = shape.corners[1] - shape.corners[0];
vec3 A2 = shape.corners[2] - shape.corners[0];
vec3 A3 = shape.corners[3] - shape.corners[0];
vec3 A4 = shape.corners[4] - shape.corners[0];
vec3 S4 = shape.corners[4] - shape.corners[1];
vec3 S2 = shape.corners[2] - shape.corners[1];
IsectPyramid data;
data.planes[0] = isect_plane_setup(shape.corners[0], A2, A1);
data.planes[1] = isect_plane_setup(shape.corners[0], A3, A2);
data.planes[2] = isect_plane_setup(shape.corners[0], A4, A3);
data.planes[3] = isect_plane_setup(shape.corners[0], A1, A4);
data.planes[4] = isect_plane_setup(shape.corners[1], S2, S4);
for (int i = 0; i < 5; i++) {
data.corners[i] = shape.corners[i];
}
return data;
}
struct IsectBox {
vec3 corners[8];
vec4 planes[6];
};
IsectBox isect_data_setup(Box shape)
{
vec3 A1 = shape.corners[1] - shape.corners[0];
vec3 A3 = shape.corners[3] - shape.corners[0];
vec3 A4 = shape.corners[4] - shape.corners[0];
IsectBox data;
data.planes[0] = isect_plane_setup(shape.corners[0], A3, A1);
data.planes[1] = isect_plane_setup(shape.corners[0], A4, A3);
data.planes[2] = isect_plane_setup(shape.corners[0], A1, A4);
/* Assumes that the box is actually a box! */
data.planes[3] = vec4(-data.planes[0].xyz, -dot(-data.planes[0].xyz, shape.corners[6]));
data.planes[4] = vec4(-data.planes[1].xyz, -dot(-data.planes[1].xyz, shape.corners[6]));
data.planes[5] = vec4(-data.planes[2].xyz, -dot(-data.planes[2].xyz, shape.corners[6]));
for (int i = 0; i < 8; i++) {
data.corners[i] = shape.corners[i];
}
return data;
}
struct IsectFrustum {
vec3 corners[8];
vec4 planes[6];
};
IsectFrustum isect_data_setup(Frustum shape)
{
vec3 A1 = shape.corners[1] - shape.corners[0];
vec3 A3 = shape.corners[3] - shape.corners[0];
vec3 A4 = shape.corners[4] - shape.corners[0];
vec3 B5 = shape.corners[5] - shape.corners[6];
vec3 B7 = shape.corners[7] - shape.corners[6];
vec3 B2 = shape.corners[2] - shape.corners[6];
IsectFrustum data;
data.planes[0] = isect_plane_setup(shape.corners[0], A3, A1);
data.planes[1] = isect_plane_setup(shape.corners[0], A4, A3);
data.planes[2] = isect_plane_setup(shape.corners[0], A1, A4);
data.planes[3] = isect_plane_setup(shape.corners[6], B7, B5);
data.planes[4] = isect_plane_setup(shape.corners[6], B5, B2);
data.planes[5] = isect_plane_setup(shape.corners[6], B2, B7);
for (int i = 0; i < 8; i++) {
data.corners[i] = shape.corners[i];
}
return data;
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name View Intersection functions.
* \{ */
bool intersect_view(Pyramid pyramid)
{
bool intersects = true;
/* Do Pyramid vertices vs Frustum planes. */
for (int p = 0; p < 6; ++p) {
bool is_any_vertex_on_positive_side = false;
for (int v = 0; v < 5; ++v) {
float test = dot(drw_view.frustum_planes[p], vec4(pyramid.corners[v], 1.0));
if (test > 0.0) {
is_any_vertex_on_positive_side = true;
break;
}
}
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
if (all_vertex_on_negative_side) {
intersects = false;
break;
}
}
if (!intersects) {
return intersects;
}
/* Now do Frustum vertices vs Pyramid planes. */
IsectPyramid i_pyramid = isect_data_setup(pyramid);
for (int p = 0; p < 5; ++p) {
bool is_any_vertex_on_positive_side = false;
for (int v = 0; v < 8; ++v) {
float test = dot(i_pyramid.planes[p], vec4(drw_view.frustum_corners[v].xyz, 1.0));
if (test > 0.0) {
is_any_vertex_on_positive_side = true;
break;
}
}
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
if (all_vertex_on_negative_side) {
intersects = false;
break;
}
}
return intersects;
}
bool intersect_view(Box box)
{
bool intersects = true;
/* Do Box vertices vs Frustum planes. */
for (int p = 0; p < 6; ++p) {
bool is_any_vertex_on_positive_side = false;
for (int v = 0; v < 8; ++v) {
float test = dot(drw_view.frustum_planes[p], vec4(box.corners[v], 1.0));
if (test > 0.0) {
is_any_vertex_on_positive_side = true;
break;
}
}
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
if (all_vertex_on_negative_side) {
intersects = false;
break;
}
}
if (!intersects) {
return intersects;
}
/* Now do Frustum vertices vs Box planes. */
IsectBox i_box = isect_data_setup(box);
for (int p = 0; p < 6; ++p) {
bool is_any_vertex_on_positive_side = false;
for (int v = 0; v < 8; ++v) {
float test = dot(i_box.planes[p], vec4(drw_view.frustum_corners[v].xyz, 1.0));
if (test > 0.0) {
is_any_vertex_on_positive_side = true;
break;
}
}
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
if (all_vertex_on_negative_side) {
intersects = false;
break;
}
}
return intersects;
}
bool intersect_view(Sphere sphere)
{
bool intersects = true;
for (int p = 0; p < 6 && intersects; ++p) {
float dist_to_plane = dot(drw_view.frustum_planes[p], vec4(sphere.center, 1.0));
if (dist_to_plane < -sphere.radius) {
intersects = false;
}
}
/* TODO reject false positive. */
return intersects;
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Shape vs. Shape Intersection functions.
* \{ */
bool intersect(IsectPyramid i_pyramid, Box box)
{
bool intersects = true;
/* Do Box vertices vs Pyramid planes. */
for (int p = 0; p < 5; ++p) {
bool is_any_vertex_on_positive_side = false;
for (int v = 0; v < 8; ++v) {
float test = dot(i_pyramid.planes[p], vec4(box.corners[v], 1.0));
if (test > 0.0) {
is_any_vertex_on_positive_side = true;
break;
}
}
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
if (all_vertex_on_negative_side) {
intersects = false;
break;
}
}
if (!intersects) {
return intersects;
}
/* Now do Pyramid vertices vs Box planes. */
IsectBox i_box = isect_data_setup(box);
for (int p = 0; p < 6; ++p) {
bool is_any_vertex_on_positive_side = false;
for (int v = 0; v < 5; ++v) {
float test = dot(i_box.planes[p], vec4(i_pyramid.corners[v], 1.0));
if (test > 0.0) {
is_any_vertex_on_positive_side = true;
break;
}
}
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
if (all_vertex_on_negative_side) {
intersects = false;
break;
}
}
return intersects;
}
bool intersect(IsectFrustum i_frustum, Pyramid pyramid)
{
bool intersects = true;
/* Do Pyramid vertices vs Frustum planes. */
for (int p = 0; p < 6; ++p) {
bool is_any_vertex_on_positive_side = false;
for (int v = 0; v < 5; ++v) {
float test = dot(i_frustum.planes[p], vec4(pyramid.corners[v], 1.0));
if (test > 0.0) {
is_any_vertex_on_positive_side = true;
break;
}
}
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
if (all_vertex_on_negative_side) {
intersects = false;
break;
}
}
if (!intersects) {
return intersects;
}
/* Now do Frustum vertices vs Pyramid planes. */
IsectPyramid i_pyramid = isect_data_setup(pyramid);
for (int p = 0; p < 5; ++p) {
bool is_any_vertex_on_positive_side = false;
for (int v = 0; v < 8; ++v) {
float test = dot(i_pyramid.planes[p], vec4(i_frustum.corners[v].xyz, 1.0));
if (test > 0.0) {
is_any_vertex_on_positive_side = true;
break;
}
}
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
if (all_vertex_on_negative_side) {
intersects = false;
break;
}
}
return intersects;
}
bool intersect(IsectFrustum i_frustum, Box box)
{
bool intersects = true;
/* Do Box vertices vs Frustum planes. */
for (int p = 0; p < 6; ++p) {
bool is_any_vertex_on_positive_side = false;
for (int v = 0; v < 8; ++v) {
float test = dot(i_frustum.planes[p], vec4(box.corners[v], 1.0));
if (test > 0.0) {
is_any_vertex_on_positive_side = true;
break;
}
}
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
if (all_vertex_on_negative_side) {
intersects = false;
break;
}
}
if (!intersects) {
return intersects;
}
/* Now do Frustum vertices vs Box planes. */
IsectBox i_box = isect_data_setup(box);
for (int p = 0; p < 6; ++p) {
bool is_any_vertex_on_positive_side = false;
for (int v = 0; v < 8; ++v) {
float test = dot(i_box.planes[p], vec4(i_frustum.corners[v].xyz, 1.0));
if (test > 0.0) {
is_any_vertex_on_positive_side = true;
break;
}
}
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
if (all_vertex_on_negative_side) {
intersects = false;
break;
}
}
return intersects;
}
bool intersect(IsectFrustum i_frustum, Sphere sphere)
{
bool intersects = true;
for (int p = 0; p < 8; ++p) {
float dist_to_plane = dot(i_frustum.planes[p], vec4(sphere.center, 1.0));
if (dist_to_plane < -sphere.radius) {
intersects = false;
break;
}
}
return intersects;
}
bool intersect(Cone cone, Sphere sphere)
{
/**
* Following "Improve Tile-based Light Culling with Spherical-sliced Cone"
* by Eric Zhang
* https://lxjk.github.io/2018/03/25/Improve-Tile-based-Light-Culling-with-Spherical-sliced-Cone.html
*/
float sphere_distance = length(sphere.center);
float sphere_distance_rcp = safe_rcp(sphere_distance);
float sphere_sin = saturate(sphere.radius * sphere_distance_rcp);
float sphere_cos = sqrt(1.0 - sphere_sin * sphere_sin);
float cone_aperture_sin = sqrt(1.0 - cone.angle_cos * cone.angle_cos);
float cone_sphere_center_cos = dot(sphere.center * sphere_distance_rcp, cone.direction);
/* cos(A+B) = cos(A) * cos(B) - sin(A) * sin(B). */
float cone_sphere_angle_sum_cos = (sphere.radius > sphere_distance) ?
-1.0 :
(cone.angle_cos * sphere_cos -
cone_aperture_sin * sphere_sin);
/* Comparing cosines instead of angles since we are interested
* only in the monotonic region [0 .. M_PI / 2]. This saves costly acos() calls. */
bool intersects = (cone_sphere_center_cos >= cone_sphere_angle_sum_cos);
return intersects;
}
bool intersect(Circle circle_a, Circle circle_b)
{
return distance_squared(circle_a.center, circle_b.center) <
sqr(circle_a.radius + circle_b.radius);
}
/** \} */

View File

@ -5,6 +5,18 @@
/** \name Math intersection & projection functions.
* \{ */
vec4 plane_from_quad(vec3 v0, vec3 v1, vec3 v2, vec3 v3)
{
vec3 nor = normalize(cross(v2 - v1, v0 - v1) + cross(v0 - v3, v2 - v3));
return vec4(nor, -dot(nor, v2));
}
vec4 plane_from_tri(vec3 v0, vec3 v1, vec3 v2)
{
vec3 nor = normalize(cross(v2 - v1, v0 - v1));
return vec4(nor, -dot(nor, v2));
}
float point_plane_projection_dist(vec3 line_origin, vec3 plane_origin, vec3 plane_normal)
{
return dot(plane_normal, plane_origin - line_origin);

View File

@ -0,0 +1,202 @@
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
/**
* Geometric shape structures.
* Some constructors might seems redundant but are here to make the API cleaner and
* allow for more than one constructor per type.
*/
/* ---------------------------------------------------------------------- */
/** \name Circle
* \{ */
struct Circle {
vec2 center;
float radius;
};
Circle shape_circle(vec2 center, float radius)
{
return Circle(center, radius);
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Sphere
* \{ */
struct Sphere {
vec3 center;
float radius;
};
Sphere shape_sphere(vec3 center, float radius)
{
return Sphere(center, radius);
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Box
* \{ */
struct Box {
vec3 corners[8];
};
/* Construct box from 4 basis points. */
Box shape_box(vec3 v000, vec3 v100, vec3 v010, vec3 v001)
{
v100 -= v000;
v010 -= v000;
v001 -= v000;
Box box;
box.corners[0] = v000;
box.corners[1] = v000 + v100;
box.corners[2] = v000 + v010 + v100;
box.corners[3] = v000 + v010;
box.corners[4] = box.corners[0] + v001;
box.corners[5] = box.corners[1] + v001;
box.corners[6] = box.corners[2] + v001;
box.corners[7] = box.corners[3] + v001;
return box;
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Square Pyramid
* \{ */
struct Pyramid {
/* Apex is the first. Base vertices are in clockwise order from front view. */
vec3 corners[5];
};
/**
* Regular Square Pyramid (can be oblique).
* Use this corner order.
* (Top-Down View of the pyramid)
* <pre>
*
* Y
* |
* |
* .-----X
*
* 4-----------3
* | \ / |
* | \ / |
* | 0 |
* | / \ |
* | / \ |
* 1-----------2
* </pre>
* base_corner_00 is vertex 1
* base_corner_01 is vertex 2
* base_corner_10 is vertex 4
*/
Pyramid shape_pyramid(vec3 apex, vec3 base_corner_00, vec3 base_corner_01, vec3 base_corner_10)
{
Pyramid pyramid;
pyramid.corners[0] = apex;
pyramid.corners[1] = base_corner_00;
pyramid.corners[2] = base_corner_01;
pyramid.corners[3] = base_corner_10 + (base_corner_01 - base_corner_00);
pyramid.corners[4] = base_corner_10;
return pyramid;
}
/**
* Regular Square Pyramid.
* <pre>
*
* Y
* |
* |
* .-----X
*
* 4-----Y-----3
* | \ | / |
* | \ | / |
* | 0-----X
* | / \ |
* | / \ |
* 1-----------2
* </pre>
* base_center_pos_x is vector from base center to X
* base_center_pos_y is vector from base center to Y
*/
Pyramid shape_pyramid_non_oblique(vec3 apex,
vec3 base_center,
vec3 base_center_pos_x,
vec3 base_center_pos_y)
{
Pyramid pyramid;
pyramid.corners[0] = apex;
pyramid.corners[1] = base_center - base_center_pos_x - base_center_pos_y;
pyramid.corners[2] = base_center + base_center_pos_x - base_center_pos_y;
pyramid.corners[3] = base_center + base_center_pos_x + base_center_pos_y;
pyramid.corners[4] = base_center - base_center_pos_x + base_center_pos_y;
return pyramid;
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Frustum
* \{ */
struct Frustum {
vec3 corners[8];
};
/**
* Use this corner order.
* <pre>
*
* Z Y
* | /
* |/
* .-----X
* 2----------6
* /| /|
* / | / |
* 1----------5 |
* | | | |
* | 3-------|--7
* | / | /
* |/ |/
* 0----------4
* </pre>
*/
Frustum shape_frustum(vec3 corners[8])
{
Frustum frustum;
for (int i = 0; i < 8; i++) {
frustum.corners[i] = corners[i];
}
return frustum;
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Cone
* \{ */
/* Cone at orign with no height. */
struct Cone {
vec3 direction;
float angle_cos;
};
Cone shape_cone(vec3 direction, float angle_cosine)
{
return Cone(direction, angle_cosine);
}
/** \} */

View File

@ -448,7 +448,7 @@ void ED_mesh_mirrtopo_init(struct BMEditMesh *em,
bool skip_em_vert_array_init);
void ED_mesh_mirrtopo_free(MirrTopoStore_t *mesh_topo_store);
/* object_vgroup.c */
/* object_vgroup.cc */
#define WEIGHT_REPLACE 1
#define WEIGHT_ADD 2

View File

@ -6458,13 +6458,13 @@ bool uiTemplateEventFromKeymapItem(struct uiLayout *layout,
for (int j = 0; j < ARRAY_SIZE(icon_mod) && icon_mod[j]; j++) {
uiItemL(layout, "", icon_mod[j]);
}
uiItemL(layout, TIP_(text), icon);
uiItemL(layout, CTX_TIP_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, text), icon);
ok = true;
}
else if (text_fallback) {
const char *event_text = WM_key_event_string(kmi->type, true);
uiItemL(layout, event_text, ICON_NONE);
uiItemL(layout, TIP_(text), ICON_NONE);
uiItemL(layout, CTX_TIP_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, text), ICON_NONE);
ok = true;
}
return ok;

View File

@ -53,7 +53,7 @@ set(SRC
object_shapekey.c
object_transform.cc
object_utils.c
object_vgroup.c
object_vgroup.cc
object_volume.c
object_warp.c

View File

@ -259,7 +259,7 @@ void CONSTRAINT_OT_objectsolver_set_inverse(struct wmOperatorType *ot);
void CONSTRAINT_OT_objectsolver_clear_inverse(struct wmOperatorType *ot);
void CONSTRAINT_OT_followpath_path_animate(struct wmOperatorType *ot);
/* object_vgroup.c */
/* object_vgroup.cc */
void OBJECT_OT_vertex_group_add(struct wmOperatorType *ot);
void OBJECT_OT_vertex_group_remove(struct wmOperatorType *ot);

View File

@ -486,6 +486,9 @@ bool ED_object_modifier_move_to_index(ReportList *reports,
}
}
/* NOTE: Dependency graph only uses modifier nodes for visibility updates, and exact order of
* modifier nodes in the graph does not matter. */
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
@ -1668,6 +1671,7 @@ static int modifier_copy_exec(bContext *C, wmOperator *op)
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;

View File

@ -443,7 +443,7 @@ void fsmenu_insert_entry(struct FSMenu *fsmenu,
if (STREQ(tfsm->path, fsm_iter->path)) {
icon = tfsm->icon;
if (tfsm->name[0] && (!name || !name[0])) {
name = tfsm->name;
name = DATA_(tfsm->name);
}
break;
}

View File

@ -495,6 +495,7 @@ set(SRC_SHADER_CREATE_INFOS
../draw/engines/basic/shaders/infos/basic_depth_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh

View File

@ -111,7 +111,7 @@ void BKE_id_attribute_copy_domains_temp(short UNUSED(id_type),
const struct CustomData *UNUSED(ldata),
const struct CustomData *UNUSED(pdata),
const struct CustomData *UNUSED(cdata),
struct ID *UNUSED(i_id))
struct ID *UNUSED(r_id))
{
}
@ -225,6 +225,13 @@ bool CustomData_has_layer(const struct CustomData *UNUSED(data), int UNUSED(type
return false;
}
void *CustomData_get_layer_named(const struct CustomData *UNUSED(data),
int UNUSED(type),
const char *UNUSED(name))
{
return nullptr;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -108,7 +108,7 @@ struct GPUSource {
string_preprocess();
}
if ((source.find("drw_debug_") != StringRef::not_found) &&
/* Avoid theses two files where it makes no sense to add the dependency. */
/* Avoid these two files where it makes no sense to add the dependency. */
(filename != "common_debug_draw_lib.glsl" &&
filename != "draw_debug_draw_display_vert.glsl")) {
builtins |= shader::BuiltinBits::USE_DEBUG_DRAW;

View File

@ -2209,7 +2209,7 @@ enum {
OB_DRAW_GROUPUSER_ALL = 2,
};
/* object_vgroup.c */
/* object_vgroup.cc */
/** #ToolSettings.vgroupsubset */
typedef enum eVGroupSelect {

View File

@ -990,6 +990,8 @@ void render_result_exr_file_cache_write(Render *re)
char str[FILE_MAXFILE + FILE_MAXFILE + MAX_ID_NAME + 100];
char *root = U.render_cachedir;
render_result_passes_allocated_ensure(rr);
render_result_exr_file_cache_path(re->scene, root, str);
printf("Caching exr file, %dx%d, %s\n", rr->rectx, rr->recty, str);

View File

@ -66,7 +66,7 @@ typedef struct wmMsg {
} wmMsg;
typedef struct wmMsgSubscribeKey {
/** Linked list for predicable ordering, otherwise we would depend on #GHash bucketing. */
/** Linked list for predictable ordering, otherwise we would depend on #GHash bucketing. */
struct wmMsgSubscribeKey *next, *prev;
ListBase values;
/* over-alloc, eg: wmMsgSubscribeKey_RNA */