Shader: Add Metallic BSDF Node
Add Metallic BSDF Node to the shader editor. This node can primarily be used to create more realistic looking metallic materials than the existing Glossy BSDF node. This commit does not add any new closures to Cycles, it simply exposes existing closures that were previous hard to access on their own. - Exposes the F82 fresnel type that is currently used by the metallic component of the Principled BSDF. Results should match between the Metallic BSDF and Principled BSDF when using the same settings. - Exposes the Physical Conductor fresnel type that was previously limited to custom OSL scripts. The Conductor fresnel type accepts IOR and Extinction coefficients to define the appearance of the material based off real life measurements. EEVEE only supports the F82 fresnel type with internal code to convert the the physical conductor inputs in to a colour format for F82, which can lead to noticeable rendering differences with some configurations. Pull Request: https://projects.blender.org/blender/blender/pulls/114958
This commit is contained in:
@@ -548,6 +548,32 @@ static ShaderNode *add_node(Scene *scene,
|
||||
|
||||
node = subsurface;
|
||||
}
|
||||
else if (b_node.is_a(&RNA_ShaderNodeBsdfMetallic)) {
|
||||
BL::ShaderNodeBsdfMetallic b_metallic_node(b_node);
|
||||
MetallicBsdfNode *metal = graph->create_node<MetallicBsdfNode>();
|
||||
|
||||
switch (b_metallic_node.distribution()) {
|
||||
case BL::ShaderNodeBsdfMetallic::distribution_BECKMANN:
|
||||
metal->set_distribution(CLOSURE_BSDF_MICROFACET_BECKMANN_ID);
|
||||
break;
|
||||
case BL::ShaderNodeBsdfMetallic::distribution_GGX:
|
||||
metal->set_distribution(CLOSURE_BSDF_MICROFACET_GGX_ID);
|
||||
break;
|
||||
case BL::ShaderNodeBsdfMetallic::distribution_MULTI_GGX:
|
||||
metal->set_distribution(CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (b_metallic_node.fresnel_type()) {
|
||||
case BL::ShaderNodeBsdfMetallic::fresnel_type_PHYSICAL_CONDUCTOR:
|
||||
metal->set_fresnel_type(CLOSURE_BSDF_PHYSICAL_CONDUCTOR);
|
||||
break;
|
||||
case BL::ShaderNodeBsdfMetallic::fresnel_type_F82:
|
||||
metal->set_fresnel_type(CLOSURE_BSDF_F82_CONDUCTOR);
|
||||
break;
|
||||
}
|
||||
node = metal;
|
||||
}
|
||||
else if (b_node.is_a(&RNA_ShaderNodeBsdfAnisotropic)) {
|
||||
BL::ShaderNodeBsdfAnisotropic b_glossy_node(b_node);
|
||||
GlossyBsdfNode *glossy = graph->create_node<GlossyBsdfNode>();
|
||||
|
||||
@@ -56,6 +56,7 @@ set(SRC_OSL
|
||||
node_map_range.osl
|
||||
node_mapping.osl
|
||||
node_math.osl
|
||||
node_metallic_bsdf.osl
|
||||
node_mix.osl
|
||||
node_mix_closure.osl
|
||||
node_mix_color.osl
|
||||
|
||||
44
intern/cycles/kernel/osl/shaders/node_metallic_bsdf.osl
Normal file
44
intern/cycles/kernel/osl/shaders/node_metallic_bsdf.osl
Normal file
@@ -0,0 +1,44 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "node_fresnel.h"
|
||||
#include "stdcycles.h"
|
||||
|
||||
shader node_metallic_bsdf(color BaseColor = color(0.617, 0.577, 0.540),
|
||||
color EdgeTint = color(0.695, 0.726, 0.770),
|
||||
vector IOR = vector(2.757, 2.513, 2.231),
|
||||
vector Extinction = vector(3.867, 3.404, 3.009),
|
||||
string distribution = "multi_ggx",
|
||||
string fresnel_type = "f82",
|
||||
float Roughness = 0.5,
|
||||
float Anisotropy = 0.0,
|
||||
float Rotation = 0.0,
|
||||
normal Normal = N,
|
||||
normal Tangent = 0.0,
|
||||
output closure color BSDF = 0)
|
||||
{
|
||||
float r2 = clamp(Roughness, 0.0, 1.0);
|
||||
r2 *= r2;
|
||||
float alpha_x = r2, alpha_y = r2;
|
||||
|
||||
/* Handle anisotropy. */
|
||||
vector T = Tangent;
|
||||
if (Anisotropy > 0.0) {
|
||||
float aspect = sqrt(1.0 - clamp(Anisotropy, 0.0, 1.0) * 0.9);
|
||||
alpha_x /= aspect;
|
||||
alpha_y *= aspect;
|
||||
if (Rotation != 0.0)
|
||||
T = rotate(T, Rotation * M_2PI, point(0.0, 0.0, 0.0), Normal);
|
||||
}
|
||||
|
||||
if (fresnel_type == "f82") {
|
||||
color F0 = clamp(BaseColor, color(0.0), color(1.0));
|
||||
color F82 = clamp(EdgeTint, color(0.0), color(1.0));
|
||||
BSDF = microfacet_f82_tint(distribution, Normal, T, alpha_x, alpha_y, F0, F82);
|
||||
}
|
||||
else {
|
||||
BSDF = conductor_bsdf(
|
||||
Normal, T, alpha_x, alpha_y, max(IOR, 0.0), max(Extinction, 0.0), distribution);
|
||||
}
|
||||
}
|
||||
@@ -479,6 +479,77 @@ ccl_device
|
||||
bsdf_transparent_setup(sd, weight, path_flag);
|
||||
break;
|
||||
}
|
||||
case CLOSURE_BSDF_PHYSICAL_CONDUCTOR:
|
||||
case CLOSURE_BSDF_F82_CONDUCTOR: {
|
||||
#ifdef __CAUSTICS_TRICKS__
|
||||
if (!kernel_data.integrator.caustics_reflective && (path_flag & PATH_RAY_DIFFUSE))
|
||||
break;
|
||||
#endif
|
||||
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
|
||||
sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(make_float3(mix_weight)));
|
||||
|
||||
if (bsdf != NULL) {
|
||||
uint base_ior_offset, edge_tint_k_offset, rotation_offset, tangent_offset;
|
||||
svm_unpack_node_uchar4(
|
||||
node.z, &base_ior_offset, &edge_tint_k_offset, &rotation_offset, &tangent_offset);
|
||||
|
||||
float3 valid_reflection_N = maybe_ensure_valid_specular_reflection(sd, N);
|
||||
float3 T = stack_load_float3(stack, tangent_offset);
|
||||
const float anisotropy = saturatef(param2);
|
||||
const float roughness = saturatef(param1);
|
||||
float alpha_x = sqr(roughness), alpha_y = sqr(roughness);
|
||||
if (anisotropy > 0.0f) {
|
||||
float aspect = sqrtf(1.0f - anisotropy * 0.9f);
|
||||
alpha_x /= aspect;
|
||||
alpha_y *= aspect;
|
||||
float anisotropic_rotation = stack_load_float(stack, rotation_offset);
|
||||
if (anisotropic_rotation != 0.0f) {
|
||||
T = rotate_around_axis(T, N, anisotropic_rotation * M_2PI_F);
|
||||
}
|
||||
}
|
||||
|
||||
bsdf->N = valid_reflection_N;
|
||||
bsdf->ior = 1.0f;
|
||||
bsdf->T = T;
|
||||
bsdf->alpha_x = alpha_x;
|
||||
bsdf->alpha_y = alpha_y;
|
||||
|
||||
ClosureType distribution = (ClosureType)node.w;
|
||||
/* Setup BSDF */
|
||||
if (distribution == CLOSURE_BSDF_MICROFACET_BECKMANN_ID) {
|
||||
sd->flag |= bsdf_microfacet_beckmann_setup(bsdf);
|
||||
}
|
||||
else {
|
||||
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
|
||||
}
|
||||
|
||||
const bool is_multiggx = (distribution == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID);
|
||||
|
||||
if (type == CLOSURE_BSDF_PHYSICAL_CONDUCTOR) {
|
||||
ccl_private FresnelConductor *fresnel = (ccl_private FresnelConductor *)
|
||||
closure_alloc_extra(sd, sizeof(FresnelConductor));
|
||||
|
||||
const float3 n = max(stack_load_float3(stack, base_ior_offset), zero_float3());
|
||||
const float3 k = max(stack_load_float3(stack, edge_tint_k_offset), zero_float3());
|
||||
|
||||
fresnel->n = rgb_to_spectrum(n);
|
||||
fresnel->k = rgb_to_spectrum(k);
|
||||
bsdf_microfacet_setup_fresnel_conductor(kg, bsdf, sd, fresnel, is_multiggx);
|
||||
}
|
||||
else {
|
||||
ccl_private FresnelF82Tint *fresnel = (ccl_private FresnelF82Tint *)closure_alloc_extra(
|
||||
sd, sizeof(FresnelF82Tint));
|
||||
|
||||
const float3 color = saturate(stack_load_float3(stack, base_ior_offset));
|
||||
const float3 tint = saturate(stack_load_float3(stack, edge_tint_k_offset));
|
||||
|
||||
fresnel->f0 = rgb_to_spectrum(color);
|
||||
const Spectrum f82 = rgb_to_spectrum(tint);
|
||||
bsdf_microfacet_setup_fresnel_f82_tint(kg, bsdf, sd, fresnel, f82, is_multiggx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CLOSURE_BSDF_RAY_PORTAL_ID: {
|
||||
Spectrum weight = closure_weight * mix_weight;
|
||||
float3 position = stack_load_float3(stack, data_node.y);
|
||||
|
||||
@@ -431,6 +431,8 @@ typedef enum ClosureType {
|
||||
CLOSURE_BSDF_TRANSLUCENT_ID,
|
||||
|
||||
/* Glossy */
|
||||
CLOSURE_BSDF_PHYSICAL_CONDUCTOR, /* virtual closure */
|
||||
CLOSURE_BSDF_F82_CONDUCTOR, /* virtual closure */
|
||||
CLOSURE_BSDF_MICROFACET_GGX_ID,
|
||||
CLOSURE_BSDF_MICROFACET_BECKMANN_ID,
|
||||
CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID, /* virtual closure */
|
||||
|
||||
@@ -1198,7 +1198,9 @@ int ShaderGraph::get_num_closures()
|
||||
* for the volume steps. */
|
||||
num_closures += MAX_VOLUME_STACK_SIZE;
|
||||
}
|
||||
else if (closure_type == CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID ||
|
||||
else if (closure_type == CLOSURE_BSDF_PHYSICAL_CONDUCTOR ||
|
||||
closure_type == CLOSURE_BSDF_F82_CONDUCTOR ||
|
||||
closure_type == CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID ||
|
||||
closure_type == CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID ||
|
||||
closure_type == CLOSURE_BSDF_HAIR_CHIANG_ID ||
|
||||
closure_type == CLOSURE_BSDF_HAIR_HUANG_ID)
|
||||
|
||||
@@ -2441,6 +2441,121 @@ void BsdfNode::compile(OSLCompiler & /*compiler*/)
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* Metallic BSDF Closure */
|
||||
|
||||
NODE_DEFINE(MetallicBsdfNode)
|
||||
{
|
||||
NodeType *type = NodeType::add("metallic_bsdf", create, NodeType::SHADER);
|
||||
|
||||
SOCKET_IN_COLOR(color, "Base Color", make_float3(0.617f, 0.577f, 0.540f));
|
||||
SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL);
|
||||
SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL);
|
||||
|
||||
static NodeEnum distribution_enum;
|
||||
distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_ID);
|
||||
distribution_enum.insert("ggx", CLOSURE_BSDF_MICROFACET_GGX_ID);
|
||||
distribution_enum.insert("multi_ggx", CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID);
|
||||
SOCKET_ENUM(
|
||||
distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID);
|
||||
|
||||
static NodeEnum fresnel_type_enum;
|
||||
fresnel_type_enum.insert("f82", CLOSURE_BSDF_F82_CONDUCTOR);
|
||||
fresnel_type_enum.insert("physical_conductor", CLOSURE_BSDF_PHYSICAL_CONDUCTOR);
|
||||
SOCKET_ENUM(fresnel_type, "fresnel_type", fresnel_type_enum, CLOSURE_BSDF_F82_CONDUCTOR);
|
||||
|
||||
SOCKET_IN_COLOR(edge_tint, "Edge Tint", make_float3(0.695f, 0.726f, 0.770f));
|
||||
|
||||
SOCKET_IN_VECTOR(ior, "IOR", make_float3(2.757f, 2.513f, 2.231f));
|
||||
SOCKET_IN_VECTOR(k, "Extinction", make_float3(3.867f, 3.404f, 3.009f));
|
||||
|
||||
SOCKET_IN_VECTOR(tangent, "Tangent", zero_float3(), SocketType::LINK_TANGENT);
|
||||
|
||||
SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f);
|
||||
SOCKET_IN_FLOAT(anisotropy, "Anisotropy", 0.0f);
|
||||
SOCKET_IN_FLOAT(rotation, "Rotation", 0.0f);
|
||||
|
||||
SOCKET_OUT_CLOSURE(BSDF, "BSDF");
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
MetallicBsdfNode::MetallicBsdfNode() : BsdfNode(get_node_type())
|
||||
{
|
||||
closure = CLOSURE_BSDF_PHYSICAL_CONDUCTOR;
|
||||
}
|
||||
|
||||
bool MetallicBsdfNode::is_isotropic()
|
||||
{
|
||||
ShaderInput *anisotropy_input = input("Anisotropy");
|
||||
/* Keep in sync with the thresholds in OSL's node_conductor_bsdf and SVM's
|
||||
* svm_node_metallic_bsdf. */
|
||||
return (!anisotropy_input->link && fabsf(anisotropy) <= 1e-4f);
|
||||
}
|
||||
|
||||
void MetallicBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes)
|
||||
{
|
||||
if (shader->has_surface_link()) {
|
||||
ShaderInput *tangent_in = input("Tangent");
|
||||
if (!tangent_in->link && !is_isotropic()) {
|
||||
attributes->add(ATTR_STD_GENERATED);
|
||||
}
|
||||
}
|
||||
|
||||
ShaderNode::attributes(shader, attributes);
|
||||
}
|
||||
|
||||
void MetallicBsdfNode::simplify_settings(Scene * /* scene */)
|
||||
{
|
||||
/* If the anisotropy is close enough to zero, fall back to the isotropic case. */
|
||||
ShaderInput *tangent_input = input("Tangent");
|
||||
if (tangent_input->link && is_isotropic()) {
|
||||
tangent_input->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void MetallicBsdfNode::compile(SVMCompiler &compiler)
|
||||
{
|
||||
compiler.add_node(NODE_CLOSURE_SET_WEIGHT, one_float3());
|
||||
|
||||
ShaderInput *base_color_in = input("Base Color");
|
||||
ShaderInput *edge_tint_in = input("Edge Tint");
|
||||
ShaderInput *ior_in = input("IOR");
|
||||
ShaderInput *k_in = input("Extinction");
|
||||
|
||||
int base_color_ior_offset = fresnel_type == CLOSURE_BSDF_PHYSICAL_CONDUCTOR ?
|
||||
compiler.stack_assign(ior_in) :
|
||||
compiler.stack_assign(base_color_in);
|
||||
int edge_tint_k_offset = fresnel_type == CLOSURE_BSDF_PHYSICAL_CONDUCTOR ?
|
||||
compiler.stack_assign(k_in) :
|
||||
compiler.stack_assign(edge_tint_in);
|
||||
|
||||
ShaderInput *anisotropy_in = input("Anisotropy");
|
||||
ShaderInput *rotation_in = input("Rotation");
|
||||
ShaderInput *roughness_in = input("Roughness");
|
||||
ShaderInput *tangent_in = input("Tangent");
|
||||
|
||||
int normal_offset = compiler.stack_assign_if_linked(input("Normal"));
|
||||
|
||||
compiler.add_node(NODE_CLOSURE_BSDF,
|
||||
compiler.encode_uchar4(fresnel_type,
|
||||
compiler.stack_assign(roughness_in),
|
||||
compiler.stack_assign(anisotropy_in),
|
||||
compiler.closure_mix_weight_offset()),
|
||||
compiler.encode_uchar4(base_color_ior_offset,
|
||||
edge_tint_k_offset,
|
||||
compiler.stack_assign(rotation_in),
|
||||
compiler.stack_assign(tangent_in)),
|
||||
distribution);
|
||||
compiler.add_node(normal_offset);
|
||||
}
|
||||
|
||||
void MetallicBsdfNode::compile(OSLCompiler &compiler)
|
||||
{
|
||||
compiler.parameter(this, "distribution");
|
||||
compiler.parameter(this, "fresnel_type");
|
||||
compiler.add(this, "node_metallic_bsdf");
|
||||
}
|
||||
|
||||
/* Glossy BSDF Closure */
|
||||
|
||||
NODE_DEFINE(GlossyBsdfNode)
|
||||
|
||||
@@ -604,6 +604,35 @@ class SheenBsdfNode : public BsdfNode {
|
||||
}
|
||||
};
|
||||
|
||||
class MetallicBsdfNode : public BsdfNode {
|
||||
public:
|
||||
SHADER_NODE_CLASS(MetallicBsdfNode)
|
||||
|
||||
void simplify_settings(Scene *scene);
|
||||
ClosureType get_closure_type()
|
||||
{
|
||||
return closure;
|
||||
}
|
||||
|
||||
NODE_SOCKET_API(float3, edge_tint)
|
||||
NODE_SOCKET_API(float3, ior)
|
||||
NODE_SOCKET_API(float3, k)
|
||||
NODE_SOCKET_API(float3, tangent)
|
||||
NODE_SOCKET_API(float, roughness)
|
||||
NODE_SOCKET_API(float, anisotropy)
|
||||
NODE_SOCKET_API(float, rotation)
|
||||
NODE_SOCKET_API(ClosureType, distribution)
|
||||
NODE_SOCKET_API(ClosureType, fresnel_type)
|
||||
|
||||
void attributes(Shader *shader, AttributeRequestSet *attributes);
|
||||
bool has_attribute_dependency()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_isotropic();
|
||||
};
|
||||
|
||||
class GlossyBsdfNode : public BsdfNode {
|
||||
public:
|
||||
SHADER_NODE_CLASS(GlossyBsdfNode)
|
||||
|
||||
@@ -138,6 +138,11 @@ class NODE_MT_category_shader_shader(Menu):
|
||||
"ShaderNodeBackground",
|
||||
poll=world_shader_nodes_poll(context),
|
||||
)
|
||||
node_add_menu.add_node_type(
|
||||
layout,
|
||||
"ShaderNodeBsdfMetallic",
|
||||
poll=object_shader_nodes_poll(context),
|
||||
)
|
||||
node_add_menu.add_node_type(
|
||||
layout,
|
||||
"ShaderNodeBsdfDiffuse",
|
||||
|
||||
@@ -997,6 +997,7 @@ void node_tree_remove_layer_n(bNodeTree *ntree, Scene *scene, int layer_index);
|
||||
#define SH_NODE_MIX 713
|
||||
#define SH_NODE_BSDF_RAY_PORTAL 714
|
||||
#define SH_NODE_TEX_GABOR 715
|
||||
#define SH_NODE_BSDF_METALLIC 716
|
||||
|
||||
/** \} */
|
||||
|
||||
|
||||
@@ -572,6 +572,7 @@ set(GLSL_SRC
|
||||
shaders/material/gpu_shader_material_light_path.glsl
|
||||
shaders/material/gpu_shader_material_mapping.glsl
|
||||
shaders/material/gpu_shader_material_map_range.glsl
|
||||
shaders/material/gpu_shader_material_metallic.glsl
|
||||
shaders/material/gpu_shader_material_mix_color.glsl
|
||||
shaders/material/gpu_shader_material_mix_shader.glsl
|
||||
shaders/material/gpu_shader_material_noise.glsl
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
vec3 fresnel_conductor(float cosi, vec3 eta, vec3 k)
|
||||
{
|
||||
|
||||
vec3 cosi_sqr = vec3(cosi * cosi);
|
||||
vec3 one = vec3(1.0);
|
||||
vec3 tmp_f = (eta * eta) + (k * k);
|
||||
|
||||
vec3 tmp_two_eta_cosi = 2.0 * eta * vec3(cosi);
|
||||
|
||||
vec3 tmp = tmp_f * cosi_sqr;
|
||||
vec3 Rparl2 = (tmp - tmp_two_eta_cosi + one) / (tmp + tmp_two_eta_cosi + one);
|
||||
vec3 Rperp2 = (tmp_f - tmp_two_eta_cosi + cosi_sqr) / (tmp_f + tmp_two_eta_cosi + cosi_sqr);
|
||||
return (Rparl2 + Rperp2) * 0.5;
|
||||
}
|
||||
|
||||
void node_bsdf_metallic(vec4 base_color,
|
||||
vec4 edge_tint,
|
||||
vec3 ior,
|
||||
vec3 extinction,
|
||||
float roughness,
|
||||
float anisotropy,
|
||||
float rotation,
|
||||
vec3 N,
|
||||
vec3 T,
|
||||
float weight,
|
||||
const float do_multiscatter,
|
||||
const float use_complex_ior,
|
||||
out Closure result)
|
||||
{
|
||||
vec3 F0 = base_color.rgb;
|
||||
vec3 F82 = edge_tint.rgb;
|
||||
if (use_complex_ior != 0.0) {
|
||||
/* Compute incidence at 0 and 82 degrees from conductor Fresnel. */
|
||||
F0 = fresnel_conductor(1.0, ior, extinction);
|
||||
F82 = fresnel_conductor(1.0 / 7.0, ior, extinction);
|
||||
}
|
||||
|
||||
/* Clamp to match Cycles */
|
||||
F0 = saturate(F0);
|
||||
F82 = saturate(F82);
|
||||
roughness = saturate(roughness);
|
||||
/* Not used by EEVEE */
|
||||
/* anisotropy = saturate(anisotropy); */
|
||||
|
||||
N = safe_normalize(N);
|
||||
vec3 V = coordinate_incoming(g_data.P);
|
||||
float NV = dot(N, V);
|
||||
|
||||
ClosureReflection reflection_data;
|
||||
reflection_data.N = N;
|
||||
reflection_data.roughness = roughness;
|
||||
|
||||
vec3 metallic_brdf;
|
||||
brdf_f82_tint_lut(F0, F82, NV, roughness, do_multiscatter != 0.0, metallic_brdf);
|
||||
reflection_data.color = metallic_brdf;
|
||||
reflection_data.weight = weight;
|
||||
|
||||
result = closure_eval(reflection_data);
|
||||
}
|
||||
@@ -2111,6 +2111,12 @@ enum {
|
||||
CMP_NODE_CHANNEL_MATTE_CS_YCC = 4,
|
||||
};
|
||||
|
||||
/* Conductive fresnel types */
|
||||
enum {
|
||||
SHD_PHYSICAL_CONDUCTOR = 0,
|
||||
SHD_CONDUCTOR_F82 = 1,
|
||||
};
|
||||
|
||||
/* glossy distributions */
|
||||
enum {
|
||||
SHD_GLOSSY_BECKMANN = 0,
|
||||
|
||||
@@ -4116,6 +4116,33 @@ static const EnumPropertyItem node_ycc_items[] = {
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static const EnumPropertyItem node_metallic_distribution_items[] = {
|
||||
{SHD_GLOSSY_BECKMANN, "BECKMANN", 0, "Beckmann", ""},
|
||||
{SHD_GLOSSY_GGX, "GGX", 0, "GGX", ""},
|
||||
{SHD_GLOSSY_MULTI_GGX,
|
||||
"MULTI_GGX",
|
||||
0,
|
||||
"Multiscatter GGX",
|
||||
"GGX with additional correction to account for multiple scattering, preserve energy and "
|
||||
"prevent unexpected darkening at high roughness"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static const EnumPropertyItem node_metallic_fresnel_type_items[] = {
|
||||
{SHD_PHYSICAL_CONDUCTOR,
|
||||
"PHYSICAL_CONDUCTOR",
|
||||
0,
|
||||
"Physical Conductor",
|
||||
"Fresnel conductor based on the complex refractive index per color channel"},
|
||||
{SHD_CONDUCTOR_F82,
|
||||
"F82",
|
||||
0,
|
||||
"F82 Tint",
|
||||
"An approximation of the Fresnel conductor curve based on the colors at perpendicular and "
|
||||
"near-grazing (roughly 82°) angles"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static const EnumPropertyItem node_glossy_items[] = {
|
||||
{SHD_GLOSSY_BECKMANN, "BECKMANN", 0, "Beckmann", ""},
|
||||
{SHD_GLOSSY_GGX, "GGX", 0, "GGX", ""},
|
||||
@@ -5655,6 +5682,23 @@ static void def_sh_tex_pointdensity(StructRNA *srna)
|
||||
RNA_def_function_output(func, parm);
|
||||
}
|
||||
|
||||
static void def_metallic(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_property(srna, "distribution", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "custom1");
|
||||
RNA_def_property_enum_items(prop, node_metallic_distribution_items);
|
||||
RNA_def_property_ui_text(prop, "Distribution", "Light scattering distribution on rough surface");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "fresnel_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "custom2");
|
||||
RNA_def_property_enum_items(prop, node_metallic_fresnel_type_items);
|
||||
RNA_def_property_ui_text(prop, "Fresnel Type", "Fresnel method used to tint the metal");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
}
|
||||
|
||||
static void def_glossy(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
@@ -61,6 +61,7 @@ DefNode(ShaderNode, SH_NODE_ATTRIBUTE, def_sh_attribute, "ATT
|
||||
DefNode(ShaderNode, SH_NODE_AMBIENT_OCCLUSION, def_sh_ambient_occlusion,"AMBIENT_OCCLUSION", AmbientOcclusion, "Ambient Occlusion", "Compute how much the hemisphere above the shading point is occluded, for example to add weathering effects to corners.\nNote: For Cycles, this may slow down renders significantly")
|
||||
DefNode(ShaderNode, SH_NODE_BACKGROUND, 0, "BACKGROUND", Background, "Background", "Add background light emission.\nNote: This node should only be used for the world surface output")
|
||||
DefNode(ShaderNode, SH_NODE_HOLDOUT, 0, "HOLDOUT", Holdout, "Holdout", "Create a \"hole\" in the image with zero alpha transparency, which is useful for compositing.\nNote: the holdout shader can only create alpha when transparency is enabled in the film settings")
|
||||
DefNode(ShaderNode, SH_NODE_BSDF_METALLIC, def_metallic, "BSDF_METALLIC", BsdfMetallic, "Metallic BSDF", "Metallic reflection with microfacet distribution, and metallic fresnel")
|
||||
DefNode(ShaderNode, SH_NODE_BSDF_DIFFUSE, 0, "BSDF_DIFFUSE", BsdfDiffuse, "Diffuse BSDF", "Lambertian and Oren-Nayar diffuse reflection")
|
||||
DefNode(ShaderNode, SH_NODE_BSDF_PRINCIPLED, def_principled, "BSDF_PRINCIPLED", BsdfPrincipled, "Principled BSDF", "Physically-based, easy-to-use shader for rendering surface materials, based on the OpenPBR model")
|
||||
DefNode(ShaderNode, SH_NODE_BSDF_GLOSSY, def_glossy, "BSDF_GLOSSY", BsdfAnisotropic, "Glossy BSDF", "Reflection with microfacet distribution, used for materials such as metal or mirrors")
|
||||
|
||||
@@ -36,6 +36,7 @@ set(SRC
|
||||
nodes/node_shader_bsdf_glossy.cc
|
||||
nodes/node_shader_bsdf_hair.cc
|
||||
nodes/node_shader_bsdf_hair_principled.cc
|
||||
nodes/node_shader_bsdf_metallic.cc
|
||||
nodes/node_shader_bsdf_principled.cc
|
||||
nodes/node_shader_bsdf_ray_portal.cc
|
||||
nodes/node_shader_bsdf_refraction.cc
|
||||
|
||||
@@ -24,6 +24,7 @@ void register_shader_nodes()
|
||||
register_node_type_sh_bsdf_glossy();
|
||||
register_node_type_sh_bsdf_hair_principled();
|
||||
register_node_type_sh_bsdf_hair();
|
||||
register_node_type_sh_bsdf_metallic();
|
||||
register_node_type_sh_bsdf_principled();
|
||||
register_node_type_sh_bsdf_ray_portal();
|
||||
register_node_type_sh_bsdf_refraction();
|
||||
|
||||
@@ -20,6 +20,7 @@ void register_node_type_sh_bsdf_glass();
|
||||
void register_node_type_sh_bsdf_glossy();
|
||||
void register_node_type_sh_bsdf_hair_principled();
|
||||
void register_node_type_sh_bsdf_hair();
|
||||
void register_node_type_sh_bsdf_metallic();
|
||||
void register_node_type_sh_bsdf_principled();
|
||||
void register_node_type_sh_bsdf_ray_portal();
|
||||
void register_node_type_sh_bsdf_refraction();
|
||||
|
||||
@@ -927,6 +927,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
|
||||
break;
|
||||
}
|
||||
case SH_NODE_BACKGROUND:
|
||||
case SH_NODE_BSDF_METALLIC:
|
||||
case SH_NODE_BSDF_DIFFUSE:
|
||||
case SH_NODE_BSDF_GLASS:
|
||||
case SH_NODE_BSDF_GLOSSY:
|
||||
@@ -985,6 +986,7 @@ static bool closure_node_filter(const bNode *node)
|
||||
case SH_NODE_ADD_SHADER:
|
||||
case SH_NODE_MIX_SHADER:
|
||||
case SH_NODE_BACKGROUND:
|
||||
case SH_NODE_BSDF_METALLIC:
|
||||
case SH_NODE_BSDF_DIFFUSE:
|
||||
case SH_NODE_BSDF_GLASS:
|
||||
case SH_NODE_BSDF_GLOSSY:
|
||||
|
||||
168
source/blender/nodes/shader/nodes/node_shader_bsdf_metallic.cc
Normal file
168
source/blender/nodes/shader/nodes/node_shader_bsdf_metallic.cc
Normal file
@@ -0,0 +1,168 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender::nodes::node_shader_bsdf_metallic_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Color>("Base Color")
|
||||
.default_value({0.617f, 0.577f, 0.540f, 1.0f})
|
||||
.description("Color of the material");
|
||||
b.add_input<decl::Color>("Edge Tint")
|
||||
.default_value({0.695f, 0.726f, 0.770f, 1.0f})
|
||||
.description(
|
||||
"Tint reflection at near-grazing incidence to simulate complex index of refraction");
|
||||
b.add_input<decl::Vector>("IOR")
|
||||
.default_value({2.757f, 2.513f, 2.231f})
|
||||
.min(0.0f)
|
||||
.max(100.0f)
|
||||
.description("Real part of the conductor's refractive index, often called n");
|
||||
b.add_input<decl::Vector>("Extinction")
|
||||
.default_value({3.867f, 3.404f, 3.009f})
|
||||
.min(0.0f)
|
||||
.max(100.0f)
|
||||
.description("Imaginary part of the conductor's refractive index, often called k");
|
||||
b.add_input<decl::Float>("Roughness")
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Microfacet roughness of the surface (0.0 is a perfect mirror reflection, 1.0 is "
|
||||
"completely rough)");
|
||||
;
|
||||
b.add_input<decl::Float>("Anisotropy")
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Amount of anisotropy for reflection. Higher values give elongated highlights along the "
|
||||
"tangent direction");
|
||||
b.add_input<decl::Float>("Rotation")
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Rotates the direction of anisotropy, with 1.0 going full circle");
|
||||
b.add_input<decl::Vector>("Normal").hide_value();
|
||||
b.add_input<decl::Vector>("Tangent").hide_value();
|
||||
b.add_input<decl::Float>("Weight").unavailable();
|
||||
b.add_output<decl::Shader>("BSDF");
|
||||
}
|
||||
|
||||
static void node_shader_buts_metallic(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
uiItemR(layout, ptr, "distribution", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
uiItemR(layout, ptr, "fresnel_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_shader_init_metallic(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->custom1 = SHD_GLOSSY_MULTI_GGX;
|
||||
node->custom2 = SHD_CONDUCTOR_F82;
|
||||
}
|
||||
|
||||
static int node_shader_gpu_bsdf_metallic(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (!in[7].link) {
|
||||
GPU_link(mat, "world_normals_get", &in[7].link);
|
||||
}
|
||||
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY);
|
||||
|
||||
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
|
||||
float use_complex_ior = (node->custom2 == SHD_PHYSICAL_CONDUCTOR) ? 1.0f : 0.0f;
|
||||
|
||||
return GPU_stack_link(mat,
|
||||
node,
|
||||
"node_bsdf_metallic",
|
||||
in,
|
||||
out,
|
||||
GPU_constant(&use_multi_scatter),
|
||||
GPU_constant(&use_complex_ior));
|
||||
}
|
||||
|
||||
static void node_shader_update_metallic(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
const bool is_physical = (node->custom2 == SHD_PHYSICAL_CONDUCTOR);
|
||||
|
||||
bke::node_set_socket_availability(
|
||||
ntree, bke::node_find_socket(node, SOCK_IN, "Base Color"), !is_physical);
|
||||
bke::node_set_socket_availability(
|
||||
ntree, bke::node_find_socket(node, SOCK_IN, "Edge Tint"), !is_physical);
|
||||
bke::node_set_socket_availability(
|
||||
ntree, bke::node_find_socket(node, SOCK_IN, "IOR"), is_physical);
|
||||
bke::node_set_socket_availability(
|
||||
ntree, bke::node_find_socket(node, SOCK_IN, "Extinction"), is_physical);
|
||||
}
|
||||
|
||||
NODE_SHADER_MATERIALX_BEGIN
|
||||
#ifdef WITH_MATERIALX
|
||||
{
|
||||
if (to_type_ != NodeItem::Type::BSDF) {
|
||||
return empty();
|
||||
}
|
||||
|
||||
NodeItem color = get_input_value("Base Color", NodeItem::Type::Color3);
|
||||
NodeItem edge_tint = get_input_value("Edge Tint", NodeItem::Type::Color3);
|
||||
NodeItem roughness = get_input_value("Roughness", NodeItem::Type::Vector2);
|
||||
NodeItem anisotropy = get_input_value("Anisotropy", NodeItem::Type::Color3);
|
||||
NodeItem normal = get_input_link("Normal", NodeItem::Type::Vector3);
|
||||
NodeItem tangent = get_input_link("Tangent", NodeItem::Type::Vector3);
|
||||
|
||||
NodeItem ior_out, extinction_out;
|
||||
if (node_->custom2 == SHD_PHYSICAL_CONDUCTOR) {
|
||||
ior_out = get_input_value("IOR", NodeItem::Type::Color3);
|
||||
extinction_out = get_input_value("Extinction", NodeItem::Type::Color3);
|
||||
}
|
||||
else {
|
||||
NodeItem artistic_ior = create_node("artistic_ior",
|
||||
NodeItem::Type::Multioutput,
|
||||
{{"reflectivity", color}, {"edge_color", edge_tint}});
|
||||
ior_out = artistic_ior.add_output("ior", NodeItem::Type::Color3);
|
||||
extinction_out = artistic_ior.add_output("extinction", NodeItem::Type::Color3);
|
||||
}
|
||||
|
||||
return create_node("conductor_bsdf",
|
||||
NodeItem::Type::BSDF,
|
||||
{{"normal", normal},
|
||||
{"tangent", tangent},
|
||||
{"ior", ior_out},
|
||||
{"extinction", extinction_out},
|
||||
{"roughness", roughness}});
|
||||
}
|
||||
#endif
|
||||
NODE_SHADER_MATERIALX_END
|
||||
|
||||
} // namespace blender::nodes::node_shader_bsdf_metallic_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_bsdf_metallic()
|
||||
{
|
||||
namespace file_ns = blender::nodes::node_shader_bsdf_metallic_cc;
|
||||
|
||||
static blender::bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, SH_NODE_BSDF_METALLIC, "Metallic BSDF", NODE_CLASS_SHADER);
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.add_ui_poll = object_shader_nodes_poll;
|
||||
ntype.draw_buttons = file_ns::node_shader_buts_metallic;
|
||||
blender::bke::node_type_size_preset(&ntype, blender::bke::eNodeSizePreset::Large);
|
||||
ntype.initfunc = file_ns::node_shader_init_metallic;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_metallic;
|
||||
ntype.updatefunc = file_ns::node_shader_update_metallic;
|
||||
ntype.materialx_fn = file_ns::node_shader_materialx;
|
||||
|
||||
blender::bke::node_register_type(&ntype);
|
||||
}
|
||||
Reference in New Issue
Block a user