Merge pull request #22483 from tagcup/fresnel
Restore the Fresnel term in the BRDF.
This commit is contained in:
commit
9c93a401b9
2 changed files with 94 additions and 57 deletions
|
@ -898,10 +898,11 @@ varying vec2 uv2_interp;
|
|||
|
||||
varying vec3 view_interp;
|
||||
|
||||
vec3 metallic_to_specular_color(float metallic, float specular, vec3 albedo) {
|
||||
float dielectric = (0.034 * 2.0) * specular;
|
||||
// energy conservation
|
||||
return mix(vec3(dielectric), albedo, metallic); // TODO: reference?
|
||||
vec3 F0(float metallic, float specular, vec3 albedo) {
|
||||
float dielectric = 0.16 * specular * specular;
|
||||
// use albedo * metallic as colored specular reflectance at 0 angle for metallic materials;
|
||||
// see https://google.github.io/filament/Filament.md.html
|
||||
return mix(vec3(dielectric), albedo, vec3(metallic));
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
|
@ -934,6 +935,7 @@ varying highp float dp_clip;
|
|||
// E. Heitz, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs", J. Comp. Graph. Tech. 3 (2) (2014).
|
||||
// Eqns 71-72 and 85-86 (see also Eqns 43 and 80).
|
||||
|
||||
/*
|
||||
float G_GGX_2cos(float cos_theta_m, float alpha) {
|
||||
// Schlick's approximation
|
||||
// C. Schlick, "An Inexpensive BRDF Model for Physically-based Rendering", Computer Graphics Forum. 13 (3): 233 (1994)
|
||||
|
@ -946,6 +948,15 @@ float G_GGX_2cos(float cos_theta_m, float alpha) {
|
|||
// float sin2 = (1.0 - cos2);
|
||||
// return 1.0 / (cos_theta_m + sqrt(cos2 + alpha * alpha * sin2));
|
||||
}
|
||||
*/
|
||||
|
||||
// This approximates G_GGX_2cos(cos_theta_l, alpha) * G_GGX_2cos(cos_theta_v, alpha)
|
||||
// See Filament docs, Specular G section.
|
||||
float V_GGX(float cos_theta_l, float cos_theta_v, float alpha) {
|
||||
float v = cos_theta_l * (cos_theta_v * (1.0 - alpha) + alpha);
|
||||
float l = cos_theta_v * (cos_theta_l * (1.0 - alpha) + alpha);
|
||||
return 0.5 / (v + l);
|
||||
}
|
||||
|
||||
float D_GGX(float cos_theta_m, float alpha) {
|
||||
float alpha2 = alpha * alpha;
|
||||
|
@ -953,6 +964,7 @@ float D_GGX(float cos_theta_m, float alpha) {
|
|||
return alpha2 / (M_PI * d * d);
|
||||
}
|
||||
|
||||
/*
|
||||
float G_GGX_anisotropic_2cos(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) {
|
||||
float cos2 = cos_theta_m * cos_theta_m;
|
||||
float sin2 = (1.0 - cos2);
|
||||
|
@ -960,14 +972,30 @@ float G_GGX_anisotropic_2cos(float cos_theta_m, float alpha_x, float alpha_y, fl
|
|||
float s_y = alpha_y * sin_phi;
|
||||
return 1.0 / max(cos_theta_m + sqrt(cos2 + (s_x * s_x + s_y * s_y) * sin2), 0.001);
|
||||
}
|
||||
*/
|
||||
|
||||
float D_GGX_anisotropic(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) {
|
||||
float cos2 = cos_theta_m * cos_theta_m;
|
||||
// This approximates G_GGX_anisotropic_2cos(cos_theta_l, ...) * G_GGX_anisotropic_2cos(cos_theta_v, ...)
|
||||
// See Filament docs, Anisotropic specular BRDF section.
|
||||
float V_GGX_anisotropic(float alpha_x, float alpha_y, float TdotV, float TdotL, float BdotV, float BdotL, float NdotV, float NdotL) {
|
||||
float Lambda_V = NdotL * length(vec3(alpha_x * TdotV, alpha_y * BdotV, NdotV));
|
||||
float Lambda_L = NdotV * length(vec3(alpha_x * TdotL, alpha_y * BdotL, NdotL));
|
||||
return 0.5 / (Lambda_V + Lambda_L);
|
||||
}
|
||||
|
||||
float D_GGX_anisotropic(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi, float NdotH) {
|
||||
float alpha2 = alpha_x * alpha_y;
|
||||
highp vec3 v = vec3(alpha_y * cos_phi, alpha_x * sin_phi, alpha2 * NdotH);
|
||||
highp float v2 = dot(v, v);
|
||||
float w2 = alpha2 / v2;
|
||||
float D = alpha2 * w2 * w2 * (1.0 / M_PI);
|
||||
return D;
|
||||
|
||||
/* float cos2 = cos_theta_m * cos_theta_m;
|
||||
float sin2 = (1.0 - cos2);
|
||||
float r_x = cos_phi / alpha_x;
|
||||
float r_y = sin_phi / alpha_y;
|
||||
float d = cos2 + sin2 * (r_x * r_x + r_y * r_y);
|
||||
return 1.0 / max(M_PI * alpha_x * alpha_y * d * d, 0.001);
|
||||
return 1.0 / max(M_PI * alpha_x * alpha_y * d * d, 0.001); */
|
||||
}
|
||||
|
||||
float SchlickFresnel(float u) {
|
||||
|
@ -996,6 +1024,7 @@ void light_compute(
|
|||
float specular_blob_intensity,
|
||||
float roughness,
|
||||
float metallic,
|
||||
float specular,
|
||||
float rim,
|
||||
float rim_tint,
|
||||
float clearcoat,
|
||||
|
@ -1112,9 +1141,11 @@ LIGHT_SHADER_CODE
|
|||
|
||||
if (roughness > 0.0) {
|
||||
|
||||
// D
|
||||
|
||||
float specular_brdf_NL;
|
||||
#if defined(SPECULAR_SCHLICK_GGX)
|
||||
vec3 specular_brdf_NL = vec3(0.0);
|
||||
#else
|
||||
float specular_brdf_NL = 0.0;
|
||||
#endif
|
||||
|
||||
#if defined(SPECULAR_BLINN)
|
||||
|
||||
|
@ -1147,7 +1178,6 @@ LIGHT_SHADER_CODE
|
|||
|
||||
#elif defined(SPECULAR_DISABLED)
|
||||
// none..
|
||||
specular_brdf_NL = 0.0;
|
||||
#elif defined(SPECULAR_SCHLICK_GGX)
|
||||
// shlick+ggx as default
|
||||
|
||||
|
@ -1157,28 +1187,28 @@ LIGHT_SHADER_CODE
|
|||
float cLdotH = max(dot(L, H), 0.0);
|
||||
|
||||
#if defined(LIGHT_USE_ANISOTROPY)
|
||||
|
||||
float alpha = roughness * roughness;
|
||||
float aspect = sqrt(1.0 - anisotropy * 0.9);
|
||||
float rx = roughness / aspect;
|
||||
float ry = roughness * aspect;
|
||||
float ax = rx * rx;
|
||||
float ay = ry * ry;
|
||||
float XdotH = dot(T, H);
|
||||
float YdotH = dot(B, H);
|
||||
float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH);
|
||||
float G = G_GGX_anisotropic_2cos(cNdotL, ax, ay, XdotH, YdotH) * G_GGX_anisotropic_2cos(cNdotV, ax, ay, XdotH, YdotH);
|
||||
float ax = alpha / aspect;
|
||||
float ay = alpha * aspect;
|
||||
//float XdotH = dot(T, H);
|
||||
//float YdotH = dot(B, H);
|
||||
float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH, cNdotH);
|
||||
//float G = G_GGX_anisotropic_2cos(cNdotL, ax, ay, XdotH, YdotH) * G_GGX_anisotropic_2cos(cNdotV, ax, ay, XdotH, YdotH);
|
||||
float G = V_GGX_anisotropic(ax, ay, dot(T, V), dot(T, L), dot(B, V), dot(B, L), cNdotV, cNdotL))
|
||||
|
||||
#else
|
||||
float alpha = roughness * roughness;
|
||||
float D = D_GGX(cNdotH, alpha);
|
||||
float G = G_GGX_2cos(cNdotL, alpha) * G_GGX_2cos(cNdotV, alpha);
|
||||
//float G = G_GGX_2cos(cNdotL, alpha) * G_GGX_2cos(cNdotV, alpha);
|
||||
float G = V_GGX(cNdotL, cNdotV, alpha);
|
||||
#endif
|
||||
// F
|
||||
//float F0 = 1.0;
|
||||
//float cLdotH5 = SchlickFresnel(cLdotH);
|
||||
//float F = mix(cLdotH5, 1.0, F0);
|
||||
vec3 f0 = F0(metallic, specular, diffuse_color);
|
||||
float cLdotH5 = SchlickFresnel(cLdotH);
|
||||
vec3 F = mix(vec3(cLdotH5), vec3(1.0), f0);
|
||||
|
||||
specular_brdf_NL = cNdotL * D /* F */ * G;
|
||||
specular_brdf_NL = cNdotL * D * F * G;
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1197,11 +1227,12 @@ LIGHT_SHADER_CODE
|
|||
#endif
|
||||
float Dr = GTR1(cNdotH, mix(.1, .001, clearcoat_gloss));
|
||||
float Fr = mix(.04, 1.0, cLdotH5);
|
||||
float Gr = G_GGX_2cos(cNdotL, .25) * G_GGX_2cos(cNdotV, .25);
|
||||
//float Gr = G_GGX_2cos(cNdotL, .25) * G_GGX_2cos(cNdotV, .25);
|
||||
float Gr = V_GGX(cNdotL, cNdotV, 0.25);
|
||||
|
||||
float specular_brdf_NL = 0.25 * clearcoat * Gr * Fr * Dr * cNdotL;
|
||||
float clearcoat_specular_brdf_NL = 0.25 * clearcoat * Gr * Fr * Dr * cNdotL;
|
||||
|
||||
specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation;
|
||||
specular_light += clearcoat_specular_brdf_NL * light_color * specular_blob_intensity * attenuation;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -1290,6 +1321,11 @@ void main() {
|
|||
float alpha = 1.0;
|
||||
float side = 1.0;
|
||||
|
||||
float specular_blob_intensity = 1.0;
|
||||
#if defined(SPECULAR_TOON)
|
||||
specular_blob_intensity *= specular * 2.0;
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_AO)
|
||||
float ao = 1.0;
|
||||
float ao_light_affect = 0.0;
|
||||
|
@ -1808,7 +1844,7 @@ FRAGMENT_SHADER_CODE
|
|||
#ifdef USE_VERTEX_LIGHTING
|
||||
//vertex lighting
|
||||
|
||||
specular_light += specular_interp * specular * light_att;
|
||||
specular_light += specular_interp * specular_blob_intensity * light_att;
|
||||
diffuse_light += diffuse_interp * albedo * light_att;
|
||||
|
||||
#else
|
||||
|
@ -1823,9 +1859,10 @@ FRAGMENT_SHADER_CODE
|
|||
light_att,
|
||||
albedo,
|
||||
transmission,
|
||||
specular * light_specular,
|
||||
specular_blob_intensity * light_specular,
|
||||
roughness,
|
||||
metallic,
|
||||
specular,
|
||||
rim,
|
||||
rim_tint,
|
||||
clearcoat,
|
||||
|
@ -1872,10 +1909,10 @@ FRAGMENT_SHADER_CODE
|
|||
vec4 r = roughness * c0 + c1;
|
||||
float ndotv = clamp(dot(normal, eye_position), 0.0, 1.0);
|
||||
float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y;
|
||||
vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw;
|
||||
vec2 env = vec2(-1.04, 1.04) * a004 + r.zw;
|
||||
|
||||
vec3 specular_color = metallic_to_specular_color(metallic, specular, albedo);
|
||||
specular_light *= AB.x * specular_color + AB.y;
|
||||
vec3 f0 = F0(metallic, specular, albedo);
|
||||
specular_light *= env.x * f0 + env.y;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -920,13 +920,14 @@ float GTR1(float NdotH, float a) {
|
|||
return (a2 - 1.0) / (M_PI * log(a2) * t);
|
||||
}
|
||||
|
||||
vec3 metallic_to_specular_color(float metallic, float specular, vec3 albedo) {
|
||||
float dielectric = (0.034 * 2.0) * specular;
|
||||
// energy conservation
|
||||
return mix(vec3(dielectric), albedo, metallic); // TODO: reference?
|
||||
vec3 F0(float metallic, float specular, vec3 albedo) {
|
||||
float dielectric = 0.16 * specular * specular;
|
||||
// use albedo * metallic as colored specular reflectance at 0 angle for metallic materials;
|
||||
// see https://google.github.io/filament/Filament.md.html
|
||||
return mix(vec3(dielectric), albedo, vec3(metallic));
|
||||
}
|
||||
|
||||
void light_compute(vec3 N, vec3 L, vec3 V, vec3 B, vec3 T, vec3 light_color, vec3 attenuation, vec3 diffuse_color, vec3 transmission, float specular_blob_intensity, float roughness, float metallic, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, inout vec3 diffuse_light, inout vec3 specular_light) {
|
||||
void light_compute(vec3 N, vec3 L, vec3 V, vec3 B, vec3 T, vec3 light_color, vec3 attenuation, vec3 diffuse_color, vec3 transmission, float specular_blob_intensity, float roughness, float metallic, float specular, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, inout vec3 diffuse_light, inout vec3 specular_light) {
|
||||
|
||||
#if defined(USE_LIGHT_SHADER_CODE)
|
||||
// light is written by the light shader
|
||||
|
@ -1069,11 +1070,10 @@ LIGHT_SHADER_CODE
|
|||
|
||||
#if defined(LIGHT_USE_ANISOTROPY)
|
||||
|
||||
float alpha = roughness * roughness;
|
||||
float aspect = sqrt(1.0 - anisotropy * 0.9);
|
||||
float rx = roughness / aspect;
|
||||
float ry = roughness * aspect;
|
||||
float ax = rx * rx;
|
||||
float ay = ry * ry;
|
||||
float ax = alpha / aspect;
|
||||
float ay = alpha * aspect;
|
||||
float XdotH = dot(T, H);
|
||||
float YdotH = dot(B, H);
|
||||
float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH);
|
||||
|
@ -1085,11 +1085,11 @@ LIGHT_SHADER_CODE
|
|||
float G = G_GGX_2cos(cNdotL, alpha) * G_GGX_2cos(cNdotV, alpha);
|
||||
#endif
|
||||
// F
|
||||
//float F0 = 1.0;
|
||||
//float cLdotH5 = SchlickFresnel(cLdotH);
|
||||
//float F = mix(cLdotH5, 1.0, F0);
|
||||
vec3 f0 = F0(metallic, specular, diffuse_color);
|
||||
float cLdotH5 = SchlickFresnel(cLdotH);
|
||||
vec3 F = mix(vec3(cLdotH5), vec3(1.0), f0);
|
||||
|
||||
float specular_brdf_NL = cNdotL * D /* F */ * G;
|
||||
vec3 specular_brdf_NL = cNdotL * D * F * G;
|
||||
|
||||
specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation;
|
||||
#endif
|
||||
|
@ -1191,7 +1191,7 @@ vec3 light_transmittance(float translucency,vec3 light_vec, vec3 normal, vec3 po
|
|||
}
|
||||
#endif
|
||||
|
||||
void light_process_omni(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 binormal, vec3 tangent, vec3 albedo, vec3 transmission, float roughness, float metallic, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light) {
|
||||
void light_process_omni(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 binormal, vec3 tangent, vec3 albedo, vec3 transmission, float roughness, float metallic, float specular, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light) {
|
||||
|
||||
vec3 light_rel_vec = omni_lights[idx].light_pos_inv_radius.xyz - vertex;
|
||||
float light_length = length(light_rel_vec);
|
||||
|
@ -1245,10 +1245,10 @@ void light_process_omni(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 bi
|
|||
light_attenuation *= mix(omni_lights[idx].shadow_color_contact.rgb, vec3(1.0), shadow);
|
||||
}
|
||||
#endif //SHADOWS_DISABLED
|
||||
light_compute(normal, normalize(light_rel_vec), eye_vec, binormal, tangent, omni_lights[idx].light_color_energy.rgb, light_attenuation, albedo, transmission, omni_lights[idx].light_params.z * p_blob_intensity, roughness, metallic, rim * omni_attenuation, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light);
|
||||
light_compute(normal, normalize(light_rel_vec), eye_vec, binormal, tangent, omni_lights[idx].light_color_energy.rgb, light_attenuation, albedo, transmission, omni_lights[idx].light_params.z * p_blob_intensity, roughness, metallic, specular, rim * omni_attenuation, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light);
|
||||
}
|
||||
|
||||
void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 binormal, vec3 tangent, vec3 albedo, vec3 transmission, float roughness, float metallic, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light) {
|
||||
void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 binormal, vec3 tangent, vec3 albedo, vec3 transmission, float roughness, float metallic, float specular, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light) {
|
||||
|
||||
vec3 light_rel_vec = spot_lights[idx].light_pos_inv_radius.xyz - vertex;
|
||||
float light_length = length(light_rel_vec);
|
||||
|
@ -1280,7 +1280,7 @@ void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 bi
|
|||
}
|
||||
#endif //SHADOWS_DISABLED
|
||||
|
||||
light_compute(normal, normalize(light_rel_vec), eye_vec, binormal, tangent, spot_lights[idx].light_color_energy.rgb, light_attenuation, albedo, transmission, spot_lights[idx].light_params.z * p_blob_intensity, roughness, metallic, rim * spot_attenuation, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light);
|
||||
light_compute(normal, normalize(light_rel_vec), eye_vec, binormal, tangent, spot_lights[idx].light_color_energy.rgb, light_attenuation, albedo, transmission, spot_lights[idx].light_params.z * p_blob_intensity, roughness, metallic, specular, rim * spot_attenuation, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light);
|
||||
}
|
||||
|
||||
void reflection_process(int idx, vec3 vertex, vec3 normal, vec3 binormal, vec3 tangent, float roughness, float anisotropy, vec3 ambient, vec3 skybox, inout highp vec4 reflection_accum, inout highp vec4 ambient_accum) {
|
||||
|
@ -1895,7 +1895,7 @@ FRAGMENT_SHADER_CODE
|
|||
specular_light *= mix(vec3(1.0), light_attenuation, specular_light_interp.a);
|
||||
|
||||
#else
|
||||
light_compute(normal, -light_direction_attenuation.xyz, eye_vec, binormal, tangent, light_color_energy.rgb, light_attenuation, albedo, transmission, light_params.z * specular_blob_intensity, roughness, metallic, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light);
|
||||
light_compute(normal, -light_direction_attenuation.xyz, eye_vec, binormal, tangent, light_color_energy.rgb, light_attenuation, albedo, transmission, light_params.z * specular_blob_intensity, roughness, metallic, specular, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light);
|
||||
#endif
|
||||
|
||||
#endif //#USE_LIGHT_DIRECTIONAL
|
||||
|
@ -1969,11 +1969,11 @@ FRAGMENT_SHADER_CODE
|
|||
#else
|
||||
|
||||
for (int i = 0; i < omni_light_count; i++) {
|
||||
light_process_omni(omni_light_indices[i], vertex, eye_vec, normal, binormal, tangent, albedo, transmission, roughness, metallic, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, specular_blob_intensity, diffuse_light, specular_light);
|
||||
light_process_omni(omni_light_indices[i], vertex, eye_vec, normal, binormal, tangent, albedo, transmission, roughness, metallic, specular, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, specular_blob_intensity, diffuse_light, specular_light);
|
||||
}
|
||||
|
||||
for (int i = 0; i < spot_light_count; i++) {
|
||||
light_process_spot(spot_light_indices[i], vertex, eye_vec, normal, binormal, tangent, albedo, transmission, roughness, metallic, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, specular_blob_intensity, diffuse_light, specular_light);
|
||||
light_process_spot(spot_light_indices[i], vertex, eye_vec, normal, binormal, tangent, albedo, transmission, roughness, metallic, specular, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, specular_blob_intensity, diffuse_light, specular_light);
|
||||
}
|
||||
|
||||
#endif //USE_VERTEX_LIGHTING
|
||||
|
@ -1994,7 +1994,7 @@ FRAGMENT_SHADER_CODE
|
|||
diffuse_light *= ao_light_affect;
|
||||
#endif
|
||||
|
||||
//energy conservation
|
||||
// base color remapping
|
||||
diffuse_light *= 1.0 - metallic; // TODO: avoid all diffuse and ambient light calculations when metallic == 1 up to this point
|
||||
ambient_light *= 1.0 - metallic;
|
||||
|
||||
|
@ -2011,10 +2011,10 @@ FRAGMENT_SHADER_CODE
|
|||
vec4 r = roughness * c0 + c1;
|
||||
float ndotv = clamp(dot(normal, eye_vec), 0.0, 1.0);
|
||||
float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y;
|
||||
vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw;
|
||||
vec2 env = vec2(-1.04, 1.04) * a004 + r.zw;
|
||||
|
||||
vec3 specular_color = metallic_to_specular_color(metallic, specular, albedo);
|
||||
specular_light *= AB.x * specular_color + AB.y;
|
||||
vec3 f0 = F0(metallic, specular, albedo);
|
||||
specular_light *= env.x * f0 + env.y;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue