Compare commits
57 Commits
info-edito
...
soc-2013-d
Author | SHA1 | Date | |
---|---|---|---|
fe7d12bba5 | |||
daf01126e6 | |||
34f4f7a2ea | |||
76cf4a4d04 | |||
de479f51b6 | |||
bdf527bfec | |||
35a683bfe4 | |||
9bea4db0d8 | |||
ea9de15d0f | |||
16d223c5e7 | |||
3301b38116 | |||
8f6358182e | |||
1031e0ba4b | |||
85ec8768ab | |||
47cd4d41ab | |||
cf17355ecc | |||
53ddaa5acd | |||
d4f7e1524e | |||
56b9d24184 | |||
84efe953ad | |||
f89a66ab06 | |||
2d9a23a7f8 | |||
39ba7447ba | |||
f54e3e79b1 | |||
df19fe9162 | |||
b28fbdd329 | |||
2024d8ee34 | |||
6ef3ac8c00 | |||
3a27f6bacd | |||
f122ffd476 | |||
7136f6abad | |||
89aae3afdd | |||
966f32a205 | |||
c07459e357 | |||
dd6ebf8ba1 | |||
f5ec195be9 | |||
![]() |
e7c2bf0b52 | ||
![]() |
649d345fdd | ||
![]() |
a88a02964d | ||
f9c2563d05 | |||
21e669c047 | |||
3d49b320f3 | |||
![]() |
145931d7b9 | ||
40df16dc51 | |||
d91f700fed | |||
e011f35456 | |||
1bb2892562 | |||
fc8fa1593b | |||
0fb6924273 | |||
932dd5cef6 | |||
836da36db3 | |||
3c56be794e | |||
8aa6d3bc2a | |||
8d8a53dcc2 | |||
aedbe54394 | |||
fb6c20df07 | |||
8b82c86170 |
@@ -455,8 +455,8 @@ static void xml_read_shader_graph(const XMLReadState& state, Shader *shader, pug
|
||||
else if(string_iequals(node.name(), "transparent_volume")) {
|
||||
snode = new TransparentVolumeNode();
|
||||
}
|
||||
else if(string_iequals(node.name(), "isotropic_volume")) {
|
||||
snode = new IsotropicVolumeNode();
|
||||
else if(string_iequals(node.name(), "scatter_volume")) {
|
||||
snode = new ScatterVolumeNode();
|
||||
}
|
||||
else if(string_iequals(node.name(), "geometry")) {
|
||||
snode = new GeometryNode();
|
||||
|
@@ -37,6 +37,7 @@ class AddPresetIntegrator(AddPresetBase, Operator):
|
||||
"cycles.diffuse_bounces",
|
||||
"cycles.glossy_bounces",
|
||||
"cycles.transmission_bounces",
|
||||
"cycles.volume_bounces",
|
||||
"cycles.transparent_min_bounces",
|
||||
"cycles.transparent_max_bounces"
|
||||
]
|
||||
|
@@ -91,6 +91,18 @@ enum_tile_order = (
|
||||
('TOP_TO_BOTTOM', "Top to Bottom", "Render from top to bottom"),
|
||||
('BOTTOM_TO_TOP', "Bottom to Top", "Render from bottom to top"),
|
||||
)
|
||||
|
||||
enum_volumetric_sampling_algorithm = (
|
||||
('VOLUMETRIC_MARCHING', "Ray Marching", "Use ray marching algorithm"),
|
||||
('VOLUMETRIC_WOODCOCK', "Woodcock", "Use Woodcock algorithm"),
|
||||
('VOLUMETRIC_MARCHING_EXP', "Ray Marching(Exp)", "Use ray marching algorithm (experimental)"),
|
||||
('VOLUMETRIC_WOODCOCK_EXP', "Woodcock(Exp)", "Use Woodcock algorithm (experimental)"),
|
||||
)
|
||||
|
||||
enum_homogeneous_volumetric_sampling_algorithm = (
|
||||
('VOLUMETRIC_IMPORTANCE_SAMPLING', "Importance Sampling", "Use perfect importance sampling"),
|
||||
('VOLUMETRIC_EQUIANGULAR', "Equi-angular light(Exp)", "Use angular sampling based on lighting (experimental)"),
|
||||
)
|
||||
|
||||
enum_use_layer_samples = (
|
||||
('USE', "Use", "Per render layer number of samples override scene samples"),
|
||||
@@ -280,6 +292,12 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
|
||||
min=0, max=1024,
|
||||
default=12,
|
||||
)
|
||||
cls.volume_bounces = IntProperty(
|
||||
name="Volume Bounces",
|
||||
description="Maximum number of volumetric scattering events",
|
||||
min=0, max=1024,
|
||||
default=128,
|
||||
)
|
||||
|
||||
cls.transparent_min_bounces = IntProperty(
|
||||
name="Transparent Min Bounces",
|
||||
@@ -409,6 +427,41 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
|
||||
default=False,
|
||||
)
|
||||
|
||||
cls.volume_sampling_algorithm = EnumProperty(
|
||||
name="Sampling algorithm",
|
||||
description="Choose volumetric sampling algorithm for inhomogeneous volumes",
|
||||
items=enum_volumetric_sampling_algorithm,
|
||||
default='VOLUMETRIC_MARCHING',
|
||||
)
|
||||
|
||||
cls.volume_homogeneous_sampling = EnumProperty(
|
||||
name="Homogeneous Sampling",
|
||||
description="Choose volumetric sampling algorithm for homogeneous volumes",
|
||||
items=enum_homogeneous_volumetric_sampling_algorithm,
|
||||
default='VOLUMETRIC_IMPORTANCE_SAMPLING',
|
||||
)
|
||||
|
||||
cls.volume_max_iterations = IntProperty(
|
||||
name="Max Iterations",
|
||||
description="Number of iterations before we give up, protect for very slow deep volume marching",
|
||||
default=200,
|
||||
min=10, max=8192
|
||||
)
|
||||
|
||||
cls.volume_cell_step = FloatProperty(
|
||||
name="Cell Step",
|
||||
description="cell size when probing for density, work same as 3d texture resolution",
|
||||
default=0.1,
|
||||
min=0.0000001, max=100000.0
|
||||
)
|
||||
|
||||
cls.volume_woodcock_max_density = FloatProperty(
|
||||
name="Max Woodcock Density",
|
||||
description="Max density in volume, band aid, I need to calculate it from texture",
|
||||
default=0.95,
|
||||
min=0.0000001, max=100000.0
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def unregister(cls):
|
||||
del bpy.types.Scene.cycles
|
||||
@@ -577,6 +630,12 @@ class CyclesWorldSettings(bpy.types.PropertyGroup):
|
||||
min=1, max=10000,
|
||||
default=4,
|
||||
)
|
||||
cls.homogeneous_volume = BoolProperty(
|
||||
name="Homogeneous Volume",
|
||||
description="When using volume rendering, assume volume has the same density everywhere, "
|
||||
"for faster rendering",
|
||||
default=False,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def unregister(cls):
|
||||
|
@@ -193,7 +193,30 @@ class CyclesRender_PT_light_paths(CyclesButtonsPanel, Panel):
|
||||
sub.prop(cscene, "diffuse_bounces", text="Diffuse")
|
||||
sub.prop(cscene, "glossy_bounces", text="Glossy")
|
||||
sub.prop(cscene, "transmission_bounces", text="Transmission")
|
||||
sub.prop(cscene, "volume_bounces", text="Volume")
|
||||
|
||||
class CyclesRender_PT_volume_sampling(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Volume Sampling"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
scene = context.scene
|
||||
cscene = scene.cycles
|
||||
|
||||
col = layout.column()
|
||||
col.prop(cscene, "volume_sampling_algorithm", text="Inhomogeneous")
|
||||
col.prop(cscene, "volume_homogeneous_sampling", text="Homogeneous")
|
||||
|
||||
layout.separator()
|
||||
|
||||
col = layout.column()
|
||||
row = col.row()
|
||||
row.prop(cscene, "volume_max_iterations")
|
||||
row.prop(cscene, "volume_cell_step")
|
||||
if cscene.volume_sampling_algorithm == 'VOLUMETRIC_WOODCOCK_EXP' or cscene.volume_sampling_algorithm == 'VOLUMETRIC_WOODCOCK':
|
||||
col.prop(cscene, "volume_woodcock_max_density")
|
||||
|
||||
class CyclesRender_PT_motion_blur(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Motion Blur"
|
||||
@@ -780,15 +803,16 @@ class CyclesWorld_PT_volume(CyclesButtonsPanel, Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
# world = context.world
|
||||
# world and world.node_tree and CyclesButtonsPanel.poll(context)
|
||||
return False
|
||||
world = context.world
|
||||
return world and world.node_tree and CyclesButtonsPanel.poll(context)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
world = context.world
|
||||
panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Volume')
|
||||
|
||||
layout.prop(world.cycles, "homogeneous_volume")
|
||||
|
||||
|
||||
class CyclesWorld_PT_ambient_occlusion(CyclesButtonsPanel, Panel):
|
||||
@@ -926,9 +950,8 @@ class CyclesMaterial_PT_volume(CyclesButtonsPanel, Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
# mat = context.material
|
||||
# mat and mat.node_tree and CyclesButtonsPanel.poll(context)
|
||||
return False
|
||||
mat = context.material
|
||||
return mat and mat.node_tree and CyclesButtonsPanel.poll(context)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
@@ -422,11 +422,11 @@ static ShaderNode *add_node(Scene *scene, BL::BlendData b_data, BL::Scene b_scen
|
||||
else if (b_node.is_a(&RNA_ShaderNodeAmbientOcclusion)) {
|
||||
node = new AmbientOcclusionNode();
|
||||
}
|
||||
else if (b_node.is_a(&RNA_ShaderNodeVolumeIsotropic)) {
|
||||
node = new IsotropicVolumeNode();
|
||||
else if (b_node.is_a(&RNA_ShaderNodeVolumeScatter)) {
|
||||
node = new ScatterVolumeNode();
|
||||
}
|
||||
else if (b_node.is_a(&RNA_ShaderNodeVolumeTransparent)) {
|
||||
node = new TransparentVolumeNode();
|
||||
else if (b_node.is_a(&RNA_ShaderNodeVolumeAbsorption)) {
|
||||
node = new AbsorptionVolumeNode();
|
||||
}
|
||||
else if (b_node.is_a(&RNA_ShaderNodeNewGeometry)) {
|
||||
node = new GeometryNode();
|
||||
@@ -958,6 +958,10 @@ void BlenderSync::sync_world(bool update_all)
|
||||
graph->connect(closure->output("Background"), out->input("Surface"));
|
||||
}
|
||||
|
||||
/* settings */
|
||||
PointerRNA wmat = RNA_pointer_get(&b_world.ptr, "cycles");
|
||||
shader->homogeneous_volume = get_boolean(wmat, "homogeneous_volume");
|
||||
|
||||
if(b_world) {
|
||||
/* AO */
|
||||
BL::WorldLighting b_light = b_world.light_settings();
|
||||
|
@@ -166,12 +166,19 @@ void BlenderSync::sync_integrator()
|
||||
integrator->max_diffuse_bounce = get_int(cscene, "diffuse_bounces");
|
||||
integrator->max_glossy_bounce = get_int(cscene, "glossy_bounces");
|
||||
integrator->max_transmission_bounce = get_int(cscene, "transmission_bounces");
|
||||
integrator->max_volume_bounce = get_int(cscene, "volume_bounces");
|
||||
|
||||
integrator->transparent_max_bounce = get_int(cscene, "transparent_max_bounces");
|
||||
integrator->transparent_min_bounce = get_int(cscene, "transparent_min_bounces");
|
||||
integrator->transparent_shadows = get_boolean(cscene, "use_transparent_shadows");
|
||||
|
||||
integrator->no_caustics = get_boolean(cscene, "no_caustics");
|
||||
integrator->volume_sampling_algorithm = get_enum(cscene, "volume_sampling_algorithm");
|
||||
integrator->volume_homogeneous_sampling = get_enum(cscene, "volume_homogeneous_sampling");
|
||||
integrator->volume_max_iterations = get_int(cscene, "volume_max_iterations");
|
||||
integrator->volume_cell_step = get_float(cscene, "volume_cell_step");
|
||||
integrator->volume_woodcock_max_density = get_float(cscene, "volume_woodcock_max_density");
|
||||
|
||||
integrator->filter_glossy = get_float(cscene, "blur_glossy");
|
||||
|
||||
integrator->seed = get_int(cscene, "seed");
|
||||
|
@@ -50,6 +50,7 @@ set(SRC_HEADERS
|
||||
kernel_textures.h
|
||||
kernel_triangle.h
|
||||
kernel_types.h
|
||||
kernel_volume.h
|
||||
)
|
||||
|
||||
set(SRC_CLOSURE_HEADERS
|
||||
|
@@ -33,6 +33,10 @@
|
||||
#include "../closure/bssrdf.h"
|
||||
#endif
|
||||
|
||||
#ifdef __VOLUME__
|
||||
#include "../closure/volume.h"
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device int bsdf_sample(KernelGlobals *kg, const ShaderData *sd, const ShaderClosure *sc, float randu, float randv, float3 *eval, float3 *omega_in, differential3 *domega_in, float *pdf)
|
||||
@@ -141,6 +145,13 @@ ccl_device float3 bsdf_eval(KernelGlobals *kg, const ShaderData *sd, const Shade
|
||||
return OSLShader::bsdf_eval(sd, sc, omega_in, *pdf);
|
||||
#endif
|
||||
|
||||
#ifdef __VOLUME__
|
||||
/* need this to keep logic in kernel_emission.h direct light in case of volume particle */
|
||||
/* todo: restructure code so this is handled better */
|
||||
if (CLOSURE_IS_VOLUME(sc->type))
|
||||
return volume_eval_phase(sc, sd->I, omega_in, pdf);
|
||||
#endif
|
||||
|
||||
if(dot(sd->Ng, omega_in) >= 0.0f) {
|
||||
switch(sc->type) {
|
||||
case CLOSURE_BSDF_DIFFUSE_ID:
|
||||
|
@@ -14,53 +14,132 @@
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
#ifndef __VOLUME_H__
|
||||
#define __VOLUME_H__
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* note: the interfaces here are just as an example, need to figure
|
||||
* out the right functions and parameters to use */
|
||||
/* HENYEY-GREENSTEIN CLOSURE */
|
||||
|
||||
/* ISOTROPIC VOLUME CLOSURE */
|
||||
|
||||
ccl_device int volume_isotropic_setup(ShaderClosure *sc, float density)
|
||||
/* Given cosine between rays, return probability density that a photon bounces
|
||||
* to that direction. The g parameter controls how different it is from the
|
||||
* uniform sphere. g=0 uniform diffuse-like, g=1 close to sharp single ray. */
|
||||
ccl_device float single_peaked_henyey_greenstein(float cos_theta, float g)
|
||||
{
|
||||
sc->type = CLOSURE_VOLUME_ISOTROPIC_ID;
|
||||
sc->data0 = density;
|
||||
if(fabsf(g) < 1e-3f)
|
||||
return M_1_PI_F * 0.25f;
|
||||
|
||||
return ((1.0f - g * g) / safe_powf(1.0f + g * g - 2.0f * g * cos_theta, 1.5f)) * (M_1_PI_F * 0.25f);
|
||||
};
|
||||
|
||||
ccl_device int volume_henyey_greenstein_setup(ShaderClosure *sc)
|
||||
{
|
||||
sc->type = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID;
|
||||
|
||||
/* positive density */
|
||||
sc->data0 = max(sc->data0, 0.0f);
|
||||
/* clamp anisotropy to avoid delta function */
|
||||
sc->data1 = signf(sc->data1) * min(fabsf(sc->data1), 1.0f - 1e-3f);
|
||||
|
||||
return SD_BSDF|SD_BSDF_HAS_EVAL;
|
||||
}
|
||||
|
||||
ccl_device float3 volume_henyey_greenstein_eval_phase(const ShaderClosure *sc, const float3 I, float3 omega_in, float *pdf)
|
||||
{
|
||||
float g = sc->data1;
|
||||
|
||||
/* note that I points towards the viewer */
|
||||
float cos_theta = dot(-I, omega_in);
|
||||
|
||||
*pdf = single_peaked_henyey_greenstein(cos_theta, g);
|
||||
|
||||
return make_float3(*pdf, *pdf, *pdf);
|
||||
}
|
||||
|
||||
ccl_device int volume_henyey_greenstein_sample(const ShaderClosure *sc, float3 I, float3 dIdx, float3 dIdy, float randu, float randv,
|
||||
float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
|
||||
{
|
||||
float g = sc->data1;
|
||||
float cos_phi, sin_phi, cos_theta;
|
||||
|
||||
/* match pdf for small g */
|
||||
if(fabsf(g) < 1e-3f) {
|
||||
cos_theta = (1.0f - 2.0f * randu);
|
||||
}
|
||||
else {
|
||||
float k = (1.0f - g * g) / (1.0f - g + 2.0f * g * randu);
|
||||
cos_theta = (1.0f + g * g - k * k) / (2.0f * g);
|
||||
}
|
||||
|
||||
float sin_theta = safe_sqrtf(1.0f - cos_theta * cos_theta);
|
||||
|
||||
float phi = M_2PI_F * randv;
|
||||
cos_phi = cosf(phi);
|
||||
sin_phi = sinf(phi);
|
||||
|
||||
/* note that I points towards the viewer and so is used negated */
|
||||
float3 T, B;
|
||||
make_orthonormals(-I, &T, &B);
|
||||
*omega_in = sin_theta * cos_phi * T + sin_theta * sin_phi * B + cos_theta * (-I);
|
||||
|
||||
*pdf = single_peaked_henyey_greenstein(cos_theta, g);
|
||||
*eval = make_float3(*pdf, *pdf, *pdf); /* perfect importance sampling */
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
/* todo: implement ray differential estimation */
|
||||
*domega_in_dx = make_float3(0.0f, 0.0f, 0.0f);
|
||||
*domega_in_dy = make_float3(0.0f, 0.0f, 0.0f);
|
||||
#endif
|
||||
|
||||
/* todo: do we need a separate light path state for volume scatter? */
|
||||
return LABEL_DIFFUSE;
|
||||
}
|
||||
|
||||
/* ABSORPTION VOLUME CLOSURE */
|
||||
|
||||
ccl_device int volume_absorption_setup(ShaderClosure *sc)
|
||||
{
|
||||
sc->type = CLOSURE_VOLUME_ABSORPTION_ID;
|
||||
|
||||
/* positive density */
|
||||
sc->data0 = max(sc->data0, 0.0f);
|
||||
|
||||
return SD_VOLUME;
|
||||
}
|
||||
|
||||
ccl_device float3 volume_isotropic_eval_phase(const ShaderClosure *sc, const float3 omega_in, const float3 omega_out)
|
||||
ccl_device float3 volume_absorption_eval_phase(const ShaderClosure *sc, const float3 I, float3 omega_in, float *pdf)
|
||||
{
|
||||
return make_float3(1.0f, 1.0f, 1.0f);
|
||||
/* eval to zero for delta functions */
|
||||
return make_float3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
/* TRANSPARENT VOLUME CLOSURE */
|
||||
|
||||
ccl_device int volume_transparent_setup(ShaderClosure *sc, float density)
|
||||
ccl_device int volume_absorption_sample(const ShaderClosure *sc, float3 I, float3 dIdx, float3 dIdy, float randu, float randv,
|
||||
float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
|
||||
{
|
||||
sc->type = CLOSURE_VOLUME_TRANSPARENT_ID;
|
||||
sc->data0 = density;
|
||||
*omega_in = -I;
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
*domega_in_dx = -dIdx;
|
||||
*domega_in_dy = -dIdy;
|
||||
#endif
|
||||
|
||||
return SD_VOLUME;
|
||||
}
|
||||
*pdf = 1.0f;
|
||||
*eval = make_float3(1.0f, 1.0f, 1.0f);
|
||||
|
||||
ccl_device float3 volume_transparent_eval_phase(const ShaderClosure *sc, const float3 omega_in, const float3 omega_out)
|
||||
{
|
||||
return make_float3(1.0f, 1.0f, 1.0f);
|
||||
return LABEL_TRANSMIT|LABEL_TRANSPARENT;
|
||||
}
|
||||
|
||||
/* VOLUME CLOSURE */
|
||||
|
||||
ccl_device float3 volume_eval_phase(KernelGlobals *kg, const ShaderClosure *sc, const float3 omega_in, const float3 omega_out)
|
||||
ccl_device float3 volume_eval_phase(const ShaderClosure *sc, const float3 I, float3 omega_in, float *pdf)
|
||||
{
|
||||
float3 eval;
|
||||
|
||||
switch(sc->type) {
|
||||
case CLOSURE_VOLUME_ISOTROPIC_ID:
|
||||
eval = volume_isotropic_eval_phase(sc, omega_in, omega_out);
|
||||
case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID:
|
||||
eval = volume_henyey_greenstein_eval_phase(sc, I, omega_in, pdf);
|
||||
break;
|
||||
case CLOSURE_VOLUME_TRANSPARENT_ID:
|
||||
eval = volume_transparent_eval_phase(sc, omega_in, omega_out);
|
||||
case CLOSURE_VOLUME_ABSORPTION_ID:
|
||||
eval = volume_absorption_eval_phase(sc, I, omega_in, pdf);
|
||||
break;
|
||||
default:
|
||||
eval = make_float3(0.0f, 0.0f, 0.0f);
|
||||
@@ -70,5 +149,27 @@ ccl_device float3 volume_eval_phase(KernelGlobals *kg, const ShaderClosure *sc,
|
||||
return eval;
|
||||
}
|
||||
|
||||
ccl_device int volume_sample(const ShaderData *sd, const ShaderClosure *sc, float randu,
|
||||
float randv, float3 *eval, float3 *omega_in, differential3 *domega_in, float *pdf)
|
||||
{
|
||||
int label;
|
||||
|
||||
switch(sc->type) {
|
||||
case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID:
|
||||
label = volume_henyey_greenstein_sample(sc, sd->I, sd->dI.dx, sd->dI.dy, randu, randv, eval, omega_in, &domega_in->dx, &domega_in->dy, pdf);
|
||||
break;
|
||||
case CLOSURE_VOLUME_ABSORPTION_ID:
|
||||
label = volume_absorption_sample(sc, sd->I, sd->dI.dx, sd->dI.dy, randu, randv, eval, omega_in, &domega_in->dx, &domega_in->dy, pdf);
|
||||
break;
|
||||
default:
|
||||
*eval = make_float3(0.0f, 0.0f, 0.0f);
|
||||
label = LABEL_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
@@ -35,7 +35,7 @@ ccl_device_inline void bsdf_eval_init(BsdfEval *eval, ClosureType type, float3 v
|
||||
|
||||
if(type == CLOSURE_BSDF_TRANSPARENT_ID)
|
||||
eval->transparent = value;
|
||||
else if(CLOSURE_IS_BSDF_DIFFUSE(type))
|
||||
else if(CLOSURE_IS_BSDF_DIFFUSE(type) || CLOSURE_IS_VOLUME(type))
|
||||
eval->diffuse = value;
|
||||
else if(CLOSURE_IS_BSDF_GLOSSY(type))
|
||||
eval->glossy = value;
|
||||
@@ -55,7 +55,7 @@ ccl_device_inline void bsdf_eval_accum(BsdfEval *eval, ClosureType type, float3
|
||||
{
|
||||
#ifdef __PASSES__
|
||||
if(eval->use_light_pass) {
|
||||
if(CLOSURE_IS_BSDF_DIFFUSE(type))
|
||||
if(CLOSURE_IS_BSDF_DIFFUSE(type) || CLOSURE_IS_VOLUME(type))
|
||||
eval->diffuse += value;
|
||||
else if(CLOSURE_IS_BSDF_GLOSSY(type))
|
||||
eval->glossy += value;
|
||||
|
@@ -70,6 +70,45 @@ ccl_device_noinline float3 direct_emissive_eval(KernelGlobals *kg, float rando,
|
||||
return eval;
|
||||
}
|
||||
|
||||
#ifdef __VOLUME__
|
||||
/* ToDo: Remove these 2 duplicate functions */
|
||||
ccl_device float sigma_from_value_(float value, float geom_factor)
|
||||
{
|
||||
#if 0
|
||||
// const float att_magic_eps = 1e-7f;
|
||||
const float att_magic_eps = 1e-15f;
|
||||
float attenuation = 1-value;
|
||||
// protect infinity nan from too big density materials
|
||||
if( attenuation < att_magic_eps) attenuation = att_magic_eps;
|
||||
return (-logf( attenuation )/geom_factor);
|
||||
#else
|
||||
return value * geom_factor;
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device float get_sigma_sample_(KernelGlobals *kg, ShaderData *sd, float randv, int path_flag, float3 p)
|
||||
{
|
||||
ShaderData vsd = *sd;
|
||||
vsd.P = p;
|
||||
|
||||
#ifdef __MULTI_CLOSURE__
|
||||
int sampled = 0;
|
||||
|
||||
// if(vsd.num_closure > 1)
|
||||
|
||||
const ShaderClosure *sc = &sd->closure[sampled];
|
||||
|
||||
shader_eval_volume(kg, &vsd, randv, path_flag, SHADER_CONTEXT_MAIN);
|
||||
float v = sc->data0;
|
||||
#else
|
||||
shader_eval_volume(kg, &vsd, randv, path_flag, SHADER_CONTEXT_MAIN);
|
||||
float v = sd->closure.data0;
|
||||
#endif
|
||||
|
||||
return sigma_from_value_(v, 1.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
ccl_device_noinline bool direct_emission(KernelGlobals *kg, ShaderData *sd, int lindex,
|
||||
float randt, float rando, float randu, float randv, Ray *ray, BsdfEval *eval,
|
||||
bool *is_lamp, int bounce)
|
||||
@@ -107,7 +146,25 @@ ccl_device_noinline bool direct_emission(KernelGlobals *kg, ShaderData *sd, int
|
||||
|
||||
if(ls.shader & SHADER_USE_MIS) {
|
||||
/* multiple importance sampling */
|
||||
#ifdef __VOLUME__
|
||||
// with respect to volume pdf
|
||||
// W = (pdf_dir*pdf_dir) / (pdf_indirect*pdf_indirect + pdf_dir*pdf_dir)
|
||||
// pdf_dir = pre_hit_bsdf_pdf * 1.0 * 1.0f / (triangle_area) ?
|
||||
// ------------angle_pdf------------------ -- volume sphere space pdf----- --light surf orient-- --light surface density--
|
||||
// pdf_indirect = pre_hit_bsdf_pdf * post_hit_bsdf_pdf * 1.0f/ (4.0f * M_PI_F * t*t) * pre_hit_bsdf_pdf * 1.0f / (triangle_area)
|
||||
// W = post_hit_bsdf_pdf * 1.0f/ (4.0f * M_PI_F * t*t) * pre_hit_bsdf_pdf
|
||||
|
||||
float volume_pdf = 1.0f;
|
||||
if(( kernel_data.integrator.use_volumetrics ) && (sd->flag & SD_HAS_VOLUME) && (sd->flag & SD_HOMOGENEOUS_VOLUME))
|
||||
{
|
||||
float sigma = get_sigma_sample_(kg, sd, 0, 0, make_float3(0.0f, 0.0f, 0.0f));
|
||||
volume_pdf = sigma * exp(-ls.t * sigma);
|
||||
}
|
||||
// float mis_weight = power_heuristic(pdf / volume_pdf, bsdf_pdf);
|
||||
float mis_weight = power_heuristic(ls.pdf, bsdf_pdf * volume_pdf);
|
||||
#else
|
||||
float mis_weight = power_heuristic(ls.pdf, bsdf_pdf);
|
||||
#endif
|
||||
light_eval *= mis_weight;
|
||||
}
|
||||
|
||||
@@ -160,7 +217,7 @@ ccl_device_noinline bool direct_emission(KernelGlobals *kg, ShaderData *sd, int
|
||||
|
||||
/* Indirect Primitive Emission */
|
||||
|
||||
ccl_device_noinline float3 indirect_primitive_emission(KernelGlobals *kg, ShaderData *sd, float t, int path_flag, float bsdf_pdf)
|
||||
ccl_device_noinline float3 indirect_primitive_emission(KernelGlobals *kg, ShaderData *sd, float t, int path_flag, float bsdf_pdf, float volume_pdf = 0.0f)
|
||||
{
|
||||
/* evaluate emissive closure */
|
||||
float3 L = shader_emissive_eval(kg, sd);
|
||||
@@ -173,8 +230,11 @@ ccl_device_noinline float3 indirect_primitive_emission(KernelGlobals *kg, Shader
|
||||
/* multiple importance sampling, get triangle light pdf,
|
||||
* and compute weight with respect to BSDF pdf */
|
||||
float pdf = triangle_light_pdf(kg, sd->Ng, sd->I, t);
|
||||
#ifdef __VOLUME__
|
||||
float mis_weight = power_heuristic(bsdf_pdf * volume_pdf, pdf);
|
||||
#else
|
||||
float mis_weight = power_heuristic(bsdf_pdf, pdf);
|
||||
|
||||
#endif
|
||||
return L*mis_weight;
|
||||
}
|
||||
|
||||
|
@@ -40,6 +40,10 @@
|
||||
#include "kernel_subsurface.h"
|
||||
#endif
|
||||
|
||||
#ifdef __VOLUME__
|
||||
#include "kernel_volume.h"
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device_inline bool shadow_blocked(KernelGlobals *kg, PathState *state, Ray *ray, float3 *shadow)
|
||||
@@ -468,6 +472,17 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
|
||||
#endif
|
||||
PathState state;
|
||||
int rng_offset = PRNG_BASE_NUM;
|
||||
|
||||
#ifdef __VOLUME__
|
||||
float3 volume_eval = make_float3(1.0f, 1.0f, 1.0f);
|
||||
float volume_pdf = 1.0f;
|
||||
float omega_cache = 0.0f;
|
||||
uint rng_congruential = lcg_init(*rng + sample*0x51633e2d);
|
||||
int media_volume_shader = kernel_data.background.shader; // assume camera always in air, need proper CSG stack based solution.
|
||||
//media_volume_shader = get_media_volume_shader(kg, ray.P);
|
||||
//media_volume_shader = gmsdr(kg, ray.P);
|
||||
#endif
|
||||
|
||||
#ifdef __CMJ__
|
||||
int num_samples = kernel_data.integrator.aa_samples;
|
||||
#else
|
||||
@@ -524,6 +539,20 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __VOLUME__
|
||||
if(state.volume_bounce < kernel_data.integrator.max_volume_bounce) {
|
||||
int vol_result = kernel_path_trace_volume(kg, rng, rng_offset, &rng_congruential, sample, &ray, &isect, isect.t,
|
||||
&state, media_volume_shader, &throughput, &L, buffer, &ray_pdf, &volume_eval, &volume_pdf, &omega_cache);
|
||||
|
||||
if (vol_result == VOLUME_PATH_CONTINUE) {
|
||||
state.volume_bounce++;
|
||||
continue;
|
||||
}
|
||||
else if (vol_result == VOLUME_PATH_TERMINATED)
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!hit) {
|
||||
/* eval background shader if nothing hit */
|
||||
if(kernel_data.background.transparent && (state.flag & PATH_RAY_CAMERA)) {
|
||||
@@ -544,6 +573,10 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef __VOLUME__
|
||||
omega_cache = 0.0f;
|
||||
#endif
|
||||
|
||||
/* setup shading */
|
||||
ShaderData sd;
|
||||
shader_setup_from_ray(kg, &sd, &isect, &ray, state.bounce);
|
||||
@@ -588,7 +621,11 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
|
||||
/* emission */
|
||||
if(sd.flag & SD_EMISSION) {
|
||||
/* todo: is isect.t wrong here for transparent surfaces? */
|
||||
#ifdef __VOLUME__
|
||||
float3 emission = indirect_primitive_emission(kg, &sd, isect.t, state.flag, ray_pdf, volume_pdf);
|
||||
#else
|
||||
float3 emission = indirect_primitive_emission(kg, &sd, isect.t, state.flag, ray_pdf);
|
||||
#endif
|
||||
path_radiance_accum_emission(&L, throughput, emission, state.bounce);
|
||||
}
|
||||
#endif
|
||||
@@ -639,7 +676,12 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
|
||||
light_ray.dP = sd.dP;
|
||||
light_ray.dD = differential3_zero();
|
||||
|
||||
#ifdef __VOLUME__
|
||||
float tmp_volume_pdf;
|
||||
if(!shadow_blocked_volume(kg, &state, &light_ray, &ao_shadow, &rng_congruential, sample, media_volume_shader, &tmp_volume_pdf))
|
||||
#else
|
||||
if(!shadow_blocked(kg, &state, &light_ray, &ao_shadow))
|
||||
#endif
|
||||
path_radiance_accum_ao(&L, throughput, ao_alpha, ao_bsdf, ao_shadow, state.bounce);
|
||||
}
|
||||
}
|
||||
@@ -718,8 +760,12 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
|
||||
if(direct_emission(kg, &sd, -1, light_t, light_o, light_u, light_v, &light_ray, &L_light, &is_lamp, state.bounce)) {
|
||||
/* trace shadow ray */
|
||||
float3 shadow;
|
||||
|
||||
#ifdef __VOLUME__
|
||||
float tmp_volume_pdf;
|
||||
if(!shadow_blocked_volume(kg, &state, &light_ray, &shadow, &rng_congruential, sample, media_volume_shader, &tmp_volume_pdf)) {
|
||||
#else
|
||||
if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
|
||||
#endif
|
||||
/* accumulate */
|
||||
path_radiance_accum_light(&L, throughput, &L_light, shadow, 1.0f, state.bounce, is_lamp);
|
||||
}
|
||||
@@ -728,52 +774,81 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
|
||||
}
|
||||
#endif
|
||||
|
||||
/* no BSDF? we can stop here */
|
||||
if(!(sd.flag & SD_BSDF))
|
||||
break;
|
||||
if(sd.flag & SD_BSDF) {
|
||||
/* sample BSDF */
|
||||
float bsdf_pdf;
|
||||
BsdfEval bsdf_eval;
|
||||
float3 bsdf_omega_in;
|
||||
differential3 bsdf_domega_in;
|
||||
float bsdf_u, bsdf_v;
|
||||
path_rng_2D(kg, rng, sample, num_samples, rng_offset + PRNG_BSDF_U, &bsdf_u, &bsdf_v);
|
||||
int label;
|
||||
|
||||
/* sample BSDF */
|
||||
float bsdf_pdf;
|
||||
BsdfEval bsdf_eval;
|
||||
float3 bsdf_omega_in;
|
||||
differential3 bsdf_domega_in;
|
||||
float bsdf_u, bsdf_v;
|
||||
path_rng_2D(kg, rng, sample, num_samples, rng_offset + PRNG_BSDF_U, &bsdf_u, &bsdf_v);
|
||||
int label;
|
||||
label = shader_bsdf_sample(kg, &sd, bsdf_u, bsdf_v, &bsdf_eval,
|
||||
&bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
|
||||
|
||||
label = shader_bsdf_sample(kg, &sd, bsdf_u, bsdf_v, &bsdf_eval,
|
||||
&bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
|
||||
if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
|
||||
break;
|
||||
|
||||
if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
|
||||
break;
|
||||
|
||||
/* modify throughput */
|
||||
path_radiance_bsdf_bounce(&L, &throughput, &bsdf_eval, bsdf_pdf, state.bounce, label);
|
||||
|
||||
/* set labels */
|
||||
if(!(label & LABEL_TRANSPARENT)) {
|
||||
ray_pdf = bsdf_pdf;
|
||||
#ifdef __LAMP_MIS__
|
||||
ray_t = 0.0f;
|
||||
/* modify throughput */
|
||||
#ifdef __VOLUME__
|
||||
bsdf_eval_mul(&bsdf_eval, volume_eval/volume_pdf);
|
||||
#endif
|
||||
min_ray_pdf = fminf(bsdf_pdf, min_ray_pdf);
|
||||
path_radiance_bsdf_bounce(&L, &throughput, &bsdf_eval, bsdf_pdf, state.bounce, label);
|
||||
|
||||
/* set labels */
|
||||
if(!(label & LABEL_TRANSPARENT)) {
|
||||
ray_pdf = bsdf_pdf;
|
||||
#ifdef __LAMP_MIS__
|
||||
ray_t = 0.0f;
|
||||
#endif
|
||||
min_ray_pdf = fminf(bsdf_pdf, min_ray_pdf);
|
||||
}
|
||||
|
||||
/* update path state */
|
||||
path_state_next(kg, &state, label);
|
||||
|
||||
/* setup ray */
|
||||
ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
|
||||
ray.D = bsdf_omega_in;
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
ray.dP = sd.dP;
|
||||
ray.dD = bsdf_domega_in;
|
||||
#endif
|
||||
}
|
||||
else if(sd.flag & SD_HAS_ONLY_VOLUME) {
|
||||
/* no surface shader but have a volume shader? act transparent */
|
||||
|
||||
/* update path state, count as transparent */
|
||||
path_state_next(kg, &state, LABEL_TRANSPARENT);
|
||||
|
||||
/* setup ray position, direction stays unchanged */
|
||||
ray.P = ray_offset(sd.P, -sd.Ng);
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
ray.dP = sd.dP;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
/* no bsdf or volume? we're done */
|
||||
break;
|
||||
}
|
||||
|
||||
/* update path state */
|
||||
path_state_next(kg, &state, label);
|
||||
|
||||
/* setup ray */
|
||||
ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
|
||||
ray.D = bsdf_omega_in;
|
||||
#ifdef __VOLUME__
|
||||
ray_pdf *= volume_pdf;
|
||||
#endif
|
||||
|
||||
/* adjust ray distance for clipping */
|
||||
if(state.bounce == 0)
|
||||
ray.t -= sd.ray_length; /* clipping works through transparent */
|
||||
else
|
||||
ray.t = FLT_MAX;
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
ray.dP = sd.dP;
|
||||
ray.dD = bsdf_domega_in;
|
||||
#ifdef __VOLUME__
|
||||
if (sd.flag & SD_BACKFACING)
|
||||
media_volume_shader = kernel_data.background.shader;
|
||||
else
|
||||
media_volume_shader = sd.shader;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@@ -24,6 +24,7 @@ typedef struct PathState {
|
||||
int glossy_bounce;
|
||||
int transmission_bounce;
|
||||
int transparent_bounce;
|
||||
int volume_bounce;
|
||||
} PathState;
|
||||
|
||||
ccl_device_inline void path_state_init(PathState *state)
|
||||
@@ -34,6 +35,7 @@ ccl_device_inline void path_state_init(PathState *state)
|
||||
state->glossy_bounce = 0;
|
||||
state->transmission_bounce = 0;
|
||||
state->transparent_bounce = 0;
|
||||
state->volume_bounce = 0;
|
||||
}
|
||||
|
||||
ccl_device_inline void path_state_next(KernelGlobals *kg, PathState *state, int label)
|
||||
@@ -116,7 +118,8 @@ ccl_device_inline float path_state_terminate_probability(KernelGlobals *kg, Path
|
||||
if((state->bounce >= kernel_data.integrator.max_bounce) ||
|
||||
(state->diffuse_bounce >= kernel_data.integrator.max_diffuse_bounce) ||
|
||||
(state->glossy_bounce >= kernel_data.integrator.max_glossy_bounce) ||
|
||||
(state->transmission_bounce >= kernel_data.integrator.max_transmission_bounce))
|
||||
(state->transmission_bounce >= kernel_data.integrator.max_transmission_bounce) ||
|
||||
(state->volume_bounce >= kernel_data.integrator.max_volume_bounce))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
@@ -100,6 +100,29 @@ ccl_device uint sobol_lookup(const uint m, const uint frame, const uint ex, cons
|
||||
return index;
|
||||
}
|
||||
|
||||
ccl_device_inline float path_rng(KernelGlobals *kg, RNG *rng, int sample, int dimension)
|
||||
{
|
||||
#ifdef __SOBOL_FULL_SCREEN__
|
||||
uint result = sobol_dimension(kg, *rng, dimension);
|
||||
float r = (float)result * (1.0f/(float)0xFFFFFFFF);
|
||||
return r;
|
||||
#else
|
||||
/* compute sobol sequence value using direction vectors */
|
||||
uint result = sobol_dimension(kg, sample + SOBOL_SKIP, dimension);
|
||||
float r = (float)result * (1.0f/(float)0xFFFFFFFF);
|
||||
|
||||
/* Cranly-Patterson rotation using rng seed */
|
||||
float shift;
|
||||
|
||||
if(dimension & 1)
|
||||
shift = (*rng >> 16) * (1.0f/(float)0xFFFF);
|
||||
else
|
||||
shift = (*rng & 0xFFFF) * (1.0f/(float)0xFFFF);
|
||||
|
||||
return r + shift - floorf(r + shift);
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device_inline float path_rng_1D(KernelGlobals *kg, RNG *rng, int sample, int num_samples, int dimension)
|
||||
{
|
||||
#ifdef __CMJ__
|
||||
@@ -194,6 +217,9 @@ ccl_device void path_rng_end(KernelGlobals *kg, ccl_global uint *rng_state, RNG
|
||||
|
||||
ccl_device float path_rng(KernelGlobals *kg, RNG& rng, int sample, int dimension)
|
||||
{
|
||||
/* implicit mod 2^32 */
|
||||
rng = (1103515245*(rng) + 12345);
|
||||
return (float)rng * (1.0f/(float)0xFFFFFFFF);
|
||||
}
|
||||
|
||||
ccl_device_inline float path_rng_1D(KernelGlobals *kg, RNG& rng, int sample, int num_samples, int dimension)
|
||||
|
@@ -384,6 +384,48 @@ ccl_device_inline void shader_setup_from_background(KernelGlobals *kg, ShaderDat
|
||||
sd->ray_length = 0.0f;
|
||||
sd->ray_depth = bounce;
|
||||
|
||||
#ifdef __INSTANCING__
|
||||
sd->object = ~0;
|
||||
#endif
|
||||
sd->prim = ~0;
|
||||
#ifdef __UV__
|
||||
sd->u = 0.0f;
|
||||
sd->v = 0.0f;
|
||||
#endif
|
||||
|
||||
#ifdef __DPDU__
|
||||
/* dPdu/dPdv */
|
||||
sd->dPdu = make_float3(0.0f, 0.0f, 0.0f);
|
||||
sd->dPdv = make_float3(0.0f, 0.0f, 0.0f);
|
||||
#endif
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
/* differentials */
|
||||
sd->dP = ray->dD;
|
||||
differential_incoming(&sd->dI, sd->dP);
|
||||
sd->du.dx = 0.0f;
|
||||
sd->du.dy = 0.0f;
|
||||
sd->dv.dx = 0.0f;
|
||||
sd->dv.dy = 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ShaderData setup from ray into background or triangle, assume input ray.t +INF or intersection point distance */
|
||||
|
||||
ccl_device_inline void shader_setup_from_volume(KernelGlobals *kg, ShaderData *sd, const Ray *ray, int media_shader)
|
||||
{
|
||||
/* vectors */
|
||||
sd->P = ray->P;
|
||||
sd->N = -ray->D;
|
||||
sd->Ng = -ray->D;
|
||||
sd->I = -ray->D;
|
||||
sd->shader = media_shader;
|
||||
sd->flag = kernel_tex_fetch(__shader_flag, (sd->shader & SHADER_MASK)*2);
|
||||
#ifdef __OBJECT_MOTION__
|
||||
sd->time = ray->time;
|
||||
#endif
|
||||
sd->ray_length = 0.0f;
|
||||
|
||||
#ifdef __INSTANCING__
|
||||
sd->object = ~0;
|
||||
#endif
|
||||
@@ -430,7 +472,7 @@ ccl_device_inline void _shader_bsdf_multi_eval(KernelGlobals *kg, const ShaderDa
|
||||
|
||||
const ShaderClosure *sc = &sd->closure[i];
|
||||
|
||||
if(CLOSURE_IS_BSDF(sc->type)) {
|
||||
if(CLOSURE_IS_BSDF(sc->type) || CLOSURE_IS_VOLUME(sc->type)) {
|
||||
float bsdf_pdf = 0.0f;
|
||||
float3 eval = bsdf_eval(kg, sd, sc, omega_in, &bsdf_pdf);
|
||||
|
||||
@@ -446,6 +488,30 @@ ccl_device_inline void _shader_bsdf_multi_eval(KernelGlobals *kg, const ShaderDa
|
||||
*pdf = (sum_sample_weight > 0.0f)? sum_pdf/sum_sample_weight: 0.0f;
|
||||
}
|
||||
|
||||
ccl_device_inline void _shader_volume_bsdf_multi_eval( KernelGlobals *kg, const ShaderData *sd, const float3 omega_in, float *pdf,
|
||||
int skip_bsdf, BsdfEval *result_eval, float sum_pdf, float sum_sample_weight)
|
||||
{
|
||||
for(int i = 0; i< sd->num_closure; i++) {
|
||||
if(i == skip_bsdf)
|
||||
continue;
|
||||
|
||||
const ShaderClosure *sc = &sd->closure[i];
|
||||
|
||||
if( CLOSURE_IS_VOLUME(sc->type)) {
|
||||
float bsdf_pdf = 0.0f;
|
||||
float3 eval = bsdf_eval(kg, sd, sc, omega_in, &bsdf_pdf);
|
||||
|
||||
if(bsdf_pdf != 0.0f) {
|
||||
bsdf_eval_accum(result_eval, sc->type, eval*sc->weight);
|
||||
sum_pdf += bsdf_pdf*sc->sample_weight;
|
||||
}
|
||||
|
||||
sum_sample_weight += sc->sample_weight;
|
||||
}
|
||||
}
|
||||
|
||||
*pdf = (sum_sample_weight > 0.0f)? sum_pdf/sum_sample_weight: 0.0f;
|
||||
}
|
||||
#endif
|
||||
|
||||
ccl_device void shader_bsdf_eval(KernelGlobals *kg, const ShaderData *sd,
|
||||
@@ -459,7 +525,13 @@ ccl_device void shader_bsdf_eval(KernelGlobals *kg, const ShaderData *sd,
|
||||
const ShaderClosure *sc = &sd->closure;
|
||||
|
||||
*pdf = 0.0f;
|
||||
#if 0
|
||||
*eval = bsdf_eval(kg, sd, sc, omega_in, pdf)*sc->weight;
|
||||
#else
|
||||
float3 tmp_eval = bsdf_eval(kg, sd, sc, omega_in, pdf);
|
||||
|
||||
bsdf_eval_init(eval, NBUILTIN_CLOSURES, tmp_eval*sc->weight, kernel_data.film.use_light_pass);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -521,8 +593,17 @@ ccl_device int shader_bsdf_sample(KernelGlobals *kg, const ShaderData *sd,
|
||||
#else
|
||||
/* sample the single closure that we picked */
|
||||
*pdf = 0.0f;
|
||||
#if 0
|
||||
int label = bsdf_sample(kg, sd, &sd->closure, randu, randv, bsdf_eval, omega_in, domega_in, pdf);
|
||||
*bsdf_eval *= sd->closure.weight;
|
||||
#else
|
||||
const ShaderClosure *sc = &sd->closure;
|
||||
int label;
|
||||
float3 eval;
|
||||
|
||||
label = bsdf_sample(kg, sd, sc, randu, randv, &eval, omega_in, domega_in, pdf);
|
||||
bsdf_eval_init(bsdf_eval, sc->type, eval*sc->weight, kernel_data.film.use_light_pass);
|
||||
#endif
|
||||
return label;
|
||||
#endif
|
||||
}
|
||||
@@ -863,6 +944,7 @@ ccl_device float3 shader_eval_background(KernelGlobals *kg, ShaderData *sd, int
|
||||
|
||||
/* Volume */
|
||||
|
||||
#if 0 /* XXX unused */
|
||||
ccl_device float3 shader_volume_eval_phase(KernelGlobals *kg, ShaderData *sd,
|
||||
float3 omega_in, float3 omega_out)
|
||||
{
|
||||
@@ -881,19 +963,95 @@ ccl_device float3 shader_volume_eval_phase(KernelGlobals *kg, ShaderData *sd,
|
||||
return volume_eval_phase(kg, &sd->closure, omega_in, omega_out);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Volume Evaluation */
|
||||
|
||||
ccl_device void shader_eval_volume(KernelGlobals *kg, ShaderData *sd,
|
||||
float randb, int path_flag, ShaderContext ctx)
|
||||
{
|
||||
#ifdef __SVM__
|
||||
#ifdef __OSL__
|
||||
if (kg->osl)
|
||||
OSLShader::eval_volume(kg, sd, randb, path_flag, ctx);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifdef __SVM__
|
||||
svm_eval_nodes(kg, sd, SHADER_TYPE_VOLUME, randb, path_flag);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device int shader_volume_bsdf_sample(KernelGlobals *kg, const ShaderData *sd,
|
||||
float randu, float randv, BsdfEval *bsdf_eval,
|
||||
float3 *omega_in, differential3 *domega_in, float *pdf)
|
||||
{
|
||||
#ifdef __MULTI_CLOSURE__
|
||||
int sampled = 0;
|
||||
|
||||
if(sd->num_closure > 1) {
|
||||
/* pick a BSDF closure based on sample weights */
|
||||
float sum = 0.0f;
|
||||
|
||||
for(sampled = 0; sampled < sd->num_closure; sampled++) {
|
||||
const ShaderClosure *sc = &sd->closure[sampled];
|
||||
|
||||
if(CLOSURE_IS_VOLUME(sc->type))
|
||||
sum += sc->sample_weight;
|
||||
}
|
||||
|
||||
float r = sd->randb_closure*sum;
|
||||
sum = 0.0f;
|
||||
|
||||
for(sampled = 0; sampled < sd->num_closure; sampled++) {
|
||||
const ShaderClosure *sc = &sd->closure[sampled];
|
||||
|
||||
if(CLOSURE_IS_VOLUME(sc->type)) {
|
||||
sum += sd->closure[sampled].sample_weight;
|
||||
|
||||
if(r <= sum)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(sampled == sd->num_closure) {
|
||||
*pdf = 0.0f;
|
||||
return LABEL_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
const ShaderClosure *sc = &sd->closure[sampled];
|
||||
int label;
|
||||
float3 eval = make_float3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
*pdf = 0.0f;
|
||||
label = volume_sample(sd, sc, randu, randv, &eval, omega_in, domega_in, pdf);
|
||||
|
||||
if(*pdf != 0.0f) {
|
||||
bsdf_eval_init(bsdf_eval, sc->type, eval*sc->weight, kernel_data.film.use_light_pass);
|
||||
|
||||
if(sd->num_closure > 1) {
|
||||
float sweight = sc->sample_weight;
|
||||
_shader_volume_bsdf_multi_eval(kg, sd, *omega_in, pdf, sampled, bsdf_eval, *pdf*sweight, sweight);
|
||||
}
|
||||
}
|
||||
|
||||
return label;
|
||||
#else
|
||||
/* sample the single closure that we picked */
|
||||
*pdf = 0.0f;
|
||||
#if 0
|
||||
int label = volume_sample(sd, &sd->closure, randu, randv, bsdf_eval, omega_in, domega_in, pdf);
|
||||
*bsdf_eval *= sd->closure.weight;
|
||||
#else
|
||||
const ShaderClosure *sc = &sd->closure;
|
||||
int label;
|
||||
float3 eval = make_float3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
label = volume_sample(sd, sc, randu, randv, &eval, omega_in, domega_in, pdf);
|
||||
bsdf_eval_init(bsdf_eval, sc->type, eval*sc->weight, kernel_data.film.use_light_pass);
|
||||
#endif
|
||||
return label;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@@ -56,12 +56,14 @@ CCL_NAMESPACE_BEGIN
|
||||
#endif
|
||||
#define __SUBSURFACE__
|
||||
#define __CMJ__
|
||||
#define __VOLUME__
|
||||
#endif
|
||||
|
||||
#ifdef __KERNEL_CUDA__
|
||||
#define __KERNEL_SHADING__
|
||||
#define __KERNEL_ADV_SHADING__
|
||||
#define __BRANCHED_PATH__
|
||||
//#define __VOLUME__
|
||||
#endif
|
||||
|
||||
#ifdef __KERNEL_OPENCL__
|
||||
@@ -180,7 +182,18 @@ enum PathTraceDimension {
|
||||
PRNG_LIGHT_V = 5,
|
||||
PRNG_LIGHT_F = 6,
|
||||
PRNG_TERMINATE = 7,
|
||||
PRNG_BOUNCE_NUM = 8
|
||||
|
||||
PRNG_VOLUME_DENSITY = 8,
|
||||
PRNG_VOLUME_DISTANCE = 9,
|
||||
// not need, reuse PRNG_BSDF_UV because we never blend particle and surface
|
||||
PRNG_VOLUME_BRDF_U = PRNG_BSDF_U,
|
||||
PRNG_VOLUME_BRDF_V = PRNG_BSDF_V,
|
||||
PRNG_VOLUME_BRDF = PRNG_BSDF,
|
||||
PRNG_VOLUME_SHADOW_DENSITY = 10,
|
||||
PRNG_VOLUME_SHADOW_DISTANCE = 11,
|
||||
PRNG_VOLUME_TERMINATE = PRNG_TERMINATE,
|
||||
|
||||
PRNG_BOUNCE_NUM = 12
|
||||
};
|
||||
|
||||
enum SamplingPattern {
|
||||
@@ -224,18 +237,12 @@ enum PathRayFlag {
|
||||
|
||||
typedef enum ClosureLabel {
|
||||
LABEL_NONE = 0,
|
||||
LABEL_CAMERA = 1,
|
||||
LABEL_LIGHT = 2,
|
||||
LABEL_BACKGROUND = 4,
|
||||
LABEL_TRANSMIT = 8,
|
||||
LABEL_REFLECT = 16,
|
||||
LABEL_VOLUME = 32,
|
||||
LABEL_OBJECT = 64,
|
||||
LABEL_DIFFUSE = 128,
|
||||
LABEL_GLOSSY = 256,
|
||||
LABEL_SINGULAR = 512,
|
||||
LABEL_TRANSPARENT = 1024,
|
||||
LABEL_STOP = 2048
|
||||
LABEL_TRANSMIT = 1,
|
||||
LABEL_REFLECT = 2,
|
||||
LABEL_DIFFUSE = 4,
|
||||
LABEL_GLOSSY = 8,
|
||||
LABEL_SINGULAR = 16,
|
||||
LABEL_TRANSPARENT = 32,
|
||||
} ClosureLabel;
|
||||
|
||||
/* Render Passes */
|
||||
@@ -480,7 +487,8 @@ typedef enum ShaderContext {
|
||||
SHADER_CONTEXT_EMISSION = 2,
|
||||
SHADER_CONTEXT_SHADOW = 3,
|
||||
SHADER_CONTEXT_SSS = 4,
|
||||
SHADER_CONTEXT_NUM = 5
|
||||
SHADER_CONTEXT_VOLUME = 5,
|
||||
SHADER_CONTEXT_NUM = 6
|
||||
} ShaderContext;
|
||||
|
||||
/* Shader Data
|
||||
@@ -506,13 +514,14 @@ enum ShaderDataFlag {
|
||||
SD_USE_MIS = 512, /* direct light sample */
|
||||
SD_HAS_TRANSPARENT_SHADOW = 1024, /* has transparent shadow */
|
||||
SD_HAS_VOLUME = 2048, /* has volume shader */
|
||||
SD_HOMOGENEOUS_VOLUME = 4096, /* has homogeneous volume */
|
||||
SD_HAS_BSSRDF_BUMP = 8192, /* bssrdf normal uses bump */
|
||||
SD_HAS_ONLY_VOLUME = 4096, /* has only volume shader, no surface */
|
||||
SD_HOMOGENEOUS_VOLUME = 8192, /* has homogeneous volume */
|
||||
SD_HAS_BSSRDF_BUMP = 16384, /* bssrdf normal uses bump */
|
||||
|
||||
/* object flags */
|
||||
SD_HOLDOUT_MASK = 16384, /* holdout for camera rays */
|
||||
SD_OBJECT_MOTION = 32768, /* has object motion blur */
|
||||
SD_TRANSFORM_APPLIED = 65536 /* vertices have transform applied */
|
||||
SD_HOLDOUT_MASK = 32768, /* holdout for camera rays */
|
||||
SD_OBJECT_MOTION = 65536, /* has object motion blur */
|
||||
SD_TRANSFORM_APPLIED = 131072 /* vertices have transform applied */
|
||||
};
|
||||
|
||||
struct KernelGlobals;
|
||||
@@ -734,6 +743,7 @@ typedef struct KernelIntegrator {
|
||||
int max_diffuse_bounce;
|
||||
int max_glossy_bounce;
|
||||
int max_transmission_bounce;
|
||||
int max_volume_bounce;
|
||||
|
||||
/* transparent */
|
||||
int transparent_min_bounce;
|
||||
@@ -744,6 +754,14 @@ typedef struct KernelIntegrator {
|
||||
int no_caustics;
|
||||
float filter_glossy;
|
||||
|
||||
/* volumetric */
|
||||
int use_volumetrics;
|
||||
int volume_sampling_algorithm; // when homogeneous = false, 0 = cell marching, 1 = woodcock delta tracking
|
||||
int volume_homogeneous_sampling;
|
||||
int volume_max_iterations;
|
||||
float volume_cell_step;
|
||||
float volume_woodcock_max_density; // set it by hands, guess max density in volume, sorry no auto generation for now
|
||||
|
||||
/* seed */
|
||||
int seed;
|
||||
|
||||
|
821
intern/cycles/kernel/kernel_volume.h
Normal file
821
intern/cycles/kernel/kernel_volume.h
Normal file
@@ -0,0 +1,821 @@
|
||||
/*
|
||||
* Copyright 2011-2013 Blender Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef __VOLUME__
|
||||
|
||||
#define rand_congruential() (lcg_step_float(rng_congruential))
|
||||
|
||||
#define VOLUME_PATH_TERMINATED 0
|
||||
#define VOLUME_PATH_CONTINUE 1
|
||||
#define VOLUME_PATH_PARTICLE_MISS 2
|
||||
|
||||
/* Epsilon defines */
|
||||
#define SIGMA_MAGIC_EPS 1e-15f
|
||||
#define DISTANCE_MAGIC_EPS 1e-4f
|
||||
#define RAND_MAGIC_EPS 0.00001f
|
||||
|
||||
// probability to hit volume if far intersection exist, 50% by default
|
||||
// help to speedup noise clear when tiny object or low density.
|
||||
//#define __VOLUME_USE_GUARANTEE_HIT_PROB 1
|
||||
#define VOLUME_GUARANTEE_HIT_PROB 0.5f
|
||||
|
||||
ccl_device float sigma_from_value(float value, float geom_factor)
|
||||
{
|
||||
/* return "sigma" that required to get "value" attenuation at "geom_factor" distance of media.
|
||||
to make input value resemble "alpha color" in 2d grapics , "value"=0 mean ransparent, 1 = opaque, so there is another a=1-v step.*/
|
||||
#if 0
|
||||
const float att_magic_eps = 1e-15f; // 1e-7f?
|
||||
float attenuation = 1-value;
|
||||
// protect infinity nan from too big density materials
|
||||
if(attenuation < att_magic_eps)
|
||||
attenuation = att_magic_eps;
|
||||
|
||||
return (-logf(attenuation) / geom_factor);
|
||||
#else
|
||||
return value * geom_factor;
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device float get_sigma_sample(KernelGlobals *kg, ShaderData *sd, int path_flag, ShaderContext ctx, float3 P)
|
||||
{
|
||||
sd->P = P;
|
||||
|
||||
shader_eval_volume(kg, sd, 0.0f, path_flag, ctx);
|
||||
|
||||
/* todo: this assumes global density and is broken, density is per closure! */
|
||||
int sampled = 0;
|
||||
const ShaderClosure *sc = &sd->closure[sampled];
|
||||
float v = sc->data0;
|
||||
|
||||
return sigma_from_value(v, 1.0f);
|
||||
}
|
||||
|
||||
ccl_device int get_media_volume_shader(KernelGlobals *kg, float3 P, int bounce)
|
||||
{
|
||||
/* check all objects that intersect random ray from given point, assume we have perfect geometry (all meshes closed, correct faces direct
|
||||
we can calculate current volume material, assuming background as start, and reassign when we cross face */
|
||||
if(!kernel_data.integrator.use_volumetrics)
|
||||
return kernel_data.background.shader;
|
||||
|
||||
Ray ray;
|
||||
|
||||
ray.P = P;
|
||||
// ray.D = make_float3(0.0f, 0.0f, 1.0f);
|
||||
ray.D = normalize(make_float3(0.1f, 0.5f, 0.9f)); //a bit more wild. Use random dir maybe ?
|
||||
ray.t = FLT_MAX;
|
||||
|
||||
Intersection isect;
|
||||
int stack = 0;
|
||||
// while (scene_intersect(kg, &ray, PATH_RAY_SHADOW, &isect))
|
||||
#ifdef __HAIR__
|
||||
while(scene_intersect(kg, &ray, 0, &isect, NULL, 0.0f, 0.0f))
|
||||
#else
|
||||
while(scene_intersect(kg, &ray, 0, &isect))
|
||||
#endif
|
||||
{
|
||||
ShaderData sd;
|
||||
shader_setup_from_ray(kg, &sd, &isect, &ray, bounce);
|
||||
shader_eval_surface(kg, &sd, 0.0f, 0, SHADER_CONTEXT_MAIN); // not needed ?
|
||||
|
||||
if(sd.flag & SD_BACKFACING) {
|
||||
stack--;
|
||||
if(stack <= 0 && (sd.flag & SD_HAS_VOLUME))
|
||||
return sd.shader; // we are inside of object, as first triangle hit is from inside
|
||||
}
|
||||
else
|
||||
stack++; //we are outside, push stack to skip closed objects
|
||||
|
||||
// ray.P = ray_offset(sd.P, -sd.Ng);
|
||||
ray.P = ray_offset(sd.P, (sd.flag & SD_BACKFACING)? sd.Ng : -sd.Ng);
|
||||
ray.t = FLT_MAX;
|
||||
}
|
||||
|
||||
return kernel_data.background.shader;
|
||||
}
|
||||
|
||||
/* used */
|
||||
|
||||
/* Volumetric sampling */
|
||||
ccl_device int kernel_volumetric_woodcock_sampler(KernelGlobals *kg, RNG *rng_congruential, ShaderData *sd,
|
||||
Ray ray, int path_flag, ShaderContext ctx, float end, float *new_t, float *pdf)
|
||||
{
|
||||
/* Google "woodcock delta tracker" algorithm, must be preprocessed to guess max density in volume,
|
||||
* better keep it as close to density as possible or we got lot of tiny steps and spend millenniums
|
||||
* marching single volume ray segment. 0.95 is good default. */
|
||||
float magic_eps = 1e-4f;
|
||||
|
||||
int max_iter = kernel_data.integrator.volume_max_iterations;
|
||||
//float max_prob = kernel_data.integrator.volume_woodcock_max_density;
|
||||
//float max_sigma_t = sigma_from_value(max_prob, 1.0f);
|
||||
float max_sigma_t = 0.0f;
|
||||
|
||||
float step = end / 10.0f; // uses 10 segments for maximum - needs parameter
|
||||
for(float s = 0.0f ; s < end ; s+= step)
|
||||
max_sigma_t = max(max_sigma_t , get_sigma_sample(kg, sd, path_flag, ctx, ray.P + ray.D * s));
|
||||
|
||||
int i = 0;
|
||||
float t = 0;
|
||||
float sigma_factor = 1.0f;
|
||||
*pdf = 1.0f;
|
||||
|
||||
if((end < magic_eps) || (max_sigma_t == 0.0f))
|
||||
return 0;
|
||||
|
||||
do {
|
||||
float r = rand_congruential();
|
||||
t += -logf(r) / max_sigma_t;
|
||||
// *pdf *= sigma_factor; // pdf that previous position was transparent pseudo-particle, obviously 1.0 for first loop step
|
||||
// *pdf *= max_sigma_t * r; // pdf of particle collision, based on conventional freefly homogeneous distance equation
|
||||
}
|
||||
while((sigma_factor = (get_sigma_sample(kg, sd, path_flag, ctx, ray.P + ray.D * t) / max_sigma_t)) < rand_congruential() &&
|
||||
t < (end - magic_eps) &&
|
||||
i++ < max_iter);
|
||||
|
||||
if(t < (end - magic_eps) && i <= max_iter) {
|
||||
*new_t = t;
|
||||
sd->P = ray.P + ray.D * t;
|
||||
// *pdf *= sigma_factor; // fixme: is it necessary ?
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Assume rest of media up to end is homogeneous, it helps when using woodcock in outdoor scenes that tend to have continuous density.
|
||||
if((i > max_iter) && (t < (end - magic_eps))) {
|
||||
float sigma = get_sigma_sample(kg, sd, path_flag, ctx, ray.P + ray.D * t);
|
||||
if(sigma < magic_eps)
|
||||
return 0;
|
||||
|
||||
float r = rand_congruential();
|
||||
t += -logf(r) / sigma;
|
||||
*pdf *= sigma * r;
|
||||
if(t < (end - magic_eps)) {
|
||||
// double check current sigma, just to be sure we do not register event for null media.
|
||||
if(get_sigma_sample(kg, sd, path_flag, ctx, ray.P + ray.D * t) > magic_eps) {
|
||||
*new_t = t;
|
||||
sd->P = ray.P + ray.D * t;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
ccl_device int kernel_volumetric_woodcock_sampler2(KernelGlobals *kg, RNG *rng_congruential, ShaderData *sd,
|
||||
Ray ray, int path_flag, ShaderContext ctx, float end, float *new_t, float *pdf)
|
||||
{
|
||||
/* Google "woodcock delta tracker" algorithm, must be preprocessed to guess max density in volume,
|
||||
* better keep it as close to density as possible or we got lot of tiny steps and spend millenniums
|
||||
* marching single volume ray segment. 0.95 is good default. */
|
||||
float magic_eps = 1e-4f;
|
||||
|
||||
int max_iter = kernel_data.integrator.volume_max_iterations;
|
||||
float max_prob = kernel_data.integrator.volume_woodcock_max_density;
|
||||
float max_sigma_t = sigma_from_value(max_prob, 1.0f);
|
||||
|
||||
int i = 0;
|
||||
float t = 0;
|
||||
float sigma_factor = 1.0f;
|
||||
*pdf = 1.0f;
|
||||
|
||||
if(end < magic_eps)
|
||||
return 0;
|
||||
|
||||
if(max_sigma_t == 0.0f)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
float r = rand_congruential();
|
||||
t += -logf(r) / max_sigma_t;
|
||||
// *pdf *= sigma_factor; // pdf that previous position was transparent pseudo-particle, obviously 1.0 for first loop step
|
||||
// *pdf *= max_sigma_t * r; // pdf of particle collision, based on conventional freefly homogeneous distance equation
|
||||
}
|
||||
while((sigma_factor = (get_sigma_sample(kg, sd, path_flag, ctx, ray.P + ray.D * t) / max_sigma_t)) < rand_congruential() &&
|
||||
(t < (end - magic_eps)) &&
|
||||
i++ < max_iter);
|
||||
|
||||
if(t < (end - magic_eps) && i <= max_iter) {
|
||||
*new_t = t;
|
||||
sd->P = ray.P + ray.D * t;
|
||||
// *pdf *= sigma_factor; // fixme: is it necessary ?
|
||||
return 1;
|
||||
}
|
||||
|
||||
// *new_t = end;
|
||||
#if 1
|
||||
// last chance trick, we cannot iterate infinity, but we can force to homogeneous last step after max_iter,
|
||||
// assume rest of media up to end is homogeneous, it help to use woodcock even in outdoor scenes that tend to have continuous density
|
||||
// even if vary a bit in close distance. of course it make sampling biased (not respect actual density).
|
||||
if((i > max_iter) && (t < (end - magic_eps))) {
|
||||
float sigma = get_sigma_sample(kg, sd, path_flag, ctx, ray.P + ray.D * t);
|
||||
if(sigma < magic_eps)
|
||||
return 0;
|
||||
// t += -logf( rand_congruential()) / sigma;
|
||||
float r = rand_congruential();
|
||||
t += -logf(r) / sigma;
|
||||
*pdf *= sigma * r;
|
||||
if(t < (end - magic_eps)) {
|
||||
// double check current sigma, just to be sure we do not register event for null media.
|
||||
if(get_sigma_sample(kg, sd, path_flag, ctx, ray.P + ray.D * t) > magic_eps) {
|
||||
*new_t = t;
|
||||
sd->P = ray.P + ray.D * t;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
ccl_device int kernel_volumetric_marching_sampler(KernelGlobals *kg, RNG *rng_congruential, ShaderData *sd,
|
||||
Ray ray, int path_flag, ShaderContext ctx, float end, float *new_t, float *pdf)
|
||||
{
|
||||
int max_steps = kernel_data.integrator.volume_max_iterations;
|
||||
//float step = end != FLT_MAX ? end / max_steps : kernel_data.integrator.volume_cell_step;
|
||||
float step = kernel_data.integrator.volume_cell_step;
|
||||
|
||||
int cell_count = 0;
|
||||
float current_cell_near_boundary_distance = 0.0f;
|
||||
float random_jitter_offset = rand_congruential() * step;
|
||||
|
||||
*pdf = 1.0f;
|
||||
|
||||
float t = 0.0f;
|
||||
float integral = 0.0f;
|
||||
float randsamp = rand_congruential();
|
||||
float previous_cell_average_sigma = 0.0f;
|
||||
float current_cell_average_sigma = 0.0f;
|
||||
|
||||
float root = -logf(randsamp);
|
||||
float intstep = 0.0f;
|
||||
do {
|
||||
current_cell_near_boundary_distance += step;
|
||||
t = current_cell_near_boundary_distance + random_jitter_offset;
|
||||
previous_cell_average_sigma = current_cell_average_sigma;
|
||||
current_cell_average_sigma = get_sigma_sample(kg, sd, path_flag, ctx, ray.P + ray.D * t);
|
||||
intstep = (previous_cell_average_sigma + current_cell_average_sigma) * step * 0.5f;
|
||||
integral += intstep;
|
||||
cell_count++;
|
||||
}
|
||||
while((integral < root) && (cell_count < max_steps) && (t < end));
|
||||
|
||||
if((cell_count >= max_steps) || (t > end)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
t = current_cell_near_boundary_distance - ((integral - root) / intstep) * step;
|
||||
//*pdf = randsamp * current_cell_average_sigma;
|
||||
*new_t = t;
|
||||
sd->P = ray.P + ray.D * *new_t;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ccl_device int kernel_volumetric_marching_sampler2(KernelGlobals *kg, RNG *rng_congruential, ShaderData *sd,
|
||||
Ray ray, int path_flag, ShaderContext ctx, float end, float *new_t, float *pdf)
|
||||
{
|
||||
float step = kernel_data.integrator.volume_cell_step;
|
||||
int max_steps = min(kernel_data.integrator.volume_max_iterations, ceil_to_int(end / step));
|
||||
|
||||
int cell_count = 0;
|
||||
float current_cell_near_boundary_distance;
|
||||
float random_jitter_offset = rand_congruential() * step;
|
||||
|
||||
float t = 0.0f;
|
||||
do {
|
||||
current_cell_near_boundary_distance = step * (float)cell_count;
|
||||
float current_cell_average_sigma = get_sigma_sample(kg, sd, path_flag, ctx, ray.P + ray.D * (current_cell_near_boundary_distance + random_jitter_offset));
|
||||
if(current_cell_average_sigma < SIGMA_MAGIC_EPS)
|
||||
t = end + step;
|
||||
else
|
||||
t = -logf( rand_congruential()) / current_cell_average_sigma;
|
||||
cell_count++;
|
||||
}
|
||||
while((t > step) && (cell_count < max_steps));
|
||||
|
||||
*pdf = 1.0f;
|
||||
|
||||
if((cell_count >= max_steps) && ((current_cell_near_boundary_distance + t) > end))
|
||||
return 0;
|
||||
|
||||
*new_t = current_cell_near_boundary_distance + t;
|
||||
sd->P = ray.P + ray.D * *new_t;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ccl_device int kernel_volumetric_homogeneous_sampler(KernelGlobals *kg, float randv, float randp, ShaderData *sd,
|
||||
Ray ray, int path_flag, ShaderContext ctx, float end, float *new_t, float *pdf, float *eval, float *omega_cache)
|
||||
{
|
||||
/* return pdf of perfect importance volume sampling at given distance
|
||||
only for homogeneous case, of course.
|
||||
TODO: cache sigma to avoid complex shader call (very CPU/GPU expensive) */
|
||||
|
||||
float distance = end;
|
||||
float sigma;
|
||||
|
||||
*pdf = 1.0f; /* pdf used for importance sampling of homogeneous data, it just sigma if x=log(1-rand())/sigma used as sampling distance */
|
||||
*eval = 1.0f;
|
||||
if((distance < DISTANCE_MAGIC_EPS) || (randv < RAND_MAGIC_EPS)) {
|
||||
/* tiny volume and preventing log (0), *new_t = end */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (omega_cache) {
|
||||
if(*omega_cache == 0.0f) {
|
||||
*omega_cache = get_sigma_sample(kg, sd, path_flag, ctx, ray.P);
|
||||
}
|
||||
sigma = *omega_cache;
|
||||
}
|
||||
else
|
||||
sigma = get_sigma_sample(kg, sd, path_flag, ctx, ray.P);
|
||||
|
||||
if(sigma < SIGMA_MAGIC_EPS) {
|
||||
/* Very transparent volume - Protect div by 0, *new_t = end; */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __VOLUME_USE_GUARANTEE_HIT_PROB
|
||||
/* split randv by VOLUME_GUARANTEE_HIT_PROB */
|
||||
if (randv > VOLUME_GUARANTEE_HIT_PROB) {
|
||||
// miss
|
||||
*pdf = VOLUME_GUARANTEE_HIT_PROB;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
// assume we hit media particle, need adjust randv
|
||||
randv = 1.0f - randv / VOLUME_GUARANTEE_HIT_PROB;
|
||||
|
||||
//*pdf = sigma * randv * VOLUME_GUARANTEE_HIT_PROB;
|
||||
}
|
||||
#endif
|
||||
|
||||
float sample_distance = -logf(randv) / sigma;
|
||||
if(sample_distance > end) { // nothing hit in between [start(0.0), end]
|
||||
//*eval = sigma * exp(-distance * sigma);
|
||||
*eval = sigma * randv;
|
||||
*pdf = sigma * randv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we hit particle!
|
||||
*new_t = sample_distance;
|
||||
*pdf = sigma * randv;
|
||||
*eval = sigma * randv;
|
||||
sd->P = ray.P + ray.D * sample_distance;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ccl_device int kernel_volumetric_equiangular_sampler(KernelGlobals *kg, RNG *rng_congruential, float randv, float randp,
|
||||
ShaderData *sd, Ray ray, int path_flag, ShaderContext ctx, float end, float *new_t, float *pdf, float *eval, float *omega_cache)
|
||||
{
|
||||
float distance = end;
|
||||
float sigma;
|
||||
|
||||
*pdf = 1.0f; /* pdf used for importance sampling of homogeneous data, it just sigma if x=log(1-rand())/sigma used as sampling distance */
|
||||
*eval = 1.0f;
|
||||
|
||||
if((distance < DISTANCE_MAGIC_EPS) || (randv < RAND_MAGIC_EPS)) {
|
||||
/* tiny volume and preventing log (0), *new_t = end */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sample a light and position on int */
|
||||
float light_t = rand_congruential();
|
||||
float light_u = rand_congruential();
|
||||
float light_v = rand_congruential();
|
||||
|
||||
LightSample ls;
|
||||
light_sample(kg, light_t, light_u, light_v, ray.time, ray.P, &ls);
|
||||
if(ls.pdf == 0.0f)
|
||||
return 0;
|
||||
|
||||
if (omega_cache) {
|
||||
if(*omega_cache == 0.0f) {
|
||||
*omega_cache = get_sigma_sample(kg, sd, path_flag, ctx, ray.P);
|
||||
}
|
||||
sigma = *omega_cache;
|
||||
}
|
||||
else
|
||||
sigma = get_sigma_sample(kg, sd, path_flag, ctx, ray.P);
|
||||
|
||||
if(sigma < SIGMA_MAGIC_EPS) {
|
||||
/* Very transparent volume - Protect div by 0, *new_t = end; */
|
||||
return 0;
|
||||
}
|
||||
|
||||
float sample_distance = dot((ls.P - ray.P) , ray.D);
|
||||
float D = sqrtf(len_squared(ls.P - ray.P) - sample_distance * sample_distance);
|
||||
float atheta = atan(sample_distance / D);
|
||||
//float endtheta = atan((end - sample_distance) / D);
|
||||
float t = D * tan((randv * M_PI_2_F) - (1 - randv) * atheta);
|
||||
sample_distance += t;
|
||||
|
||||
*pdf = D / ((M_PI_2_F + atheta) * (D * D + t * t));
|
||||
*eval = *pdf;
|
||||
if(sample_distance > end)
|
||||
return 0;
|
||||
else
|
||||
/* we hit particle */
|
||||
*new_t = sample_distance;
|
||||
sd->P = ray.P + ray.D * sample_distance;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ccl_device int kernel_volumetric_sample(KernelGlobals *kg, RNG *rng_congruential, int pass, float randv, float randp,
|
||||
ShaderData *sd, Ray ray, float distance, float *particle_isect_t, int path_flag, ShaderContext ctx,
|
||||
float *pdf, float *eval, float3 *throughput, float *omega_cache = NULL)
|
||||
{
|
||||
/* sample point on volumetric ray (return false - no hit, true - hit : fill new hit t value on path [start, end] */
|
||||
|
||||
if((sd->flag & SD_HAS_VOLUME) == 0 || (distance < DISTANCE_MAGIC_EPS))
|
||||
return 0; /* empty volume shader slot or escape from bottle when scattering in solid */
|
||||
|
||||
*pdf = 1.0f;
|
||||
*eval = 1.0f;
|
||||
*particle_isect_t = 0.0f;
|
||||
|
||||
if(sd->flag & SD_HOMOGENEOUS_VOLUME) {
|
||||
/* homogeneous media */
|
||||
if(kernel_data.integrator.volume_homogeneous_sampling == 1 && kernel_data.integrator.num_all_lights) {
|
||||
bool ok = kernel_volumetric_equiangular_sampler(kg, rng_congruential, randv, randp, sd, ray, path_flag, ctx, distance, particle_isect_t, pdf, eval, omega_cache);
|
||||
return ok;
|
||||
}
|
||||
else {
|
||||
bool ok = kernel_volumetric_homogeneous_sampler(kg, randv, randp, sd, ray, path_flag, ctx, distance, particle_isect_t, pdf, eval, omega_cache);
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(kernel_data.integrator.volume_sampling_algorithm == 3) {
|
||||
/* Woodcock delta tracking */
|
||||
bool ok = kernel_volumetric_woodcock_sampler(kg, rng_congruential, sd, ray, path_flag, ctx, distance, particle_isect_t, pdf);
|
||||
*eval = *pdf;
|
||||
return ok;
|
||||
}
|
||||
else if(kernel_data.integrator.volume_sampling_algorithm == 2){
|
||||
/* Volume marching. Move particles through one region at a time, until collision occurs */
|
||||
bool ok = kernel_volumetric_marching_sampler(kg, rng_congruential, sd, ray, path_flag, ctx, distance, particle_isect_t, pdf);
|
||||
*eval = *pdf;
|
||||
return ok;
|
||||
}
|
||||
else if(kernel_data.integrator.volume_sampling_algorithm == 1){
|
||||
/* Woodcock delta tracking */
|
||||
bool ok = kernel_volumetric_woodcock_sampler2(kg, rng_congruential, sd, ray, path_flag, ctx, distance, particle_isect_t, pdf);
|
||||
*eval = *pdf;
|
||||
return ok;
|
||||
}
|
||||
else {
|
||||
/* Volume marching. Move particles through one region at a time, until collision occurs */
|
||||
bool ok = kernel_volumetric_marching_sampler2(kg, rng_congruential, sd, ray, path_flag, ctx, distance, particle_isect_t, pdf);
|
||||
*eval = *pdf;
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Volumetric shadows */
|
||||
ccl_device float3 kernel_volume_get_shadow_attenuation(KernelGlobals *kg, RNG *rng_congruential, int sample,
|
||||
Ray *light_ray, int media_volume_shader, float *volume_pdf)
|
||||
{
|
||||
// helper for shadow probes, optimised for homogeneous volume, variable density other return 0 or 1.
|
||||
// assume there are no objects inside light_ray, so it mus be preceded by shdow_blocked() or scene_intersect().
|
||||
float3 attenuation = make_float3(1.0f, 1.0f, 1.0f);
|
||||
*volume_pdf = 1.0f;
|
||||
|
||||
if(!kernel_data.integrator.use_volumetrics)
|
||||
return attenuation;
|
||||
|
||||
ShaderData tsd;
|
||||
shader_setup_from_volume(kg, &tsd, light_ray, media_volume_shader);
|
||||
if((tsd.flag & SD_HAS_VOLUME) != 0) { // check for empty volume shader
|
||||
float tparticle_isect_t;
|
||||
float tpdf;
|
||||
float teval;
|
||||
float3 tthroughput = make_float3(1.0f, 1.0f, 1.0f);
|
||||
|
||||
if(tsd.flag & SD_HOMOGENEOUS_VOLUME) {
|
||||
// special case
|
||||
float sigma = get_sigma_sample(kg, &tsd, PATH_RAY_SHADOW, SHADER_CONTEXT_SHADOW, light_ray->P);
|
||||
if (sigma < 0.0f) sigma = 0.0f;
|
||||
// sigma = 1.0f;
|
||||
#if 0
|
||||
// get transition probability
|
||||
BsdfEval eval;
|
||||
float3 omega_in = -tsd.I;
|
||||
float transition_pdf;
|
||||
shader_bsdf_eval(kg, &tsd, omega_in, &eval, &transition_pdf);
|
||||
// sigma /= 1.0f / 4 * M_PI; //diffusion?
|
||||
sigma /= transition_pdf; //diffusion?
|
||||
#endif
|
||||
float magic_eps = 0.00001f;
|
||||
// if( light_ray->t < magic_eps)
|
||||
if( light_ray->t < magic_eps || (sigma < 0.00001f))
|
||||
attenuation = make_float3(1.0f, 1.0f, 1.0f);
|
||||
else {
|
||||
*volume_pdf = sigma * exp(-light_ray->t * sigma);
|
||||
|
||||
/* todo: sigma should become a float3 with per color channel
|
||||
* density returned by get_sigma_sample */
|
||||
attenuation.x = exp(-light_ray->t * sigma);
|
||||
attenuation.y = exp(-light_ray->t * sigma);
|
||||
attenuation.z = exp(-light_ray->t * sigma);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
/* todo: these are actually unused in heterogeneous volumes, we can
|
||||
* reorganize this code so it's more clear which numbers are used */
|
||||
float trandv = 0.0f;
|
||||
float trandp = 0.0f;
|
||||
|
||||
if(!kernel_volumetric_sample(kg, rng_congruential, sample, trandv, trandp, &tsd, *light_ray, light_ray->t, &tparticle_isect_t, PATH_RAY_SHADOW, SHADER_CONTEXT_SHADOW, &tpdf, &teval, &tthroughput))
|
||||
attenuation = make_float3(1.0f, 1.0f, 1.0f);
|
||||
else {
|
||||
*volume_pdf = 0.0f;
|
||||
attenuation = make_float3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
attenuation = make_float3(1.0f, 1.0f, 1.0f);
|
||||
|
||||
return attenuation;
|
||||
}
|
||||
|
||||
ccl_device_inline bool shadow_blocked_volume(KernelGlobals *kg, PathState *state, Ray *ray, float3 *shadow,
|
||||
RNG *rng_congruential, int sample, int media_volume_shader, float *volume_pdf)
|
||||
{
|
||||
*shadow = make_float3(1.0f, 1.0f, 1.0f);
|
||||
*volume_pdf = 1.0f;
|
||||
|
||||
if(ray->t == 0.0f)
|
||||
return false;
|
||||
|
||||
float tmp_volume_pdf;
|
||||
|
||||
uint visibility = path_state_ray_visibility(kg, state);
|
||||
visibility |= PATH_RAY_SHADOW_OPAQUE;
|
||||
Intersection isect;
|
||||
#ifdef __HAIR__
|
||||
// bool result = scene_intersect(kg, ray, PATH_RAY_SHADOW_OPAQUE, &isect, NULL, 0.0f, 0.0f);
|
||||
bool result = scene_intersect(kg, ray, visibility, &isect, NULL, 0.0f, 0.0f);
|
||||
#else
|
||||
// bool result = scene_intersect(kg, ray, PATH_RAY_SHADOW_OPAQUE, &isect);
|
||||
bool result = scene_intersect(kg, ray, visibility, &isect);
|
||||
#endif
|
||||
|
||||
#ifdef __TRANSPARENT_SHADOWS__
|
||||
if(result && kernel_data.integrator.transparent_shadows) {
|
||||
/* transparent shadows work in such a way to try to minimize overhead
|
||||
* in cases where we don't need them. after a regular shadow ray is
|
||||
* cast we check if the hit primitive was potentially transparent, and
|
||||
* only in that case start marching. this gives on extra ray cast for
|
||||
* the cases were we do want transparency.
|
||||
*
|
||||
* also note that for this to work correct, multi close sampling must
|
||||
* be used, since we don't pass a random number to shader_eval_surface */
|
||||
if(shader_transparent_shadow(kg, &isect)) {
|
||||
float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
|
||||
float3 Pend = ray->P + ray->D*ray->t;
|
||||
int bounce = state->transparent_bounce;
|
||||
|
||||
for(;;) {
|
||||
if(bounce >= kernel_data.integrator.transparent_max_bounce) {
|
||||
*shadow = make_float3(1.0f, 1.0f, 1.0f);
|
||||
*volume_pdf = 1.0f;
|
||||
return true;
|
||||
}
|
||||
else if(bounce >= kernel_data.integrator.transparent_min_bounce) {
|
||||
/* todo: get random number somewhere for probabilistic terminate */
|
||||
#if 0
|
||||
float probability = average(throughput);
|
||||
float terminate = 0.0f;
|
||||
|
||||
if(terminate >= probability)
|
||||
return true;
|
||||
|
||||
throughput /= probability;
|
||||
#endif
|
||||
}
|
||||
#ifdef __HAIR__
|
||||
// if(!scene_intersect(kg, ray, PATH_RAY_SHADOW_TRANSPARENT, &isect, NULL, 0.0f, 0.0f)) {
|
||||
if(!scene_intersect(kg, ray, visibility, &isect, NULL, 0.0f, 0.0f)) {
|
||||
#else
|
||||
// if(!scene_intersect(kg, ray, PATH_RAY_SHADOW_TRANSPARENT, &isect)) {
|
||||
if(!scene_intersect(kg, ray, visibility, &isect)) {
|
||||
#endif
|
||||
float3 attenuation = kernel_volume_get_shadow_attenuation(kg, rng_congruential, sample, ray, media_volume_shader, &tmp_volume_pdf);
|
||||
throughput *= attenuation;
|
||||
|
||||
*shadow *= throughput;
|
||||
*volume_pdf *= tmp_volume_pdf;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!shader_transparent_shadow(kg, &isect)) {
|
||||
// *shadow = make_float3(1.0f, 1.0f, 1.0f);
|
||||
*shadow = make_float3(0.0f, 0.0f, 0.0f); // black
|
||||
*volume_pdf = 1.0f;
|
||||
return true;
|
||||
}
|
||||
|
||||
Ray v_ray = *ray;
|
||||
v_ray.t = isect.t;
|
||||
float3 attenuation = kernel_volume_get_shadow_attenuation(kg, rng_congruential, sample, &v_ray, media_volume_shader, &tmp_volume_pdf);
|
||||
*volume_pdf *= tmp_volume_pdf;
|
||||
throughput *= attenuation;
|
||||
|
||||
ShaderData sd;
|
||||
shader_setup_from_ray(kg, &sd, &isect, ray, state->bounce);
|
||||
|
||||
if(!(sd.flag & SD_HAS_ONLY_VOLUME)) {
|
||||
shader_eval_surface(kg, &sd, 0.0f, PATH_RAY_SHADOW, SHADER_CONTEXT_SHADOW);
|
||||
throughput *= shader_bsdf_transparency(kg, &sd);
|
||||
}
|
||||
|
||||
ray->P = ray_offset(sd.P, -sd.Ng);
|
||||
if(ray->t != FLT_MAX)
|
||||
ray->D = normalize_len(Pend - ray->P, &ray->t);
|
||||
|
||||
bounce++;
|
||||
|
||||
if(media_volume_shader == kernel_data.background.shader)
|
||||
media_volume_shader = sd.shader;
|
||||
else
|
||||
media_volume_shader = kernel_data.background.shader;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!result) {
|
||||
float3 attenuation = kernel_volume_get_shadow_attenuation(kg, rng_congruential, sample, ray, media_volume_shader, &tmp_volume_pdf);
|
||||
*shadow *= attenuation;
|
||||
*volume_pdf *= tmp_volume_pdf;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* volumetric tracing */
|
||||
ccl_device int kernel_path_trace_volume(KernelGlobals *kg, RNG *rng, int rng_offset, RNG *rng_congruential, int sample,
|
||||
Ray *ray, Intersection *isect, float isect_t, PathState *state, int media_volume_shader, float3 *throughput,
|
||||
PathRadiance *L, ccl_global float *buffer, float *ray_pdf, float3 *volume_eval, float *volume_pdf, float *omega_cache)
|
||||
{
|
||||
// we sampling volume using different algorithms, respect importance sampling
|
||||
*volume_pdf = 1.0f;
|
||||
*volume_eval = make_float3( *volume_pdf, *volume_pdf, *volume_pdf);
|
||||
|
||||
if(!kernel_data.integrator.use_volumetrics)
|
||||
return VOLUME_PATH_PARTICLE_MISS;
|
||||
|
||||
ShaderData vsd;
|
||||
shader_setup_from_volume(kg, &vsd, ray, media_volume_shader);
|
||||
if((vsd.flag & SD_HAS_VOLUME) == 0)
|
||||
return VOLUME_PATH_PARTICLE_MISS; // null volume slot, assume transparent.
|
||||
|
||||
float randv = path_rng(kg, rng, sample, rng_offset + PRNG_VOLUME_DISTANCE);
|
||||
float randp = path_rng(kg, rng, sample, rng_offset + PRNG_VOLUME_DENSITY);
|
||||
float particle_isect_t;
|
||||
float pdf;
|
||||
float eval;
|
||||
if(kernel_volumetric_sample(kg, rng_congruential, sample, randv, randp, &vsd,
|
||||
*ray, isect_t, &particle_isect_t, state->flag, SHADER_CONTEXT_VOLUME, &pdf, &eval, throughput, omega_cache)) {
|
||||
|
||||
*volume_pdf = pdf;
|
||||
*volume_eval = make_float3( eval, eval, eval);
|
||||
//if (vsd.flag & SD_HOMOGENEOUS_VOLUME)
|
||||
// *volume_eval = make_float3( *volume_pdf, *volume_pdf, *volume_pdf); // perfect importance sampling for homogeneous
|
||||
|
||||
kernel_write_data_passes(kg, buffer, L, &vsd, sample, state->flag, *throughput);
|
||||
|
||||
#ifdef __EMISSION__
|
||||
/* emission */
|
||||
if(vsd.flag & SD_EMISSION) {
|
||||
// float3 emission = indirect_emission(kg, &vsd, particle_isect_t, state->flag, *ray_pdf) / pdf;
|
||||
// float3 emission = indirect_emission(kg, &vsd, particle_isect_t, state->flag, *ray_pdf, *volume_pdf) / pdf;
|
||||
// float3 emission = indirect_emission(kg, &vsd, particle_isect_t, state->flag, *ray_pdf, *volume_pdf);
|
||||
float3 emission = indirect_primitive_emission(kg, &vsd, particle_isect_t, state->flag, *ray_pdf, *volume_pdf);
|
||||
path_radiance_accum_emission(L, *throughput, emission, state->bounce);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* path termination. this is a strange place to put the termination, it's
|
||||
mainly due to the mixed in MIS that we use. gives too many unneeded
|
||||
shader evaluations, only need emission if we are going to terminate */
|
||||
float probability = path_state_terminate_probability(kg, state, *throughput);
|
||||
float terminate = path_rng(kg, rng, sample, rng_offset + PRNG_VOLUME_TERMINATE);
|
||||
|
||||
if(terminate >= probability)
|
||||
return VOLUME_PATH_TERMINATED;
|
||||
|
||||
*throughput /= probability;
|
||||
|
||||
#ifdef __EMISSION__
|
||||
if(kernel_data.integrator.use_direct_light) {
|
||||
/* sample illumination from lights to find path contribution */
|
||||
if(vsd.flag & SD_BSDF_HAS_EVAL) {
|
||||
float light_t = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT);
|
||||
float light_o = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT_F);
|
||||
float light_u = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT_U);
|
||||
float light_v = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT_V);
|
||||
|
||||
Ray light_ray;
|
||||
BsdfEval L_light;
|
||||
// int lamp;
|
||||
bool is_lamp;
|
||||
|
||||
#ifdef __OBJECT_MOTION__
|
||||
light_ray.time = vsd.time;
|
||||
#endif
|
||||
|
||||
#ifdef __MULTI_LIGHT__ /* ToDo: Fix, Branched Path trace feature */
|
||||
/* index -1 means randomly sample from distribution */
|
||||
int i = (kernel_data.integrator.num_distribution)? -1: 0;
|
||||
|
||||
for(; i < kernel_data.integrator.num_all_lights; i++) {
|
||||
#else
|
||||
const int i = -1;
|
||||
#endif
|
||||
|
||||
if(direct_emission(kg, &vsd, i, light_t, light_o, light_u, light_v, &light_ray, &L_light, &is_lamp, state->bounce)) {
|
||||
/* trace shadow ray */
|
||||
float3 shadow;
|
||||
float tmp_volume_pdf;
|
||||
|
||||
if(!shadow_blocked_volume(kg, state, &light_ray, &shadow, rng_congruential, sample, media_volume_shader, &tmp_volume_pdf)) {
|
||||
/* accumulate */
|
||||
// bool is_lamp = (lamp != ~0);
|
||||
path_radiance_accum_light(L, *throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
|
||||
}
|
||||
}
|
||||
#ifdef __MULTI_LIGHT__
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* sample BRDF */
|
||||
float bsdf_pdf;
|
||||
BsdfEval bsdf_eval;
|
||||
float3 bsdf_omega_in;
|
||||
differential3 bsdf_domega_in;
|
||||
float bsdf_u = path_rng(kg, rng, sample, rng_offset + PRNG_VOLUME_BRDF_U);
|
||||
float bsdf_v = path_rng(kg, rng, sample, rng_offset + PRNG_VOLUME_BRDF_V);
|
||||
int label;
|
||||
|
||||
label = shader_volume_bsdf_sample(kg, &vsd, bsdf_u, bsdf_v, &bsdf_eval,
|
||||
&bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
|
||||
|
||||
if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval) || pdf == 0.0f)
|
||||
return VOLUME_PATH_TERMINATED;
|
||||
|
||||
/* modify throughput */
|
||||
bsdf_eval_mul(&bsdf_eval, (*volume_eval)/(*volume_pdf));
|
||||
|
||||
path_radiance_bsdf_bounce(L, throughput, &bsdf_eval, bsdf_pdf, state->bounce, label);
|
||||
|
||||
/* set labels */
|
||||
#if defined(__EMISSION__) || defined(__BACKGROUND__)
|
||||
*ray_pdf = bsdf_pdf * (*volume_pdf);
|
||||
#endif
|
||||
|
||||
/* update path state */
|
||||
path_state_next(kg, state, label);
|
||||
|
||||
/* setup ray */
|
||||
// ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -vsd.Ng: vsd.Ng);
|
||||
ray->P = vsd.P;
|
||||
ray->D = bsdf_omega_in;
|
||||
ray->t = FLT_MAX;
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
ray->dP = vsd.dP;
|
||||
ray->dD = bsdf_domega_in;
|
||||
#endif
|
||||
return VOLUME_PATH_CONTINUE;
|
||||
|
||||
}
|
||||
*volume_pdf = pdf;
|
||||
//*volume_eval = make_float3(1.0f, 1.0f, 1.0f);
|
||||
*volume_eval = make_float3( eval, eval, eval);
|
||||
//*volume_eval = make_float3( *volume_pdf, *volume_pdf, *volume_pdf); // perfect importance sampling for homogeneous
|
||||
|
||||
return VOLUME_PATH_PARTICLE_MISS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_END
|
@@ -55,6 +55,7 @@
|
||||
#include "closure/bsdf_westin.h"
|
||||
#include "closure/bsdf_toon.h"
|
||||
#include "closure/bsdf_hair.h"
|
||||
#include "closure/volume.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
@@ -169,6 +170,15 @@ BSDF_CLOSURE_CLASS_BEGIN(HairTransmission, hair_transmission, hair_transmission,
|
||||
#endif
|
||||
BSDF_CLOSURE_CLASS_END(HairTransmission, hair_transmission)
|
||||
|
||||
VOLUME_CLOSURE_CLASS_BEGIN(VolumeHenyeyGreenstein, henyey_greenstein, LABEL_DIFFUSE)
|
||||
CLOSURE_FLOAT_PARAM(VolumeHenyeyGreensteinClosure, sc.data0),
|
||||
CLOSURE_FLOAT_PARAM(VolumeHenyeyGreensteinClosure, sc.data1),
|
||||
VOLUME_CLOSURE_CLASS_END(VolumeHenyeyGreenstein, henyey_greenstein)
|
||||
|
||||
VOLUME_CLOSURE_CLASS_BEGIN(VolumeAbsorption, absorption, LABEL_SINGULAR)
|
||||
CLOSURE_FLOAT_PARAM(VolumeAbsorptionClosure, sc.data0),
|
||||
VOLUME_CLOSURE_CLASS_END(VolumeAbsorption, absorption)
|
||||
|
||||
/* Registration */
|
||||
|
||||
static void register_closure(OSL::ShadingSystem *ss, const char *name, int id, OSL::ClosureParam *params, OSL::PrepareClosureFunc prepare)
|
||||
@@ -248,6 +258,11 @@ void OSLShader::register_closures(OSLShadingSystem *ss_)
|
||||
bsdf_hair_reflection_params(), bsdf_hair_reflection_prepare);
|
||||
register_closure(ss, "hair_transmission", id++,
|
||||
bsdf_hair_transmission_params(), bsdf_hair_transmission_prepare);
|
||||
|
||||
register_closure(ss, "henyey_greenstein", id++,
|
||||
volume_henyey_greenstein_params(), volume_henyey_greenstein_prepare);
|
||||
register_closure(ss, "absorption_volume", id++,
|
||||
volume_absorption_params(), volume_absorption_prepare);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@@ -54,6 +54,7 @@ OSL::ClosureParam *closure_bssrdf_cubic_params();
|
||||
OSL::ClosureParam *closure_bssrdf_gaussian_params();
|
||||
OSL::ClosureParam *closure_bssrdf_cubic_extended_params();
|
||||
OSL::ClosureParam *closure_bssrdf_gaussian_extended_params();
|
||||
OSL::ClosureParam *closure_henyey_greenstein_volume_params();
|
||||
|
||||
void closure_emission_prepare(OSL::RendererServices *, int id, void *data);
|
||||
void closure_background_prepare(OSL::RendererServices *, int id, void *data);
|
||||
@@ -65,6 +66,7 @@ void closure_westin_backscatter_prepare(OSL::RendererServices *, int id, void *d
|
||||
void closure_westin_sheen_prepare(OSL::RendererServices *, int id, void *data);
|
||||
void closure_bssrdf_cubic_prepare(OSL::RendererServices *, int id, void *data);
|
||||
void closure_bssrdf_gaussian_prepare(OSL::RendererServices *, int id, void *data);
|
||||
void closure_henyey_greenstein_volume_prepare(OSL::RendererServices *, int id, void *data);
|
||||
|
||||
#define CCLOSURE_PREPARE(name, classname) \
|
||||
void name(RendererServices *, int id, void *data) \
|
||||
@@ -186,6 +188,76 @@ static ClosureParam *bsdf_##lower##_params() \
|
||||
\
|
||||
CCLOSURE_PREPARE_STATIC(bsdf_##lower##_prepare, Upper##Closure)
|
||||
|
||||
|
||||
/* Volume */
|
||||
|
||||
class CVolumeClosure : public CClosurePrimitive {
|
||||
public:
|
||||
ShaderClosure sc;
|
||||
|
||||
CVolumeClosure(int scattering) : CClosurePrimitive(Volume),
|
||||
m_scattering_label(scattering), m_shaderdata_flag(0)
|
||||
{ memset(&sc, 0, sizeof(sc)); }
|
||||
~CVolumeClosure() { }
|
||||
|
||||
int scattering() const { return m_scattering_label; }
|
||||
int shaderdata_flag() const { return m_shaderdata_flag; }
|
||||
|
||||
virtual float3 eval_phase(const float3 &omega_out, const float3 &omega_in, float& pdf) const = 0;
|
||||
|
||||
virtual int sample(const float3 &omega_out, const float3 &domega_out_dx, const float3 &domega_out_dy,
|
||||
float randu, float randv,
|
||||
float3 &omega_in, float3 &domega_in_dx, float3 &domega_in_dy,
|
||||
float &pdf, float3 &eval) const = 0;
|
||||
|
||||
protected:
|
||||
int m_scattering_label;
|
||||
int m_shaderdata_flag;
|
||||
};
|
||||
|
||||
#define VOLUME_CLOSURE_CLASS_BEGIN(Upper, lower, TYPE) \
|
||||
\
|
||||
class Upper##Closure : public CVolumeClosure { \
|
||||
public: \
|
||||
Upper##Closure() : CVolumeClosure(TYPE) {} \
|
||||
\
|
||||
void setup() \
|
||||
{ \
|
||||
sc.prim = NULL; \
|
||||
m_shaderdata_flag = volume_##lower##_setup(&sc); \
|
||||
} \
|
||||
\
|
||||
float3 eval_phase(const float3 &omega_out, const float3 &omega_in, float& pdf) const \
|
||||
{ \
|
||||
return volume_##lower##_eval_phase(&sc, omega_out, omega_in, &pdf); \
|
||||
} \
|
||||
\
|
||||
int sample(const float3 &omega_out, const float3 &domega_out_dx, const float3 &domega_out_dy, \
|
||||
float randu, float randv, \
|
||||
float3 &omega_in, float3 &domega_in_dx, float3 &domega_in_dy, \
|
||||
float &pdf, float3 &eval) const \
|
||||
{ \
|
||||
return volume_##lower##_sample(&sc, omega_out, domega_out_dx, domega_out_dy, \
|
||||
randu, randv, &eval, &omega_in, &domega_in_dx, &domega_in_dy, &pdf); \
|
||||
} \
|
||||
\
|
||||
}; \
|
||||
\
|
||||
static ClosureParam *volume_##lower##_params() \
|
||||
{ \
|
||||
static ClosureParam params[] = {
|
||||
|
||||
/* parameters */
|
||||
|
||||
#define VOLUME_CLOSURE_CLASS_END(Upper, lower) \
|
||||
CLOSURE_STRING_KEYPARAM("label"), \
|
||||
CLOSURE_FINISH_PARAM(Upper##Closure) \
|
||||
}; \
|
||||
return params; \
|
||||
} \
|
||||
\
|
||||
CCLOSURE_PREPARE_STATIC(volume_##lower##_prepare, Upper##Closure)
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* __OSL_CLOSURES_H__ */
|
||||
|
@@ -401,18 +401,23 @@ static void flatten_volume_closure_tree(ShaderData *sd,
|
||||
|
||||
switch (prim->category) {
|
||||
case CClosurePrimitive::Volume: {
|
||||
CVolumeClosure *volume = (CVolumeClosure *)prim;
|
||||
/* sample weight */
|
||||
float sample_weight = fabsf(average(weight));
|
||||
|
||||
sc.sample_weight = sample_weight;
|
||||
sc.type = CLOSURE_VOLUME_ID;
|
||||
sc.data0 = 0.0f;
|
||||
sc.data1 = 0.0f;
|
||||
sc.prim = NULL;
|
||||
sc.type = volume->sc.type;
|
||||
sc.N = volume->sc.N;
|
||||
sc.T = volume->sc.T;
|
||||
sc.data0 = volume->sc.data0;
|
||||
sc.data1 = volume->sc.data1;
|
||||
sc.prim = volume->sc.prim;
|
||||
|
||||
/* add */
|
||||
if(sc.sample_weight > 1e-5f && sd->num_closure < MAX_CLOSURE)
|
||||
if(sc.sample_weight > 1e-5f && sd->num_closure < MAX_CLOSURE) {
|
||||
sd->closure[sd->num_closure++] = sc;
|
||||
sd->flag |= volume->shaderdata_flag();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CClosurePrimitive::Holdout:
|
||||
@@ -451,6 +456,10 @@ void OSLShader::eval_volume(KernelGlobals *kg, ShaderData *sd, float randb, int
|
||||
|
||||
if (kg->osl->volume_state[shader])
|
||||
ss->execute(*octx, *(kg->osl->volume_state[shader]), *globals);
|
||||
|
||||
/* flatten closure tree */
|
||||
sd->num_closure = 0;
|
||||
sd->randb_closure = randb;
|
||||
|
||||
if (globals->Ci)
|
||||
flatten_volume_closure_tree(sd, globals->Ci);
|
||||
|
@@ -29,6 +29,8 @@ set(SRC_OSL
|
||||
node_glossy_bsdf.osl
|
||||
node_gradient_texture.osl
|
||||
node_hair_info.osl
|
||||
node_scatter_volume.osl
|
||||
node_absorption_volume.osl
|
||||
node_holdout.osl
|
||||
node_hsv.osl
|
||||
node_image_texture.osl
|
||||
|
26
intern/cycles/kernel/shaders/node_absorption_volume.osl
Normal file
26
intern/cycles/kernel/shaders/node_absorption_volume.osl
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2011-2013 Blender Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
#include "stdosl.h"
|
||||
|
||||
shader node_absorption_volume(
|
||||
color Color = color(0.8, 0.8, 0.8),
|
||||
float Density = 1.0,
|
||||
output closure color Volume = 0)
|
||||
{
|
||||
Volume = Color * absorption_volume(Density);
|
||||
}
|
||||
|
27
intern/cycles/kernel/shaders/node_scatter_volume.osl
Normal file
27
intern/cycles/kernel/shaders/node_scatter_volume.osl
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2011-2013 Blender Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
#include "stdosl.h"
|
||||
|
||||
shader node_scatter_volume(
|
||||
color Color = color(0.8, 0.8, 0.8),
|
||||
float Density = 1.0,
|
||||
float Anisotropy = 0.0,
|
||||
output closure color Volume = 0)
|
||||
{
|
||||
Volume = Color * henyey_greenstein(Density, Anisotropy);
|
||||
}
|
||||
|
@@ -497,9 +497,14 @@ closure color ambient_occlusion() BUILTIN;
|
||||
closure color bssrdf_cubic(normal N, vector radius, float texture_blur, float sharpness) BUILTIN;
|
||||
closure color bssrdf_gaussian(normal N, vector radius, float texture_blur) BUILTIN;
|
||||
|
||||
// Hair
|
||||
closure color hair_reflection(normal N, float roughnessu, float roughnessv, vector T, float offset) BUILTIN;
|
||||
closure color hair_transmission(normal N, float roughnessu, float roughnessv, vector T, float offset) BUILTIN;
|
||||
|
||||
// Volume
|
||||
closure color henyey_greenstein(float density, float g) BUILTIN;
|
||||
closure color absorption_volume(float density) BUILTIN;
|
||||
|
||||
// Backwards compatibility
|
||||
closure color bssrdf_cubic(normal N, vector radius) BUILTIN;
|
||||
closure color bssrdf_gaussian(normal N, vector radius) BUILTIN;
|
||||
|
@@ -473,24 +473,28 @@ ccl_device void svm_node_closure_volume(KernelGlobals *kg, ShaderData *sd, float
|
||||
#endif
|
||||
|
||||
float param1 = (stack_valid(param1_offset))? stack_load_float(stack, param1_offset): __uint_as_float(node.z);
|
||||
//float param2 = (stack_valid(param2_offset))? stack_load_float(stack, param2_offset): __uint_as_float(node.w);
|
||||
float param2 = (stack_valid(param2_offset))? stack_load_float(stack, param2_offset): __uint_as_float(node.w);
|
||||
|
||||
switch(type) {
|
||||
case CLOSURE_VOLUME_TRANSPARENT_ID: {
|
||||
case CLOSURE_VOLUME_ABSORPTION_ID: {
|
||||
ShaderClosure *sc = svm_node_closure_get_bsdf(sd, mix_weight);
|
||||
|
||||
if(sc) {
|
||||
float density = param1;
|
||||
sd->flag |= volume_transparent_setup(sc, density);
|
||||
sc->data0 = density;
|
||||
sd->flag |= volume_absorption_setup(sc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CLOSURE_VOLUME_ISOTROPIC_ID: {
|
||||
case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: {
|
||||
ShaderClosure *sc = svm_node_closure_get_bsdf(sd, mix_weight);
|
||||
|
||||
if(sc) {
|
||||
float density = param1;
|
||||
sd->flag |= volume_isotropic_setup(sc, density);
|
||||
float g = param2;
|
||||
sc->data0 = density;
|
||||
sc->data1 = g;
|
||||
sd->flag |= volume_henyey_greenstein_setup(sc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@@ -389,8 +389,8 @@ typedef enum ClosureType {
|
||||
|
||||
/* Volume */
|
||||
CLOSURE_VOLUME_ID,
|
||||
CLOSURE_VOLUME_TRANSPARENT_ID,
|
||||
CLOSURE_VOLUME_ISOTROPIC_ID,
|
||||
CLOSURE_VOLUME_ABSORPTION_ID,
|
||||
CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID,
|
||||
|
||||
NBUILTIN_CLOSURES
|
||||
} ClosureType;
|
||||
@@ -402,7 +402,7 @@ typedef enum ClosureType {
|
||||
#define CLOSURE_IS_BSDF_TRANSMISSION(type) (type >= CLOSURE_BSDF_TRANSMISSION_ID && type <= CLOSURE_BSDF_HAIR_TRANSMISSION_ID)
|
||||
#define CLOSURE_IS_BSDF_BSSRDF(type) (type == CLOSURE_BSDF_BSSRDF_ID)
|
||||
#define CLOSURE_IS_BSSRDF(type) (type >= CLOSURE_BSSRDF_CUBIC_ID && type <= CLOSURE_BSSRDF_GAUSSIAN_ID)
|
||||
#define CLOSURE_IS_VOLUME(type) (type >= CLOSURE_VOLUME_ID && type <= CLOSURE_VOLUME_ISOTROPIC_ID)
|
||||
#define CLOSURE_IS_VOLUME(type) (type >= CLOSURE_VOLUME_ID && type <= CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID)
|
||||
#define CLOSURE_IS_EMISSION(type) (type == CLOSURE_EMISSION_ID)
|
||||
#define CLOSURE_IS_HOLDOUT(type) (type == CLOSURE_HOLDOUT_ID)
|
||||
#define CLOSURE_IS_BACKGROUND(type) (type == CLOSURE_BACKGROUND_ID)
|
||||
|
@@ -33,6 +33,7 @@ Integrator::Integrator()
|
||||
max_diffuse_bounce = max_bounce;
|
||||
max_glossy_bounce = max_bounce;
|
||||
max_transmission_bounce = max_bounce;
|
||||
max_volume_bounce = max_bounce;
|
||||
probalistic_termination = true;
|
||||
|
||||
transparent_min_bounce = min_bounce;
|
||||
@@ -41,6 +42,13 @@ Integrator::Integrator()
|
||||
transparent_shadows = false;
|
||||
|
||||
no_caustics = false;
|
||||
volume_sampling_algorithm = 0;
|
||||
volume_homogeneous_sampling = 0;
|
||||
|
||||
volume_max_iterations = 200;
|
||||
volume_cell_step = 0.1;
|
||||
volume_woodcock_max_density = 0.95;
|
||||
|
||||
filter_glossy = 0.0f;
|
||||
seed = 0;
|
||||
layer_flag = ~0;
|
||||
@@ -84,6 +92,7 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
|
||||
kintegrator->max_diffuse_bounce = max_diffuse_bounce + 1;
|
||||
kintegrator->max_glossy_bounce = max_glossy_bounce + 1;
|
||||
kintegrator->max_transmission_bounce = max_transmission_bounce + 1;
|
||||
kintegrator->max_volume_bounce = max_volume_bounce + 1;
|
||||
|
||||
kintegrator->transparent_max_bounce = transparent_max_bounce + 1;
|
||||
if(transparent_probalistic)
|
||||
@@ -94,6 +103,12 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
|
||||
kintegrator->transparent_shadows = transparent_shadows;
|
||||
|
||||
kintegrator->no_caustics = no_caustics;
|
||||
kintegrator->volume_sampling_algorithm = volume_sampling_algorithm;
|
||||
kintegrator->volume_homogeneous_sampling = volume_homogeneous_sampling;
|
||||
kintegrator->volume_max_iterations = volume_max_iterations;
|
||||
kintegrator->volume_cell_step = volume_cell_step;
|
||||
kintegrator->volume_woodcock_max_density = volume_woodcock_max_density;
|
||||
|
||||
kintegrator->filter_glossy = (filter_glossy == 0.0f)? FLT_MAX: 1.0f/filter_glossy;
|
||||
|
||||
kintegrator->seed = hash_int(seed);
|
||||
@@ -153,6 +168,7 @@ bool Integrator::modified(const Integrator& integrator)
|
||||
max_diffuse_bounce == integrator.max_diffuse_bounce &&
|
||||
max_glossy_bounce == integrator.max_glossy_bounce &&
|
||||
max_transmission_bounce == integrator.max_transmission_bounce &&
|
||||
max_volume_bounce == integrator.max_volume_bounce &&
|
||||
probalistic_termination == integrator.probalistic_termination &&
|
||||
transparent_min_bounce == integrator.transparent_min_bounce &&
|
||||
transparent_max_bounce == integrator.transparent_max_bounce &&
|
||||
@@ -161,6 +177,11 @@ bool Integrator::modified(const Integrator& integrator)
|
||||
no_caustics == integrator.no_caustics &&
|
||||
filter_glossy == integrator.filter_glossy &&
|
||||
layer_flag == integrator.layer_flag &&
|
||||
volume_sampling_algorithm == integrator.volume_sampling_algorithm &&
|
||||
volume_homogeneous_sampling == integrator.volume_homogeneous_sampling &&
|
||||
volume_max_iterations == integrator.volume_max_iterations &&
|
||||
volume_cell_step == integrator.volume_cell_step &&
|
||||
volume_woodcock_max_density == integrator.volume_woodcock_max_density &&
|
||||
seed == integrator.seed &&
|
||||
sample_clamp == integrator.sample_clamp &&
|
||||
method == integrator.method &&
|
||||
|
@@ -33,6 +33,7 @@ public:
|
||||
int max_diffuse_bounce;
|
||||
int max_glossy_bounce;
|
||||
int max_transmission_bounce;
|
||||
int max_volume_bounce;
|
||||
bool probalistic_termination;
|
||||
|
||||
int transparent_min_bounce;
|
||||
@@ -41,6 +42,13 @@ public:
|
||||
bool transparent_shadows;
|
||||
|
||||
bool no_caustics;
|
||||
|
||||
int volume_sampling_algorithm; // when homogeneous = false, 0 = cell marching, 1 = woodcock delta tracking
|
||||
int volume_homogeneous_sampling;
|
||||
int volume_max_iterations;
|
||||
float volume_cell_step;
|
||||
float volume_woodcock_max_density; // set it by hands, guess max density in volume, sorry no auto generation for now
|
||||
|
||||
float filter_glossy;
|
||||
|
||||
int seed;
|
||||
|
@@ -1951,7 +1951,7 @@ void AmbientOcclusionNode::compile(OSLCompiler& compiler)
|
||||
VolumeNode::VolumeNode()
|
||||
: ShaderNode("volume")
|
||||
{
|
||||
closure = CLOSURE_VOLUME_ISOTROPIC_ID;
|
||||
closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID;
|
||||
|
||||
add_input("Color", SHADER_SOCKET_COLOR, make_float3(0.8f, 0.8f, 0.8f));
|
||||
add_input("Density", SHADER_SOCKET_FLOAT, 1.0f);
|
||||
@@ -1995,38 +1995,40 @@ void VolumeNode::compile(OSLCompiler& compiler)
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* Transparent Volume Closure */
|
||||
/* Absorption Volume Closure */
|
||||
|
||||
TransparentVolumeNode::TransparentVolumeNode()
|
||||
AbsorptionVolumeNode::AbsorptionVolumeNode()
|
||||
{
|
||||
closure = CLOSURE_VOLUME_TRANSPARENT_ID;
|
||||
closure = CLOSURE_VOLUME_ABSORPTION_ID;
|
||||
}
|
||||
|
||||
void TransparentVolumeNode::compile(SVMCompiler& compiler)
|
||||
void AbsorptionVolumeNode::compile(SVMCompiler& compiler)
|
||||
{
|
||||
VolumeNode::compile(compiler, input("Density"), NULL);
|
||||
}
|
||||
|
||||
void TransparentVolumeNode::compile(OSLCompiler& compiler)
|
||||
void AbsorptionVolumeNode::compile(OSLCompiler& compiler)
|
||||
{
|
||||
compiler.add(this, "node_transparent_volume");
|
||||
compiler.add(this, "node_absorption_volume");
|
||||
}
|
||||
|
||||
/* Isotropic Volume Closure */
|
||||
/* Scatter Volume Closure */
|
||||
|
||||
IsotropicVolumeNode::IsotropicVolumeNode()
|
||||
ScatterVolumeNode::ScatterVolumeNode()
|
||||
{
|
||||
closure = CLOSURE_VOLUME_ISOTROPIC_ID;
|
||||
closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID;
|
||||
|
||||
add_input("Anisotropy", SHADER_SOCKET_FLOAT, 0.0f);
|
||||
}
|
||||
|
||||
void IsotropicVolumeNode::compile(SVMCompiler& compiler)
|
||||
void ScatterVolumeNode::compile(SVMCompiler& compiler)
|
||||
{
|
||||
VolumeNode::compile(compiler, input("Density"), NULL);
|
||||
VolumeNode::compile(compiler, input("Density"), input("Anisotropy"));
|
||||
}
|
||||
|
||||
void IsotropicVolumeNode::compile(OSLCompiler& compiler)
|
||||
void ScatterVolumeNode::compile(OSLCompiler& compiler)
|
||||
{
|
||||
compiler.add(this, "node_isotropic_volume");
|
||||
compiler.add(this, "node_scatter_volume");
|
||||
}
|
||||
|
||||
/* Hair BSDF Closure */
|
||||
|
@@ -316,14 +316,14 @@ public:
|
||||
ClosureType closure;
|
||||
};
|
||||
|
||||
class TransparentVolumeNode : public VolumeNode {
|
||||
class AbsorptionVolumeNode : public VolumeNode {
|
||||
public:
|
||||
SHADER_NODE_CLASS(TransparentVolumeNode)
|
||||
SHADER_NODE_CLASS(AbsorptionVolumeNode)
|
||||
};
|
||||
|
||||
class IsotropicVolumeNode : public VolumeNode {
|
||||
class ScatterVolumeNode : public VolumeNode {
|
||||
public:
|
||||
SHADER_NODE_CLASS(IsotropicVolumeNode)
|
||||
SHADER_NODE_CLASS(ScatterVolumeNode)
|
||||
};
|
||||
|
||||
class HairBsdfNode : public BsdfNode {
|
||||
|
@@ -179,6 +179,7 @@ public:
|
||||
int default_surface;
|
||||
int default_light;
|
||||
int default_background;
|
||||
int default_volume;
|
||||
int default_empty;
|
||||
|
||||
/* device */
|
||||
|
@@ -218,6 +218,7 @@ void ShaderManager::device_update_common(Device *device, DeviceScene *dscene, Sc
|
||||
uint *shader_flag = dscene->shader_flag.resize(shader_flag_size);
|
||||
uint i = 0;
|
||||
bool has_converter_blackbody = false;
|
||||
bool has_volumetrics = false;
|
||||
|
||||
foreach(Shader *shader, scene->shaders) {
|
||||
uint flag = 0;
|
||||
@@ -226,8 +227,19 @@ void ShaderManager::device_update_common(Device *device, DeviceScene *dscene, Sc
|
||||
flag |= SD_USE_MIS;
|
||||
if(shader->has_surface_transparent && shader->use_transparent_shadow)
|
||||
flag |= SD_HAS_TRANSPARENT_SHADOW;
|
||||
if(shader->has_volume)
|
||||
if(shader->has_volume) {
|
||||
flag |= SD_HAS_VOLUME;
|
||||
has_volumetrics = true;
|
||||
|
||||
/* in this case we can assume transparent surface */
|
||||
if(!shader->has_surface)
|
||||
flag |= SD_HAS_ONLY_VOLUME;
|
||||
|
||||
/* todo: this could check more fine grained, to skip useless volumes
|
||||
* enclosed inside an opaque bsdf, although we still need to handle
|
||||
* the case with camera inside volumes too */
|
||||
flag |= SD_HAS_TRANSPARENT_SHADOW;
|
||||
}
|
||||
if(shader->homogeneous_volume)
|
||||
flag |= SD_HOMOGENEOUS_VOLUME;
|
||||
if(shader->has_bssrdf_bump)
|
||||
@@ -263,6 +275,9 @@ void ShaderManager::device_update_common(Device *device, DeviceScene *dscene, Sc
|
||||
blackbody_table_offset = TABLE_OFFSET_INVALID;
|
||||
}
|
||||
|
||||
/* volumetrics */
|
||||
KernelIntegrator *kintegrator = &dscene->data.integrator;
|
||||
kintegrator->use_volumetrics = has_volumetrics;
|
||||
}
|
||||
|
||||
void ShaderManager::device_free_common(Device *device, DeviceScene *dscene, Scene *scene)
|
||||
@@ -299,6 +314,25 @@ void ShaderManager::add_default(Scene *scene)
|
||||
scene->default_surface = scene->shaders.size() - 1;
|
||||
}
|
||||
|
||||
/* default volume */
|
||||
{
|
||||
graph = new ShaderGraph();
|
||||
|
||||
closure = graph->add(new ScatterVolumeNode());
|
||||
closure->input("Color")->value = make_float3(0.8f, 0.8f, 0.8f);
|
||||
closure->input("Density")->value.x = 1.0f;
|
||||
closure->input("Anisotropy")->value.x = 0.0f;
|
||||
out = graph->output();
|
||||
|
||||
graph->connect(closure->output("Volume"), out->input("Volume"));
|
||||
|
||||
shader = new Shader();
|
||||
shader->name = "default_volume";
|
||||
shader->graph = graph;
|
||||
scene->shaders.push_back(shader);
|
||||
scene->default_volume = scene->shaders.size() - 1;
|
||||
}
|
||||
|
||||
/* default light */
|
||||
{
|
||||
graph = new ShaderGraph();
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 216 KiB After Width: | Height: | Size: 120 KiB |
Submodule release/scripts/addons updated: 92a03a1b94...c50944e808
Submodule release/scripts/addons_contrib updated: 941d5d1857...31545d25c9
@@ -7,5 +7,6 @@ cycles.no_caustics = True
|
||||
cycles.diffuse_bounces = 0
|
||||
cycles.glossy_bounces = 1
|
||||
cycles.transmission_bounces = 2
|
||||
cycles.volume_bounces = 0
|
||||
cycles.transparent_min_bounces = 8
|
||||
cycles.transparent_max_bounces = 8
|
||||
|
@@ -7,5 +7,6 @@ cycles.no_caustics = False
|
||||
cycles.diffuse_bounces = 128
|
||||
cycles.glossy_bounces = 128
|
||||
cycles.transmission_bounces = 128
|
||||
cycles.volume_bounces = 128
|
||||
cycles.transparent_min_bounces = 8
|
||||
cycles.transparent_max_bounces = 128
|
||||
|
@@ -7,5 +7,6 @@ cycles.no_caustics = True
|
||||
cycles.diffuse_bounces = 1
|
||||
cycles.glossy_bounces = 4
|
||||
cycles.transmission_bounces = 8
|
||||
cycles.volume_bounces = 1
|
||||
cycles.transparent_min_bounces = 8
|
||||
cycles.transparent_max_bounces = 8
|
||||
|
@@ -193,6 +193,8 @@ shader_node_categories = [
|
||||
NodeItem("ShaderNodeBackground"),
|
||||
NodeItem("ShaderNodeAmbientOcclusion"),
|
||||
NodeItem("ShaderNodeHoldout"),
|
||||
NodeItem("ShaderNodeVolumeScatter"),
|
||||
NodeItem("ShaderNodeVolumeAbsorption"),
|
||||
]),
|
||||
ShaderNewNodeCategory("SH_NEW_TEXTURE", "Texture", items=[
|
||||
NodeItem("ShaderNodeTexImage"),
|
||||
|
@@ -717,8 +717,8 @@ struct ShadeResult;
|
||||
#define SH_NODE_OUTPUT_TEXTURE 158
|
||||
#define SH_NODE_HOLDOUT 159
|
||||
#define SH_NODE_LAYER_WEIGHT 160
|
||||
#define SH_NODE_VOLUME_TRANSPARENT 161
|
||||
#define SH_NODE_VOLUME_ISOTROPIC 162
|
||||
#define SH_NODE_VOLUME_ABSORPTION 161
|
||||
#define SH_NODE_VOLUME_SCATTER 162
|
||||
#define SH_NODE_GAMMA 163
|
||||
#define SH_NODE_TEX_CHECKER 164
|
||||
#define SH_NODE_BRIGHTCONTRAST 165
|
||||
|
@@ -3494,8 +3494,8 @@ static void registerShaderNodes(void)
|
||||
register_node_type_sh_bsdf_hair();
|
||||
register_node_type_sh_emission();
|
||||
register_node_type_sh_holdout();
|
||||
//register_node_type_sh_volume_transparent();
|
||||
//register_node_type_sh_volume_isotropic();
|
||||
register_node_type_sh_volume_absorption();
|
||||
register_node_type_sh_volume_scatter();
|
||||
register_node_type_sh_subsurface_scattering();
|
||||
register_node_type_sh_mix_shader();
|
||||
register_node_type_sh_add_shader();
|
||||
|
@@ -197,8 +197,8 @@ set(SRC
|
||||
shader/nodes/node_shader_tex_sky.c
|
||||
shader/nodes/node_shader_tex_voronoi.c
|
||||
shader/nodes/node_shader_tex_wave.c
|
||||
shader/nodes/node_shader_volume_isotropic.c
|
||||
shader/nodes/node_shader_volume_transparent.c
|
||||
shader/nodes/node_shader_volume_scatter.c
|
||||
shader/nodes/node_shader_volume_absorption.c
|
||||
shader/node_shader_tree.c
|
||||
shader/node_shader_util.c
|
||||
|
||||
|
@@ -105,8 +105,8 @@ void register_node_type_sh_bsdf_toon(void);
|
||||
void register_node_type_sh_bsdf_anisotropic(void);
|
||||
void register_node_type_sh_emission(void);
|
||||
void register_node_type_sh_holdout(void);
|
||||
void register_node_type_sh_volume_transparent(void);
|
||||
void register_node_type_sh_volume_isotropic(void);
|
||||
void register_node_type_sh_volume_absorption(void);
|
||||
void register_node_type_sh_volume_scatter(void);
|
||||
void register_node_type_sh_bsdf_hair(void);
|
||||
void register_node_type_sh_subsurface_scattering(void);
|
||||
void register_node_type_sh_mix_shader(void);
|
||||
|
@@ -88,8 +88,8 @@ DefNode( ShaderNode, SH_NODE_BSDF_VELVET, 0, "BS
|
||||
DefNode( ShaderNode, SH_NODE_BSDF_TOON, def_toon, "BSDF_TOON", BsdfToon, "Toon BSDF", "" )
|
||||
DefNode( ShaderNode, SH_NODE_BSDF_HAIR, def_hair, "BSDF_HAIR", BsdfHair, "Hair BSDF", "" )
|
||||
DefNode( ShaderNode, SH_NODE_SUBSURFACE_SCATTERING, def_sh_subsurface, "SUBSURFACE_SCATTERING",SubsurfaceScattering,"Subsurface Scattering","")
|
||||
DefNode( ShaderNode, SH_NODE_VOLUME_TRANSPARENT, 0, "VOLUME_TRANSPARENT", VolumeTransparent,"Transparent Volume","" )
|
||||
DefNode( ShaderNode, SH_NODE_VOLUME_ISOTROPIC, 0, "VOLUME_ISOTROPIC", VolumeIsotropic, "Isotropic Volume", "" )
|
||||
DefNode( ShaderNode, SH_NODE_VOLUME_ABSORPTION, 0, "VOLUME_ABSORPTION", VolumeAbsorption, "Volume Absorption", "" )
|
||||
DefNode( ShaderNode, SH_NODE_VOLUME_SCATTER, 0, "VOLUME_SCATTER", VolumeScatter, "Volume Scatter", "" )
|
||||
DefNode( ShaderNode, SH_NODE_EMISSION, 0, "EMISSION", Emission, "Emission", "" )
|
||||
DefNode( ShaderNode, SH_NODE_NEW_GEOMETRY, 0, "NEW_GEOMETRY", NewGeometry, "Geometry", "" )
|
||||
DefNode( ShaderNode, SH_NODE_LIGHT_PATH, 0, "LIGHT_PATH", LightPath, "Light Path", "" )
|
||||
|
@@ -29,33 +29,33 @@
|
||||
|
||||
/* **************** OUTPUT ******************** */
|
||||
|
||||
static bNodeSocketTemplate sh_node_volume_transparent_in[] = {
|
||||
static bNodeSocketTemplate sh_node_volume_absorption_in[] = {
|
||||
{ SOCK_RGBA, 1, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
|
||||
{ SOCK_FLOAT, 1, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f},
|
||||
{ -1, 0, "" }
|
||||
};
|
||||
|
||||
static bNodeSocketTemplate sh_node_volume_transparent_out[] = {
|
||||
static bNodeSocketTemplate sh_node_volume_absorption_out[] = {
|
||||
{ SOCK_SHADER, 0, N_("Volume")},
|
||||
{ -1, 0, "" }
|
||||
};
|
||||
|
||||
static int node_shader_gpu_volume_transparent(GPUMaterial *UNUSED(mat), bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *UNUSED(in), GPUNodeStack *UNUSED(out))
|
||||
static int node_shader_gpu_volume_absorption(GPUMaterial *UNUSED(mat), bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *UNUSED(in), GPUNodeStack *UNUSED(out))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_volume_transparent(void)
|
||||
void register_node_type_sh_volume_absorption(void)
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, SH_NODE_VOLUME_TRANSPARENT, "Transparent Volume", NODE_CLASS_SHADER, 0);
|
||||
sh_node_type_base(&ntype, SH_NODE_VOLUME_ABSORPTION, "Volume Absorption", NODE_CLASS_SHADER, 0);
|
||||
node_type_compatibility(&ntype, NODE_NEW_SHADING);
|
||||
node_type_socket_templates(&ntype, sh_node_volume_transparent_in, sh_node_volume_transparent_out);
|
||||
node_type_socket_templates(&ntype, sh_node_volume_absorption_in, sh_node_volume_absorption_out);
|
||||
node_type_init(&ntype, NULL);
|
||||
node_type_storage(&ntype, "", NULL, NULL);
|
||||
node_type_gpu(&ntype, node_shader_gpu_volume_transparent);
|
||||
node_type_gpu(&ntype, node_shader_gpu_volume_absorption);
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -29,33 +29,35 @@
|
||||
|
||||
/* **************** OUTPUT ******************** */
|
||||
|
||||
static bNodeSocketTemplate sh_node_volume_isotropic_in[] = {
|
||||
static bNodeSocketTemplate sh_node_volume_scatter_in[] = {
|
||||
{ SOCK_RGBA, 1, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
|
||||
{ SOCK_FLOAT, 1, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f},
|
||||
{ SOCK_FLOAT, 1, N_("Anisotropy"),0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f},
|
||||
{ -1, 0, "" }
|
||||
};
|
||||
|
||||
static bNodeSocketTemplate sh_node_volume_isotropic_out[] = {
|
||||
static bNodeSocketTemplate sh_node_volume_scatter_out[] = {
|
||||
{ SOCK_SHADER, 0, N_("Volume")},
|
||||
{ -1, 0, "" }
|
||||
};
|
||||
|
||||
static int node_shader_gpu_volume_isotropic(GPUMaterial *UNUSED(mat), bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *UNUSED(in), GPUNodeStack *UNUSED(out))
|
||||
static int node_shader_gpu_volume_scatter(GPUMaterial *UNUSED(mat), bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *UNUSED(in), GPUNodeStack *UNUSED(out))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_volume_isotropic(void)
|
||||
void register_node_type_sh_volume_scatter(void)
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, SH_NODE_VOLUME_ISOTROPIC, "Isotropic Volume", NODE_CLASS_SHADER, 0);
|
||||
sh_node_type_base(&ntype, SH_NODE_VOLUME_SCATTER, "Volume Scatter", NODE_CLASS_SHADER, 0);
|
||||
node_type_compatibility(&ntype, NODE_NEW_SHADING);
|
||||
node_type_socket_templates(&ntype, sh_node_volume_isotropic_in, sh_node_volume_isotropic_out);
|
||||
node_type_socket_templates(&ntype, sh_node_volume_scatter_in, sh_node_volume_scatter_out);
|
||||
node_type_init(&ntype, NULL);
|
||||
node_type_storage(&ntype, "", NULL, NULL);
|
||||
node_type_gpu(&ntype, node_shader_gpu_volume_isotropic);
|
||||
node_type_gpu(&ntype, node_shader_gpu_volume_scatter);
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
Reference in New Issue
Block a user