Merge branch 'master' into refactor-mesh-selection-generic
This commit is contained in:
commit
c4dc870a45
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ typedef struct NlaEvalData {
|
|||
int num_channels;
|
||||
NlaEvalSnapshot base_snapshot;
|
||||
|
||||
/* Evaluation result shapshot. */
|
||||
/* Evaluation result snapshot. */
|
||||
NlaEvalSnapshot eval_snapshot;
|
||||
} NlaEvalData;
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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(
|
||||
|
|
|
@ -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 "";
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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 \
|
||||
} \
|
||||
} \
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
||||
/** \} */
|
|
@ -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"
|
||||
);
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define GP_LIGHT
|
||||
|
||||
#include "gpencil_defines.h"
|
||||
#include "gpencil_shader_shared.h"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -2209,7 +2209,7 @@ enum {
|
|||
OB_DRAW_GROUPUSER_ALL = 2,
|
||||
};
|
||||
|
||||
/* object_vgroup.c */
|
||||
/* object_vgroup.cc */
|
||||
|
||||
/** #ToolSettings.vgroupsubset */
|
||||
typedef enum eVGroupSelect {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue