Cycles: only apply function #ensure_valid_reflection to glossy materials

This function checks if the shading normal would result in an invalid reflection into the lower hemisphere; if it is the case, the function raises the shading normal just enough so that the specular reflection lies above the surface. This is a trick to prevent dark regions at grazing angles caused by normal/bump maps. However, the specular direction is not a good representation for a diffuse material, applying this function sometimes brightens the result too much and causes unexpected results. This patch applies the function to only glossy materials instead.

Pull Request: #105776
This commit is contained in:
Weizhen Huang
2023-03-14 17:02:09 +01:00
parent 70f3382c45
commit dfe7b839bc
6 changed files with 32 additions and 63 deletions

View File

@@ -180,4 +180,14 @@ ccl_device float3 ensure_valid_reflection(float3 Ng, float3 I, float3 N)
return Nx * X + Nz * Ng;
}
/* Do not call #ensure_valid_reflection if the primitive type is curve or if the geometry
* normal and the shading normal is the same. */
ccl_device float3 maybe_ensure_valid_reflection(ccl_private ShaderData *sd, float3 N)
{
if ((sd->type & PRIMITIVE_CURVE) || isequal(sd->Ng, N)) {
return N;
}
return ensure_valid_reflection(sd->Ng, sd->wi, N);
}
CCL_NAMESPACE_END

View File

@@ -78,7 +78,7 @@ ccl_device void osl_closure_diffuse_setup(KernelGlobals kg,
return;
}
bsdf->N = ensure_valid_reflection(sd->Ng, sd->wi, closure->N);
bsdf->N = closure->N;
sd->flag |= bsdf_diffuse_setup(bsdf);
}
@@ -99,7 +99,7 @@ ccl_device void osl_closure_oren_nayar_setup(KernelGlobals kg,
return;
}
bsdf->N = ensure_valid_reflection(sd->Ng, sd->wi, closure->N);
bsdf->N = closure->N;
bsdf->roughness = closure->roughness;
sd->flag |= bsdf_oren_nayar_setup(bsdf);
@@ -121,7 +121,7 @@ ccl_device void osl_closure_translucent_setup(KernelGlobals kg,
return;
}
bsdf->N = ensure_valid_reflection(sd->Ng, sd->wi, closure->N);
bsdf->N = closure->N;
sd->flag |= bsdf_translucent_setup(bsdf);
}
@@ -999,7 +999,7 @@ ccl_device void osl_closure_principled_diffuse_setup(
return;
}
bsdf->N = ensure_valid_reflection(sd->Ng, sd->wi, closure->N);
bsdf->N = closure->N;
bsdf->roughness = closure->roughness;
sd->flag |= bsdf_principled_diffuse_setup(bsdf);
@@ -1022,7 +1022,7 @@ ccl_device void osl_closure_principled_sheen_setup(
return;
}
bsdf->N = ensure_valid_reflection(sd->Ng, sd->wi, closure->N);
bsdf->N = closure->N;
bsdf->avg_value = 0.0f;
sd->flag |= bsdf_principled_sheen_setup(sd, bsdf);
@@ -1109,7 +1109,7 @@ ccl_device void osl_closure_diffuse_ramp_setup(KernelGlobals kg,
return;
}
bsdf->N = ensure_valid_reflection(sd->Ng, sd->wi, closure->N);
bsdf->N = closure->N;
bsdf->colors = (float3 *)closure_alloc_extra(sd, sizeof(float3) * 8);
if (!bsdf->colors) {
@@ -1185,7 +1185,7 @@ ccl_device void osl_closure_bssrdf_setup(KernelGlobals kg,
/* create one closure per color channel */
bssrdf->albedo = closure->albedo;
bssrdf->N = ensure_valid_reflection(sd->Ng, sd->wi, closure->N);
bssrdf->N = closure->N;
bssrdf->roughness = closure->roughness;
bssrdf->anisotropy = clamp(closure->anisotropy, 0.0f, 0.9f);

View File

@@ -50,6 +50,4 @@ surface node_bump(int invert = 0,
if (use_object_space) {
NormalOut = normalize(transform("object", "world", NormalOut));
}
NormalOut = ensure_valid_reflection(Ng, I, NormalOut);
}

View File

@@ -65,38 +65,4 @@ closure color principled_hair(normal N,
closure color henyey_greenstein(float g) BUILTIN;
closure color absorption() BUILTIN;
normal ensure_valid_reflection(normal Ng, vector I, normal N)
{
/* The implementation here mirrors the one in bsdf_util.h,
* check there for an explanation of the algorithm. */
float sqr(float x)
{
return x * x;
}
vector R = 2 * dot(N, I) * N - I;
float Iz = dot(I, Ng);
float threshold = min(0.9 * Iz, 0.01);
if (dot(Ng, R) >= threshold) {
return N;
}
vector X = normalize(N - dot(N, Ng) * Ng);
float Ix = dot(I, X);
float a = sqr(Ix) + sqr(Iz);
float b = 2.0 * (a + Iz * threshold);
float c = sqr(threshold + Iz);
float Nz2 = (Ix < 0) ? 0.25 * (b + sqrt(sqr(b) - 4.0 * a * c)) / a :
0.25 * (b - sqrt(sqr(b) - 4.0 * a * c)) / a;
float Nx = sqrt(1.0 - Nz2);
float Nz = sqrt(Nz2);
return Nx * X + Nz * Ng;
}
#endif /* CCL_STDOSL_H */

View File

@@ -59,9 +59,6 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
float3 N = stack_valid(data_node.x) ? safe_normalize(stack_load_float3(stack, data_node.x)) :
sd->N;
if (!(sd->type & PRIMITIVE_CURVE)) {
N = ensure_valid_reflection(sd->Ng, sd->wi, N);
}
float param1 = (stack_valid(param1_offset)) ? stack_load_float(stack, param1_offset) :
__uint_as_float(node.z);
@@ -119,8 +116,9 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
/* calculate ior */
float ior = (sd->flag & SD_BACKFACING) ? 1.0f / eta : eta;
// calculate fresnel for refraction
float cosNI = dot(N, sd->wi);
/* Calculate fresnel for refraction. */
float3 valid_reflection_N = maybe_ensure_valid_reflection(sd, N);
float cosNI = dot(valid_reflection_N, sd->wi);
float fresnel = fresnel_dielectric_cos(cosNI, ior);
// calculate weights of the diffuse and specular part
@@ -142,9 +140,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
float3 clearcoat_normal = stack_valid(data_cn_ssr.x) ?
stack_load_float3(stack, data_cn_ssr.x) :
sd->N;
if (!(sd->type & PRIMITIVE_CURVE)) {
clearcoat_normal = ensure_valid_reflection(sd->Ng, sd->wi, clearcoat_normal);
}
clearcoat_normal = maybe_ensure_valid_reflection(sd, clearcoat_normal);
float3 subsurface_radius = stack_valid(data_cn_ssr.y) ?
stack_load_float3(stack, data_cn_ssr.y) :
one_float3();
@@ -271,7 +267,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
NULL;
if (bsdf && fresnel) {
bsdf->N = N;
bsdf->N = valid_reflection_N;
bsdf->ior = (2.0f / (1.0f - safe_sqrtf(0.08f * specular))) - 1.0f;
bsdf->T = T;
@@ -333,7 +329,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
NULL;
if (bsdf && fresnel) {
bsdf->N = N;
bsdf->N = valid_reflection_N;
bsdf->T = zero_float3();
bsdf->fresnel = fresnel;
@@ -362,7 +358,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
sizeof(MicrofacetBsdf),
rgb_to_spectrum(base_color) * glass_weight * refraction_fresnel);
if (bsdf) {
bsdf->N = N;
bsdf->N = valid_reflection_N;
bsdf->T = zero_float3();
bsdf->fresnel = NULL;
@@ -390,7 +386,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
NULL;
if (bsdf && fresnel) {
bsdf->N = N;
bsdf->N = valid_reflection_N;
bsdf->fresnel = fresnel;
bsdf->T = zero_float3();
@@ -461,7 +457,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
sd, sizeof(DiffuseBsdf), weight);
if (bsdf) {
bsdf->N = N;
bsdf->N = maybe_ensure_valid_reflection(sd, N);
sd->flag |= bsdf_translucent_setup(bsdf);
}
break;
@@ -490,7 +486,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
float roughness = sqr(param1);
bsdf->N = N;
bsdf->N = maybe_ensure_valid_reflection(sd, N);
bsdf->ior = 1.0f;
bsdf->fresnel = NULL;
@@ -554,7 +550,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
sd, sizeof(MicrofacetBsdf), weight);
if (bsdf) {
bsdf->N = N;
bsdf->N = maybe_ensure_valid_reflection(sd, N);
bsdf->T = zero_float3();
bsdf->fresnel = NULL;
@@ -597,7 +593,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
sd, sizeof(MicrofacetBsdf), weight);
if (bsdf) {
bsdf->N = N;
bsdf->N = maybe_ensure_valid_reflection(sd, N);
bsdf->T = zero_float3();
bsdf->fresnel = NULL;
@@ -646,7 +642,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
break;
}
bsdf->N = N;
bsdf->N = maybe_ensure_valid_reflection(sd, N);
bsdf->fresnel = fresnel;
bsdf->T = zero_float3();
@@ -752,7 +748,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
float coat = stack_load_float_default(stack, coat_ofs, data_node2.y);
float m0_roughness = 1.0f - clamp(coat, 0.0f, 1.0f);
bsdf->N = N;
bsdf->N = maybe_ensure_valid_reflection(sd, N);
bsdf->v = roughness;
bsdf->s = radial_roughness;
bsdf->m0_roughness = m0_roughness;
@@ -820,7 +816,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
sd, sizeof(HairBsdf), weight);
if (bsdf) {
bsdf->N = N;
bsdf->N = maybe_ensure_valid_reflection(sd, N);
bsdf->roughness1 = param1;
bsdf->roughness2 = param2;
bsdf->offset = -stack_load_float(stack, data_node.z);

View File

@@ -71,7 +71,6 @@ ccl_device_noinline void svm_node_set_bump(KernelGlobals kg,
object_normal_transform(kg, sd, &normal_out);
}
normal_out = ensure_valid_reflection(sd->Ng, sd->wi, normal_out);
stack_store_float3(stack, node.w, normal_out);
}
else