Shader: New Volume Coefficients Shader
Add a new shader node to control volume coefficients (scattering, absorption and emission) directly, making it easier to model existing volumes with measured data. Pull Request: https://projects.blender.org/blender/blender/pulls/136287
This commit is contained in:
committed by
Weizhen Huang
parent
d018c12b61
commit
921c2b9d61
@@ -747,6 +747,28 @@ static ShaderNode *add_node(Scene *scene,
|
||||
else if (b_node.is_a(&RNA_ShaderNodeVolumeAbsorption)) {
|
||||
node = graph->create_node<AbsorptionVolumeNode>();
|
||||
}
|
||||
else if (b_node.is_a(&RNA_ShaderNodeVolumeCoefficients)) {
|
||||
BL::ShaderNodeVolumeCoefficients b_coeffs_node(b_node);
|
||||
VolumeCoefficientsNode *coeffs = graph->create_node<VolumeCoefficientsNode>();
|
||||
switch (b_coeffs_node.phase()) {
|
||||
case BL::ShaderNodeVolumeCoefficients::phase_HENYEY_GREENSTEIN:
|
||||
coeffs->set_phase(CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID);
|
||||
break;
|
||||
case BL::ShaderNodeVolumeCoefficients::phase_FOURNIER_FORAND:
|
||||
coeffs->set_phase(CLOSURE_VOLUME_FOURNIER_FORAND_ID);
|
||||
break;
|
||||
case BL::ShaderNodeVolumeCoefficients::phase_DRAINE:
|
||||
coeffs->set_phase(CLOSURE_VOLUME_DRAINE_ID);
|
||||
break;
|
||||
case BL::ShaderNodeVolumeCoefficients::phase_RAYLEIGH:
|
||||
coeffs->set_phase(CLOSURE_VOLUME_RAYLEIGH_ID);
|
||||
break;
|
||||
case BL::ShaderNodeVolumeCoefficients::phase_MIE:
|
||||
coeffs->set_phase(CLOSURE_VOLUME_MIE_ID);
|
||||
break;
|
||||
}
|
||||
node = coeffs;
|
||||
}
|
||||
else if (b_node.is_a(&RNA_ShaderNodeVolumePrincipled)) {
|
||||
PrincipledVolumeNode *principled = graph->create_node<PrincipledVolumeNode>();
|
||||
node = principled;
|
||||
|
||||
@@ -43,6 +43,7 @@ set(SRC_OSL
|
||||
node_point_info.osl
|
||||
node_scatter_volume.osl
|
||||
node_absorption_volume.osl
|
||||
node_volume_coefficients.osl
|
||||
node_principled_volume.osl
|
||||
node_holdout.osl
|
||||
node_hsv.osl
|
||||
|
||||
74
intern/cycles/kernel/osl/shaders/node_scatter.h
Normal file
74
intern/cycles/kernel/osl/shaders/node_scatter.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "stdcycles.h"
|
||||
|
||||
struct MieParameters {
|
||||
float g_HG;
|
||||
float g_D;
|
||||
float alpha;
|
||||
float mixture;
|
||||
};
|
||||
|
||||
MieParameters phase_mie_fitted_parameters(float Diameter)
|
||||
{
|
||||
float d = max(Diameter, 0.0);
|
||||
if (d <= 0.1) {
|
||||
/* Eq (11 - 14). */
|
||||
return {13.8 * d * d, 1.1456 * d * sin(9.29044 * d), 250.0, 0.252977 - 312.983 * pow(d, 4.3)};
|
||||
}
|
||||
|
||||
if (d < 1.5) {
|
||||
/* Eq (15 - 18). */
|
||||
float log_d = log(d);
|
||||
float a = (log_d - 0.238604) * (log_d + 1.00667);
|
||||
float b = 0.507522 - 0.15677 * log_d;
|
||||
float c = 1.19692 * cos(a / b) + 1.37932 * log_d + 0.0625835;
|
||||
return {0.862 - 0.143 * log_d * log_d,
|
||||
0.379685 * cos(c) + 0.344213,
|
||||
250.0,
|
||||
0.146209 * cos(3.38707 * log_d + 2.11193) + 0.316072 + 0.0778917 * log_d};
|
||||
}
|
||||
|
||||
if (d < 5.0) {
|
||||
/* Eq (19 - 22). */
|
||||
float log_d = log(d);
|
||||
float temp = cos(5.68947 * (log(log_d) - 0.0292149));
|
||||
return {0.0604931 * log(log_d) + 0.940256,
|
||||
0.500411 - (0.081287 / (-2.0 * log_d + tan(log_d) + 1.27551)),
|
||||
7.30354 * log_d + 6.31675,
|
||||
0.026914 * (log_d - temp) + 0.3764};
|
||||
}
|
||||
|
||||
/* Eq (7 - 10). */
|
||||
return {exp(-0.0990567 / (d - 1.67154)),
|
||||
exp(-2.20679 / (d + 3.91029) - 0.428934),
|
||||
exp(3.62489 - 8.29288 / (d + 5.52825)),
|
||||
exp(-0.599085 / (d - 0.641583) - 0.665888)};
|
||||
}
|
||||
|
||||
closure color
|
||||
scatter(string phase, float Anisotropy, float IOR, float Backscatter, float Alpha, float Diameter)
|
||||
{
|
||||
closure color scatter = 0;
|
||||
if (phase == "Fournier-Forand") {
|
||||
scatter = fournier_forand(Backscatter, IOR);
|
||||
}
|
||||
else if (phase == "Draine") {
|
||||
scatter = draine(Anisotropy, Alpha);
|
||||
}
|
||||
else if (phase == "Rayleigh") {
|
||||
scatter = rayleigh();
|
||||
}
|
||||
else if (phase == "Mie") {
|
||||
/* Approximation of Mie phase function for water droplets using a mix of Draine and H-G.
|
||||
* See `kernel/svm/closure.h` for details. */
|
||||
MieParameters param = phase_mie_fitted_parameters(Diameter);
|
||||
scatter = mix(henyey_greenstein(param.g_HG), draine(param.g_D, param.alpha), param.mixture);
|
||||
}
|
||||
else {
|
||||
scatter = henyey_greenstein(Anisotropy);
|
||||
}
|
||||
return scatter;
|
||||
}
|
||||
@@ -2,51 +2,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "stdcycles.h"
|
||||
|
||||
struct MieParameters {
|
||||
float g_HG;
|
||||
float g_D;
|
||||
float alpha;
|
||||
float mixture;
|
||||
};
|
||||
|
||||
MieParameters phase_mie_fitted_parameters(float Diameter)
|
||||
{
|
||||
float d = max(Diameter, 0.0);
|
||||
if (d <= 0.1) {
|
||||
/* Eq (11 - 14). */
|
||||
return {13.8 * d * d, 1.1456 * d * sin(9.29044 * d), 250.0, 0.252977 - 312.983 * pow(d, 4.3)};
|
||||
}
|
||||
|
||||
if (d < 1.5) {
|
||||
/* Eq (15 - 18). */
|
||||
float log_d = log(d);
|
||||
float a = (log_d - 0.238604) * (log_d + 1.00667);
|
||||
float b = 0.507522 - 0.15677 * log_d;
|
||||
float c = 1.19692 * cos(a / b) + 1.37932 * log_d + 0.0625835;
|
||||
return {0.862 - 0.143 * log_d * log_d,
|
||||
0.379685 * cos(c) + 0.344213,
|
||||
250.0,
|
||||
0.146209 * cos(3.38707 * log_d + 2.11193) + 0.316072 + 0.0778917 * log_d};
|
||||
}
|
||||
|
||||
if (d < 5.0) {
|
||||
/* Eq (19 - 22). */
|
||||
float log_d = log(d);
|
||||
float temp = cos(5.68947 * (log(log_d) - 0.0292149));
|
||||
return {0.0604931 * log(log_d) + 0.940256,
|
||||
0.500411 - (0.081287 / (-2.0 * log_d + tan(log_d) + 1.27551)),
|
||||
7.30354 * log_d + 6.31675,
|
||||
0.026914 * (log_d - temp) + 0.3764};
|
||||
}
|
||||
|
||||
/* Eq (7 - 10). */
|
||||
return {exp(-0.0990567 / (d - 1.67154)),
|
||||
exp(-2.20679 / (d + 3.91029) - 0.428934),
|
||||
exp(3.62489 - 8.29288 / (d + 5.52825)),
|
||||
exp(-0.599085 / (d - 0.641583) - 0.665888)};
|
||||
}
|
||||
#include "node_scatter.h"
|
||||
|
||||
shader node_scatter_volume(string phase = "Henyey-Greenstein",
|
||||
color Color = color(0.8, 0.8, 0.8),
|
||||
@@ -58,25 +14,7 @@ shader node_scatter_volume(string phase = "Henyey-Greenstein",
|
||||
float Diameter = 20.0,
|
||||
output closure color Volume = 0)
|
||||
{
|
||||
closure color scatter = 0;
|
||||
if (phase == "Fournier-Forand") {
|
||||
scatter = fournier_forand(Backscatter, IOR);
|
||||
}
|
||||
else if (phase == "Draine") {
|
||||
scatter = draine(Anisotropy, Alpha);
|
||||
}
|
||||
else if (phase == "Rayleigh") {
|
||||
scatter = rayleigh();
|
||||
}
|
||||
else if (phase == "Mie") {
|
||||
/* Approximation of Mie phase function for water droplets using a mix of Draine and H-G.
|
||||
* See `kernel/svm/closure.h` for details. */
|
||||
MieParameters param = phase_mie_fitted_parameters(Diameter);
|
||||
scatter = mix(henyey_greenstein(param.g_HG), draine(param.g_D, param.alpha), param.mixture);
|
||||
}
|
||||
else {
|
||||
scatter = henyey_greenstein(Anisotropy);
|
||||
}
|
||||
closure color scatter_closure = scatter(phase, Anisotropy, IOR, Backscatter, Alpha, Diameter);
|
||||
|
||||
Volume = (Color * max(Density, 0.0)) * scatter;
|
||||
Volume = (Color * max(Density, 0.0)) * scatter_closure;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "node_scatter.h"
|
||||
|
||||
shader node_volume_coefficients(string phase = "Henyey-Greenstein",
|
||||
vector AbsorptionCoefficients = vector(1.0, 1.0, 1.0),
|
||||
vector ScatterCoefficients = vector(1.0, 1.0, 1.0),
|
||||
float Anisotropy = 0.0,
|
||||
float IOR = 1.33,
|
||||
float Backscatter = 0.1,
|
||||
float Alpha = 0.5,
|
||||
float Diameter = 20.0,
|
||||
vector EmissionCoefficients = vector(0.0, 0.0, 0.0),
|
||||
output closure color Volume = 0)
|
||||
{
|
||||
closure color scatter_closure = scatter(phase, Anisotropy, IOR, Backscatter, Alpha, Diameter);
|
||||
|
||||
/* Add scattering and absorption closures. */
|
||||
Volume = color(ScatterCoefficients) * scatter_closure +
|
||||
color(AbsorptionCoefficients) * absorption();
|
||||
|
||||
/* Compute emission. */
|
||||
Volume += color(EmissionCoefficients) * emission();
|
||||
}
|
||||
@@ -1053,6 +1053,78 @@ ccl_device
|
||||
return offset;
|
||||
}
|
||||
|
||||
ccl_device_inline void svm_alloc_closure_volume_scatter(ccl_private ShaderData *sd,
|
||||
ccl_private float *stack,
|
||||
Spectrum weight,
|
||||
const uint type,
|
||||
const uint param1_offset,
|
||||
const uint param_extra)
|
||||
{
|
||||
switch (type) {
|
||||
case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: {
|
||||
ccl_private HenyeyGreensteinVolume *volume = (ccl_private HenyeyGreensteinVolume *)
|
||||
bsdf_alloc(sd, sizeof(HenyeyGreensteinVolume), weight);
|
||||
if (volume) {
|
||||
volume->g = stack_valid(param1_offset) ? stack_load_float(stack, param1_offset) :
|
||||
__uint_as_float(param_extra);
|
||||
sd->flag |= volume_henyey_greenstein_setup(volume);
|
||||
}
|
||||
} break;
|
||||
case CLOSURE_VOLUME_FOURNIER_FORAND_ID: {
|
||||
ccl_private FournierForandVolume *volume = (ccl_private FournierForandVolume *)bsdf_alloc(
|
||||
sd, sizeof(FournierForandVolume), weight);
|
||||
if (volume) {
|
||||
const float IOR = stack_load_float(stack, param1_offset);
|
||||
const float B = stack_load_float(stack, param_extra);
|
||||
sd->flag |= volume_fournier_forand_setup(volume, B, IOR);
|
||||
}
|
||||
} break;
|
||||
case CLOSURE_VOLUME_RAYLEIGH_ID: {
|
||||
ccl_private RayleighVolume *volume = (ccl_private RayleighVolume *)bsdf_alloc(
|
||||
sd, sizeof(RayleighVolume), weight);
|
||||
if (volume) {
|
||||
sd->flag |= volume_rayleigh_setup(volume);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CLOSURE_VOLUME_DRAINE_ID: {
|
||||
ccl_private DraineVolume *volume = (ccl_private DraineVolume *)bsdf_alloc(
|
||||
sd, sizeof(DraineVolume), weight);
|
||||
if (volume) {
|
||||
volume->g = stack_load_float(stack, param1_offset);
|
||||
volume->alpha = stack_load_float(stack, param_extra);
|
||||
sd->flag |= volume_draine_setup(volume);
|
||||
}
|
||||
} break;
|
||||
case CLOSURE_VOLUME_MIE_ID: {
|
||||
const float d = stack_valid(param1_offset) ? stack_load_float(stack, param1_offset) :
|
||||
__uint_as_float(param_extra);
|
||||
float g_HG;
|
||||
float g_D;
|
||||
float alpha;
|
||||
float mixture;
|
||||
phase_mie_fitted_parameters(d, &g_HG, &g_D, &alpha, &mixture);
|
||||
ccl_private HenyeyGreensteinVolume *hg = (ccl_private HenyeyGreensteinVolume *)bsdf_alloc(
|
||||
sd, sizeof(HenyeyGreensteinVolume), weight * (1.0f - mixture));
|
||||
if (hg) {
|
||||
hg->g = g_HG;
|
||||
sd->flag |= volume_henyey_greenstein_setup(hg);
|
||||
}
|
||||
ccl_private DraineVolume *draine = (ccl_private DraineVolume *)bsdf_alloc(
|
||||
sd, sizeof(DraineVolume), weight * mixture);
|
||||
if (draine) {
|
||||
draine->g = g_D;
|
||||
draine->alpha = alpha;
|
||||
sd->flag |= volume_draine_setup(draine);
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
kernel_assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<ShaderType shader_type>
|
||||
ccl_device_noinline void svm_node_closure_volume(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
@@ -1093,69 +1165,7 @@ ccl_device_noinline void svm_node_closure_volume(KernelGlobals kg,
|
||||
|
||||
/* Add closure for volume scattering. */
|
||||
if (CLOSURE_IS_VOLUME_SCATTER(type)) {
|
||||
switch (type) {
|
||||
case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: {
|
||||
ccl_private HenyeyGreensteinVolume *volume = (ccl_private HenyeyGreensteinVolume *)
|
||||
bsdf_alloc(sd, sizeof(HenyeyGreensteinVolume), weight);
|
||||
if (volume) {
|
||||
volume->g = stack_valid(param1_offset) ? stack_load_float(stack, param1_offset) :
|
||||
__uint_as_float(node.w);
|
||||
sd->flag |= volume_henyey_greenstein_setup(volume);
|
||||
}
|
||||
} break;
|
||||
case CLOSURE_VOLUME_FOURNIER_FORAND_ID: {
|
||||
ccl_private FournierForandVolume *volume = (ccl_private FournierForandVolume *)bsdf_alloc(
|
||||
sd, sizeof(FournierForandVolume), weight);
|
||||
if (volume) {
|
||||
const float IOR = stack_load_float(stack, param1_offset);
|
||||
const float B = stack_load_float(stack, node.w);
|
||||
sd->flag |= volume_fournier_forand_setup(volume, B, IOR);
|
||||
}
|
||||
} break;
|
||||
case CLOSURE_VOLUME_RAYLEIGH_ID: {
|
||||
ccl_private RayleighVolume *volume = (ccl_private RayleighVolume *)bsdf_alloc(
|
||||
sd, sizeof(RayleighVolume), weight);
|
||||
if (volume) {
|
||||
sd->flag |= volume_rayleigh_setup(volume);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CLOSURE_VOLUME_DRAINE_ID: {
|
||||
ccl_private DraineVolume *volume = (ccl_private DraineVolume *)bsdf_alloc(
|
||||
sd, sizeof(DraineVolume), weight);
|
||||
if (volume) {
|
||||
volume->g = stack_load_float(stack, param1_offset);
|
||||
volume->alpha = stack_load_float(stack, node.w);
|
||||
sd->flag |= volume_draine_setup(volume);
|
||||
}
|
||||
} break;
|
||||
case CLOSURE_VOLUME_MIE_ID: {
|
||||
const float d = stack_valid(param1_offset) ? stack_load_float(stack, param1_offset) :
|
||||
__uint_as_float(node.w);
|
||||
float g_HG;
|
||||
float g_D;
|
||||
float alpha;
|
||||
float mixture;
|
||||
phase_mie_fitted_parameters(d, &g_HG, &g_D, &alpha, &mixture);
|
||||
ccl_private HenyeyGreensteinVolume *hg = (ccl_private HenyeyGreensteinVolume *)bsdf_alloc(
|
||||
sd, sizeof(HenyeyGreensteinVolume), weight * (1.0f - mixture));
|
||||
if (hg) {
|
||||
hg->g = g_HG;
|
||||
sd->flag |= volume_henyey_greenstein_setup(hg);
|
||||
}
|
||||
ccl_private DraineVolume *draine = (ccl_private DraineVolume *)bsdf_alloc(
|
||||
sd, sizeof(DraineVolume), weight * mixture);
|
||||
if (draine) {
|
||||
draine->g = g_D;
|
||||
draine->alpha = alpha;
|
||||
sd->flag |= volume_draine_setup(draine);
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
kernel_assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
svm_alloc_closure_volume_scatter(sd, stack, weight, type, param1_offset, node.w);
|
||||
}
|
||||
|
||||
/* Sum total extinction weight. */
|
||||
@@ -1163,6 +1173,62 @@ ccl_device_noinline void svm_node_closure_volume(KernelGlobals kg,
|
||||
#endif
|
||||
}
|
||||
|
||||
template<ShaderType shader_type>
|
||||
ccl_device_noinline void svm_node_volume_coefficients(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
ccl_private float *stack,
|
||||
Spectrum scatter_coeffs,
|
||||
const uint4 node,
|
||||
const uint32_t path_flag)
|
||||
{
|
||||
#ifdef __VOLUME__
|
||||
/* Only sum extinction for volumes, variable is shared with surface transparency. */
|
||||
if (shader_type != SHADER_TYPE_VOLUME) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint type;
|
||||
uint empty_offset;
|
||||
uint param1_offset;
|
||||
uint mix_weight_offset;
|
||||
svm_unpack_node_uchar4(node.y, &type, &empty_offset, ¶m1_offset, &mix_weight_offset);
|
||||
const float mix_weight = (stack_valid(mix_weight_offset) ?
|
||||
stack_load_float(stack, mix_weight_offset) :
|
||||
1.0f);
|
||||
if (mix_weight == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compute scattering coefficient. */
|
||||
const float weight = mix_weight * object_volume_density(kg, sd->object);
|
||||
|
||||
/* Add closure for volume scattering. */
|
||||
if (!is_zero(scatter_coeffs) && CLOSURE_IS_VOLUME_SCATTER(type)) {
|
||||
svm_alloc_closure_volume_scatter(
|
||||
sd, stack, weight * scatter_coeffs, type, param1_offset, node.z);
|
||||
}
|
||||
uint absorption_coeffs_offset;
|
||||
uint emission_coeffs_offset;
|
||||
svm_unpack_node_uchar4(
|
||||
node.w, &absorption_coeffs_offset, &emission_coeffs_offset, &empty_offset, &empty_offset);
|
||||
const float3 absorption_coeffs = stack_load_float3(stack, absorption_coeffs_offset);
|
||||
volume_extinction_setup(sd, weight * (scatter_coeffs + absorption_coeffs));
|
||||
|
||||
const float3 emission_coeffs = stack_load_float3(stack, emission_coeffs_offset);
|
||||
/* Compute emission. */
|
||||
if (path_flag & PATH_RAY_SHADOW) {
|
||||
/* Don't need emission for shadows. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_zero(emission_coeffs)) {
|
||||
return;
|
||||
}
|
||||
emission_setup(sd, weight * emission_coeffs);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
template<ShaderType shader_type>
|
||||
ccl_device_noinline int svm_node_principled_volume(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
|
||||
@@ -50,6 +50,7 @@ SHADER_NODE_TYPE(NODE_CLOSURE_HOLDOUT)
|
||||
SHADER_NODE_TYPE(NODE_FRESNEL)
|
||||
SHADER_NODE_TYPE(NODE_LAYER_WEIGHT)
|
||||
SHADER_NODE_TYPE(NODE_CLOSURE_VOLUME)
|
||||
SHADER_NODE_TYPE(NODE_VOLUME_COEFFICIENTS)
|
||||
SHADER_NODE_TYPE(NODE_PRINCIPLED_VOLUME)
|
||||
SHADER_NODE_TYPE(NODE_MATH)
|
||||
SHADER_NODE_TYPE(NODE_VECTOR_MATH)
|
||||
|
||||
@@ -296,6 +296,12 @@ ccl_device void svm_eval_nodes(KernelGlobals kg,
|
||||
svm_node_closure_volume<type>(kg, sd, stack, closure_weight, node);
|
||||
}
|
||||
break;
|
||||
SVM_CASE(NODE_VOLUME_COEFFICIENTS)
|
||||
IF_KERNEL_NODES_FEATURE(VOLUME)
|
||||
{
|
||||
svm_node_volume_coefficients<type>(kg, sd, stack, closure_weight, node, path_flag);
|
||||
}
|
||||
break;
|
||||
SVM_CASE(NODE_PRINCIPLED_VOLUME)
|
||||
IF_KERNEL_NODES_FEATURE(VOLUME)
|
||||
{
|
||||
|
||||
@@ -3513,6 +3513,11 @@ NODE_DEFINE(ScatterVolumeNode)
|
||||
return type;
|
||||
}
|
||||
|
||||
ScatterVolumeNode::ScatterVolumeNode(const NodeType *node_type) : VolumeNode(node_type)
|
||||
{
|
||||
closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID;
|
||||
}
|
||||
|
||||
ScatterVolumeNode::ScatterVolumeNode() : VolumeNode(get_node_type())
|
||||
{
|
||||
closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID;
|
||||
@@ -3550,6 +3555,111 @@ void ScatterVolumeNode::compile(OSLCompiler &compiler)
|
||||
compiler.add(this, "node_scatter_volume");
|
||||
}
|
||||
|
||||
/* Volume Coefficients Closure */
|
||||
|
||||
NODE_DEFINE(VolumeCoefficientsNode)
|
||||
{
|
||||
NodeType *type = NodeType::add("volume_coefficients", create, NodeType::SHADER);
|
||||
|
||||
SOCKET_IN_VECTOR(scatter_coeffs, "Scatter Coefficients", make_float3(1.0f, 1.0f, 1.0f));
|
||||
SOCKET_IN_VECTOR(absorption_coeffs, "Absorption Coefficients", make_float3(1.0f, 1.0f, 1.0f));
|
||||
SOCKET_IN_FLOAT(anisotropy, "Anisotropy", 0.0f);
|
||||
SOCKET_IN_FLOAT(IOR, "IOR", 1.33f);
|
||||
SOCKET_IN_FLOAT(backscatter, "Backscatter", 0.1f);
|
||||
SOCKET_IN_FLOAT(alpha, "Alpha", 0.5f);
|
||||
SOCKET_IN_FLOAT(diameter, "Diameter", 20.0f);
|
||||
SOCKET_IN_VECTOR(emission_coeffs, "Emission Coefficients", make_float3(0.0f, 0.0f, 0.0f));
|
||||
|
||||
static NodeEnum phase_enum;
|
||||
phase_enum.insert("Henyey-Greenstein", CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID);
|
||||
phase_enum.insert("Fournier-Forand", CLOSURE_VOLUME_FOURNIER_FORAND_ID);
|
||||
phase_enum.insert("Draine", CLOSURE_VOLUME_DRAINE_ID);
|
||||
phase_enum.insert("Rayleigh", CLOSURE_VOLUME_RAYLEIGH_ID);
|
||||
phase_enum.insert("Mie", CLOSURE_VOLUME_MIE_ID);
|
||||
SOCKET_ENUM(phase, "Phase", phase_enum, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID);
|
||||
|
||||
SOCKET_IN_FLOAT(volume_mix_weight, "VolumeMixWeight", 0.0f, SocketType::SVM_INTERNAL);
|
||||
|
||||
SOCKET_OUT_CLOSURE(volume, "Volume");
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
VolumeCoefficientsNode::VolumeCoefficientsNode() : ScatterVolumeNode(get_node_type())
|
||||
{
|
||||
closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID;
|
||||
}
|
||||
|
||||
void VolumeCoefficientsNode::compile(SVMCompiler &compiler)
|
||||
{
|
||||
closure = phase;
|
||||
ShaderInput *param1 = nullptr;
|
||||
ShaderInput *param2 = nullptr;
|
||||
|
||||
switch (phase) {
|
||||
case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID:
|
||||
param1 = input("Anisotropy");
|
||||
break;
|
||||
case CLOSURE_VOLUME_FOURNIER_FORAND_ID:
|
||||
param1 = input("IOR");
|
||||
param2 = input("Backscatter");
|
||||
break;
|
||||
case CLOSURE_VOLUME_RAYLEIGH_ID:
|
||||
break;
|
||||
case CLOSURE_VOLUME_DRAINE_ID:
|
||||
param1 = input("Anisotropy");
|
||||
param2 = input("Alpha");
|
||||
break;
|
||||
case CLOSURE_VOLUME_MIE_ID:
|
||||
param1 = input("Diameter");
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
ShaderInput *coeffs_in = input("Scatter Coefficients");
|
||||
ShaderInput *absorption_coeffs_in = input("Absorption Coefficients");
|
||||
ShaderInput *emission_coeffs_in = input("Emission Coefficients");
|
||||
|
||||
if (coeffs_in->link) {
|
||||
compiler.add_node(NODE_CLOSURE_WEIGHT, compiler.stack_assign(coeffs_in));
|
||||
}
|
||||
else {
|
||||
compiler.add_node(NODE_CLOSURE_SET_WEIGHT, scatter_coeffs);
|
||||
}
|
||||
|
||||
const uint mix_weight_ofs = compiler.closure_mix_weight_offset();
|
||||
|
||||
if (param2 == nullptr) {
|
||||
/* More efficient packing if we don't need the second parameter. */
|
||||
const uint param1_ofs = (param1) ? compiler.stack_assign_if_linked(param1) : SVM_STACK_INVALID;
|
||||
compiler.add_node(NODE_VOLUME_COEFFICIENTS,
|
||||
compiler.encode_uchar4(closure, 0, param1_ofs, mix_weight_ofs),
|
||||
__float_as_int((param1) ? get_float(param1->socket_type) : 0.0f),
|
||||
compiler.encode_uchar4(compiler.stack_assign(absorption_coeffs_in),
|
||||
compiler.stack_assign(emission_coeffs_in),
|
||||
0,
|
||||
0));
|
||||
}
|
||||
else {
|
||||
const uint param1_ofs = (param1) ? compiler.stack_assign(param1) : SVM_STACK_INVALID;
|
||||
const uint param2_ofs = (param2) ? compiler.stack_assign(param2) : SVM_STACK_INVALID;
|
||||
compiler.add_node(NODE_VOLUME_COEFFICIENTS,
|
||||
compiler.encode_uchar4(closure, 0, param1_ofs, mix_weight_ofs),
|
||||
param2_ofs,
|
||||
compiler.encode_uchar4(compiler.stack_assign(absorption_coeffs_in),
|
||||
compiler.stack_assign(emission_coeffs_in),
|
||||
0,
|
||||
0));
|
||||
}
|
||||
}
|
||||
|
||||
void VolumeCoefficientsNode::compile(OSLCompiler &compiler)
|
||||
{
|
||||
compiler.parameter(this, "phase");
|
||||
compiler.add(this, "node_volume_coefficients");
|
||||
}
|
||||
|
||||
/* Principled Volume Closure */
|
||||
|
||||
NODE_DEFINE(PrincipledVolumeNode)
|
||||
|
||||
@@ -836,6 +836,7 @@ class AbsorptionVolumeNode : public VolumeNode {
|
||||
|
||||
class ScatterVolumeNode : public VolumeNode {
|
||||
public:
|
||||
ScatterVolumeNode(const NodeType *node_type);
|
||||
SHADER_NODE_CLASS(ScatterVolumeNode)
|
||||
|
||||
NODE_SOCKET_API(float, anisotropy)
|
||||
@@ -846,6 +847,15 @@ class ScatterVolumeNode : public VolumeNode {
|
||||
NODE_SOCKET_API(ClosureType, phase)
|
||||
};
|
||||
|
||||
class VolumeCoefficientsNode : public ScatterVolumeNode {
|
||||
public:
|
||||
SHADER_NODE_CLASS(VolumeCoefficientsNode)
|
||||
|
||||
NODE_SOCKET_API(float3, scatter_coeffs)
|
||||
NODE_SOCKET_API(float3, absorption_coeffs)
|
||||
NODE_SOCKET_API(float3, emission_coeffs)
|
||||
};
|
||||
|
||||
class PrincipledVolumeNode : public VolumeNode {
|
||||
public:
|
||||
SHADER_NODE_CLASS(PrincipledVolumeNode)
|
||||
|
||||
@@ -232,6 +232,10 @@ class NODE_MT_category_shader_shader(Menu):
|
||||
layout,
|
||||
"ShaderNodeVolumeScatter",
|
||||
)
|
||||
node_add_menu.add_node_type(
|
||||
layout,
|
||||
"ShaderNodeVolumeCoefficients",
|
||||
)
|
||||
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
@@ -133,6 +133,7 @@
|
||||
#define SH_NODE_BSDF_RAY_PORTAL 714
|
||||
#define SH_NODE_TEX_GABOR 715
|
||||
#define SH_NODE_BSDF_METALLIC 716
|
||||
#define SH_NODE_VOLUME_COEFFICIENTS 717
|
||||
|
||||
/** \} */
|
||||
|
||||
|
||||
@@ -629,6 +629,7 @@ set(GLSL_SRC
|
||||
shaders/material/gpu_shader_material_volume_absorption.glsl
|
||||
shaders/material/gpu_shader_material_volume_principled.glsl
|
||||
shaders/material/gpu_shader_material_volume_scatter.glsl
|
||||
shaders/material/gpu_shader_material_volume_coefficients.glsl
|
||||
shaders/material/gpu_shader_material_voronoi.glsl
|
||||
shaders/material/gpu_shader_material_wireframe.glsl
|
||||
shaders/material/gpu_shader_material_world_normals.glsl
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/* SPDX-FileCopyrightText: 2019-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_material_blackbody.glsl"
|
||||
|
||||
void node_volume_coefficients(float weight,
|
||||
float3 AbsorptionCoefficients,
|
||||
float3 ScatterCoefficients,
|
||||
float Anisotropy,
|
||||
float IOR,
|
||||
float Backscatter,
|
||||
float Alpha,
|
||||
float Diameter,
|
||||
float3 EmissionCoefficients,
|
||||
out Closure result)
|
||||
{
|
||||
ClosureVolumeScatter volume_scatter_data;
|
||||
volume_scatter_data.weight = weight;
|
||||
volume_scatter_data.scattering = ScatterCoefficients;
|
||||
volume_scatter_data.anisotropy = Anisotropy;
|
||||
|
||||
ClosureVolumeAbsorption volume_absorption_data;
|
||||
volume_absorption_data.weight = weight;
|
||||
volume_absorption_data.absorption = AbsorptionCoefficients;
|
||||
|
||||
ClosureEmission emission_data;
|
||||
emission_data.weight = weight;
|
||||
emission_data.emission = EmissionCoefficients;
|
||||
|
||||
result = closure_eval(volume_scatter_data, volume_absorption_data, emission_data);
|
||||
}
|
||||
@@ -6862,6 +6862,17 @@ static void def_scatter(BlenderRNA * /*brna*/, StructRNA *srna)
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
}
|
||||
|
||||
static void def_volume_coefficients(BlenderRNA * /*brna*/, StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_property(srna, "phase", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "custom1");
|
||||
RNA_def_property_enum_items(prop, node_scatter_phase_items);
|
||||
RNA_def_property_ui_text(prop, "Phase", "Phase function for the scattered light");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
}
|
||||
|
||||
static void def_toon(BlenderRNA * /*brna*/, StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
@@ -13617,6 +13628,7 @@ static void rna_def_nodes(BlenderRNA *brna)
|
||||
define("ShaderNode", "ShaderNodeVolumeInfo");
|
||||
define("ShaderNode", "ShaderNodeVolumePrincipled");
|
||||
define("ShaderNode", "ShaderNodeVolumeScatter", def_scatter);
|
||||
define("ShaderNode", "ShaderNodeVolumeCoefficients", def_volume_coefficients);
|
||||
define("ShaderNode", "ShaderNodeWavelength");
|
||||
define("ShaderNode", "ShaderNodeWireframe", def_sh_tex_wireframe);
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ set(SRC
|
||||
nodes/node_shader_volume_info.cc
|
||||
nodes/node_shader_volume_principled.cc
|
||||
nodes/node_shader_volume_scatter.cc
|
||||
nodes/node_shader_volume_coefficients.cc
|
||||
nodes/node_shader_wavelength.cc
|
||||
nodes/node_shader_wireframe.cc
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ void register_shader_nodes()
|
||||
register_node_type_sh_volume_info();
|
||||
register_node_type_sh_volume_principled();
|
||||
register_node_type_sh_volume_scatter();
|
||||
register_node_type_sh_volume_coefficients();
|
||||
register_node_type_sh_wavelength();
|
||||
register_node_type_sh_wireframe();
|
||||
}
|
||||
|
||||
@@ -109,5 +109,6 @@ void register_node_type_sh_volume_absorption();
|
||||
void register_node_type_sh_volume_info();
|
||||
void register_node_type_sh_volume_principled();
|
||||
void register_node_type_sh_volume_scatter();
|
||||
void register_node_type_sh_volume_coefficients();
|
||||
void register_node_type_sh_wavelength();
|
||||
void register_node_type_sh_wireframe();
|
||||
|
||||
@@ -969,6 +969,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
|
||||
case SH_NODE_VOLUME_ABSORPTION:
|
||||
case SH_NODE_VOLUME_PRINCIPLED:
|
||||
case SH_NODE_VOLUME_SCATTER:
|
||||
case SH_NODE_VOLUME_COEFFICIENTS:
|
||||
fromsock = ntree_shader_node_find_input(fromnode, "Weight");
|
||||
/* Make "weight" sockets available so that links to it are available as well and are
|
||||
* not ignored in other places. */
|
||||
@@ -1035,6 +1036,7 @@ static bool closure_node_filter(const bNode *node)
|
||||
case SH_NODE_VOLUME_ABSORPTION:
|
||||
case SH_NODE_VOLUME_PRINCIPLED:
|
||||
case SH_NODE_VOLUME_SCATTER:
|
||||
case SH_NODE_VOLUME_COEFFICIENTS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
namespace blender::nodes::node_shader_volume_coefficients_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.use_custom_socket_order();
|
||||
|
||||
b.add_output<decl::Shader>("Volume").translation_context(BLT_I18NCONTEXT_ID_ID);
|
||||
|
||||
b.add_input<decl::Float>("Weight").available(false);
|
||||
#define SOCK_WEIGHT_ID 0
|
||||
|
||||
PanelDeclarationBuilder &abs = b.add_panel("Absorption").default_closed(false);
|
||||
abs.add_input<decl::Vector>("Absorption Coefficients")
|
||||
.default_value({1.0f, 1.0f, 1.0f})
|
||||
.min(0.0f)
|
||||
.max(1000.0f)
|
||||
.description(
|
||||
"Probability density per color channel that light is absorbed per unit distance "
|
||||
"traveled in the medium");
|
||||
#define SOCK_ABSORPTION_COEFFICIENTS_ID 1
|
||||
PanelDeclarationBuilder &sca = b.add_panel("Scatter").default_closed(false);
|
||||
sca.add_layout([](uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) {
|
||||
uiItemR(layout, ptr, "phase", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
|
||||
});
|
||||
sca.add_input<decl::Vector>("Scatter Coefficients")
|
||||
.default_value({1.0f, 1.0f, 1.0f})
|
||||
.min(0.0f)
|
||||
.max(1000.0f)
|
||||
.description(
|
||||
"Probability density per color channel of an out-scattering event occurring per unit "
|
||||
"distance");
|
||||
#define SOCK_SCATTER_COEFFICIENTS_ID 2
|
||||
sca.add_input<decl::Float>("Anisotropy")
|
||||
.default_value(0.0f)
|
||||
.min(-1.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description(
|
||||
"Directionality of the scattering. Zero is isotropic, negative is backward, "
|
||||
"positive is forward");
|
||||
#define SOCK_SCATTER_ANISOTROPY_ID 3
|
||||
sca.add_input<decl::Float>("IOR")
|
||||
.default_value(1.33f)
|
||||
.min(1.0f)
|
||||
.max(2.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Index Of Refraction of the scattering particles");
|
||||
#define SOCK_SCATTER_IOR_ID 4
|
||||
sca.add_input<decl::Float>("Backscatter")
|
||||
.default_value(0.1f)
|
||||
.min(0.0f)
|
||||
.max(0.5f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.description("Fraction of light that is scattered backwards");
|
||||
#define SOCK_SCATTER_BACKSCATTER_ID 5
|
||||
sca.add_input<decl::Float>("Alpha").default_value(0.5f).min(0.0f).max(500.0f);
|
||||
#define SOCK_SCATTER_ALPHA_ID 6
|
||||
sca.add_input<decl::Float>("Diameter")
|
||||
.default_value(20.0f)
|
||||
.min(0.0f)
|
||||
.max(50.0f)
|
||||
.description("Diameter of the water droplets, in micrometers");
|
||||
#define SOCK_SCATTER_DIAMETER_ID 7
|
||||
PanelDeclarationBuilder &emi = b.add_panel("Emission").default_closed(false);
|
||||
emi.add_input<decl::Vector>("Emission Coefficients")
|
||||
.default_value({0.0f, 0.0f, 0.0f})
|
||||
.min(0.0f)
|
||||
.max(1000.0f)
|
||||
.description("Emitted radiance per color channel that is added to a ray per unit distance");
|
||||
#define SOCK_EMISSION_COEFFICIENTS_ID 8
|
||||
}
|
||||
|
||||
static void node_shader_init_coefficients(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
node->custom1 = SHD_PHASE_HENYEY_GREENSTEIN;
|
||||
}
|
||||
|
||||
static void node_shader_update_coefficients(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
const int phase_function = node->custom1;
|
||||
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
||||
if (STR_ELEM(sock->name, "IOR", "Backscatter")) {
|
||||
bke::node_set_socket_availability(
|
||||
*ntree, *sock, phase_function == SHD_PHASE_FOURNIER_FORAND);
|
||||
}
|
||||
else if (STR_ELEM(sock->name, "Anisotropy")) {
|
||||
bke::node_set_socket_availability(
|
||||
*ntree, *sock, ELEM(phase_function, SHD_PHASE_HENYEY_GREENSTEIN, SHD_PHASE_DRAINE));
|
||||
}
|
||||
else if (STR_ELEM(sock->name, "Alpha")) {
|
||||
bke::node_set_socket_availability(*ntree, *sock, phase_function == SHD_PHASE_DRAINE);
|
||||
}
|
||||
else if (STR_ELEM(sock->name, "Diameter")) {
|
||||
bke::node_set_socket_availability(*ntree, *sock, phase_function == SHD_PHASE_MIE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int node_shader_gpu_volume_coefficients(GPUMaterial *mat,
|
||||
bNode *node,
|
||||
bNodeExecData * /*execdata*/,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out)
|
||||
{
|
||||
if (node_socket_not_black(in[SOCK_SCATTER_COEFFICIENTS_ID])) {
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_VOLUME_SCATTER | GPU_MATFLAG_VOLUME_ABSORPTION);
|
||||
}
|
||||
if (node_socket_not_black(in[SOCK_ABSORPTION_COEFFICIENTS_ID])) {
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_VOLUME_ABSORPTION);
|
||||
}
|
||||
return GPU_stack_link(mat, node, "node_volume_coefficients", in, out);
|
||||
}
|
||||
|
||||
#undef SOCK_WEIGHT_ID
|
||||
#undef SOCK_ABSORPTION_COEFFICIENTS_ID
|
||||
#undef SOCK_SCATTER_COEFFICIENTS_ID
|
||||
#undef SOCK_SCATTER_ANISOTROPY_ID
|
||||
#undef SOCK_SCATTER_IOR_ID
|
||||
#undef SOCK_SCATTER_BACKSCATTER_ID
|
||||
#undef SOCK_SCATTER_ALPHA_ID
|
||||
#undef SOCK_SCATTER_DIAMETER_ID
|
||||
#undef SOCK_EMISSION_COEFFICIENTS_ID
|
||||
|
||||
} // namespace blender::nodes::node_shader_volume_coefficients_cc
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_volume_coefficients()
|
||||
{
|
||||
namespace file_ns = blender::nodes::node_shader_volume_coefficients_cc;
|
||||
|
||||
static blender::bke::bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, "ShaderNodeVolumeCoefficients", SH_NODE_VOLUME_COEFFICIENTS);
|
||||
ntype.ui_name = "Volume Coefficients";
|
||||
ntype.ui_description =
|
||||
"Model all three physical processes in a volume, represented by their coefficients";
|
||||
ntype.enum_name_legacy = "VOLUME_COEFFICIENTS";
|
||||
ntype.nclass = NODE_CLASS_SHADER;
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.add_ui_poll = object_shader_nodes_poll;
|
||||
blender::bke::node_type_size_preset(ntype, blender::bke::eNodeSizePreset::Large);
|
||||
ntype.initfunc = file_ns::node_shader_init_coefficients;
|
||||
ntype.gpu_fn = file_ns::node_shader_gpu_volume_coefficients;
|
||||
ntype.updatefunc = file_ns::node_shader_update_coefficients;
|
||||
|
||||
blender::bke::node_register_type(ntype);
|
||||
}
|
||||
BIN
tests/files/render/volume/cycles_renders/implicit_volume.png
(Stored with Git LFS)
Normal file
BIN
tests/files/render/volume/cycles_renders/implicit_volume.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
tests/files/render/volume/eevee_next_renders/implicit_volume.png
(Stored with Git LFS)
Normal file
BIN
tests/files/render/volume/eevee_next_renders/implicit_volume.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
tests/files/render/volume/implicit_volume.blend
(Stored with Git LFS)
Normal file
BIN
tests/files/render/volume/implicit_volume.blend
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
tests/files/render/volume/storm_hydra_renders/implicit_volume.png
(Stored with Git LFS)
Normal file
BIN
tests/files/render/volume/storm_hydra_renders/implicit_volume.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
tests/files/render/volume/storm_usd_renders/implicit_volume.png
(Stored with Git LFS)
Normal file
BIN
tests/files/render/volume/storm_usd_renders/implicit_volume.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
tests/files/render/volume/workbench_renders/implicit_volume.png
(Stored with Git LFS)
Normal file
BIN
tests/files/render/volume/workbench_renders/implicit_volume.png
(Stored with Git LFS)
Normal file
Binary file not shown.
Reference in New Issue
Block a user