/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation * * SPDX-License-Identifier: Apache-2.0 */ #include "scene/shader_nodes.h" #include "kernel/svm/types.h" #include "kernel/types.h" #include "scene/colorspace.h" #include "scene/constant_fold.h" #include "scene/film.h" #include "scene/image.h" #include "scene/image_sky.h" #include "scene/integrator.h" #include "scene/light.h" #include "scene/mesh.h" #include "scene/osl.h" #include "scene/scene.h" #include "scene/svm.h" #include "sky_hosek.h" #include "sky_nishita.h" #include "util/color.h" #include "util/log.h" #include "util/math_base.h" #include "util/transform.h" #include "kernel/svm/color_util.h" #include "kernel/svm/mapping_util.h" #include "kernel/svm/math_util.h" #include "kernel/svm/ramp_util.h" CCL_NAMESPACE_BEGIN /* Texture Mapping */ #define TEXTURE_MAPPING_DEFINE(TextureNode) \ SOCKET_POINT(tex_mapping.translation, "Translation", zero_float3()); \ SOCKET_VECTOR(tex_mapping.rotation, "Rotation", zero_float3()); \ SOCKET_VECTOR(tex_mapping.scale, "Scale", one_float3()); \ \ SOCKET_VECTOR(tex_mapping.min, "Min", make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX)); \ SOCKET_VECTOR(tex_mapping.max, "Max", make_float3(FLT_MAX, FLT_MAX, FLT_MAX)); \ SOCKET_BOOLEAN(tex_mapping.use_minmax, "Use Min Max", false); \ \ static NodeEnum mapping_axis_enum; \ mapping_axis_enum.insert("none", TextureMapping::NONE); \ mapping_axis_enum.insert("x", TextureMapping::X); \ mapping_axis_enum.insert("y", TextureMapping::Y); \ mapping_axis_enum.insert("z", TextureMapping::Z); \ SOCKET_ENUM(tex_mapping.x_mapping, "x_mapping", mapping_axis_enum, TextureMapping::X); \ SOCKET_ENUM(tex_mapping.y_mapping, "y_mapping", mapping_axis_enum, TextureMapping::Y); \ SOCKET_ENUM(tex_mapping.z_mapping, "z_mapping", mapping_axis_enum, TextureMapping::Z); \ \ static NodeEnum mapping_type_enum; \ mapping_type_enum.insert("point", TextureMapping::POINT); \ mapping_type_enum.insert("texture", TextureMapping::TEXTURE); \ mapping_type_enum.insert("vector", TextureMapping::VECTOR); \ mapping_type_enum.insert("normal", TextureMapping::NORMAL); \ SOCKET_ENUM(tex_mapping.type, "Type", mapping_type_enum, TextureMapping::TEXTURE); \ \ static NodeEnum mapping_projection_enum; \ mapping_projection_enum.insert("flat", TextureMapping::FLAT); \ mapping_projection_enum.insert("cube", TextureMapping::CUBE); \ mapping_projection_enum.insert("tube", TextureMapping::TUBE); \ mapping_projection_enum.insert("sphere", TextureMapping::SPHERE); \ SOCKET_ENUM(tex_mapping.projection, "Projection", mapping_projection_enum, TextureMapping::FLAT); TextureMapping::TextureMapping() = default; Transform TextureMapping::compute_transform() { Transform mmat = transform_scale(zero_float3()); if (x_mapping != NONE) { mmat[0][x_mapping - 1] = 1.0f; } if (y_mapping != NONE) { mmat[1][y_mapping - 1] = 1.0f; } if (z_mapping != NONE) { mmat[2][z_mapping - 1] = 1.0f; } float3 scale_clamped = scale; if (type == TEXTURE || type == NORMAL) { /* keep matrix invertible */ if (fabsf(scale.x) < 1e-5f) { scale_clamped.x = signf(scale.x) * 1e-5f; } if (fabsf(scale.y) < 1e-5f) { scale_clamped.y = signf(scale.y) * 1e-5f; } if (fabsf(scale.z) < 1e-5f) { scale_clamped.z = signf(scale.z) * 1e-5f; } } const Transform smat = transform_scale(scale_clamped); const Transform rmat = transform_euler(rotation); const Transform tmat = transform_translate(translation); Transform mat; switch (type) { case TEXTURE: /* inverse transform on texture coordinate gives * forward transform on texture */ mat = tmat * rmat * smat; mat = transform_inverse(mat); break; case POINT: /* full transform */ mat = tmat * rmat * smat; break; case VECTOR: /* no translation for vectors */ mat = rmat * smat; break; case NORMAL: /* no translation for normals, and inverse transpose */ mat = rmat * smat; mat = transform_transposed_inverse(mat); break; } /* projection last */ mat = mat * mmat; return mat; } bool TextureMapping::skip() { if (translation != zero_float3()) { return false; } if (rotation != zero_float3()) { return false; } if (scale != one_float3()) { return false; } if (x_mapping != X || y_mapping != Y || z_mapping != Z) { return false; } if (use_minmax) { return false; } return true; } void TextureMapping::compile(SVMCompiler &compiler, const int offset_in, const int offset_out) { compiler.add_node(NODE_TEXTURE_MAPPING, offset_in, offset_out); const Transform tfm = compute_transform(); compiler.add_node(tfm.x); compiler.add_node(tfm.y); compiler.add_node(tfm.z); if (use_minmax) { compiler.add_node(NODE_MIN_MAX, offset_out, offset_out); compiler.add_node(make_float4(min)); compiler.add_node(make_float4(max)); } if (type == NORMAL) { compiler.add_node(NODE_VECTOR_MATH, NODE_VECTOR_MATH_NORMALIZE, compiler.encode_uchar4(offset_out, offset_out, offset_out), compiler.encode_uchar4(SVM_STACK_INVALID, offset_out)); } } /* Convenience function for texture nodes, allocating stack space to output * a modified vector and returning its offset */ int TextureMapping::compile_begin(SVMCompiler &compiler, ShaderInput *vector_in) { if (!skip()) { const int offset_in = compiler.stack_assign(vector_in); const int offset_out = compiler.stack_find_offset(SocketType::VECTOR); compile(compiler, offset_in, offset_out); return offset_out; } return compiler.stack_assign(vector_in); } void TextureMapping::compile_end(SVMCompiler &compiler, ShaderInput *vector_in, const int vector_offset) { if (!skip()) { compiler.stack_clear_offset(vector_in->type(), vector_offset); } } void TextureMapping::compile(OSLCompiler &compiler) { if (!skip()) { compiler.parameter("mapping", compute_transform()); compiler.parameter("use_mapping", 1); } } /* Image Texture */ NODE_DEFINE(ImageTextureNode) { NodeType *type = NodeType::add("image_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(ImageTextureNode); SOCKET_STRING(filename, "Filename", ustring()); SOCKET_STRING(colorspace, "Colorspace", u_colorspace_auto); static NodeEnum alpha_type_enum; alpha_type_enum.insert("auto", IMAGE_ALPHA_AUTO); alpha_type_enum.insert("unassociated", IMAGE_ALPHA_UNASSOCIATED); alpha_type_enum.insert("associated", IMAGE_ALPHA_ASSOCIATED); alpha_type_enum.insert("channel_packed", IMAGE_ALPHA_CHANNEL_PACKED); alpha_type_enum.insert("ignore", IMAGE_ALPHA_IGNORE); SOCKET_ENUM(alpha_type, "Alpha Type", alpha_type_enum, IMAGE_ALPHA_AUTO); static NodeEnum interpolation_enum; interpolation_enum.insert("closest", INTERPOLATION_CLOSEST); interpolation_enum.insert("linear", INTERPOLATION_LINEAR); interpolation_enum.insert("cubic", INTERPOLATION_CUBIC); interpolation_enum.insert("smart", INTERPOLATION_SMART); SOCKET_ENUM(interpolation, "Interpolation", interpolation_enum, INTERPOLATION_LINEAR); static NodeEnum extension_enum; extension_enum.insert("periodic", EXTENSION_REPEAT); extension_enum.insert("clamp", EXTENSION_EXTEND); extension_enum.insert("black", EXTENSION_CLIP); extension_enum.insert("mirror", EXTENSION_MIRROR); SOCKET_ENUM(extension, "Extension", extension_enum, EXTENSION_REPEAT); static NodeEnum projection_enum; projection_enum.insert("flat", NODE_IMAGE_PROJ_FLAT); projection_enum.insert("box", NODE_IMAGE_PROJ_BOX); projection_enum.insert("sphere", NODE_IMAGE_PROJ_SPHERE); projection_enum.insert("tube", NODE_IMAGE_PROJ_TUBE); SOCKET_ENUM(projection, "Projection", projection_enum, NODE_IMAGE_PROJ_FLAT); SOCKET_FLOAT(projection_blend, "Projection Blend", 0.0f); SOCKET_INT_ARRAY(tiles, "Tiles", array()); SOCKET_BOOLEAN(animated, "Animated", false); SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_TEXTURE_UV); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(alpha, "Alpha"); return type; } ImageTextureNode::ImageTextureNode() : ImageSlotTextureNode(get_node_type()) { colorspace = u_colorspace_raw; animated = false; } ShaderNode *ImageTextureNode::clone(ShaderGraph *graph) const { ImageTextureNode *node = graph->create_node(*this); node->handle = handle; return node; } ImageParams ImageTextureNode::image_params() const { ImageParams params; params.animated = animated; params.interpolation = interpolation; params.extension = extension; params.alpha_type = alpha_type; params.colorspace = colorspace; return params; } void ImageTextureNode::cull_tiles(Scene *scene, ShaderGraph *graph) { /* Box projection computes its own UVs that always lie in the * 1001 tile, so there's no point in loading any others. */ if (projection == NODE_IMAGE_PROJ_BOX) { if (tiles.size()) { tiles.clear(); tiles.push_back_slow(1001); } return; } if (!scene->params.background) { /* During interactive renders, all tiles are loaded. * While we could support updating this when UVs change, that could lead * to annoying interruptions when loading images while editing UVs. */ return; } /* Only check UVs for tile culling when using tiles. */ if (tiles.size() == 0) { return; } ShaderInput *vector_in = input("Vector"); ustring attribute; if (vector_in->link) { ShaderNode *node = vector_in->link->parent; if (node->type == UVMapNode::get_node_type()) { UVMapNode *uvmap = (UVMapNode *)node; attribute = uvmap->get_attribute(); } else if (node->type == TextureCoordinateNode::get_node_type()) { if (vector_in->link != node->output("UV")) { return; } } else { return; } } unordered_set used_tiles; /* TODO(lukas): This is quite inefficient. A fairly simple improvement would * be to have a cache in each mesh that is indexed by attribute. * Additionally, building a graph-to-meshes list once could help. */ for (Geometry *geom : scene->geometry) { for (Node *node : geom->get_used_shaders()) { Shader *shader = static_cast(node); if (shader->graph.get() == graph) { geom->get_uv_tiles(attribute, used_tiles); } } } array new_tiles; for (const int tile : tiles) { if (used_tiles.count(tile)) { new_tiles.push_back_slow(tile); } } tiles.steal_data(new_tiles); } void ImageTextureNode::attributes(Shader *shader, AttributeRequestSet *attributes) { #ifdef WITH_PTEX /* todo: avoid loading other texture coordinates when using ptex, * and hide texture coordinate socket in the UI */ if (shader->has_surface_link() && string_endswith(filename, ".ptx")) { /* ptex */ attributes->add(ATTR_STD_PTEX_FACE_ID); attributes->add(ATTR_STD_PTEX_UV); } #endif ShaderNode::attributes(shader, attributes); } void ImageTextureNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderOutput *color_out = output("Color"); ShaderOutput *alpha_out = output("Alpha"); if (handle.empty()) { cull_tiles(compiler.scene, compiler.current_graph); ImageManager *image_manager = compiler.scene->image_manager.get(); handle = image_manager->add_image(filename.string(), image_params(), tiles); } /* All tiles have the same metadata. */ const ImageMetaData metadata = handle.metadata(); const bool compress_as_srgb = metadata.compress_as_srgb; const int vector_offset = tex_mapping.compile_begin(compiler, vector_in); uint flags = 0; if (compress_as_srgb) { flags |= NODE_IMAGE_COMPRESS_AS_SRGB; } if (!alpha_out->links.empty()) { const bool unassociate_alpha = !(ColorSpaceManager::colorspace_is_data(colorspace) || alpha_type == IMAGE_ALPHA_CHANNEL_PACKED || alpha_type == IMAGE_ALPHA_IGNORE); if (unassociate_alpha) { flags |= NODE_IMAGE_ALPHA_UNASSOCIATE; } } if (projection != NODE_IMAGE_PROJ_BOX) { /* If there only is one image (a very common case), we encode it as a negative value. */ int num_nodes; if (handle.num_tiles() == 0) { num_nodes = -handle.svm_slot(); } else { num_nodes = divide_up(handle.num_tiles(), 2); } compiler.add_node(NODE_TEX_IMAGE, num_nodes, compiler.encode_uchar4(vector_offset, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out), flags), projection); if (num_nodes > 0) { for (int i = 0; i < num_nodes; i++) { int4 node; node.x = tiles[2 * i]; node.y = handle.svm_slot(2 * i); if (2 * i + 1 < tiles.size()) { node.z = tiles[2 * i + 1]; node.w = handle.svm_slot(2 * i + 1); } else { node.z = -1; node.w = -1; } compiler.add_node(node.x, node.y, node.z, node.w); } } } else { assert(handle.num_svm_slots() == 1); compiler.add_node(NODE_TEX_IMAGE_BOX, handle.svm_slot(), compiler.encode_uchar4(vector_offset, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out), flags), __float_as_int(projection_blend)); } tex_mapping.compile_end(compiler, vector_in, vector_offset); } void ImageTextureNode::compile(OSLCompiler &compiler) { ShaderOutput *alpha_out = output("Alpha"); tex_mapping.compile(compiler); if (handle.empty()) { ImageManager *image_manager = compiler.scene->image_manager.get(); handle = image_manager->add_image(filename.string(), image_params()); } const ImageMetaData metadata = handle.metadata(); const bool is_float = metadata.is_float(); const bool compress_as_srgb = metadata.compress_as_srgb; const ustring known_colorspace = metadata.colorspace; if (handle.svm_slot() == -1) { compiler.parameter_texture( "filename", filename, compress_as_srgb ? u_colorspace_raw : known_colorspace); } else { compiler.parameter_texture("filename", handle); } const bool unassociate_alpha = !(ColorSpaceManager::colorspace_is_data(colorspace) || alpha_type == IMAGE_ALPHA_CHANNEL_PACKED || alpha_type == IMAGE_ALPHA_IGNORE); const bool is_tiled = (filename.find("") != string::npos || filename.find("") != string::npos) || handle.num_tiles() > 0; compiler.parameter(this, "projection"); compiler.parameter(this, "projection_blend"); compiler.parameter("compress_as_srgb", compress_as_srgb); compiler.parameter("ignore_alpha", alpha_type == IMAGE_ALPHA_IGNORE); compiler.parameter("unassociate_alpha", !alpha_out->links.empty() && unassociate_alpha); compiler.parameter("is_float", is_float); compiler.parameter("is_tiled", is_tiled); compiler.parameter(this, "interpolation"); compiler.parameter(this, "extension"); compiler.add(this, "node_image_texture"); } /* Environment Texture */ NODE_DEFINE(EnvironmentTextureNode) { NodeType *type = NodeType::add("environment_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(EnvironmentTextureNode); SOCKET_STRING(filename, "Filename", ustring()); SOCKET_STRING(colorspace, "Colorspace", u_colorspace_auto); static NodeEnum alpha_type_enum; alpha_type_enum.insert("auto", IMAGE_ALPHA_AUTO); alpha_type_enum.insert("unassociated", IMAGE_ALPHA_UNASSOCIATED); alpha_type_enum.insert("associated", IMAGE_ALPHA_ASSOCIATED); alpha_type_enum.insert("channel_packed", IMAGE_ALPHA_CHANNEL_PACKED); alpha_type_enum.insert("ignore", IMAGE_ALPHA_IGNORE); SOCKET_ENUM(alpha_type, "Alpha Type", alpha_type_enum, IMAGE_ALPHA_AUTO); static NodeEnum interpolation_enum; interpolation_enum.insert("closest", INTERPOLATION_CLOSEST); interpolation_enum.insert("linear", INTERPOLATION_LINEAR); interpolation_enum.insert("cubic", INTERPOLATION_CUBIC); interpolation_enum.insert("smart", INTERPOLATION_SMART); SOCKET_ENUM(interpolation, "Interpolation", interpolation_enum, INTERPOLATION_LINEAR); static NodeEnum projection_enum; projection_enum.insert("equirectangular", NODE_ENVIRONMENT_EQUIRECTANGULAR); projection_enum.insert("mirror_ball", NODE_ENVIRONMENT_MIRROR_BALL); SOCKET_ENUM(projection, "Projection", projection_enum, NODE_ENVIRONMENT_EQUIRECTANGULAR); SOCKET_BOOLEAN(animated, "Animated", false); SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_POSITION); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(alpha, "Alpha"); return type; } EnvironmentTextureNode::EnvironmentTextureNode() : ImageSlotTextureNode(get_node_type()) { colorspace = u_colorspace_raw; animated = false; } ShaderNode *EnvironmentTextureNode::clone(ShaderGraph *graph) const { EnvironmentTextureNode *node = graph->create_node(*this); node->handle = handle; return node; } ImageParams EnvironmentTextureNode::image_params() const { ImageParams params; params.animated = animated; params.interpolation = interpolation; params.extension = EXTENSION_REPEAT; params.alpha_type = alpha_type; params.colorspace = colorspace; return params; } void EnvironmentTextureNode::attributes(Shader *shader, AttributeRequestSet *attributes) { #ifdef WITH_PTEX if (shader->has_surface_link() && string_endswith(filename, ".ptx")) { /* ptex */ attributes->add(ATTR_STD_PTEX_FACE_ID); attributes->add(ATTR_STD_PTEX_UV); } #endif ShaderNode::attributes(shader, attributes); } void EnvironmentTextureNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderOutput *color_out = output("Color"); ShaderOutput *alpha_out = output("Alpha"); if (handle.empty()) { ImageManager *image_manager = compiler.scene->image_manager.get(); handle = image_manager->add_image(filename.string(), image_params()); } const ImageMetaData metadata = handle.metadata(); const bool compress_as_srgb = metadata.compress_as_srgb; const int vector_offset = tex_mapping.compile_begin(compiler, vector_in); uint flags = 0; if (compress_as_srgb) { flags |= NODE_IMAGE_COMPRESS_AS_SRGB; } compiler.add_node(NODE_TEX_ENVIRONMENT, handle.svm_slot(), compiler.encode_uchar4(vector_offset, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out), flags), projection); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void EnvironmentTextureNode::compile(OSLCompiler &compiler) { if (handle.empty()) { ImageManager *image_manager = compiler.scene->image_manager.get(); handle = image_manager->add_image(filename.string(), image_params()); } tex_mapping.compile(compiler); const ImageMetaData metadata = handle.metadata(); const bool is_float = metadata.is_float(); const bool compress_as_srgb = metadata.compress_as_srgb; const ustring known_colorspace = metadata.colorspace; if (handle.svm_slot() == -1) { compiler.parameter_texture( "filename", filename, compress_as_srgb ? u_colorspace_raw : known_colorspace); } else { compiler.parameter_texture("filename", handle); } compiler.parameter(this, "projection"); compiler.parameter(this, "interpolation"); compiler.parameter("compress_as_srgb", compress_as_srgb); compiler.parameter("ignore_alpha", alpha_type == IMAGE_ALPHA_IGNORE); compiler.parameter("is_float", is_float); compiler.add(this, "node_environment_texture"); } /* Sky Texture */ static float2 sky_spherical_coordinates(const float3 dir) { return make_float2(acosf(dir.z), atan2f(dir.x, dir.y)); } struct SunSky { /* sun direction in spherical and cartesian */ float theta, phi; /* Parameter */ float radiance_x, radiance_y, radiance_z; float config_x[9], config_y[9], config_z[9], nishita_data[11]; }; /* Preetham model */ static float sky_perez_function(const float lam[6], float theta, const float gamma) { return (1.0f + lam[0] * expf(lam[1] / cosf(theta))) * (1.0f + lam[2] * expf(lam[3] * gamma) + lam[4] * cosf(gamma) * cosf(gamma)); } static void sky_texture_precompute_preetham(SunSky *sunsky, const float3 dir, const float turbidity) { /* * We re-use the SunSky struct of the new model, to avoid extra variables * zenith_Y/x/y is now radiance_x/y/z * perez_Y/x/y is now config_x/y/z */ const float2 spherical = sky_spherical_coordinates(dir); const float theta = spherical.x; const float phi = spherical.y; sunsky->theta = theta; sunsky->phi = phi; const float theta2 = theta * theta; const float theta3 = theta2 * theta; const float T = turbidity; const float T2 = T * T; const float chi = (4.0f / 9.0f - T / 120.0f) * (M_PI_F - 2.0f * theta); sunsky->radiance_x = (4.0453f * T - 4.9710f) * tanf(chi) - 0.2155f * T + 2.4192f; sunsky->radiance_x *= 0.06f; sunsky->radiance_y = (0.00166f * theta3 - 0.00375f * theta2 + 0.00209f * theta) * T2 + (-0.02903f * theta3 + 0.06377f * theta2 - 0.03202f * theta + 0.00394f) * T + (0.11693f * theta3 - 0.21196f * theta2 + 0.06052f * theta + 0.25886f); sunsky->radiance_z = (0.00275f * theta3 - 0.00610f * theta2 + 0.00317f * theta) * T2 + (-0.04214f * theta3 + 0.08970f * theta2 - 0.04153f * theta + 0.00516f) * T + (0.15346f * theta3 - 0.26756f * theta2 + 0.06670f * theta + 0.26688f); sunsky->config_x[0] = (0.1787f * T - 1.4630f); sunsky->config_x[1] = (-0.3554f * T + 0.4275f); sunsky->config_x[2] = (-0.0227f * T + 5.3251f); sunsky->config_x[3] = (0.1206f * T - 2.5771f); sunsky->config_x[4] = (-0.0670f * T + 0.3703f); sunsky->config_y[0] = (-0.0193f * T - 0.2592f); sunsky->config_y[1] = (-0.0665f * T + 0.0008f); sunsky->config_y[2] = (-0.0004f * T + 0.2125f); sunsky->config_y[3] = (-0.0641f * T - 0.8989f); sunsky->config_y[4] = (-0.0033f * T + 0.0452f); sunsky->config_z[0] = (-0.0167f * T - 0.2608f); sunsky->config_z[1] = (-0.0950f * T + 0.0092f); sunsky->config_z[2] = (-0.0079f * T + 0.2102f); sunsky->config_z[3] = (-0.0441f * T - 1.6537f); sunsky->config_z[4] = (-0.0109f * T + 0.0529f); /* unused for old sky model */ for (int i = 5; i < 9; i++) { sunsky->config_x[i] = 0.0f; sunsky->config_y[i] = 0.0f; sunsky->config_z[i] = 0.0f; } sunsky->radiance_x /= sky_perez_function(sunsky->config_x, 0, theta); sunsky->radiance_y /= sky_perez_function(sunsky->config_y, 0, theta); sunsky->radiance_z /= sky_perez_function(sunsky->config_z, 0, theta); } /* Hosek / Wilkie */ static void sky_texture_precompute_hosek(SunSky *sunsky, const float3 dir, float turbidity, const float ground_albedo) { /* Calculate Sun Direction and save coordinates */ const float2 spherical = sky_spherical_coordinates(dir); float theta = spherical.x; const float phi = spherical.y; /* Clamp Turbidity */ turbidity = clamp(turbidity, 0.0f, 10.0f); /* Clamp to Horizon */ theta = clamp(theta, 0.0f, M_PI_2_F); sunsky->theta = theta; sunsky->phi = phi; const float solarElevation = M_PI_2_F - theta; /* Initialize Sky Model */ SKY_ArHosekSkyModelState *sky_state; sky_state = SKY_arhosek_xyz_skymodelstate_alloc_init( (double)turbidity, (double)ground_albedo, (double)solarElevation); /* Copy values from sky_state to SunSky */ for (int i = 0; i < 9; ++i) { sunsky->config_x[i] = (float)sky_state->configs[0][i]; sunsky->config_y[i] = (float)sky_state->configs[1][i]; sunsky->config_z[i] = (float)sky_state->configs[2][i]; } sunsky->radiance_x = (float)sky_state->radiances[0]; sunsky->radiance_y = (float)sky_state->radiances[1]; sunsky->radiance_z = (float)sky_state->radiances[2]; /* Free sky_state */ SKY_arhosekskymodelstate_free(sky_state); } /* Nishita improved */ static void sky_texture_precompute_nishita(SunSky *sunsky, bool multiple_scattering, bool sun_disc, const float sun_size, const float sun_intensity, const float sun_elevation, const float sun_rotation, const float altitude, const float air_density, const float aerosol_density, const float ozone_density) { /* Sample 2 Sun pixels */ float pixel_bottom[3]; float pixel_top[3]; if (multiple_scattering) { SKY_multiple_scattering_precompute_sun(sun_elevation, sun_size, altitude, air_density, aerosol_density, ozone_density, pixel_bottom, pixel_top); } else { SKY_single_scattering_precompute_sun( sun_elevation, sun_size, altitude, air_density, aerosol_density, pixel_bottom, pixel_top); } float earth_intersection_angle = SKY_earth_intersection_angle(altitude); /* Send data to sky.h */ sunsky->nishita_data[0] = pixel_bottom[0]; sunsky->nishita_data[1] = pixel_bottom[1]; sunsky->nishita_data[2] = pixel_bottom[2]; sunsky->nishita_data[3] = pixel_top[0]; sunsky->nishita_data[4] = pixel_top[1]; sunsky->nishita_data[5] = pixel_top[2]; sunsky->nishita_data[6] = sun_elevation; sunsky->nishita_data[7] = sun_rotation; sunsky->nishita_data[8] = sun_disc ? sun_size : -1.0f; sunsky->nishita_data[9] = sun_intensity; sunsky->nishita_data[10] = -earth_intersection_angle; } float SkyTextureNode::get_sun_average_radiance() { const float angular_diameter = get_sun_size(); float pix_bottom[3]; float pix_top[3]; if (sky_type == NODE_SKY_SINGLE_SCATTERING) { SKY_single_scattering_precompute_sun(sun_elevation, angular_diameter, altitude, air_density, aerosol_density, pix_bottom, pix_top); } else { SKY_multiple_scattering_precompute_sun(sun_elevation, angular_diameter, altitude, air_density, aerosol_density, ozone_density, pix_bottom, pix_top); } /* Sample center of Sun. */ const float3 pixel_bottom = make_float3(pix_bottom[0], pix_bottom[1], pix_bottom[2]); const float3 pixel_top = make_float3(pix_top[0], pix_top[1], pix_top[2]); float3 xyz = interp(pixel_bottom, pixel_top, 0.5f) * sun_intensity; /* We first approximate the Sun's contribution by * multiplying the evaluated point by the square of the angular diameter. * Then we scale the approximation using a piecewise function (determined empirically). */ float sun_contribution = average(xyz) * sqr(angular_diameter); const float first_point = 0.8f / 180.0f * M_PI_F; const float second_point = 1.0f / 180.0f * M_PI_F; const float third_point = M_PI_2_F; if (angular_diameter < first_point) { sun_contribution *= 1.0f; } else if (angular_diameter < second_point) { const float diff = angular_diameter - first_point; const float slope = (0.8f - 1.0f) / (second_point - first_point); sun_contribution *= 1.0f + slope * diff; } else { const float diff = angular_diameter - 1.0f / 180.0f * M_PI_F; const float slope = (0.45f - 0.8f) / (third_point - second_point); sun_contribution *= 0.8f + slope * diff; } return sun_contribution; } NODE_DEFINE(SkyTextureNode) { NodeType *type = NodeType::add("sky_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(SkyTextureNode); static NodeEnum type_enum; type_enum.insert("preetham", NODE_SKY_PREETHAM); type_enum.insert("hosek_wilkie", NODE_SKY_HOSEK); type_enum.insert("single_scattering", NODE_SKY_SINGLE_SCATTERING); type_enum.insert("multiple_scattering", NODE_SKY_MULTIPLE_SCATTERING); SOCKET_ENUM(sky_type, "Type", type_enum, NODE_SKY_MULTIPLE_SCATTERING); /* Nishita parameters. */ SOCKET_BOOLEAN(sun_disc, "Sun Disc", true); SOCKET_FLOAT(sun_size, "Sun Size", 0.009512f); SOCKET_FLOAT(sun_intensity, "Sun Intensity", 1.0f); SOCKET_FLOAT(sun_elevation, "Sun Elevation", 15.0f * M_PI_F / 180.0f); SOCKET_FLOAT(sun_rotation, "Sun Rotation", 0.0f); SOCKET_FLOAT(altitude, "Altitude", 100.0f); SOCKET_FLOAT(air_density, "Air", 1.0f); SOCKET_FLOAT(aerosol_density, "Aerosol", 1.0f); SOCKET_FLOAT(ozone_density, "Ozone", 1.0f); SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_TEXTURE_GENERATED); SOCKET_OUT_COLOR(color, "Color"); /* Legacy parameters. */ SOCKET_VECTOR(sun_direction, "Sun Direction", make_float3(0.0f, 0.0f, 1.0f)); SOCKET_FLOAT(turbidity, "Turbidity", 2.2f); SOCKET_FLOAT(ground_albedo, "Ground Albedo", 0.3f); return type; } SkyTextureNode::SkyTextureNode() : TextureNode(get_node_type()) {} void SkyTextureNode::simplify_settings(Scene * /* scene */) { /* Patch Sun position so users are able to animate the daylight cycle while keeping the shading * code simple. */ float new_sun_elevation = sun_elevation; float new_sun_rotation = sun_rotation; /* Wrap `new_sun_elevation` into [-2PI..2PI] range. */ new_sun_elevation = fmodf(new_sun_elevation, M_2PI_F); /* Wrap `new_sun_elevation` into [-PI..PI] range. */ if (fabsf(new_sun_elevation) >= M_PI_F) { new_sun_elevation -= copysignf(2.0f, new_sun_elevation) * M_PI_F; } /* Wrap `new_sun_elevation` into [-PI/2..PI/2] range while keeping the same absolute position. */ if (new_sun_elevation >= M_PI_2_F || new_sun_elevation <= -M_PI_2_F) { new_sun_elevation = copysignf(M_PI_F, new_sun_elevation) - new_sun_elevation; new_sun_rotation += M_PI_F; } /* Wrap `new_sun_rotation` into [-2PI..2PI] range. */ new_sun_rotation = fmodf(new_sun_rotation, M_2PI_F); /* Wrap `new_sun_rotation` into [0..2PI] range. */ if (new_sun_rotation < 0.0f) { new_sun_rotation += M_2PI_F; } new_sun_rotation = M_2PI_F - new_sun_rotation; sun_elevation = new_sun_elevation; sun_rotation = new_sun_rotation; } void SkyTextureNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderOutput *color_out = output("Color"); SunSky sunsky = {}; if (sky_type == NODE_SKY_PREETHAM) { sky_texture_precompute_preetham(&sunsky, sun_direction, turbidity); } else if (sky_type == NODE_SKY_HOSEK) { sky_texture_precompute_hosek(&sunsky, sun_direction, turbidity, ground_albedo); } else { sky_texture_precompute_nishita(&sunsky, sky_type == NODE_SKY_MULTIPLE_SCATTERING, sun_disc, get_sun_size(), sun_intensity, sun_elevation, sun_rotation, altitude, air_density, aerosol_density, ozone_density); /* Sky texture image parameters */ ImageManager *image_manager = compiler.scene->image_manager.get(); ImageParams impar; impar.interpolation = INTERPOLATION_LINEAR; impar.extension = EXTENSION_EXTEND; /* Precompute sky texture */ if (handle.empty()) { unique_ptr loader = make_unique(sky_type == NODE_SKY_MULTIPLE_SCATTERING, sun_elevation, altitude, air_density, aerosol_density, ozone_density); handle = image_manager->add_image(std::move(loader), impar); } } const int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.stack_assign(color_out); compiler.add_node(NODE_TEX_SKY, vector_offset, compiler.stack_assign(color_out), sky_type); if (sky_type == NODE_SKY_PREETHAM || sky_type == NODE_SKY_HOSEK) { compiler.add_node(__float_as_uint(sunsky.phi), __float_as_uint(sunsky.theta), __float_as_uint(sunsky.radiance_x), __float_as_uint(sunsky.radiance_y)); compiler.add_node(__float_as_uint(sunsky.radiance_z), __float_as_uint(sunsky.config_x[0]), __float_as_uint(sunsky.config_x[1]), __float_as_uint(sunsky.config_x[2])); compiler.add_node(__float_as_uint(sunsky.config_x[3]), __float_as_uint(sunsky.config_x[4]), __float_as_uint(sunsky.config_x[5]), __float_as_uint(sunsky.config_x[6])); compiler.add_node(__float_as_uint(sunsky.config_x[7]), __float_as_uint(sunsky.config_x[8]), __float_as_uint(sunsky.config_y[0]), __float_as_uint(sunsky.config_y[1])); compiler.add_node(__float_as_uint(sunsky.config_y[2]), __float_as_uint(sunsky.config_y[3]), __float_as_uint(sunsky.config_y[4]), __float_as_uint(sunsky.config_y[5])); compiler.add_node(__float_as_uint(sunsky.config_y[6]), __float_as_uint(sunsky.config_y[7]), __float_as_uint(sunsky.config_y[8]), __float_as_uint(sunsky.config_z[0])); compiler.add_node(__float_as_uint(sunsky.config_z[1]), __float_as_uint(sunsky.config_z[2]), __float_as_uint(sunsky.config_z[3]), __float_as_uint(sunsky.config_z[4])); compiler.add_node(__float_as_uint(sunsky.config_z[5]), __float_as_uint(sunsky.config_z[6]), __float_as_uint(sunsky.config_z[7]), __float_as_uint(sunsky.config_z[8])); } else { compiler.add_node(__float_as_uint(sunsky.nishita_data[0]), __float_as_uint(sunsky.nishita_data[1]), __float_as_uint(sunsky.nishita_data[2]), __float_as_uint(sunsky.nishita_data[3])); compiler.add_node(__float_as_uint(sunsky.nishita_data[4]), __float_as_uint(sunsky.nishita_data[5]), __float_as_uint(sunsky.nishita_data[6]), __float_as_uint(sunsky.nishita_data[7])); compiler.add_node(__float_as_uint(sunsky.nishita_data[8]), __float_as_uint(sunsky.nishita_data[9]), __float_as_uint(sunsky.nishita_data[10]), handle.svm_slot()); } tex_mapping.compile_end(compiler, vector_in, vector_offset); } void SkyTextureNode::compile(OSLCompiler &compiler) { tex_mapping.compile(compiler); SunSky sunsky = {}; if (sky_type == NODE_SKY_PREETHAM) { sky_texture_precompute_preetham(&sunsky, sun_direction, turbidity); } else if (sky_type == NODE_SKY_HOSEK) { sky_texture_precompute_hosek(&sunsky, sun_direction, turbidity, ground_albedo); } else { sky_texture_precompute_nishita(&sunsky, sky_type == NODE_SKY_MULTIPLE_SCATTERING, sun_disc, get_sun_size(), sun_intensity, sun_elevation, sun_rotation, altitude, air_density, aerosol_density, ozone_density); /* Sky texture image parameters */ ImageManager *image_manager = compiler.scene->image_manager.get(); ImageParams impar; impar.interpolation = INTERPOLATION_LINEAR; impar.extension = EXTENSION_EXTEND; /* Precompute sky texture */ { unique_ptr loader = make_unique(sky_type == NODE_SKY_MULTIPLE_SCATTERING, sun_elevation, altitude, air_density, aerosol_density, ozone_density); handle = image_manager->add_image(std::move(loader), impar); } compiler.parameter_texture("filename", handle); } compiler.parameter(this, "sky_type"); compiler.parameter("theta", sunsky.theta); compiler.parameter("phi", sunsky.phi); compiler.parameter_color("radiance", make_float3(sunsky.radiance_x, sunsky.radiance_y, sunsky.radiance_z)); compiler.parameter_array("config_x", sunsky.config_x, 9); compiler.parameter_array("config_y", sunsky.config_y, 9); compiler.parameter_array("config_z", sunsky.config_z, 9); compiler.parameter_array("nishita_data", sunsky.nishita_data, 11); compiler.add(this, "node_sky_texture"); } /* Gradient Texture */ NODE_DEFINE(GradientTextureNode) { NodeType *type = NodeType::add("gradient_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(GradientTextureNode); static NodeEnum type_enum; type_enum.insert("linear", NODE_BLEND_LINEAR); type_enum.insert("quadratic", NODE_BLEND_QUADRATIC); type_enum.insert("easing", NODE_BLEND_EASING); type_enum.insert("diagonal", NODE_BLEND_DIAGONAL); type_enum.insert("radial", NODE_BLEND_RADIAL); type_enum.insert("quadratic_sphere", NODE_BLEND_QUADRATIC_SPHERE); type_enum.insert("spherical", NODE_BLEND_SPHERICAL); SOCKET_ENUM(gradient_type, "Type", type_enum, NODE_BLEND_LINEAR); SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_TEXTURE_GENERATED); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } GradientTextureNode::GradientTextureNode() : TextureNode(get_node_type()) {} void GradientTextureNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderOutput *color_out = output("Color"); ShaderOutput *fac_out = output("Fac"); const int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_GRADIENT, compiler.encode_uchar4(gradient_type, vector_offset, compiler.stack_assign_if_linked(fac_out), compiler.stack_assign_if_linked(color_out))); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void GradientTextureNode::compile(OSLCompiler &compiler) { tex_mapping.compile(compiler); compiler.parameter(this, "gradient_type"); compiler.add(this, "node_gradient_texture"); } /* Noise Texture */ NODE_DEFINE(NoiseTextureNode) { NodeType *type = NodeType::add("noise_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(NoiseTextureNode); static NodeEnum dimensions_enum; dimensions_enum.insert("1D", 1); dimensions_enum.insert("2D", 2); dimensions_enum.insert("3D", 3); dimensions_enum.insert("4D", 4); SOCKET_ENUM(dimensions, "Dimensions", dimensions_enum, 3); static NodeEnum type_enum; type_enum.insert("multifractal", NODE_NOISE_MULTIFRACTAL); type_enum.insert("fBM", NODE_NOISE_FBM); type_enum.insert("hybrid_multifractal", NODE_NOISE_HYBRID_MULTIFRACTAL); type_enum.insert("ridged_multifractal", NODE_NOISE_RIDGED_MULTIFRACTAL); type_enum.insert("hetero_terrain", NODE_NOISE_HETERO_TERRAIN); SOCKET_ENUM(type, "Type", type_enum, NODE_NOISE_FBM); SOCKET_BOOLEAN(use_normalize, "Normalize", true); SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_TEXTURE_GENERATED); SOCKET_IN_FLOAT(w, "W", 0.0f); SOCKET_IN_FLOAT(scale, "Scale", 1.0f); SOCKET_IN_FLOAT(detail, "Detail", 2.0f); SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f); SOCKET_IN_FLOAT(lacunarity, "Lacunarity", 2.0f); SOCKET_IN_FLOAT(offset, "Offset", 0.0f); SOCKET_IN_FLOAT(gain, "Gain", 1.0f); SOCKET_IN_FLOAT(distortion, "Distortion", 0.0f); SOCKET_OUT_FLOAT(fac, "Fac"); SOCKET_OUT_COLOR(color, "Color"); return type; } NoiseTextureNode::NoiseTextureNode() : TextureNode(get_node_type()) {} void NoiseTextureNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *w_in = input("W"); ShaderInput *scale_in = input("Scale"); ShaderInput *detail_in = input("Detail"); ShaderInput *roughness_in = input("Roughness"); ShaderInput *lacunarity_in = input("Lacunarity"); ShaderInput *offset_in = input("Offset"); ShaderInput *gain_in = input("Gain"); ShaderInput *distortion_in = input("Distortion"); ShaderOutput *fac_out = output("Fac"); ShaderOutput *color_out = output("Color"); const int vector_stack_offset = tex_mapping.compile_begin(compiler, vector_in); const int w_stack_offset = compiler.stack_assign_if_linked(w_in); const int scale_stack_offset = compiler.stack_assign_if_linked(scale_in); const int detail_stack_offset = compiler.stack_assign_if_linked(detail_in); const int roughness_stack_offset = compiler.stack_assign_if_linked(roughness_in); const int lacunarity_stack_offset = compiler.stack_assign_if_linked(lacunarity_in); const int offset_stack_offset = compiler.stack_assign_if_linked(offset_in); const int gain_stack_offset = compiler.stack_assign_if_linked(gain_in); const int distortion_stack_offset = compiler.stack_assign_if_linked(distortion_in); const int fac_stack_offset = compiler.stack_assign_if_linked(fac_out); const int color_stack_offset = compiler.stack_assign_if_linked(color_out); compiler.add_node( NODE_TEX_NOISE, compiler.encode_uchar4( vector_stack_offset, w_stack_offset, scale_stack_offset, detail_stack_offset), compiler.encode_uchar4( roughness_stack_offset, lacunarity_stack_offset, offset_stack_offset, gain_stack_offset), compiler.encode_uchar4(distortion_stack_offset, fac_stack_offset, color_stack_offset)); compiler.add_node( __float_as_int(w), __float_as_int(scale), __float_as_int(detail), __float_as_int(roughness)); compiler.add_node(__float_as_int(lacunarity), __float_as_int(offset), __float_as_int(gain), __float_as_int(distortion)); compiler.add_node(dimensions, type, use_normalize, SVM_STACK_INVALID); tex_mapping.compile_end(compiler, vector_in, vector_stack_offset); } void NoiseTextureNode::compile(OSLCompiler &compiler) { tex_mapping.compile(compiler); compiler.parameter(this, "dimensions"); compiler.parameter(this, "type"); compiler.parameter(this, "use_normalize"); compiler.add(this, "node_noise_texture"); } /* Gabor Texture */ NODE_DEFINE(GaborTextureNode) { NodeType *type = NodeType::add("gabor_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(GaborTextureNode); static NodeEnum type_enum; type_enum.insert("2D", NODE_GABOR_TYPE_2D); type_enum.insert("3D", NODE_GABOR_TYPE_3D); SOCKET_ENUM(type, "Type", type_enum, NODE_GABOR_TYPE_2D); SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_TEXTURE_GENERATED); SOCKET_IN_FLOAT(scale, "Scale", 5.0f); SOCKET_IN_FLOAT(frequency, "Frequency", 2.0f); SOCKET_IN_FLOAT(anisotropy, "Anisotropy", 1.0f); SOCKET_IN_FLOAT(orientation_2d, "Orientation 2D", M_PI_F / 4.0f); SOCKET_IN_VECTOR(orientation_3d, "Orientation 3D", make_float3(M_SQRT2_F, M_SQRT2_F, 0.0f)); SOCKET_OUT_FLOAT(value, "Value"); SOCKET_OUT_FLOAT(phase, "Phase"); SOCKET_OUT_FLOAT(intensity, "Intensity"); return type; } GaborTextureNode::GaborTextureNode() : TextureNode(get_node_type()) {} void GaborTextureNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *scale_in = input("Scale"); ShaderInput *frequency_in = input("Frequency"); ShaderInput *anisotropy_in = input("Anisotropy"); ShaderInput *orientation_2d_in = input("Orientation 2D"); ShaderInput *orientation_3d_in = input("Orientation 3D"); ShaderOutput *value_out = output("Value"); ShaderOutput *phase_out = output("Phase"); ShaderOutput *intensity_out = output("Intensity"); const int vector_stack_offset = tex_mapping.compile_begin(compiler, vector_in); const int scale_stack_offset = compiler.stack_assign_if_linked(scale_in); const int frequency_stack_offset = compiler.stack_assign_if_linked(frequency_in); const int anisotropy_stack_offset = compiler.stack_assign_if_linked(anisotropy_in); const int orientation_2d_stack_offset = compiler.stack_assign_if_linked(orientation_2d_in); const int orientation_3d_stack_offset = compiler.stack_assign(orientation_3d_in); const int value_stack_offset = compiler.stack_assign_if_linked(value_out); const int phase_stack_offset = compiler.stack_assign_if_linked(phase_out); const int intensity_stack_offset = compiler.stack_assign_if_linked(intensity_out); compiler.add_node( NODE_TEX_GABOR, type, compiler.encode_uchar4(vector_stack_offset, scale_stack_offset, frequency_stack_offset, anisotropy_stack_offset), compiler.encode_uchar4(orientation_2d_stack_offset, orientation_3d_stack_offset)); compiler.add_node( compiler.encode_uchar4(value_stack_offset, phase_stack_offset, intensity_stack_offset), __float_as_int(scale), __float_as_int(frequency), __float_as_int(anisotropy)); compiler.add_node(__float_as_int(orientation_2d)); tex_mapping.compile_end(compiler, vector_in, vector_stack_offset); } void GaborTextureNode::compile(OSLCompiler &compiler) { tex_mapping.compile(compiler); compiler.parameter(this, "type"); compiler.add(this, "node_gabor_texture"); } /* Voronoi Texture */ NODE_DEFINE(VoronoiTextureNode) { NodeType *type = NodeType::add("voronoi_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(VoronoiTextureNode); static NodeEnum dimensions_enum; dimensions_enum.insert("1D", 1); dimensions_enum.insert("2D", 2); dimensions_enum.insert("3D", 3); dimensions_enum.insert("4D", 4); SOCKET_ENUM(dimensions, "Dimensions", dimensions_enum, 3); static NodeEnum metric_enum; metric_enum.insert("euclidean", NODE_VORONOI_EUCLIDEAN); metric_enum.insert("manhattan", NODE_VORONOI_MANHATTAN); metric_enum.insert("chebychev", NODE_VORONOI_CHEBYCHEV); metric_enum.insert("minkowski", NODE_VORONOI_MINKOWSKI); SOCKET_ENUM(metric, "Distance Metric", metric_enum, NODE_VORONOI_EUCLIDEAN); static NodeEnum feature_enum; feature_enum.insert("f1", NODE_VORONOI_F1); feature_enum.insert("f2", NODE_VORONOI_F2); feature_enum.insert("smooth_f1", NODE_VORONOI_SMOOTH_F1); feature_enum.insert("distance_to_edge", NODE_VORONOI_DISTANCE_TO_EDGE); feature_enum.insert("n_sphere_radius", NODE_VORONOI_N_SPHERE_RADIUS); SOCKET_ENUM(feature, "Feature", feature_enum, NODE_VORONOI_F1); SOCKET_BOOLEAN(use_normalize, "Normalize", false); SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_TEXTURE_GENERATED); SOCKET_IN_FLOAT(w, "W", 0.0f); SOCKET_IN_FLOAT(scale, "Scale", 5.0f); SOCKET_IN_FLOAT(detail, "Detail", 0.0f); SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f); SOCKET_IN_FLOAT(lacunarity, "Lacunarity", 2.0f); SOCKET_IN_FLOAT(smoothness, "Smoothness", 5.0f); SOCKET_IN_FLOAT(exponent, "Exponent", 0.5f); SOCKET_IN_FLOAT(randomness, "Randomness", 1.0f); SOCKET_OUT_FLOAT(distance, "Distance"); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_POINT(position, "Position"); SOCKET_OUT_FLOAT(w, "W"); SOCKET_OUT_FLOAT(radius, "Radius"); return type; } VoronoiTextureNode::VoronoiTextureNode() : TextureNode(get_node_type()) {} void VoronoiTextureNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *w_in = input("W"); ShaderInput *scale_in = input("Scale"); ShaderInput *detail_in = input("Detail"); ShaderInput *roughness_in = input("Roughness"); ShaderInput *lacunarity_in = input("Lacunarity"); ShaderInput *smoothness_in = input("Smoothness"); ShaderInput *exponent_in = input("Exponent"); ShaderInput *randomness_in = input("Randomness"); ShaderOutput *distance_out = output("Distance"); ShaderOutput *color_out = output("Color"); ShaderOutput *position_out = output("Position"); ShaderOutput *w_out = output("W"); ShaderOutput *radius_out = output("Radius"); const int vector_stack_offset = tex_mapping.compile_begin(compiler, vector_in); const int w_in_stack_offset = compiler.stack_assign_if_linked(w_in); const int scale_stack_offset = compiler.stack_assign_if_linked(scale_in); const int detail_stack_offset = compiler.stack_assign_if_linked(detail_in); const int roughness_stack_offset = compiler.stack_assign_if_linked(roughness_in); const int lacunarity_stack_offset = compiler.stack_assign_if_linked(lacunarity_in); const int smoothness_stack_offset = compiler.stack_assign_if_linked(smoothness_in); const int exponent_stack_offset = compiler.stack_assign_if_linked(exponent_in); const int randomness_stack_offset = compiler.stack_assign_if_linked(randomness_in); const int distance_stack_offset = compiler.stack_assign_if_linked(distance_out); const int color_stack_offset = compiler.stack_assign_if_linked(color_out); const int position_stack_offset = compiler.stack_assign_if_linked(position_out); const int w_out_stack_offset = compiler.stack_assign_if_linked(w_out); const int radius_stack_offset = compiler.stack_assign_if_linked(radius_out); compiler.add_node(NODE_TEX_VORONOI, dimensions, feature, metric); compiler.add_node( compiler.encode_uchar4( vector_stack_offset, w_in_stack_offset, scale_stack_offset, detail_stack_offset), compiler.encode_uchar4(roughness_stack_offset, lacunarity_stack_offset, smoothness_stack_offset, exponent_stack_offset), compiler.encode_uchar4( randomness_stack_offset, use_normalize, distance_stack_offset, color_stack_offset), compiler.encode_uchar4(position_stack_offset, w_out_stack_offset, radius_stack_offset)); compiler.add_node( __float_as_int(w), __float_as_int(scale), __float_as_int(detail), __float_as_int(roughness)); compiler.add_node(__float_as_int(lacunarity), __float_as_int(smoothness), __float_as_int(exponent), __float_as_int(randomness)); tex_mapping.compile_end(compiler, vector_in, vector_stack_offset); } void VoronoiTextureNode::compile(OSLCompiler &compiler) { tex_mapping.compile(compiler); compiler.parameter(this, "dimensions"); compiler.parameter(this, "feature"); compiler.parameter(this, "metric"); compiler.parameter(this, "use_normalize"); compiler.add(this, "node_voronoi_texture"); } /* IES Light */ NODE_DEFINE(IESLightNode) { NodeType *type = NodeType::add("ies_light", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(IESLightNode); SOCKET_STRING(ies, "IES", ustring()); SOCKET_STRING(filename, "File Name", ustring()); SOCKET_IN_FLOAT(strength, "Strength", 1.0f); SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_TEXTURE_INCOMING); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } IESLightNode::IESLightNode() : TextureNode(get_node_type()) { light_manager = nullptr; slot = -1; } ShaderNode *IESLightNode::clone(ShaderGraph *graph) const { IESLightNode *node = graph->create_node(*this); node->light_manager = nullptr; node->slot = -1; return node; } IESLightNode::~IESLightNode() { if (light_manager) { light_manager->remove_ies(slot); } } void IESLightNode::get_slot() { assert(light_manager); if (slot == -1) { if (ies.empty()) { slot = light_manager->add_ies_from_file(filename.string()); } else { slot = light_manager->add_ies(ies.string()); } } } void IESLightNode::compile(SVMCompiler &compiler) { light_manager = compiler.scene->light_manager.get(); get_slot(); ShaderInput *strength_in = input("Strength"); ShaderInput *vector_in = input("Vector"); ShaderOutput *fac_out = output("Fac"); const int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_IES, compiler.encode_uchar4(compiler.stack_assign_if_linked(strength_in), vector_offset, compiler.stack_assign(fac_out), 0), slot, __float_as_int(strength)); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void IESLightNode::compile(OSLCompiler &compiler) { light_manager = compiler.scene->light_manager.get(); get_slot(); tex_mapping.compile(compiler); compiler.parameter_texture_ies("filename", slot); compiler.add(this, "node_ies_light"); } /* White Noise Texture */ NODE_DEFINE(WhiteNoiseTextureNode) { NodeType *type = NodeType::add("white_noise_texture", create, NodeType::SHADER); static NodeEnum dimensions_enum; dimensions_enum.insert("1D", 1); dimensions_enum.insert("2D", 2); dimensions_enum.insert("3D", 3); dimensions_enum.insert("4D", 4); SOCKET_ENUM(dimensions, "Dimensions", dimensions_enum, 3); SOCKET_IN_POINT(vector, "Vector", zero_float3()); SOCKET_IN_FLOAT(w, "W", 0.0f); SOCKET_OUT_FLOAT(value, "Value"); SOCKET_OUT_COLOR(color, "Color"); return type; } WhiteNoiseTextureNode::WhiteNoiseTextureNode() : ShaderNode(get_node_type()) {} void WhiteNoiseTextureNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *w_in = input("W"); ShaderOutput *value_out = output("Value"); ShaderOutput *color_out = output("Color"); const int vector_stack_offset = compiler.stack_assign(vector_in); const int w_stack_offset = compiler.stack_assign(w_in); const int value_stack_offset = compiler.stack_assign(value_out); const int color_stack_offset = compiler.stack_assign(color_out); compiler.add_node(NODE_TEX_WHITE_NOISE, dimensions, compiler.encode_uchar4(vector_stack_offset, w_stack_offset), compiler.encode_uchar4(value_stack_offset, color_stack_offset)); } void WhiteNoiseTextureNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "dimensions"); compiler.add(this, "node_white_noise_texture"); } /* Wave Texture */ NODE_DEFINE(WaveTextureNode) { NodeType *type = NodeType::add("wave_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(WaveTextureNode); static NodeEnum type_enum; type_enum.insert("bands", NODE_WAVE_BANDS); type_enum.insert("rings", NODE_WAVE_RINGS); SOCKET_ENUM(wave_type, "Type", type_enum, NODE_WAVE_BANDS); static NodeEnum bands_direction_enum; bands_direction_enum.insert("x", NODE_WAVE_BANDS_DIRECTION_X); bands_direction_enum.insert("y", NODE_WAVE_BANDS_DIRECTION_Y); bands_direction_enum.insert("z", NODE_WAVE_BANDS_DIRECTION_Z); bands_direction_enum.insert("diagonal", NODE_WAVE_BANDS_DIRECTION_DIAGONAL); SOCKET_ENUM( bands_direction, "Bands Direction", bands_direction_enum, NODE_WAVE_BANDS_DIRECTION_X); static NodeEnum rings_direction_enum; rings_direction_enum.insert("x", NODE_WAVE_RINGS_DIRECTION_X); rings_direction_enum.insert("y", NODE_WAVE_RINGS_DIRECTION_Y); rings_direction_enum.insert("z", NODE_WAVE_RINGS_DIRECTION_Z); rings_direction_enum.insert("spherical", NODE_WAVE_RINGS_DIRECTION_SPHERICAL); SOCKET_ENUM( rings_direction, "Rings Direction", rings_direction_enum, NODE_WAVE_BANDS_DIRECTION_X); static NodeEnum profile_enum; profile_enum.insert("sine", NODE_WAVE_PROFILE_SIN); profile_enum.insert("saw", NODE_WAVE_PROFILE_SAW); profile_enum.insert("tri", NODE_WAVE_PROFILE_TRI); SOCKET_ENUM(profile, "Profile", profile_enum, NODE_WAVE_PROFILE_SIN); SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_TEXTURE_GENERATED); SOCKET_IN_FLOAT(scale, "Scale", 1.0f); SOCKET_IN_FLOAT(distortion, "Distortion", 0.0f); SOCKET_IN_FLOAT(detail, "Detail", 2.0f); SOCKET_IN_FLOAT(detail_scale, "Detail Scale", 0.0f); SOCKET_IN_FLOAT(detail_roughness, "Detail Roughness", 0.5f); SOCKET_IN_FLOAT(phase, "Phase Offset", 0.0f); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } WaveTextureNode::WaveTextureNode() : TextureNode(get_node_type()) {} void WaveTextureNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *scale_in = input("Scale"); ShaderInput *distortion_in = input("Distortion"); ShaderInput *detail_in = input("Detail"); ShaderInput *dscale_in = input("Detail Scale"); ShaderInput *droughness_in = input("Detail Roughness"); ShaderInput *phase_in = input("Phase Offset"); ShaderOutput *color_out = output("Color"); ShaderOutput *fac_out = output("Fac"); const int vector_offset = tex_mapping.compile_begin(compiler, vector_in); const int scale_ofs = compiler.stack_assign_if_linked(scale_in); const int distortion_ofs = compiler.stack_assign_if_linked(distortion_in); const int detail_ofs = compiler.stack_assign_if_linked(detail_in); const int dscale_ofs = compiler.stack_assign_if_linked(dscale_in); const int droughness_ofs = compiler.stack_assign_if_linked(droughness_in); const int phase_ofs = compiler.stack_assign_if_linked(phase_in); const int color_ofs = compiler.stack_assign_if_linked(color_out); const int fac_ofs = compiler.stack_assign_if_linked(fac_out); compiler.add_node(NODE_TEX_WAVE, compiler.encode_uchar4(wave_type, bands_direction, rings_direction, profile), compiler.encode_uchar4(vector_offset, scale_ofs, distortion_ofs), compiler.encode_uchar4(detail_ofs, dscale_ofs, droughness_ofs, phase_ofs)); compiler.add_node(compiler.encode_uchar4(color_ofs, fac_ofs), __float_as_int(scale), __float_as_int(distortion), __float_as_int(detail)); compiler.add_node(__float_as_int(detail_scale), __float_as_int(detail_roughness), __float_as_int(phase), SVM_STACK_INVALID); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void WaveTextureNode::compile(OSLCompiler &compiler) { tex_mapping.compile(compiler); compiler.parameter(this, "wave_type"); compiler.parameter(this, "bands_direction"); compiler.parameter(this, "rings_direction"); compiler.parameter(this, "profile"); compiler.add(this, "node_wave_texture"); } /* Magic Texture */ NODE_DEFINE(MagicTextureNode) { NodeType *type = NodeType::add("magic_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(MagicTextureNode); SOCKET_INT(depth, "Depth", 2); SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_TEXTURE_GENERATED); SOCKET_IN_FLOAT(scale, "Scale", 5.0f); SOCKET_IN_FLOAT(distortion, "Distortion", 1.0f); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } MagicTextureNode::MagicTextureNode() : TextureNode(get_node_type()) {} void MagicTextureNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *scale_in = input("Scale"); ShaderInput *distortion_in = input("Distortion"); ShaderOutput *color_out = output("Color"); ShaderOutput *fac_out = output("Fac"); const int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_MAGIC, compiler.encode_uchar4(depth, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(fac_out)), compiler.encode_uchar4(vector_offset, compiler.stack_assign_if_linked(scale_in), compiler.stack_assign_if_linked(distortion_in))); compiler.add_node(__float_as_int(scale), __float_as_int(distortion)); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void MagicTextureNode::compile(OSLCompiler &compiler) { tex_mapping.compile(compiler); compiler.parameter(this, "depth"); compiler.add(this, "node_magic_texture"); } /* Checker Texture */ NODE_DEFINE(CheckerTextureNode) { NodeType *type = NodeType::add("checker_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(CheckerTextureNode); SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_TEXTURE_GENERATED); SOCKET_IN_COLOR(color1, "Color1", zero_float3()); SOCKET_IN_COLOR(color2, "Color2", zero_float3()); SOCKET_IN_FLOAT(scale, "Scale", 1.0f); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } CheckerTextureNode::CheckerTextureNode() : TextureNode(get_node_type()) {} void CheckerTextureNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *color1_in = input("Color1"); ShaderInput *color2_in = input("Color2"); ShaderInput *scale_in = input("Scale"); ShaderOutput *color_out = output("Color"); ShaderOutput *fac_out = output("Fac"); const int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_CHECKER, compiler.encode_uchar4(vector_offset, compiler.stack_assign(color1_in), compiler.stack_assign(color2_in), compiler.stack_assign_if_linked(scale_in)), compiler.encode_uchar4(compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(fac_out)), __float_as_int(scale)); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void CheckerTextureNode::compile(OSLCompiler &compiler) { tex_mapping.compile(compiler); compiler.add(this, "node_checker_texture"); } /* Brick Texture */ NODE_DEFINE(BrickTextureNode) { NodeType *type = NodeType::add("brick_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(BrickTextureNode); SOCKET_FLOAT(offset, "Offset", 0.5f); SOCKET_INT(offset_frequency, "Offset Frequency", 2); SOCKET_FLOAT(squash, "Squash", 1.0f); SOCKET_INT(squash_frequency, "Squash Frequency", 2); SOCKET_IN_POINT(vector, "Vector", zero_float3(), SocketType::LINK_TEXTURE_GENERATED); SOCKET_IN_COLOR(color1, "Color1", zero_float3()); SOCKET_IN_COLOR(color2, "Color2", zero_float3()); SOCKET_IN_COLOR(mortar, "Mortar", zero_float3()); SOCKET_IN_FLOAT(scale, "Scale", 5.0f); SOCKET_IN_FLOAT(mortar_size, "Mortar Size", 0.02f); SOCKET_IN_FLOAT(mortar_smooth, "Mortar Smooth", 0.0f); SOCKET_IN_FLOAT(bias, "Bias", 0.0f); SOCKET_IN_FLOAT(brick_width, "Brick Width", 0.5f); SOCKET_IN_FLOAT(row_height, "Row Height", 0.25f); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } BrickTextureNode::BrickTextureNode() : TextureNode(get_node_type()) {} void BrickTextureNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *color1_in = input("Color1"); ShaderInput *color2_in = input("Color2"); ShaderInput *mortar_in = input("Mortar"); ShaderInput *scale_in = input("Scale"); ShaderInput *mortar_size_in = input("Mortar Size"); ShaderInput *mortar_smooth_in = input("Mortar Smooth"); ShaderInput *bias_in = input("Bias"); ShaderInput *brick_width_in = input("Brick Width"); ShaderInput *row_height_in = input("Row Height"); ShaderOutput *color_out = output("Color"); ShaderOutput *fac_out = output("Fac"); const int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_BRICK, compiler.encode_uchar4(vector_offset, compiler.stack_assign(color1_in), compiler.stack_assign(color2_in), compiler.stack_assign(mortar_in)), compiler.encode_uchar4(compiler.stack_assign_if_linked(scale_in), compiler.stack_assign_if_linked(mortar_size_in), compiler.stack_assign_if_linked(bias_in), compiler.stack_assign_if_linked(brick_width_in)), compiler.encode_uchar4(compiler.stack_assign_if_linked(row_height_in), compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(fac_out), compiler.stack_assign_if_linked(mortar_smooth_in))); compiler.add_node(compiler.encode_uchar4(offset_frequency, squash_frequency), __float_as_int(scale), __float_as_int(mortar_size), __float_as_int(bias)); compiler.add_node(__float_as_int(brick_width), __float_as_int(row_height), __float_as_int(offset), __float_as_int(squash)); compiler.add_node( __float_as_int(mortar_smooth), SVM_STACK_INVALID, SVM_STACK_INVALID, SVM_STACK_INVALID); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void BrickTextureNode::compile(OSLCompiler &compiler) { tex_mapping.compile(compiler); compiler.parameter(this, "offset"); compiler.parameter(this, "offset_frequency"); compiler.parameter(this, "squash"); compiler.parameter(this, "squash_frequency"); compiler.add(this, "node_brick_texture"); } /* Normal */ NODE_DEFINE(NormalNode) { NodeType *type = NodeType::add("normal", create, NodeType::SHADER); SOCKET_VECTOR(direction, "direction", zero_float3()); SOCKET_IN_NORMAL(normal, "Normal", zero_float3()); SOCKET_OUT_NORMAL(normal, "Normal"); SOCKET_OUT_FLOAT(dot, "Dot"); return type; } NormalNode::NormalNode() : ShaderNode(get_node_type()) {} void NormalNode::compile(SVMCompiler &compiler) { ShaderInput *normal_in = input("Normal"); ShaderOutput *normal_out = output("Normal"); ShaderOutput *dot_out = output("Dot"); compiler.add_node(NODE_NORMAL, compiler.stack_assign(normal_in), compiler.stack_assign(normal_out), compiler.stack_assign(dot_out)); compiler.add_node( __float_as_int(direction.x), __float_as_int(direction.y), __float_as_int(direction.z)); } void NormalNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "direction"); compiler.add(this, "node_normal"); } /* Mapping */ NODE_DEFINE(MappingNode) { NodeType *type = NodeType::add("mapping", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("point", NODE_MAPPING_TYPE_POINT); type_enum.insert("texture", NODE_MAPPING_TYPE_TEXTURE); type_enum.insert("vector", NODE_MAPPING_TYPE_VECTOR); type_enum.insert("normal", NODE_MAPPING_TYPE_NORMAL); SOCKET_ENUM(mapping_type, "Type", type_enum, NODE_MAPPING_TYPE_POINT); SOCKET_IN_POINT(vector, "Vector", zero_float3()); SOCKET_IN_POINT(location, "Location", zero_float3()); SOCKET_IN_POINT(rotation, "Rotation", zero_float3()); SOCKET_IN_POINT(scale, "Scale", one_float3()); SOCKET_OUT_POINT(vector, "Vector"); return type; } MappingNode::MappingNode() : ShaderNode(get_node_type()) {} void MappingNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { const float3 result = svm_mapping(mapping_type, vector, location, rotation, scale); folder.make_constant(result); } else { folder.fold_mapping(mapping_type); } } void MappingNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *location_in = input("Location"); ShaderInput *rotation_in = input("Rotation"); ShaderInput *scale_in = input("Scale"); ShaderOutput *vector_out = output("Vector"); const int vector_stack_offset = compiler.stack_assign(vector_in); const int location_stack_offset = compiler.stack_assign(location_in); const int rotation_stack_offset = compiler.stack_assign(rotation_in); const int scale_stack_offset = compiler.stack_assign(scale_in); const int result_stack_offset = compiler.stack_assign(vector_out); compiler.add_node( NODE_MAPPING, mapping_type, compiler.encode_uchar4( vector_stack_offset, location_stack_offset, rotation_stack_offset, scale_stack_offset), result_stack_offset); } void MappingNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "mapping_type"); compiler.add(this, "node_mapping"); } /* RGBToBW */ NODE_DEFINE(RGBToBWNode) { NodeType *type = NodeType::add("rgb_to_bw", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", zero_float3()); SOCKET_OUT_FLOAT(val, "Val"); return type; } RGBToBWNode::RGBToBWNode() : ShaderNode(get_node_type()) {} void RGBToBWNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { const float val = folder.scene->shader_manager->linear_rgb_to_gray(color); folder.make_constant(val); } } void RGBToBWNode::compile(SVMCompiler &compiler) { compiler.add_node(NODE_CONVERT, NODE_CONVERT_CF, compiler.stack_assign(inputs[0]), compiler.stack_assign(outputs[0])); } void RGBToBWNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_rgb_to_bw"); } /* Convert */ const NodeType *ConvertNode::node_types[ConvertNode::MAX_TYPE][ConvertNode::MAX_TYPE]; bool ConvertNode::initialized = ConvertNode::register_types(); unique_ptr ConvertNode::create(const NodeType *type) { return make_unique(type->inputs[0].type, type->outputs[0].type); } bool ConvertNode::register_types() { const int num_types = 8; const SocketType::Type types[num_types] = {SocketType::FLOAT, SocketType::INT, SocketType::COLOR, SocketType::VECTOR, SocketType::POINT, SocketType::NORMAL, SocketType::STRING, SocketType::CLOSURE}; for (size_t i = 0; i < num_types; i++) { const SocketType::Type from = types[i]; const ustring from_name(SocketType::type_name(from)); const ustring from_value_name("value_" + from_name.string()); for (size_t j = 0; j < num_types; j++) { const SocketType::Type to = types[j]; const ustring to_name(SocketType::type_name(to)); const ustring to_value_name("value_" + to_name.string()); const string node_name = "convert_" + from_name.string() + "_to_" + to_name.string(); NodeType *type = NodeType::add(node_name.c_str(), create, NodeType::SHADER); type->register_input(from_value_name, from_value_name, from, SOCKET_OFFSETOF(ConvertNode, value_float), SocketType::zero_default_value(), nullptr, nullptr, SocketType::LINKABLE); type->register_output(to_value_name, to_value_name, to); assert(from < MAX_TYPE); assert(to < MAX_TYPE); node_types[from][to] = type; } } return true; } ConvertNode::ConvertNode(SocketType::Type from_, SocketType::Type to_, bool autoconvert) : ShaderNode(node_types[from_][to_]) { from = from_; to = to_; if (from == to) { special_type = SHADER_SPECIAL_TYPE_PROXY; } else if (autoconvert) { special_type = SHADER_SPECIAL_TYPE_AUTOCONVERT; } } /* Union usage requires a manual copy constructor. */ ConvertNode::ConvertNode(const ConvertNode &other) : ShaderNode(other), from(other.from), to(other.to), value_color(other.value_color), value_string(other.value_string) { } void ConvertNode::constant_fold(const ConstantFolder &folder) { /* proxy nodes should have been removed at this point */ assert(special_type != SHADER_SPECIAL_TYPE_PROXY); if (folder.all_inputs_constant()) { if (from == SocketType::FLOAT || from == SocketType::INT) { float val = value_float; if (from == SocketType::INT) { val = value_int; } if (SocketType::is_float3(to)) { folder.make_constant(make_float3(val, val, val)); } else if (to == SocketType::INT) { folder.make_constant((int)val); } else if (to == SocketType::FLOAT) { folder.make_constant(val); } } else if (SocketType::is_float3(from)) { if (to == SocketType::FLOAT || to == SocketType::INT) { float val; if (from == SocketType::COLOR) { /* color to scalar */ val = folder.scene->shader_manager->linear_rgb_to_gray(value_color); } else { /* vector/point/normal to scalar */ val = average(value_vector); } if (to == SocketType::INT) { folder.make_constant((int)val); } else if (to == SocketType::FLOAT) { folder.make_constant(val); } } else if (SocketType::is_float3(to)) { folder.make_constant(value_color); } } } else { ShaderInput *in = inputs[0]; ShaderNode *prev = in->link->parent; /* no-op conversion of A to B to A */ if (prev->type == node_types[to][from]) { ShaderInput *prev_in = prev->inputs[0]; if (SocketType::is_float3(from) && (to == SocketType::FLOAT || SocketType::is_float3(to)) && prev_in->link) { folder.bypass(prev_in->link); } } } } void ConvertNode::compile(SVMCompiler &compiler) { /* proxy nodes should have been removed at this point */ assert(special_type != SHADER_SPECIAL_TYPE_PROXY); ShaderInput *in = inputs[0]; ShaderOutput *out = outputs[0]; if (from == SocketType::FLOAT) { if (to == SocketType::INT) { /* float to int */ compiler.add_node( NODE_CONVERT, NODE_CONVERT_FI, compiler.stack_assign(in), compiler.stack_assign(out)); } else { /* float to float3 */ compiler.add_node( NODE_CONVERT, NODE_CONVERT_FV, compiler.stack_assign(in), compiler.stack_assign(out)); } } else if (from == SocketType::INT) { if (to == SocketType::FLOAT) { /* int to float */ compiler.add_node( NODE_CONVERT, NODE_CONVERT_IF, compiler.stack_assign(in), compiler.stack_assign(out)); } else { /* int to vector/point/normal */ compiler.add_node( NODE_CONVERT, NODE_CONVERT_IV, compiler.stack_assign(in), compiler.stack_assign(out)); } } else if (to == SocketType::FLOAT) { if (from == SocketType::COLOR) { /* color to float */ compiler.add_node( NODE_CONVERT, NODE_CONVERT_CF, compiler.stack_assign(in), compiler.stack_assign(out)); } else { /* vector/point/normal to float */ compiler.add_node( NODE_CONVERT, NODE_CONVERT_VF, compiler.stack_assign(in), compiler.stack_assign(out)); } } else if (to == SocketType::INT) { if (from == SocketType::COLOR) { /* color to int */ compiler.add_node( NODE_CONVERT, NODE_CONVERT_CI, compiler.stack_assign(in), compiler.stack_assign(out)); } else { /* vector/point/normal to int */ compiler.add_node( NODE_CONVERT, NODE_CONVERT_VI, compiler.stack_assign(in), compiler.stack_assign(out)); } } else { /* float3 to float3 */ if (in->link) { /* no op in SVM */ compiler.stack_link(in, out); } else { /* set 0,0,0 value */ compiler.add_node(NODE_VALUE_V, compiler.stack_assign(out)); compiler.add_node(NODE_VALUE_V, value_color); } } } void ConvertNode::compile(OSLCompiler &compiler) { /* proxy nodes should have been removed at this point */ assert(special_type != SHADER_SPECIAL_TYPE_PROXY); if (from == SocketType::FLOAT) { compiler.add(this, "node_convert_from_float"); } else if (from == SocketType::INT) { compiler.add(this, "node_convert_from_int"); } else if (from == SocketType::COLOR) { compiler.add(this, "node_convert_from_color"); } else if (from == SocketType::VECTOR) { compiler.add(this, "node_convert_from_vector"); } else if (from == SocketType::POINT) { compiler.add(this, "node_convert_from_point"); } else if (from == SocketType::NORMAL) { compiler.add(this, "node_convert_from_normal"); } else { assert(0); } } /* Base type for all closure-type nodes */ BsdfBaseNode::BsdfBaseNode(const NodeType *node_type) : ShaderNode(node_type) { special_type = SHADER_SPECIAL_TYPE_CLOSURE; } bool BsdfBaseNode::has_bump() { /* detect if anything is plugged into the normal input besides the default */ ShaderInput *normal_in = input("Normal"); return (normal_in && normal_in->link && normal_in->link->parent->special_type != SHADER_SPECIAL_TYPE_GEOMETRY); } /* BSDF Closure */ BsdfNode::BsdfNode(const NodeType *node_type) : BsdfBaseNode(node_type) {} void BsdfNode::compile(SVMCompiler &compiler, ShaderInput *bsdf_y, ShaderInput *bsdf_z, ShaderInput *data_y, ShaderInput *data_z, ShaderInput *data_w) { ShaderInput *color_in = input("Color"); ShaderInput *normal_in = input("Normal"); if (color_in->link) { compiler.add_node(NODE_CLOSURE_WEIGHT, compiler.stack_assign(color_in)); } else { compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color); } const int normal_offset = (normal_in) ? compiler.stack_assign_if_linked(normal_in) : SVM_STACK_INVALID; const int data_y_offset = (data_y) ? compiler.stack_assign(data_y) : SVM_STACK_INVALID; const int data_z_offset = (data_z) ? compiler.stack_assign(data_z) : SVM_STACK_INVALID; const int data_w_offset = (data_w) ? compiler.stack_assign(data_w) : SVM_STACK_INVALID; compiler.add_node(NODE_CLOSURE_BSDF, compiler.encode_uchar4( closure, (bsdf_y) ? compiler.stack_assign_if_linked(bsdf_y) : SVM_STACK_INVALID, (bsdf_z) ? compiler.stack_assign_if_linked(bsdf_z) : SVM_STACK_INVALID, compiler.closure_mix_weight_offset()), __float_as_int((bsdf_y) ? get_float(bsdf_y->socket_type) : 0.0f), __float_as_int((bsdf_z) ? get_float(bsdf_z->socket_type) : 0.0f)); compiler.add_node(normal_offset, data_y_offset, data_z_offset, data_w_offset); } void BsdfNode::compile(SVMCompiler &compiler) { compile(compiler, nullptr, nullptr); } void BsdfNode::compile(OSLCompiler & /*compiler*/) { assert(0); } /* Metallic BSDF Closure */ NODE_DEFINE(MetallicBsdfNode) { NodeType *type = NodeType::add("metallic_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Base Color", make_float3(0.617f, 0.577f, 0.540f)); SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum distribution_enum; distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_ID); distribution_enum.insert("ggx", CLOSURE_BSDF_MICROFACET_GGX_ID); distribution_enum.insert("multi_ggx", CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID); SOCKET_ENUM( distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID); static NodeEnum fresnel_type_enum; fresnel_type_enum.insert("f82", CLOSURE_BSDF_F82_CONDUCTOR); fresnel_type_enum.insert("physical_conductor", CLOSURE_BSDF_PHYSICAL_CONDUCTOR); SOCKET_ENUM(fresnel_type, "fresnel_type", fresnel_type_enum, CLOSURE_BSDF_F82_CONDUCTOR); SOCKET_IN_COLOR(edge_tint, "Edge Tint", make_float3(0.695f, 0.726f, 0.770f)); SOCKET_IN_VECTOR(ior, "IOR", make_float3(2.757f, 2.513f, 2.231f)); SOCKET_IN_VECTOR(k, "Extinction", make_float3(3.867f, 3.404f, 3.009f)); SOCKET_IN_VECTOR(tangent, "Tangent", zero_float3(), SocketType::LINK_TANGENT); SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f); SOCKET_IN_FLOAT(anisotropy, "Anisotropy", 0.0f); SOCKET_IN_FLOAT(rotation, "Rotation", 0.0f); SOCKET_IN_FLOAT(thin_film_thickness, "Thin Film Thickness", 0.0f); SOCKET_IN_FLOAT(thin_film_ior, "Thin Film IOR", 1.33f); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } MetallicBsdfNode::MetallicBsdfNode() : BsdfNode(get_node_type()) { closure = CLOSURE_BSDF_PHYSICAL_CONDUCTOR; } bool MetallicBsdfNode::is_isotropic() { ShaderInput *anisotropy_input = input("Anisotropy"); /* Keep in sync with the thresholds in OSL's node_conductor_bsdf and SVM's * svm_node_metallic_bsdf. */ return (!anisotropy_input->link && fabsf(anisotropy) <= 1e-4f); } void MetallicBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (shader->has_surface_link()) { ShaderInput *tangent_in = input("Tangent"); if (!tangent_in->link && !is_isotropic()) { attributes->add(ATTR_STD_GENERATED); } } ShaderNode::attributes(shader, attributes); } void MetallicBsdfNode::simplify_settings(Scene * /* scene */) { /* If the anisotropy is close enough to zero, fall back to the isotropic case. */ if (is_isotropic()) { disconnect_unused_input("Tangent"); } } void MetallicBsdfNode::compile(SVMCompiler &compiler) { const int base_color_ior_offset = fresnel_type == CLOSURE_BSDF_PHYSICAL_CONDUCTOR ? compiler.stack_assign(input("IOR")) : compiler.stack_assign(input("Base Color")); const int edge_tint_k_offset = fresnel_type == CLOSURE_BSDF_PHYSICAL_CONDUCTOR ? compiler.stack_assign(input("Extinction")) : compiler.stack_assign(input("Edge Tint")); const int thin_film_thickness_offset = compiler.stack_assign(input("Thin Film Thickness")); const int thin_film_ior_offset = compiler.stack_assign(input("Thin Film IOR")); ShaderInput *roughness_in = input("Roughness"); ShaderInput *anisotropy_in = input("Anisotropy"); const int normal_offset = compiler.stack_assign_if_linked(input("Normal")); const int tangent_offset = compiler.stack_assign_if_linked(input("Tangent")); const int rotation_offset = compiler.stack_assign(input("Rotation")); compiler.add_node(NODE_CLOSURE_BSDF, compiler.encode_uchar4(fresnel_type, compiler.stack_assign_if_linked(roughness_in), compiler.stack_assign_if_linked(anisotropy_in), compiler.closure_mix_weight_offset()), __float_as_int(get_float(roughness_in->socket_type)), __float_as_int(get_float(anisotropy_in->socket_type))); compiler.add_node( normal_offset, compiler.encode_uchar4( base_color_ior_offset, edge_tint_k_offset, rotation_offset, tangent_offset), compiler.encode_uchar4(distribution, thin_film_thickness_offset, thin_film_ior_offset)); } void MetallicBsdfNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "distribution"); compiler.parameter(this, "fresnel_type"); compiler.add(this, "node_metallic_bsdf"); } /* Glossy BSDF Closure */ NODE_DEFINE(GlossyBsdfNode) { NodeType *type = NodeType::add("glossy_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum distribution_enum; distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_ID); distribution_enum.insert("ggx", CLOSURE_BSDF_MICROFACET_GGX_ID); distribution_enum.insert("ashikhmin_shirley", CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID); distribution_enum.insert("multi_ggx", CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID); SOCKET_ENUM(distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_GGX_ID); SOCKET_IN_VECTOR(tangent, "Tangent", zero_float3(), SocketType::LINK_TANGENT); SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f); SOCKET_IN_FLOAT(anisotropy, "Anisotropy", 0.0f); SOCKET_IN_FLOAT(rotation, "Rotation", 0.0f); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } GlossyBsdfNode::GlossyBsdfNode() : BsdfNode(get_node_type()) { closure = CLOSURE_BSDF_MICROFACET_GGX_ID; } bool GlossyBsdfNode::is_isotropic() { ShaderInput *anisotropy_input = input("Anisotropy"); /* Keep in sync with the thresholds in OSL's node_glossy_bsdf and SVM's svm_node_closure_bsdf. */ return (!anisotropy_input->link && fabsf(anisotropy) <= 1e-4f); } void GlossyBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (shader->has_surface_link()) { ShaderInput *tangent_in = input("Tangent"); if (!tangent_in->link && !is_isotropic()) { attributes->add(ATTR_STD_GENERATED); } } ShaderNode::attributes(shader, attributes); } void GlossyBsdfNode::simplify_settings(Scene * /* scene */) { /* If the anisotropy is close enough to zero, fall back to the isotropic case. */ if (is_isotropic()) { disconnect_unused_input("Tangent"); } } void GlossyBsdfNode::compile(SVMCompiler &compiler) { closure = distribution; ShaderInput *tangent = input("Tangent"); tangent = compiler.is_linked(tangent) ? tangent : nullptr; /* TODO: Just use weight for legacy MultiGGX? Would also simplify OSL. */ if (closure == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID) { BsdfNode::compile(compiler, input("Roughness"), input("Anisotropy"), input("Rotation"), input("Color"), tangent); } else { BsdfNode::compile( compiler, input("Roughness"), input("Anisotropy"), input("Rotation"), nullptr, tangent); } } void GlossyBsdfNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "distribution"); compiler.add(this, "node_glossy_bsdf"); } /* Glass BSDF Closure */ NODE_DEFINE(GlassBsdfNode) { NodeType *type = NodeType::add("glass_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum distribution_enum; distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID); distribution_enum.insert("ggx", CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID); distribution_enum.insert("multi_ggx", CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID); SOCKET_ENUM( distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID); SOCKET_IN_FLOAT(roughness, "Roughness", 0.0f); SOCKET_IN_FLOAT(IOR, "IOR", 1.5f); SOCKET_IN_FLOAT(thin_film_thickness, "Thin Film Thickness", 0.0f); SOCKET_IN_FLOAT(thin_film_ior, "Thin Film IOR", 1.3f); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } GlassBsdfNode::GlassBsdfNode() : BsdfNode(get_node_type()) { closure = CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID; } void GlassBsdfNode::compile(SVMCompiler &compiler) { closure = distribution; BsdfNode::compile(compiler, input("Roughness"), input("IOR"), input("Color"), input("Thin Film Thickness"), input("Thin Film IOR")); } void GlassBsdfNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "distribution"); compiler.add(this, "node_glass_bsdf"); } /* Refraction BSDF Closure */ NODE_DEFINE(RefractionBsdfNode) { NodeType *type = NodeType::add("refraction_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum distribution_enum; distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID); distribution_enum.insert("ggx", CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID); SOCKET_ENUM( distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID); SOCKET_IN_FLOAT(roughness, "Roughness", 0.0f); SOCKET_IN_FLOAT(IOR, "IOR", 0.3f); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } RefractionBsdfNode::RefractionBsdfNode() : BsdfNode(get_node_type()) { closure = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; } void RefractionBsdfNode::compile(SVMCompiler &compiler) { closure = distribution; BsdfNode::compile(compiler, input("Roughness"), input("IOR")); } void RefractionBsdfNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "distribution"); compiler.add(this, "node_refraction_bsdf"); } /* Toon BSDF Closure */ NODE_DEFINE(ToonBsdfNode) { NodeType *type = NodeType::add("toon_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum component_enum; component_enum.insert("diffuse", CLOSURE_BSDF_DIFFUSE_TOON_ID); component_enum.insert("glossy", CLOSURE_BSDF_GLOSSY_TOON_ID); SOCKET_ENUM(component, "Component", component_enum, CLOSURE_BSDF_DIFFUSE_TOON_ID); SOCKET_IN_FLOAT(size, "Size", 0.5f); SOCKET_IN_FLOAT(smooth, "Smooth", 0.0f); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } ToonBsdfNode::ToonBsdfNode() : BsdfNode(get_node_type()) { closure = CLOSURE_BSDF_DIFFUSE_TOON_ID; } void ToonBsdfNode::compile(SVMCompiler &compiler) { closure = component; BsdfNode::compile(compiler, input("Size"), input("Smooth")); } void ToonBsdfNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "component"); compiler.add(this, "node_toon_bsdf"); } /* Sheen BSDF Closure */ NODE_DEFINE(SheenBsdfNode) { NodeType *type = NodeType::add("sheen_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_IN_FLOAT(roughness, "Roughness", 1.0f); static NodeEnum distribution_enum; distribution_enum.insert("ashikhmin", CLOSURE_BSDF_ASHIKHMIN_VELVET_ID); distribution_enum.insert("microfiber", CLOSURE_BSDF_SHEEN_ID); SOCKET_ENUM(distribution, "Distribution", distribution_enum, CLOSURE_BSDF_SHEEN_ID); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } SheenBsdfNode::SheenBsdfNode() : BsdfNode(get_node_type()) { closure = CLOSURE_BSDF_SHEEN_ID; } void SheenBsdfNode::compile(SVMCompiler &compiler) { closure = distribution; BsdfNode::compile(compiler, input("Roughness"), nullptr); } void SheenBsdfNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "distribution"); compiler.add(this, "node_sheen_bsdf"); } /* Diffuse BSDF Closure */ NODE_DEFINE(DiffuseBsdfNode) { NodeType *type = NodeType::add("diffuse_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_IN_FLOAT(roughness, "Roughness", 0.0f); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } DiffuseBsdfNode::DiffuseBsdfNode() : BsdfNode(get_node_type()) { closure = CLOSURE_BSDF_DIFFUSE_ID; } void DiffuseBsdfNode::compile(SVMCompiler &compiler) { BsdfNode::compile(compiler, input("Roughness"), nullptr, input("Color")); } void DiffuseBsdfNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_diffuse_bsdf"); } /* Disney principled BSDF Closure */ NODE_DEFINE(PrincipledBsdfNode) { NodeType *type = NodeType::add("principled_bsdf", create, NodeType::SHADER); static NodeEnum distribution_enum; distribution_enum.insert("ggx", CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID); distribution_enum.insert("multi_ggx", CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID); SOCKET_ENUM( distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID); static NodeEnum subsurface_method_enum; subsurface_method_enum.insert("burley", CLOSURE_BSSRDF_BURLEY_ID); subsurface_method_enum.insert("random_walk", CLOSURE_BSSRDF_RANDOM_WALK_ID); subsurface_method_enum.insert("random_walk_skin", CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID); SOCKET_ENUM(subsurface_method, "Subsurface Method", subsurface_method_enum, CLOSURE_BSSRDF_RANDOM_WALK_ID); SOCKET_IN_COLOR(base_color, "Base Color", make_float3(0.8f, 0.8f, 0.8f)) SOCKET_IN_FLOAT(metallic, "Metallic", 0.0f); SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f); SOCKET_IN_FLOAT(ior, "IOR", 1.5f); SOCKET_IN_FLOAT(alpha, "Alpha", 1.0f); SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(diffuse_roughness, "Diffuse Roughness", 0.0f); SOCKET_IN_FLOAT(subsurface_weight, "Subsurface Weight", 0.0f); SOCKET_IN_FLOAT(subsurface_scale, "Subsurface Scale", 0.1f); SOCKET_IN_VECTOR(subsurface_radius, "Subsurface Radius", make_float3(0.1f, 0.1f, 0.1f)); SOCKET_IN_FLOAT(subsurface_ior, "Subsurface IOR", 1.4f); SOCKET_IN_FLOAT(subsurface_anisotropy, "Subsurface Anisotropy", 0.0f); SOCKET_IN_FLOAT(specular_ior_level, "Specular IOR Level", 0.5f); SOCKET_IN_COLOR(specular_tint, "Specular Tint", one_float3()); SOCKET_IN_FLOAT(anisotropic, "Anisotropic", 0.0f); SOCKET_IN_FLOAT(anisotropic_rotation, "Anisotropic Rotation", 0.0f); SOCKET_IN_NORMAL(tangent, "Tangent", zero_float3(), SocketType::LINK_TANGENT); SOCKET_IN_FLOAT(transmission_weight, "Transmission Weight", 0.0f); SOCKET_IN_FLOAT(sheen_weight, "Sheen Weight", 0.0f); SOCKET_IN_FLOAT(sheen_roughness, "Sheen Roughness", 0.5f); SOCKET_IN_COLOR(sheen_tint, "Sheen Tint", one_float3()); SOCKET_IN_FLOAT(coat_weight, "Coat Weight", 0.0f); SOCKET_IN_FLOAT(coat_roughness, "Coat Roughness", 0.03f); SOCKET_IN_FLOAT(coat_ior, "Coat IOR", 1.5f); SOCKET_IN_COLOR(coat_tint, "Coat Tint", one_float3()); SOCKET_IN_NORMAL(coat_normal, "Coat Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_IN_COLOR(emission_color, "Emission Color", one_float3()); SOCKET_IN_FLOAT(emission_strength, "Emission Strength", 0.0f); SOCKET_IN_FLOAT(thin_film_thickness, "Thin Film Thickness", 0.0f); SOCKET_IN_FLOAT(thin_film_ior, "Thin Film IOR", 1.3f); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } PrincipledBsdfNode::PrincipledBsdfNode() : BsdfBaseNode(get_node_type()) { closure = CLOSURE_BSDF_PRINCIPLED_ID; distribution = CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID; } void PrincipledBsdfNode::simplify_settings(Scene * /* scene */) { if (!has_surface_emission()) { /* Emission will be zero, so optimize away any connected emission input. */ disconnect_unused_input("Emission Color"); disconnect_unused_input("Emission Strength"); } if (!has_surface_bssrdf()) { disconnect_unused_input("Subsurface Weight"); disconnect_unused_input("Subsurface Radius"); disconnect_unused_input("Subsurface Scale"); disconnect_unused_input("Subsurface IOR"); disconnect_unused_input("Subsurface Anisotropy"); } if (!has_nonzero_weight("Coat Weight")) { disconnect_unused_input("Coat Weight"); disconnect_unused_input("Coat IOR"); disconnect_unused_input("Coat Roughness"); disconnect_unused_input("Coat Tint"); } if (!has_nonzero_weight("Sheen Weight")) { disconnect_unused_input("Sheen Weight"); disconnect_unused_input("Sheen Roughness"); disconnect_unused_input("Sheen Tint"); } if (!has_nonzero_weight("Anisotropic")) { disconnect_unused_input("Anisotropic"); disconnect_unused_input("Anisotropic Rotation"); disconnect_unused_input("Tangent"); } if (!has_nonzero_weight("Thin Film Thickness")) { disconnect_unused_input("Thin Film Thickness"); disconnect_unused_input("Thin Film IOR"); } } bool PrincipledBsdfNode::has_surface_transparent() { ShaderInput *alpha_in = input("Alpha"); return (alpha_in->link != nullptr || alpha < (1.0f - CLOSURE_WEIGHT_CUTOFF)); } bool PrincipledBsdfNode::has_surface_emission() { ShaderInput *emission_color_in = input("Emission Color"); ShaderInput *emission_strength_in = input("Emission Strength"); return (emission_color_in->link != nullptr || reduce_max(emission_color) > CLOSURE_WEIGHT_CUTOFF) && (emission_strength_in->link != nullptr || emission_strength > CLOSURE_WEIGHT_CUTOFF); } bool PrincipledBsdfNode::has_surface_bssrdf() { ShaderInput *subsurface_weight_in = input("Subsurface Weight"); ShaderInput *subsurface_scale_in = input("Subsurface Scale"); return (subsurface_weight_in->link != nullptr || subsurface_weight > CLOSURE_WEIGHT_CUTOFF) && (subsurface_scale_in->link != nullptr || subsurface_scale != 0.0f); } bool PrincipledBsdfNode::has_nonzero_weight(const char *name) { ShaderInput *weight_in = input(name); if (weight_in == nullptr) { return true; } if (weight_in->link != nullptr) { return true; } return (get_float(weight_in->socket_type) >= CLOSURE_WEIGHT_CUTOFF); } void PrincipledBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (shader->has_surface_link()) { ShaderInput *tangent_in = input("Tangent"); if (!tangent_in->link) { attributes->add(ATTR_STD_GENERATED); } } ShaderNode::attributes(shader, attributes); } void PrincipledBsdfNode::compile(SVMCompiler &compiler) { /* Allocate basic material inputs. */ const int base_color_offset = compiler.stack_assign_if_linked(input("Base Color")); const int ior_offset = compiler.stack_assign_if_linked(input("IOR")); const int roughness_offset = compiler.stack_assign_if_linked(input("Roughness")); const int metallic_offset = compiler.stack_assign_if_not_equal(input("Metallic"), 0.0f); /* Allocate miscellaneous inputs. */ const int alpha_offset = compiler.stack_assign_if_not_equal(input("Alpha"), 1.0f); const int normal_offset = compiler.stack_assign_if_linked(input("Normal")); const int coat_normal_offset = compiler.stack_assign_if_linked(input("Coat Normal")); const int transmission_weight_offset = compiler.stack_assign_if_not_equal( input("Transmission Weight"), 0.0f); const int diffuse_roughness_offset = compiler.stack_assign_if_not_equal( input("Diffuse Roughness"), 0.0f); const int specular_ior_level_offset = compiler.stack_assign_if_not_equal( input("Specular IOR Level"), 0.5f); const int specular_tint_offset = compiler.stack_assign_if_not_equal(input("Specular Tint"), one_float3()); /* Allocate emission inputs, if enabled. */ int emission_strength_offset = SVM_STACK_INVALID; int emission_color_offset = SVM_STACK_INVALID; if (has_surface_emission()) { emission_strength_offset = compiler.stack_assign(input("Emission Strength")); emission_color_offset = compiler.stack_assign(input("Emission Color")); } /* Allocate subsurface inputs, if enabled. */ int subsurface_weight_offset = SVM_STACK_INVALID; int subsurface_radius_offset = SVM_STACK_INVALID; int subsurface_scale_offset = SVM_STACK_INVALID; int subsurface_ior_offset = SVM_STACK_INVALID; int subsurface_anisotropy_offset = SVM_STACK_INVALID; if (has_surface_bssrdf()) { subsurface_weight_offset = compiler.stack_assign(input("Subsurface Weight")); subsurface_radius_offset = compiler.stack_assign(input("Subsurface Radius")); subsurface_scale_offset = compiler.stack_assign(input("Subsurface Scale")); subsurface_ior_offset = compiler.stack_assign_if_not_equal(input("Subsurface IOR"), 1.4f); subsurface_anisotropy_offset = compiler.stack_assign_if_not_equal( input("Subsurface Anisotropy"), 0.0f); } /* Allocate coat inputs, if enabled. */ int coat_weight_offset = SVM_STACK_INVALID; int coat_roughness_offset = SVM_STACK_INVALID; int coat_ior_offset = SVM_STACK_INVALID; int coat_tint_offset = SVM_STACK_INVALID; if (has_nonzero_weight("Coat Weight")) { coat_weight_offset = compiler.stack_assign(input("Coat Weight")); coat_roughness_offset = compiler.stack_assign(input("Coat Roughness")); coat_ior_offset = compiler.stack_assign(input("Coat IOR")); coat_tint_offset = compiler.stack_assign_if_not_equal(input("Coat Tint"), one_float3()); } /* Allocate sheen inputs, if enabled. */ int sheen_weight_offset = SVM_STACK_INVALID; int sheen_roughness_offset = SVM_STACK_INVALID; int sheen_tint_offset = SVM_STACK_INVALID; if (has_nonzero_weight("Sheen Weight")) { sheen_weight_offset = compiler.stack_assign(input("Sheen Weight")); sheen_roughness_offset = compiler.stack_assign(input("Sheen Roughness")); sheen_tint_offset = compiler.stack_assign_if_not_equal(input("Sheen Tint"), one_float3()); } /* Allocate anisotropy inputs, if enabled. */ int anisotropic_offset = SVM_STACK_INVALID; int anisotropic_rotation_offset = SVM_STACK_INVALID; int tangent_offset = SVM_STACK_INVALID; if (has_nonzero_weight("Anisotropic")) { anisotropic_offset = compiler.stack_assign(input("Anisotropic")); anisotropic_rotation_offset = compiler.stack_assign_if_not_equal(input("Anisotropic Rotation"), 0.0f); tangent_offset = compiler.stack_assign_if_linked(input("Tangent")); } /* Allocate thin film inputs, if enabled. */ int thin_film_thickness_offset = SVM_STACK_INVALID; int thin_film_ior_offset = SVM_STACK_INVALID; if (has_nonzero_weight("Thin Film Thickness")) { thin_film_thickness_offset = compiler.stack_assign(input("Thin Film Thickness")); thin_film_ior_offset = compiler.stack_assign(input("Thin Film IOR")); } compiler.add_node( NODE_CLOSURE_BSDF, compiler.encode_uchar4( closure, ior_offset, roughness_offset, compiler.closure_mix_weight_offset()), __float_as_int(get_float(input("IOR")->socket_type)), __float_as_int(get_float(input("Roughness")->socket_type))); compiler.add_node( normal_offset, compiler.encode_uchar4(base_color_offset, metallic_offset, alpha_offset, coat_normal_offset), compiler.encode_uchar4( distribution, diffuse_roughness_offset, specular_ior_level_offset, specular_tint_offset), compiler.encode_uchar4(emission_strength_offset, emission_color_offset, anisotropic_offset, thin_film_thickness_offset)); compiler.add_node( compiler.encode_uchar4(subsurface_weight_offset, coat_weight_offset, sheen_weight_offset, transmission_weight_offset), compiler.encode_uchar4( coat_roughness_offset, coat_ior_offset, coat_tint_offset, subsurface_method), compiler.encode_uchar4(subsurface_radius_offset, subsurface_scale_offset, subsurface_ior_offset, subsurface_anisotropy_offset), compiler.encode_uchar4( sheen_roughness_offset, sheen_tint_offset, anisotropic_rotation_offset, tangent_offset)); const float3 base_color = get_float3(input("Base Color")->socket_type); compiler.add_node(thin_film_ior_offset, __float_as_int(base_color.x), __float_as_int(base_color.y), __float_as_int(base_color.z)); } void PrincipledBsdfNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "distribution"); compiler.parameter(this, "subsurface_method"); compiler.add(this, "node_principled_bsdf"); } bool PrincipledBsdfNode::has_bssrdf_bump() { return has_surface_bssrdf() && has_bump(); } /* Translucent BSDF Closure */ NODE_DEFINE(TranslucentBsdfNode) { NodeType *type = NodeType::add("translucent_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } TranslucentBsdfNode::TranslucentBsdfNode() : BsdfNode(get_node_type()) { closure = CLOSURE_BSDF_TRANSLUCENT_ID; } void TranslucentBsdfNode::compile(SVMCompiler &compiler) { BsdfNode::compile(compiler, nullptr, nullptr); } void TranslucentBsdfNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_translucent_bsdf"); } /* Transparent BSDF Closure */ NODE_DEFINE(TransparentBsdfNode) { NodeType *type = NodeType::add("transparent_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", one_float3()); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } TransparentBsdfNode::TransparentBsdfNode() : BsdfNode(get_node_type()) { closure = CLOSURE_BSDF_TRANSPARENT_ID; } void TransparentBsdfNode::compile(SVMCompiler &compiler) { BsdfNode::compile(compiler, nullptr, nullptr); } void TransparentBsdfNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_transparent_bsdf"); } /* Ray Portal BSDF Closure */ NODE_DEFINE(RayPortalBsdfNode) { NodeType *type = NodeType::add("ray_portal_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", one_float3()); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_IN_VECTOR(position, "Position", zero_float3(), SocketType::LINK_POSITION); SOCKET_IN_VECTOR(direction, "Direction", zero_float3()); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } RayPortalBsdfNode::RayPortalBsdfNode() : BsdfNode(get_node_type()) { closure = CLOSURE_BSDF_RAY_PORTAL_ID; } void RayPortalBsdfNode::compile(SVMCompiler &compiler) { BsdfNode::compile(compiler, nullptr, nullptr, input("Position"), input("Direction")); } void RayPortalBsdfNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_ray_portal_bsdf"); } /* Subsurface Scattering Closure */ NODE_DEFINE(SubsurfaceScatteringNode) { NodeType *type = NodeType::add("subsurface_scattering", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum method_enum; method_enum.insert("burley", CLOSURE_BSSRDF_BURLEY_ID); method_enum.insert("random_walk", CLOSURE_BSSRDF_RANDOM_WALK_ID); method_enum.insert("random_walk_skin", CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID); SOCKET_ENUM(method, "Method", method_enum, CLOSURE_BSSRDF_RANDOM_WALK_ID); SOCKET_IN_FLOAT(scale, "Scale", 0.01f); SOCKET_IN_VECTOR(radius, "Radius", make_float3(0.1f, 0.1f, 0.1f)); SOCKET_IN_FLOAT(subsurface_ior, "IOR", 1.4f); SOCKET_IN_FLOAT(subsurface_roughness, "Roughness", 1.0f); SOCKET_IN_FLOAT(subsurface_anisotropy, "Anisotropy", 0.0f); SOCKET_OUT_CLOSURE(BSSRDF, "BSSRDF"); return type; } SubsurfaceScatteringNode::SubsurfaceScatteringNode() : BsdfNode(get_node_type()) { closure = method; } void SubsurfaceScatteringNode::compile(SVMCompiler &compiler) { closure = method; BsdfNode::compile(compiler, input("Scale"), input("IOR"), input("Radius"), input("Anisotropy"), input("Roughness")); } void SubsurfaceScatteringNode::compile(OSLCompiler &compiler) { closure = method; compiler.parameter(this, "method"); compiler.add(this, "node_subsurface_scattering"); } bool SubsurfaceScatteringNode::has_bssrdf_bump() { /* detect if anything is plugged into the normal input besides the default */ ShaderInput *normal_in = input("Normal"); return (normal_in->link && normal_in->link->parent->special_type != SHADER_SPECIAL_TYPE_GEOMETRY); } /* Emissive Closure */ NODE_DEFINE(EmissionNode) { NodeType *type = NodeType::add("emission", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_FLOAT(strength, "Strength", 10.0f); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_IN_FLOAT(volume_mix_weight, "VolumeMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(emission, "Emission"); return type; } EmissionNode::EmissionNode() : ShaderNode(get_node_type()) {} void EmissionNode::compile(SVMCompiler &compiler) { ShaderInput *color_in = input("Color"); ShaderInput *strength_in = input("Strength"); const int strength_offset = compiler.stack_assign_if_linked(strength_in); if (color_in->link || strength_in->link) { compiler.add_node(NODE_EMISSION_WEIGHT, compiler.stack_assign(color_in), strength_offset, __float_as_int(get_float(strength_in->socket_type))); } else { compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color * strength); } compiler.add_node(NODE_CLOSURE_EMISSION, compiler.closure_mix_weight_offset()); } void EmissionNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_emission"); } void EmissionNode::constant_fold(const ConstantFolder &folder) { ShaderInput *color_in = input("Color"); ShaderInput *strength_in = input("Strength"); if ((!color_in->link && color == zero_float3()) || (!strength_in->link && strength == 0.0f)) { folder.discard(); } } /* Background Closure */ NODE_DEFINE(BackgroundNode) { NodeType *type = NodeType::add("background_shader", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_FLOAT(strength, "Strength", 1.0f); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(background, "Background"); return type; } BackgroundNode::BackgroundNode() : ShaderNode(get_node_type()) {} void BackgroundNode::compile(SVMCompiler &compiler) { ShaderInput *color_in = input("Color"); ShaderInput *strength_in = input("Strength"); const int strength_offset = compiler.stack_assign_if_linked(strength_in); if (color_in->link || strength_in->link) { compiler.add_node(NODE_EMISSION_WEIGHT, compiler.stack_assign(color_in), strength_offset, __float_as_int(get_float(strength_in->socket_type))); } else { compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color * strength); } compiler.add_node(NODE_CLOSURE_BACKGROUND, compiler.closure_mix_weight_offset()); } void BackgroundNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_background"); } void BackgroundNode::constant_fold(const ConstantFolder &folder) { ShaderInput *color_in = input("Color"); ShaderInput *strength_in = input("Strength"); if ((!color_in->link && color == zero_float3()) || (!strength_in->link && strength == 0.0f)) { folder.discard(); } } /* Holdout Closure */ NODE_DEFINE(HoldoutNode) { NodeType *type = NodeType::add("holdout", create, NodeType::SHADER); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_IN_FLOAT(volume_mix_weight, "VolumeMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(holdout, "Holdout"); return type; } HoldoutNode::HoldoutNode() : ShaderNode(get_node_type()) {} void HoldoutNode::compile(SVMCompiler &compiler) { const float3 value = one_float3(); compiler.add_node(NODE_CLOSURE_SET_WEIGHT, value); compiler.add_node(NODE_CLOSURE_HOLDOUT, compiler.closure_mix_weight_offset()); } void HoldoutNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_holdout"); } /* Ambient Occlusion */ NODE_DEFINE(AmbientOcclusionNode) { NodeType *type = NodeType::add("ambient_occlusion", create, NodeType::SHADER); SOCKET_INT(samples, "Samples", 16); SOCKET_IN_COLOR(color, "Color", one_float3()); SOCKET_IN_FLOAT(distance, "Distance", 1.0f); SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_BOOLEAN(inside, "Inside", false); SOCKET_BOOLEAN(only_local, "Only Local", false); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(ao, "AO"); return type; } AmbientOcclusionNode::AmbientOcclusionNode() : ShaderNode(get_node_type()) {} void AmbientOcclusionNode::compile(SVMCompiler &compiler) { ShaderInput *color_in = input("Color"); ShaderInput *distance_in = input("Distance"); ShaderInput *normal_in = input("Normal"); ShaderOutput *color_out = output("Color"); ShaderOutput *ao_out = output("AO"); int flags = (inside ? NODE_AO_INSIDE : 0) | (only_local ? NODE_AO_ONLY_LOCAL : 0); if (!distance_in->link && distance == 0.0f) { flags |= NODE_AO_GLOBAL_RADIUS; } compiler.add_node(NODE_AMBIENT_OCCLUSION, compiler.encode_uchar4(flags, compiler.stack_assign_if_linked(distance_in), compiler.stack_assign_if_linked(normal_in), compiler.stack_assign(ao_out)), compiler.encode_uchar4(compiler.stack_assign(color_in), compiler.stack_assign(color_out), samples), __float_as_uint(distance)); } void AmbientOcclusionNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "samples"); compiler.parameter(this, "inside"); compiler.parameter(this, "only_local"); compiler.add(this, "node_ambient_occlusion"); } /* Volume Closure */ VolumeNode::VolumeNode(const NodeType *node_type) : ShaderNode(node_type) { closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID; } void VolumeNode::compile(SVMCompiler &compiler, ShaderInput *density, ShaderInput *param1, ShaderInput *param2) { ShaderInput *color_in = input("Color"); if (color_in->link) { compiler.add_node(NODE_CLOSURE_WEIGHT, compiler.stack_assign(color_in)); } else { compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color); } /* Density and mix weight need to be stored the same way for all volume closures since there's * a shortcut code path if we only need the extinction value. */ const uint density_ofs = (density) ? compiler.stack_assign_if_linked(density) : SVM_STACK_INVALID; const uint mix_weight_ofs = compiler.closure_mix_weight_offset(); if (param2 == nullptr) { /* More efficient packing if we don't need the second parameter. */ const uint param1_ofs = (param1) ? compiler.stack_assign_if_linked(param1) : SVM_STACK_INVALID; compiler.add_node(NODE_CLOSURE_VOLUME, compiler.encode_uchar4(closure, density_ofs, param1_ofs, mix_weight_ofs), __float_as_int((density) ? get_float(density->socket_type) : 0.0f), __float_as_int((param1) ? get_float(param1->socket_type) : 0.0f)); } else { const uint param1_ofs = (param1) ? compiler.stack_assign(param1) : SVM_STACK_INVALID; const uint param2_ofs = (param2) ? compiler.stack_assign(param2) : SVM_STACK_INVALID; compiler.add_node(NODE_CLOSURE_VOLUME, compiler.encode_uchar4(closure, density_ofs, param1_ofs, mix_weight_ofs), __float_as_int((density) ? get_float(density->socket_type) : 0.0f), param2_ofs); } } void VolumeNode::compile(SVMCompiler &compiler) { compile(compiler, nullptr, nullptr, nullptr); } void VolumeNode::compile(OSLCompiler & /*compiler*/) { assert(0); } /* Absorption Volume Closure */ NODE_DEFINE(AbsorptionVolumeNode) { NodeType *type = NodeType::add("absorption_volume", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_FLOAT(density, "Density", 1.0f); SOCKET_IN_FLOAT(volume_mix_weight, "VolumeMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(volume, "Volume"); return type; } AbsorptionVolumeNode::AbsorptionVolumeNode() : VolumeNode(get_node_type()) { closure = CLOSURE_VOLUME_ABSORPTION_ID; } void AbsorptionVolumeNode::compile(SVMCompiler &compiler) { VolumeNode::compile(compiler, input("Density")); } void AbsorptionVolumeNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_absorption_volume"); } /* Scatter Volume Closure */ NODE_DEFINE(ScatterVolumeNode) { NodeType *type = NodeType::add("scatter_volume", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_FLOAT(density, "Density", 1.0f); SOCKET_IN_FLOAT(anisotropy, "Anisotropy", 0.0f); SOCKET_IN_FLOAT(IOR, "IOR", 1.33f); SOCKET_IN_FLOAT(backscatter, "Backscatter", 0.1f); SOCKET_IN_FLOAT(alpha, "Alpha", 0.5f); SOCKET_IN_FLOAT(diameter, "Diameter", 20.0f); static NodeEnum phase_enum; phase_enum.insert("Henyey-Greenstein", CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID); phase_enum.insert("Fournier-Forand", CLOSURE_VOLUME_FOURNIER_FORAND_ID); phase_enum.insert("Draine", CLOSURE_VOLUME_DRAINE_ID); phase_enum.insert("Rayleigh", CLOSURE_VOLUME_RAYLEIGH_ID); phase_enum.insert("Mie", CLOSURE_VOLUME_MIE_ID); SOCKET_ENUM(phase, "Phase", phase_enum, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID); SOCKET_IN_FLOAT(volume_mix_weight, "VolumeMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(volume, "Volume"); return type; } ScatterVolumeNode::ScatterVolumeNode(const NodeType *node_type) : VolumeNode(node_type) { closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID; } ScatterVolumeNode::ScatterVolumeNode() : VolumeNode(get_node_type()) { closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID; } void ScatterVolumeNode::compile(SVMCompiler &compiler) { closure = phase; switch (phase) { case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: VolumeNode::compile(compiler, input("Density"), input("Anisotropy")); break; case CLOSURE_VOLUME_FOURNIER_FORAND_ID: VolumeNode::compile(compiler, input("Density"), input("IOR"), input("Backscatter")); break; case CLOSURE_VOLUME_RAYLEIGH_ID: VolumeNode::compile(compiler, input("Density")); break; case CLOSURE_VOLUME_DRAINE_ID: VolumeNode::compile(compiler, input("Density"), input("Anisotropy"), input("Alpha")); break; case CLOSURE_VOLUME_MIE_ID: VolumeNode::compile(compiler, input("Density"), input("Diameter")); break; default: assert(false); break; } } void ScatterVolumeNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "phase"); compiler.add(this, "node_scatter_volume"); } /* Volume Coefficients Closure */ NODE_DEFINE(VolumeCoefficientsNode) { NodeType *type = NodeType::add("volume_coefficients", create, NodeType::SHADER); SOCKET_IN_VECTOR(scatter_coeffs, "Scatter Coefficients", make_float3(1.0f, 1.0f, 1.0f)); SOCKET_IN_VECTOR(absorption_coeffs, "Absorption Coefficients", make_float3(1.0f, 1.0f, 1.0f)); SOCKET_IN_FLOAT(anisotropy, "Anisotropy", 0.0f); SOCKET_IN_FLOAT(IOR, "IOR", 1.33f); SOCKET_IN_FLOAT(backscatter, "Backscatter", 0.1f); SOCKET_IN_FLOAT(alpha, "Alpha", 0.5f); SOCKET_IN_FLOAT(diameter, "Diameter", 20.0f); SOCKET_IN_VECTOR(emission_coeffs, "Emission Coefficients", make_float3(0.0f, 0.0f, 0.0f)); static NodeEnum phase_enum; phase_enum.insert("Henyey-Greenstein", CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID); phase_enum.insert("Fournier-Forand", CLOSURE_VOLUME_FOURNIER_FORAND_ID); phase_enum.insert("Draine", CLOSURE_VOLUME_DRAINE_ID); phase_enum.insert("Rayleigh", CLOSURE_VOLUME_RAYLEIGH_ID); phase_enum.insert("Mie", CLOSURE_VOLUME_MIE_ID); SOCKET_ENUM(phase, "Phase", phase_enum, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID); SOCKET_IN_FLOAT(volume_mix_weight, "VolumeMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(volume, "Volume"); return type; } VolumeCoefficientsNode::VolumeCoefficientsNode() : ScatterVolumeNode(get_node_type()) { closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID; } void VolumeCoefficientsNode::compile(SVMCompiler &compiler) { closure = phase; ShaderInput *param1 = nullptr; ShaderInput *param2 = nullptr; switch (phase) { case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: param1 = input("Anisotropy"); break; case CLOSURE_VOLUME_FOURNIER_FORAND_ID: param1 = input("IOR"); param2 = input("Backscatter"); break; case CLOSURE_VOLUME_RAYLEIGH_ID: break; case CLOSURE_VOLUME_DRAINE_ID: param1 = input("Anisotropy"); param2 = input("Alpha"); break; case CLOSURE_VOLUME_MIE_ID: param1 = input("Diameter"); break; default: assert(false); break; } ShaderInput *coeffs_in = input("Scatter Coefficients"); ShaderInput *absorption_coeffs_in = input("Absorption Coefficients"); ShaderInput *emission_coeffs_in = input("Emission Coefficients"); if (coeffs_in->link) { compiler.add_node(NODE_CLOSURE_WEIGHT, compiler.stack_assign(coeffs_in)); } else { compiler.add_node(NODE_CLOSURE_SET_WEIGHT, scatter_coeffs); } const uint mix_weight_ofs = compiler.closure_mix_weight_offset(); if (param2 == nullptr) { /* More efficient packing if we don't need the second parameter. */ const uint param1_ofs = (param1) ? compiler.stack_assign_if_linked(param1) : SVM_STACK_INVALID; compiler.add_node(NODE_VOLUME_COEFFICIENTS, compiler.encode_uchar4(closure, 0, param1_ofs, mix_weight_ofs), __float_as_int((param1) ? get_float(param1->socket_type) : 0.0f), compiler.encode_uchar4(compiler.stack_assign(absorption_coeffs_in), compiler.stack_assign(emission_coeffs_in), 0, 0)); } else { const uint param1_ofs = (param1) ? compiler.stack_assign(param1) : SVM_STACK_INVALID; const uint param2_ofs = (param2) ? compiler.stack_assign(param2) : SVM_STACK_INVALID; compiler.add_node(NODE_VOLUME_COEFFICIENTS, compiler.encode_uchar4(closure, 0, param1_ofs, mix_weight_ofs), param2_ofs, compiler.encode_uchar4(compiler.stack_assign(absorption_coeffs_in), compiler.stack_assign(emission_coeffs_in), 0, 0)); } } void VolumeCoefficientsNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "phase"); compiler.add(this, "node_volume_coefficients"); } /* Principled Volume Closure */ NODE_DEFINE(PrincipledVolumeNode) { NodeType *type = NodeType::add("principled_volume", create, NodeType::SHADER); SOCKET_IN_STRING(density_attribute, "Density Attribute", ustring()); SOCKET_IN_STRING(color_attribute, "Color Attribute", ustring()); SOCKET_IN_STRING(temperature_attribute, "Temperature Attribute", ustring()); SOCKET_IN_COLOR(color, "Color", make_float3(0.5f, 0.5f, 0.5f)); SOCKET_IN_FLOAT(density, "Density", 1.0f); SOCKET_IN_FLOAT(anisotropy, "Anisotropy", 0.0f); SOCKET_IN_COLOR(absorption_color, "Absorption Color", zero_float3()); SOCKET_IN_FLOAT(emission_strength, "Emission Strength", 0.0f); SOCKET_IN_COLOR(emission_color, "Emission Color", one_float3()); SOCKET_IN_FLOAT(blackbody_intensity, "Blackbody Intensity", 0.0f); SOCKET_IN_COLOR(blackbody_tint, "Blackbody Tint", one_float3()); SOCKET_IN_FLOAT(temperature, "Temperature", 1000.0f); SOCKET_IN_FLOAT(volume_mix_weight, "VolumeMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(volume, "Volume"); return type; } PrincipledVolumeNode::PrincipledVolumeNode() : VolumeNode(get_node_type()) { closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID; density_attribute = ustring("density"); temperature_attribute = ustring("temperature"); } void PrincipledVolumeNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (shader->has_volume) { ShaderInput *density_in = input("Density"); ShaderInput *blackbody_in = input("Blackbody Intensity"); if (density_in->link || density > 0.0f) { attributes->add_standard(density_attribute); attributes->add_standard(color_attribute); } if (blackbody_in->link || blackbody_intensity > 0.0f) { attributes->add_standard(temperature_attribute); } attributes->add(ATTR_STD_GENERATED_TRANSFORM); } ShaderNode::attributes(shader, attributes); } void PrincipledVolumeNode::compile(SVMCompiler &compiler) { ShaderInput *color_in = input("Color"); ShaderInput *density_in = input("Density"); ShaderInput *anisotropy_in = input("Anisotropy"); ShaderInput *absorption_color_in = input("Absorption Color"); ShaderInput *emission_in = input("Emission Strength"); ShaderInput *emission_color_in = input("Emission Color"); ShaderInput *blackbody_in = input("Blackbody Intensity"); ShaderInput *blackbody_tint_in = input("Blackbody Tint"); ShaderInput *temperature_in = input("Temperature"); if (color_in->link) { compiler.add_node(NODE_CLOSURE_WEIGHT, compiler.stack_assign(color_in)); } else { compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color); } compiler.add_node(NODE_PRINCIPLED_VOLUME, compiler.encode_uchar4(compiler.stack_assign_if_linked(density_in), compiler.stack_assign_if_linked(anisotropy_in), compiler.stack_assign(absorption_color_in), compiler.closure_mix_weight_offset()), compiler.encode_uchar4(compiler.stack_assign_if_linked(emission_in), compiler.stack_assign(emission_color_in), compiler.stack_assign_if_linked(blackbody_in), compiler.stack_assign(temperature_in)), compiler.stack_assign(blackbody_tint_in)); const int attr_density = compiler.attribute_standard(density_attribute); const int attr_color = compiler.attribute_standard(color_attribute); const int attr_temperature = compiler.attribute_standard(temperature_attribute); compiler.add_node(__float_as_int(density), __float_as_int(anisotropy), __float_as_int(emission_strength), __float_as_int(blackbody_intensity)); compiler.add_node(attr_density, attr_color, attr_temperature); } void PrincipledVolumeNode::compile(OSLCompiler &compiler) { if (Attribute::name_standard(density_attribute.c_str())) { density_attribute = ustring("geom:" + density_attribute.string()); } if (Attribute::name_standard(color_attribute.c_str())) { color_attribute = ustring("geom:" + color_attribute.string()); } if (Attribute::name_standard(temperature_attribute.c_str())) { temperature_attribute = ustring("geom:" + temperature_attribute.string()); } compiler.add(this, "node_principled_volume"); } /* Principled Hair BSDF Closure */ NODE_DEFINE(PrincipledHairBsdfNode) { NodeType *type = NodeType::add("principled_hair_bsdf", create, NodeType::SHADER); /* Scattering models. */ static NodeEnum model_enum; model_enum.insert("Chiang", NODE_PRINCIPLED_HAIR_CHIANG); model_enum.insert("Huang", NODE_PRINCIPLED_HAIR_HUANG); SOCKET_ENUM(model, "Model", model_enum, NODE_PRINCIPLED_HAIR_HUANG); /* Color parametrization specified as enum. */ static NodeEnum parametrization_enum; parametrization_enum.insert("Direct coloring", NODE_PRINCIPLED_HAIR_REFLECTANCE); parametrization_enum.insert("Melanin concentration", NODE_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION); parametrization_enum.insert("Absorption coefficient", NODE_PRINCIPLED_HAIR_DIRECT_ABSORPTION); SOCKET_ENUM( parametrization, "Parametrization", parametrization_enum, NODE_PRINCIPLED_HAIR_REFLECTANCE); /* Initialize sockets to their default values. */ SOCKET_IN_COLOR(color, "Color", make_float3(0.017513f, 0.005763f, 0.002059f)); SOCKET_IN_FLOAT(melanin, "Melanin", 0.8f); SOCKET_IN_FLOAT(melanin_redness, "Melanin Redness", 1.0f); SOCKET_IN_COLOR(tint, "Tint", make_float3(1.f, 1.f, 1.f)); SOCKET_IN_VECTOR( absorption_coefficient, "Absorption Coefficient", make_float3(0.245531f, 0.52f, 1.365f)); SOCKET_IN_FLOAT(aspect_ratio, "Aspect Ratio", 0.85f); SOCKET_IN_FLOAT(offset, "Offset", 2.f * M_PI_F / 180.f); SOCKET_IN_FLOAT(roughness, "Roughness", 0.3f); SOCKET_IN_FLOAT(radial_roughness, "Radial Roughness", 0.3f); SOCKET_IN_FLOAT(coat, "Coat", 0.0f); SOCKET_IN_FLOAT(ior, "IOR", 1.55f); SOCKET_IN_FLOAT(random_roughness, "Random Roughness", 0.0f); SOCKET_IN_FLOAT(random_color, "Random Color", 0.0f); SOCKET_IN_FLOAT(random, "Random", 0.0f); SOCKET_IN_FLOAT(R, "R lobe", 1.0f); SOCKET_IN_FLOAT(TT, "TT lobe", 1.0f); SOCKET_IN_FLOAT(TRT, "TRT lobe", 1.0f); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } PrincipledHairBsdfNode::PrincipledHairBsdfNode() : BsdfBaseNode(get_node_type()) { closure = CLOSURE_BSDF_HAIR_HUANG_ID; } /* Treat hair as transparent if the hit is outside of the projected width. */ bool PrincipledHairBsdfNode::has_surface_transparent() { if (model == NODE_PRINCIPLED_HAIR_HUANG) { if (aspect_ratio != 1.0f || input("Aspect Ratio")->link) { return true; } } return false; } void PrincipledHairBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (has_surface_transparent()) { /* Make sure we have the normal for elliptical cross section tracking. */ attributes->add(ATTR_STD_VERTEX_NORMAL); } if (!input("Random")->link) { /* Enable retrieving Hair Info -> Random if Random isn't linked. */ attributes->add(ATTR_STD_CURVE_RANDOM); } ShaderNode::attributes(shader, attributes); } /* Prepares the input data for the SVM shader. */ void PrincipledHairBsdfNode::compile(SVMCompiler &compiler) { closure = (model == NODE_PRINCIPLED_HAIR_HUANG) ? CLOSURE_BSDF_HAIR_HUANG_ID : CLOSURE_BSDF_HAIR_CHIANG_ID; compiler.add_node(NODE_CLOSURE_SET_WEIGHT, one_float3()); ShaderInput *roughness_in = input("Roughness"); ShaderInput *radial_roughness_in = input("Radial Roughness"); ShaderInput *random_roughness_in = input("Random Roughness"); ShaderInput *offset_in = input("Offset"); ShaderInput *coat_in = input("Coat"); ShaderInput *ior_in = input("IOR"); ShaderInput *melanin_in = input("Melanin"); ShaderInput *melanin_redness_in = input("Melanin Redness"); ShaderInput *random_color_in = input("Random Color"); ShaderInput *R_in = input("R lobe"); ShaderInput *TT_in = input("TT lobe"); ShaderInput *TRT_in = input("TRT lobe"); ShaderInput *aspect_ratio_in = input("Aspect Ratio"); const int color_ofs = compiler.stack_assign(input("Color")); const int tint_ofs = compiler.stack_assign(input("Tint")); const int absorption_coefficient_ofs = compiler.stack_assign(input("Absorption Coefficient")); const int roughness_ofs = compiler.stack_assign_if_linked(roughness_in); const int radial_roughness_ofs = compiler.stack_assign_if_linked(radial_roughness_in); const int offset_ofs = compiler.stack_assign_if_linked(offset_in); const int ior_ofs = compiler.stack_assign_if_linked(ior_in); const int coat_ofs = compiler.stack_assign_if_linked(coat_in); const int melanin_ofs = compiler.stack_assign_if_linked(melanin_in); const int melanin_redness_ofs = compiler.stack_assign_if_linked(melanin_redness_in); ShaderInput *random_in = input("Random"); const int attr_random = random_in->link ? SVM_STACK_INVALID : compiler.attribute(ATTR_STD_CURVE_RANDOM); const int random_in_ofs = compiler.stack_assign_if_linked(random_in); const int random_color_ofs = compiler.stack_assign_if_linked(random_color_in); const int random_roughness_ofs = compiler.stack_assign_if_linked(random_roughness_in); /* Encode all parameters into data nodes. */ /* node */ compiler.add_node( NODE_CLOSURE_BSDF, /* Socket IDs can be packed 4 at a time into a single data packet */ compiler.encode_uchar4( closure, roughness_ofs, random_roughness_ofs, compiler.closure_mix_weight_offset()), /* The rest are stored as unsigned integers */ __float_as_uint(roughness), __float_as_uint(random_roughness)); /* data node */ compiler.add_node(SVM_STACK_INVALID, compiler.encode_uchar4(offset_ofs, ior_ofs, color_ofs, parametrization), __float_as_uint(offset), __float_as_uint(ior)); /* data node 2 */ compiler.add_node(compiler.encode_uchar4( tint_ofs, melanin_ofs, melanin_redness_ofs, absorption_coefficient_ofs), attr_random, __float_as_uint(melanin), __float_as_uint(melanin_redness)); /* data node 3 */ if (model == NODE_PRINCIPLED_HAIR_HUANG) { compiler.add_node(compiler.encode_uchar4(compiler.stack_assign_if_linked(aspect_ratio_in), random_in_ofs, random_color_ofs, compiler.attribute(ATTR_STD_VERTEX_NORMAL)), __float_as_uint(random), __float_as_uint(random_color), __float_as_uint(aspect_ratio)); } else { compiler.add_node( compiler.encode_uchar4(coat_ofs, random_in_ofs, random_color_ofs, radial_roughness_ofs), __float_as_uint(random), __float_as_uint(random_color), __float_as_uint(coat)); } /* data node 4 */ compiler.add_node(compiler.encode_uchar4(compiler.stack_assign_if_linked(R_in), compiler.stack_assign_if_linked(TT_in), compiler.stack_assign_if_linked(TRT_in), SVM_STACK_INVALID), __float_as_uint(model == NODE_PRINCIPLED_HAIR_HUANG ? R : radial_roughness), __float_as_uint(TT), __float_as_uint(TRT)); } /* Prepares the input data for the OSL shader. */ void PrincipledHairBsdfNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "model"); compiler.parameter(this, "parametrization"); compiler.add(this, "node_principled_hair_bsdf"); } /* Hair BSDF Closure */ NODE_DEFINE(HairBsdfNode) { NodeType *type = NodeType::add("hair_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum component_enum; component_enum.insert("reflection", CLOSURE_BSDF_HAIR_REFLECTION_ID); component_enum.insert("transmission", CLOSURE_BSDF_HAIR_TRANSMISSION_ID); SOCKET_ENUM(component, "Component", component_enum, CLOSURE_BSDF_HAIR_REFLECTION_ID); SOCKET_IN_FLOAT(offset, "Offset", 0.0f); SOCKET_IN_FLOAT(roughness_u, "RoughnessU", 0.2f); SOCKET_IN_FLOAT(roughness_v, "RoughnessV", 0.2f); SOCKET_IN_VECTOR(tangent, "Tangent", zero_float3()); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } HairBsdfNode::HairBsdfNode() : BsdfNode(get_node_type()) { closure = CLOSURE_BSDF_HAIR_REFLECTION_ID; } void HairBsdfNode::compile(SVMCompiler &compiler) { closure = component; ShaderInput *tangent = input("Tangent"); tangent = compiler.is_linked(tangent) ? tangent : nullptr; BsdfNode::compile( compiler, input("RoughnessU"), input("RoughnessV"), input("Offset"), nullptr, tangent); } void HairBsdfNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "component"); compiler.add(this, "node_hair_bsdf"); } /* Geometry */ NODE_DEFINE(GeometryNode) { NodeType *type = NodeType::add("geometry", create, NodeType::SHADER); SOCKET_OUT_POINT(position, "Position"); SOCKET_OUT_NORMAL(normal, "Normal"); SOCKET_OUT_NORMAL(tangent, "Tangent"); SOCKET_OUT_NORMAL(true_normal, "True Normal"); SOCKET_OUT_VECTOR(incoming, "Incoming"); SOCKET_OUT_POINT(parametric, "Parametric"); SOCKET_OUT_FLOAT(backfacing, "Backfacing"); SOCKET_OUT_FLOAT(pointiness, "Pointiness"); SOCKET_OUT_FLOAT(random_per_island, "Random Per Island"); return type; } GeometryNode::GeometryNode() : ShaderNode(get_node_type()) { special_type = SHADER_SPECIAL_TYPE_GEOMETRY; } void GeometryNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (shader->has_surface_link()) { if (!output("Tangent")->links.empty()) { attributes->add(ATTR_STD_GENERATED); } if (!output("Pointiness")->links.empty()) { attributes->add(ATTR_STD_POINTINESS); } if (!output("Random Per Island")->links.empty()) { attributes->add(ATTR_STD_RANDOM_PER_ISLAND); } } ShaderNode::attributes(shader, attributes); } void GeometryNode::compile(SVMCompiler &compiler) { ShaderOutput *out; ShaderNodeType geom_node = NODE_GEOMETRY; ShaderNodeType attr_node = NODE_ATTR; if (bump == SHADER_BUMP_DX) { geom_node = NODE_GEOMETRY_BUMP_DX; attr_node = NODE_ATTR_BUMP_DX; } else if (bump == SHADER_BUMP_DY) { geom_node = NODE_GEOMETRY_BUMP_DY; attr_node = NODE_ATTR_BUMP_DY; } out = output("Position"); if (!out->links.empty()) { compiler.add_node( geom_node, NODE_GEOM_P, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } out = output("Normal"); if (!out->links.empty()) { compiler.add_node( geom_node, NODE_GEOM_N, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } out = output("Tangent"); if (!out->links.empty()) { compiler.add_node( geom_node, NODE_GEOM_T, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } out = output("True Normal"); if (!out->links.empty()) { compiler.add_node( geom_node, NODE_GEOM_Ng, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } out = output("Incoming"); if (!out->links.empty()) { compiler.add_node( geom_node, NODE_GEOM_I, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } out = output("Parametric"); if (!out->links.empty()) { compiler.add_node( geom_node, NODE_GEOM_uv, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } out = output("Backfacing"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_backfacing, compiler.stack_assign(out)); } out = output("Pointiness"); if (!out->links.empty()) { if (compiler.output_type() != SHADER_TYPE_VOLUME) { compiler.add_node(attr_node, ATTR_STD_POINTINESS, compiler.encode_uchar4(compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT), __float_as_uint(bump_filter_width)); } else { compiler.add_node(NODE_VALUE_F, __float_as_int(0.0f), compiler.stack_assign(out)); } } out = output("Random Per Island"); if (!out->links.empty()) { if (compiler.output_type() != SHADER_TYPE_VOLUME) { compiler.add_node(attr_node, ATTR_STD_RANDOM_PER_ISLAND, compiler.encode_uchar4(compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT), __float_as_uint(bump_filter_width)); } else { compiler.add_node(NODE_VALUE_F, __float_as_int(0.0f), compiler.stack_assign(out)); } } } void GeometryNode::compile(OSLCompiler &compiler) { if (bump == SHADER_BUMP_DX) { compiler.parameter("bump_offset", "dx"); } else if (bump == SHADER_BUMP_DY) { compiler.parameter("bump_offset", "dy"); } else { compiler.parameter("bump_offset", "center"); } compiler.parameter("bump_filter_width", bump_filter_width); compiler.add(this, "node_geometry"); } /* TextureCoordinate */ NODE_DEFINE(TextureCoordinateNode) { NodeType *type = NodeType::add("texture_coordinate", create, NodeType::SHADER); SOCKET_BOOLEAN(from_dupli, "From Dupli", false); SOCKET_BOOLEAN(use_transform, "Use Transform", false); SOCKET_TRANSFORM(ob_tfm, "Object Transform", transform_identity()); SOCKET_OUT_POINT(generated, "Generated"); SOCKET_OUT_NORMAL(normal, "Normal"); SOCKET_OUT_POINT(UV, "UV"); SOCKET_OUT_POINT(object, "Object"); SOCKET_OUT_POINT(camera, "Camera"); SOCKET_OUT_POINT(window, "Window"); SOCKET_OUT_NORMAL(reflection, "Reflection"); return type; } TextureCoordinateNode::TextureCoordinateNode() : ShaderNode(get_node_type()) {} void TextureCoordinateNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (shader->has_surface_link()) { if (!from_dupli) { if (!output("Generated")->links.empty()) { attributes->add(ATTR_STD_GENERATED); } if (!output("UV")->links.empty()) { attributes->add(ATTR_STD_UV); } } } if (shader->has_volume) { if (!from_dupli) { if (!output("Generated")->links.empty()) { attributes->add(ATTR_STD_GENERATED_TRANSFORM); } } } ShaderNode::attributes(shader, attributes); } void TextureCoordinateNode::compile(SVMCompiler &compiler) { ShaderOutput *out; ShaderNodeType texco_node = NODE_TEX_COORD; ShaderNodeType attr_node = NODE_ATTR; ShaderNodeType geom_node = NODE_GEOMETRY; if (bump == SHADER_BUMP_DX) { texco_node = NODE_TEX_COORD_BUMP_DX; attr_node = NODE_ATTR_BUMP_DX; geom_node = NODE_GEOMETRY_BUMP_DX; } else if (bump == SHADER_BUMP_DY) { texco_node = NODE_TEX_COORD_BUMP_DY; attr_node = NODE_ATTR_BUMP_DY; geom_node = NODE_GEOMETRY_BUMP_DY; } out = output("Generated"); if (!out->links.empty()) { if (compiler.background) { compiler.add_node( geom_node, NODE_GEOM_P, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } else { if (from_dupli) { compiler.add_node(texco_node, NODE_TEXCO_DUPLI_GENERATED, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } else if (compiler.output_type() == SHADER_TYPE_VOLUME) { compiler.add_node(texco_node, NODE_TEXCO_VOLUME_GENERATED, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } else { const int attr = compiler.attribute(ATTR_STD_GENERATED); compiler.add_node( attr_node, attr, compiler.encode_uchar4(compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT3), __float_as_uint(bump_filter_width)); } } } out = output("Normal"); if (!out->links.empty()) { compiler.add_node(texco_node, NODE_TEXCO_NORMAL, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } out = output("UV"); if (!out->links.empty()) { if (from_dupli) { compiler.add_node(texco_node, NODE_TEXCO_DUPLI_UV, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } else { const int attr = compiler.attribute(ATTR_STD_UV); compiler.add_node( attr_node, attr, compiler.encode_uchar4(compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT3), __float_as_uint(bump_filter_width)); } } out = output("Object"); if (!out->links.empty()) { compiler.add_node(texco_node, (use_transform) ? NODE_TEXCO_OBJECT_WITH_TRANSFORM : NODE_TEXCO_OBJECT, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); if (use_transform) { const Transform ob_itfm = transform_inverse(ob_tfm); compiler.add_node(ob_itfm.x); compiler.add_node(ob_itfm.y); compiler.add_node(ob_itfm.z); } } out = output("Camera"); if (!out->links.empty()) { compiler.add_node(texco_node, NODE_TEXCO_CAMERA, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } out = output("Window"); if (!out->links.empty()) { compiler.add_node(texco_node, NODE_TEXCO_WINDOW, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } out = output("Reflection"); if (!out->links.empty()) { if (compiler.background) { compiler.add_node( geom_node, NODE_GEOM_I, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } else { compiler.add_node(texco_node, NODE_TEXCO_REFLECTION, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } } } void TextureCoordinateNode::compile(OSLCompiler &compiler) { if (bump == SHADER_BUMP_DX) { compiler.parameter("bump_offset", "dx"); } else if (bump == SHADER_BUMP_DY) { compiler.parameter("bump_offset", "dy"); } else { compiler.parameter("bump_offset", "center"); } compiler.parameter("bump_filter_width", bump_filter_width); if (compiler.background) { compiler.parameter("is_background", true); } if (compiler.output_type() == SHADER_TYPE_VOLUME) { compiler.parameter("is_volume", true); } compiler.parameter(this, "use_transform"); const Transform ob_itfm = transform_inverse(ob_tfm); compiler.parameter("object_itfm", ob_itfm); compiler.parameter(this, "from_dupli"); compiler.add(this, "node_texture_coordinate"); } /* UV Map */ NODE_DEFINE(UVMapNode) { NodeType *type = NodeType::add("uvmap", create, NodeType::SHADER); SOCKET_STRING(attribute, "attribute", ustring()); SOCKET_IN_BOOLEAN(from_dupli, "from dupli", false); SOCKET_OUT_POINT(UV, "UV"); return type; } UVMapNode::UVMapNode() : ShaderNode(get_node_type()) {} void UVMapNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (shader->has_surface) { if (!from_dupli) { if (!output("UV")->links.empty()) { if (!attribute.empty()) { attributes->add(attribute); } else { attributes->add(ATTR_STD_UV); } } } } ShaderNode::attributes(shader, attributes); } void UVMapNode::compile(SVMCompiler &compiler) { ShaderOutput *out = output("UV"); ShaderNodeType texco_node = NODE_TEX_COORD; ShaderNodeType attr_node = NODE_ATTR; int attr; if (bump == SHADER_BUMP_DX) { texco_node = NODE_TEX_COORD_BUMP_DX; attr_node = NODE_ATTR_BUMP_DX; } else if (bump == SHADER_BUMP_DY) { texco_node = NODE_TEX_COORD_BUMP_DY; attr_node = NODE_ATTR_BUMP_DY; } if (!out->links.empty()) { if (from_dupli) { compiler.add_node(texco_node, NODE_TEXCO_DUPLI_UV, compiler.stack_assign(out), __float_as_uint(bump_filter_width)); } else { if (!attribute.empty()) { attr = compiler.attribute(attribute); } else { attr = compiler.attribute(ATTR_STD_UV); } compiler.add_node( attr_node, attr, compiler.encode_uchar4(compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT3), __float_as_uint(bump_filter_width)); } } } void UVMapNode::compile(OSLCompiler &compiler) { if (bump == SHADER_BUMP_DX) { compiler.parameter("bump_offset", "dx"); } else if (bump == SHADER_BUMP_DY) { compiler.parameter("bump_offset", "dy"); } else { compiler.parameter("bump_offset", "center"); } compiler.parameter("bump_filter_width", bump_filter_width); compiler.parameter(this, "from_dupli"); compiler.parameter(this, "attribute"); compiler.add(this, "node_uv_map"); } /* Light Path */ NODE_DEFINE(LightPathNode) { NodeType *type = NodeType::add("light_path", create, NodeType::SHADER); SOCKET_OUT_FLOAT(is_camera_ray, "Is Camera Ray"); SOCKET_OUT_FLOAT(is_shadow_ray, "Is Shadow Ray"); SOCKET_OUT_FLOAT(is_diffuse_ray, "Is Diffuse Ray"); SOCKET_OUT_FLOAT(is_glossy_ray, "Is Glossy Ray"); SOCKET_OUT_FLOAT(is_singular_ray, "Is Singular Ray"); SOCKET_OUT_FLOAT(is_reflection_ray, "Is Reflection Ray"); SOCKET_OUT_FLOAT(is_transmission_ray, "Is Transmission Ray"); SOCKET_OUT_FLOAT(is_volume_scatter_ray, "Is Volume Scatter Ray"); SOCKET_OUT_FLOAT(ray_length, "Ray Length"); SOCKET_OUT_FLOAT(ray_depth, "Ray Depth"); SOCKET_OUT_FLOAT(diffuse_depth, "Diffuse Depth"); SOCKET_OUT_FLOAT(glossy_depth, "Glossy Depth"); SOCKET_OUT_FLOAT(transparent_depth, "Transparent Depth"); SOCKET_OUT_FLOAT(transmission_depth, "Transmission Depth"); SOCKET_OUT_FLOAT(portal_depth, "Portal Depth"); return type; } LightPathNode::LightPathNode() : ShaderNode(get_node_type()) { special_type = SHADER_SPECIAL_TYPE_LIGHT_PATH; } void LightPathNode::compile(SVMCompiler &compiler) { ShaderOutput *out; out = output("Is Camera Ray"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_camera, compiler.stack_assign(out)); } out = output("Is Shadow Ray"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_shadow, compiler.stack_assign(out)); } out = output("Is Diffuse Ray"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_diffuse, compiler.stack_assign(out)); } out = output("Is Glossy Ray"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_glossy, compiler.stack_assign(out)); } out = output("Is Singular Ray"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_singular, compiler.stack_assign(out)); } out = output("Is Reflection Ray"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_reflection, compiler.stack_assign(out)); } out = output("Is Transmission Ray"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_transmission, compiler.stack_assign(out)); } out = output("Is Volume Scatter Ray"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_volume_scatter, compiler.stack_assign(out)); } out = output("Ray Length"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_length, compiler.stack_assign(out)); } out = output("Ray Depth"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_depth, compiler.stack_assign(out)); } out = output("Diffuse Depth"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_diffuse, compiler.stack_assign(out)); } out = output("Glossy Depth"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_glossy, compiler.stack_assign(out)); } out = output("Transparent Depth"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_transparent, compiler.stack_assign(out)); } out = output("Transmission Depth"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_transmission, compiler.stack_assign(out)); } out = output("Portal Depth"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_portal, compiler.stack_assign(out)); } } void LightPathNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_light_path"); } /* Light Falloff */ NODE_DEFINE(LightFalloffNode) { NodeType *type = NodeType::add("light_falloff", create, NodeType::SHADER); SOCKET_IN_FLOAT(strength, "Strength", 100.0f); SOCKET_IN_FLOAT(smooth, "Smooth", 0.0f); SOCKET_OUT_FLOAT(quadratic, "Quadratic"); SOCKET_OUT_FLOAT(linear, "Linear"); SOCKET_OUT_FLOAT(constant, "Constant"); return type; } LightFalloffNode::LightFalloffNode() : ShaderNode(get_node_type()) {} void LightFalloffNode::compile(SVMCompiler &compiler) { ShaderInput *strength_in = input("Strength"); ShaderInput *smooth_in = input("Smooth"); ShaderOutput *out = output("Quadratic"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_FALLOFF, NODE_LIGHT_FALLOFF_QUADRATIC, compiler.encode_uchar4(compiler.stack_assign(strength_in), compiler.stack_assign(smooth_in), compiler.stack_assign(out))); } out = output("Linear"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_FALLOFF, NODE_LIGHT_FALLOFF_LINEAR, compiler.encode_uchar4(compiler.stack_assign(strength_in), compiler.stack_assign(smooth_in), compiler.stack_assign(out))); } out = output("Constant"); if (!out->links.empty()) { compiler.add_node(NODE_LIGHT_FALLOFF, NODE_LIGHT_FALLOFF_CONSTANT, compiler.encode_uchar4(compiler.stack_assign(strength_in), compiler.stack_assign(smooth_in), compiler.stack_assign(out))); } } void LightFalloffNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_light_falloff"); } /* Object Info */ NODE_DEFINE(ObjectInfoNode) { NodeType *type = NodeType::add("object_info", create, NodeType::SHADER); SOCKET_OUT_VECTOR(location, "Location"); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(alpha, "Alpha"); SOCKET_OUT_FLOAT(object_index, "Object Index"); SOCKET_OUT_FLOAT(material_index, "Material Index"); SOCKET_OUT_FLOAT(random, "Random"); return type; } ObjectInfoNode::ObjectInfoNode() : ShaderNode(get_node_type()) {} void ObjectInfoNode::compile(SVMCompiler &compiler) { ShaderOutput *out = output("Location"); if (!out->links.empty()) { compiler.add_node(NODE_OBJECT_INFO, NODE_INFO_OB_LOCATION, compiler.stack_assign(out)); } out = output("Color"); if (!out->links.empty()) { compiler.add_node(NODE_OBJECT_INFO, NODE_INFO_OB_COLOR, compiler.stack_assign(out)); } out = output("Alpha"); if (!out->links.empty()) { compiler.add_node(NODE_OBJECT_INFO, NODE_INFO_OB_ALPHA, compiler.stack_assign(out)); } out = output("Object Index"); if (!out->links.empty()) { compiler.add_node(NODE_OBJECT_INFO, NODE_INFO_OB_INDEX, compiler.stack_assign(out)); } out = output("Material Index"); if (!out->links.empty()) { compiler.add_node(NODE_OBJECT_INFO, NODE_INFO_MAT_INDEX, compiler.stack_assign(out)); } out = output("Random"); if (!out->links.empty()) { compiler.add_node(NODE_OBJECT_INFO, NODE_INFO_OB_RANDOM, compiler.stack_assign(out)); } } void ObjectInfoNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_object_info"); } /* Particle Info */ NODE_DEFINE(ParticleInfoNode) { NodeType *type = NodeType::add("particle_info", create, NodeType::SHADER); SOCKET_OUT_FLOAT(index, "Index"); SOCKET_OUT_FLOAT(random, "Random"); SOCKET_OUT_FLOAT(age, "Age"); SOCKET_OUT_FLOAT(lifetime, "Lifetime"); SOCKET_OUT_POINT(location, "Location"); #if 0 /* not yet supported */ SOCKET_OUT_QUATERNION(rotation, "Rotation"); #endif SOCKET_OUT_FLOAT(size, "Size"); SOCKET_OUT_VECTOR(velocity, "Velocity"); SOCKET_OUT_VECTOR(angular_velocity, "Angular Velocity"); return type; } ParticleInfoNode::ParticleInfoNode() : ShaderNode(get_node_type()) {} void ParticleInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (!output("Index")->links.empty()) { attributes->add(ATTR_STD_PARTICLE); } if (!output("Random")->links.empty()) { attributes->add(ATTR_STD_PARTICLE); } if (!output("Age")->links.empty()) { attributes->add(ATTR_STD_PARTICLE); } if (!output("Lifetime")->links.empty()) { attributes->add(ATTR_STD_PARTICLE); } if (!output("Location")->links.empty()) { attributes->add(ATTR_STD_PARTICLE); } #if 0 /* not yet supported */ if (!output("Rotation")->links.empty()) { attributes->add(ATTR_STD_PARTICLE); } #endif if (!output("Size")->links.empty()) { attributes->add(ATTR_STD_PARTICLE); } if (!output("Velocity")->links.empty()) { attributes->add(ATTR_STD_PARTICLE); } if (!output("Angular Velocity")->links.empty()) { attributes->add(ATTR_STD_PARTICLE); } ShaderNode::attributes(shader, attributes); } void ParticleInfoNode::compile(SVMCompiler &compiler) { ShaderOutput *out; out = output("Index"); if (!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_INDEX, compiler.stack_assign(out)); } out = output("Random"); if (!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_RANDOM, compiler.stack_assign(out)); } out = output("Age"); if (!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_AGE, compiler.stack_assign(out)); } out = output("Lifetime"); if (!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_LIFETIME, compiler.stack_assign(out)); } out = output("Location"); if (!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_LOCATION, compiler.stack_assign(out)); } /* quaternion data is not yet supported by Cycles */ #if 0 out = output("Rotation"); if (!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_ROTATION, compiler.stack_assign(out)); } #endif out = output("Size"); if (!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_SIZE, compiler.stack_assign(out)); } out = output("Velocity"); if (!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_VELOCITY, compiler.stack_assign(out)); } out = output("Angular Velocity"); if (!out->links.empty()) { compiler.add_node( NODE_PARTICLE_INFO, NODE_INFO_PAR_ANGULAR_VELOCITY, compiler.stack_assign(out)); } } void ParticleInfoNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_particle_info"); } /* Hair Info */ NODE_DEFINE(HairInfoNode) { NodeType *type = NodeType::add("hair_info", create, NodeType::SHADER); SOCKET_OUT_FLOAT(is_strand, "Is Strand"); SOCKET_OUT_FLOAT(intercept, "Intercept"); SOCKET_OUT_FLOAT(size, "Length"); SOCKET_OUT_FLOAT(thickness, "Thickness"); SOCKET_OUT_NORMAL(tangent_normal, "Tangent Normal"); SOCKET_OUT_FLOAT(index, "Random"); return type; } HairInfoNode::HairInfoNode() : ShaderNode(get_node_type()) {} void HairInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (shader->has_surface_link()) { ShaderOutput *intercept_out = output("Intercept"); if (!intercept_out->links.empty()) { attributes->add(ATTR_STD_CURVE_INTERCEPT); } if (!output("Length")->links.empty()) { attributes->add(ATTR_STD_CURVE_LENGTH); } if (!output("Random")->links.empty()) { attributes->add(ATTR_STD_CURVE_RANDOM); } } ShaderNode::attributes(shader, attributes); } void HairInfoNode::compile(SVMCompiler &compiler) { ShaderOutput *out; out = output("Is Strand"); if (!out->links.empty()) { compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_IS_STRAND, compiler.stack_assign(out)); } out = output("Intercept"); if (!out->links.empty()) { const int attr = compiler.attribute(ATTR_STD_CURVE_INTERCEPT); compiler.add_node(NODE_ATTR, attr, compiler.encode_uchar4(compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT), __float_as_uint(0.0f)); } out = output("Length"); if (!out->links.empty()) { const int attr = compiler.attribute(ATTR_STD_CURVE_LENGTH); compiler.add_node(NODE_ATTR, attr, compiler.encode_uchar4(compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT), __float_as_uint(0.0f)); } out = output("Thickness"); if (!out->links.empty()) { compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_THICKNESS, compiler.stack_assign(out)); } out = output("Tangent Normal"); if (!out->links.empty()) { compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_TANGENT_NORMAL, compiler.stack_assign(out)); } out = output("Random"); if (!out->links.empty()) { const int attr = compiler.attribute(ATTR_STD_CURVE_RANDOM); compiler.add_node(NODE_ATTR, attr, compiler.encode_uchar4(compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT), __float_as_uint(0.0f)); } } void HairInfoNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_hair_info"); } /* Point Info */ NODE_DEFINE(PointInfoNode) { NodeType *type = NodeType::add("point_info", create, NodeType::SHADER); SOCKET_OUT_POINT(position, "Position"); SOCKET_OUT_FLOAT(radius, "Radius"); SOCKET_OUT_FLOAT(random, "Random"); return type; } PointInfoNode::PointInfoNode() : ShaderNode(get_node_type()) {} void PointInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (shader->has_surface_link()) { if (!output("Random")->links.empty()) { attributes->add(ATTR_STD_POINT_RANDOM); } } ShaderNode::attributes(shader, attributes); } void PointInfoNode::compile(SVMCompiler &compiler) { ShaderOutput *out; out = output("Position"); if (!out->links.empty()) { compiler.add_node(NODE_POINT_INFO, NODE_INFO_POINT_POSITION, compiler.stack_assign(out)); } out = output("Radius"); if (!out->links.empty()) { compiler.add_node(NODE_POINT_INFO, NODE_INFO_POINT_RADIUS, compiler.stack_assign(out)); } out = output("Random"); if (!out->links.empty()) { const int attr = compiler.attribute(ATTR_STD_POINT_RANDOM); compiler.add_node(NODE_ATTR, attr, compiler.encode_uchar4(compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT), __float_as_uint(0.0f)); } } void PointInfoNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_point_info"); } /* Volume Info */ NODE_DEFINE(VolumeInfoNode) { NodeType *type = NodeType::add("volume_info", create, NodeType::SHADER); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(density, "Density"); SOCKET_OUT_FLOAT(flame, "Flame"); SOCKET_OUT_FLOAT(temperature, "Temperature"); return type; } VolumeInfoNode::VolumeInfoNode() : ShaderNode(get_node_type()) {} /* The requested attributes are not updated after node expansion. * So we explicitly request the required attributes. */ void VolumeInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (shader->has_volume) { if (!output("Color")->links.empty()) { attributes->add(ATTR_STD_VOLUME_COLOR); } if (!output("Density")->links.empty()) { attributes->add(ATTR_STD_VOLUME_DENSITY); } if (!output("Flame")->links.empty()) { attributes->add(ATTR_STD_VOLUME_FLAME); } if (!output("Temperature")->links.empty()) { attributes->add(ATTR_STD_VOLUME_TEMPERATURE); } attributes->add(ATTR_STD_GENERATED_TRANSFORM); } ShaderNode::attributes(shader, attributes); } void VolumeInfoNode::expand(ShaderGraph *graph) { ShaderOutput *color_out = output("Color"); if (!color_out->links.empty()) { AttributeNode *attr = graph->create_node(); attr->set_attribute(ustring("color")); graph->relink(color_out, attr->output("Color")); } ShaderOutput *density_out = output("Density"); if (!density_out->links.empty()) { AttributeNode *attr = graph->create_node(); attr->set_attribute(ustring("density")); graph->relink(density_out, attr->output("Fac")); } ShaderOutput *flame_out = output("Flame"); if (!flame_out->links.empty()) { AttributeNode *attr = graph->create_node(); attr->set_attribute(ustring("flame")); graph->relink(flame_out, attr->output("Fac")); } ShaderOutput *temperature_out = output("Temperature"); if (!temperature_out->links.empty()) { AttributeNode *attr = graph->create_node(); attr->set_attribute(ustring("temperature")); graph->relink(temperature_out, attr->output("Fac")); } } void VolumeInfoNode::compile(SVMCompiler & /*compiler*/) {} void VolumeInfoNode::compile(OSLCompiler & /*compiler*/) {} NODE_DEFINE(VertexColorNode) { NodeType *type = NodeType::add("vertex_color", create, NodeType::SHADER); SOCKET_STRING(layer_name, "Layer Name", ustring()); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(alpha, "Alpha"); return type; } VertexColorNode::VertexColorNode() : ShaderNode(get_node_type()) {} void VertexColorNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (!(output("Color")->links.empty() && output("Alpha")->links.empty())) { if (!layer_name.empty()) { attributes->add_standard(layer_name); } else { attributes->add(ATTR_STD_VERTEX_COLOR); } } ShaderNode::attributes(shader, attributes); } void VertexColorNode::compile(SVMCompiler &compiler) { ShaderOutput *color_out = output("Color"); ShaderOutput *alpha_out = output("Alpha"); int layer_id = 0; if (!layer_name.empty()) { layer_id = compiler.attribute(layer_name); } else { layer_id = compiler.attribute(ATTR_STD_VERTEX_COLOR); } ShaderNodeType node; if (bump == SHADER_BUMP_DX) { node = NODE_VERTEX_COLOR_BUMP_DX; } else if (bump == SHADER_BUMP_DY) { node = NODE_VERTEX_COLOR_BUMP_DY; } else { node = NODE_VERTEX_COLOR; } compiler.add_node(node, compiler.encode_uchar4(layer_id, compiler.stack_assign(color_out), compiler.stack_assign(alpha_out)), __float_as_uint(bump_filter_width)); } void VertexColorNode::compile(OSLCompiler &compiler) { if (bump == SHADER_BUMP_DX) { compiler.parameter("bump_offset", "dx"); } else if (bump == SHADER_BUMP_DY) { compiler.parameter("bump_offset", "dy"); } else { compiler.parameter("bump_offset", "center"); } compiler.parameter("bump_filter_width", bump_filter_width); if (layer_name.empty()) { compiler.parameter("layer_name", ustring("geom:vertex_color")); } else { if (Attribute::name_standard(layer_name.c_str()) != ATTR_STD_NONE) { compiler.parameter("name", (string("geom:") + layer_name.c_str()).c_str()); } else { compiler.parameter("layer_name", layer_name.c_str()); } } compiler.add(this, "node_vertex_color"); } /* Value */ NODE_DEFINE(ValueNode) { NodeType *type = NodeType::add("value", create, NodeType::SHADER); SOCKET_FLOAT(value, "Value", 0.0f); SOCKET_OUT_FLOAT(value, "Value"); return type; } ValueNode::ValueNode() : ShaderNode(get_node_type()) {} void ValueNode::constant_fold(const ConstantFolder &folder) { folder.make_constant(value); } void ValueNode::compile(SVMCompiler &compiler) { ShaderOutput *val_out = output("Value"); compiler.add_node(NODE_VALUE_F, __float_as_int(value), compiler.stack_assign(val_out)); } void ValueNode::compile(OSLCompiler &compiler) { compiler.parameter("value_value", value); compiler.add(this, "node_value"); } /* Color */ NODE_DEFINE(ColorNode) { NodeType *type = NodeType::add("color", create, NodeType::SHADER); SOCKET_COLOR(value, "Value", zero_float3()); SOCKET_OUT_COLOR(color, "Color"); return type; } ColorNode::ColorNode() : ShaderNode(get_node_type()) {} void ColorNode::constant_fold(const ConstantFolder &folder) { folder.make_constant(value); } void ColorNode::compile(SVMCompiler &compiler) { ShaderOutput *color_out = output("Color"); if (!color_out->links.empty()) { compiler.add_node(NODE_VALUE_V, compiler.stack_assign(color_out)); compiler.add_node(NODE_VALUE_V, value); } } void ColorNode::compile(OSLCompiler &compiler) { compiler.parameter_color("color_value", value); compiler.add(this, "node_value"); } /* Add Closure */ NODE_DEFINE(AddClosureNode) { NodeType *type = NodeType::add("add_closure", create, NodeType::SHADER); SOCKET_IN_CLOSURE(closure1, "Closure1"); SOCKET_IN_CLOSURE(closure2, "Closure2"); SOCKET_OUT_CLOSURE(closure, "Closure"); return type; } AddClosureNode::AddClosureNode() : ShaderNode(get_node_type()) { special_type = SHADER_SPECIAL_TYPE_COMBINE_CLOSURE; } void AddClosureNode::compile(SVMCompiler & /*compiler*/) { /* handled in the SVM compiler */ } void AddClosureNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_add_closure"); } void AddClosureNode::constant_fold(const ConstantFolder &folder) { ShaderInput *closure1_in = input("Closure1"); ShaderInput *closure2_in = input("Closure2"); /* remove useless add closures nodes */ if (!closure1_in->link) { folder.bypass_or_discard(closure2_in); } else if (!closure2_in->link) { folder.bypass_or_discard(closure1_in); } } /* Mix Closure */ NODE_DEFINE(MixClosureNode) { NodeType *type = NodeType::add("mix_closure", create, NodeType::SHADER); SOCKET_IN_FLOAT(fac, "Fac", 0.5f); SOCKET_IN_CLOSURE(closure1, "Closure1"); SOCKET_IN_CLOSURE(closure2, "Closure2"); SOCKET_OUT_CLOSURE(closure, "Closure"); return type; } MixClosureNode::MixClosureNode() : ShaderNode(get_node_type()) { special_type = SHADER_SPECIAL_TYPE_COMBINE_CLOSURE; } void MixClosureNode::compile(SVMCompiler & /*compiler*/) { /* handled in the SVM compiler */ } void MixClosureNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_mix_closure"); } void MixClosureNode::constant_fold(const ConstantFolder &folder) { ShaderInput *fac_in = input("Fac"); ShaderInput *closure1_in = input("Closure1"); ShaderInput *closure2_in = input("Closure2"); /* remove useless mix closures nodes */ if (closure1_in->link == closure2_in->link) { folder.bypass_or_discard(closure1_in); } /* remove unused mix closure input when factor is 0.0 or 1.0 * check for closure links and make sure factor link is disconnected */ else if (!fac_in->link) { /* factor 0.0 */ if (fac <= 0.0f) { folder.bypass_or_discard(closure1_in); } /* factor 1.0 */ else if (fac >= 1.0f) { folder.bypass_or_discard(closure2_in); } } } /* Mix Closure */ NODE_DEFINE(MixClosureWeightNode) { NodeType *type = NodeType::add("mix_closure_weight", create, NodeType::SHADER); SOCKET_IN_FLOAT(weight, "Weight", 1.0f); SOCKET_IN_FLOAT(fac, "Fac", 1.0f); SOCKET_OUT_FLOAT(weight1, "Weight1"); SOCKET_OUT_FLOAT(weight2, "Weight2"); return type; } MixClosureWeightNode::MixClosureWeightNode() : ShaderNode(get_node_type()) {} void MixClosureWeightNode::compile(SVMCompiler &compiler) { ShaderInput *weight_in = input("Weight"); ShaderInput *fac_in = input("Fac"); ShaderOutput *weight1_out = output("Weight1"); ShaderOutput *weight2_out = output("Weight2"); compiler.add_node(NODE_MIX_CLOSURE, compiler.encode_uchar4(compiler.stack_assign(fac_in), compiler.stack_assign(weight_in), compiler.stack_assign(weight1_out), compiler.stack_assign(weight2_out))); } void MixClosureWeightNode::compile(OSLCompiler & /*compiler*/) { assert(0); } /* Invert */ NODE_DEFINE(InvertNode) { NodeType *type = NodeType::add("invert", create, NodeType::SHADER); SOCKET_IN_FLOAT(fac, "Fac", 1.0f); SOCKET_IN_COLOR(color, "Color", zero_float3()); SOCKET_OUT_COLOR(color, "Color"); return type; } InvertNode::InvertNode() : ShaderNode(get_node_type()) {} void InvertNode::constant_fold(const ConstantFolder &folder) { ShaderInput *fac_in = input("Fac"); ShaderInput *color_in = input("Color"); if (!fac_in->link) { /* evaluate fully constant node */ if (!color_in->link) { folder.make_constant(interp(color, one_float3() - color, fac)); } /* remove no-op node */ else if (fac == 0.0f) { folder.bypass(color_in->link); } } } void InvertNode::compile(SVMCompiler &compiler) { ShaderInput *fac_in = input("Fac"); ShaderInput *color_in = input("Color"); ShaderOutput *color_out = output("Color"); compiler.add_node(NODE_INVERT, compiler.stack_assign(fac_in), compiler.stack_assign(color_in), compiler.stack_assign(color_out)); } void InvertNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_invert"); } /* Mix */ NODE_DEFINE(MixNode) { NodeType *type = NodeType::add("mix", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("mix", NODE_MIX_BLEND); type_enum.insert("add", NODE_MIX_ADD); type_enum.insert("multiply", NODE_MIX_MUL); type_enum.insert("screen", NODE_MIX_SCREEN); type_enum.insert("overlay", NODE_MIX_OVERLAY); type_enum.insert("subtract", NODE_MIX_SUB); type_enum.insert("divide", NODE_MIX_DIV); type_enum.insert("difference", NODE_MIX_DIFF); type_enum.insert("darken", NODE_MIX_DARK); type_enum.insert("lighten", NODE_MIX_LIGHT); type_enum.insert("dodge", NODE_MIX_DODGE); type_enum.insert("burn", NODE_MIX_BURN); type_enum.insert("hue", NODE_MIX_HUE); type_enum.insert("saturation", NODE_MIX_SAT); type_enum.insert("value", NODE_MIX_VAL); type_enum.insert("color", NODE_MIX_COL); type_enum.insert("soft_light", NODE_MIX_SOFT); type_enum.insert("linear_light", NODE_MIX_LINEAR); type_enum.insert("exclusion", NODE_MIX_EXCLUSION); SOCKET_ENUM(mix_type, "Type", type_enum, NODE_MIX_BLEND); SOCKET_BOOLEAN(use_clamp, "Use Clamp", false); SOCKET_IN_FLOAT(fac, "Fac", 0.5f); SOCKET_IN_COLOR(color1, "Color1", zero_float3()); SOCKET_IN_COLOR(color2, "Color2", zero_float3()); SOCKET_OUT_COLOR(color, "Color"); return type; } MixNode::MixNode() : ShaderNode(get_node_type()) {} void MixNode::compile(SVMCompiler &compiler) { ShaderInput *fac_in = input("Fac"); ShaderInput *color1_in = input("Color1"); ShaderInput *color2_in = input("Color2"); ShaderOutput *color_out = output("Color"); compiler.add_node(NODE_MIX, compiler.stack_assign(fac_in), compiler.stack_assign(color1_in), compiler.stack_assign(color2_in)); compiler.add_node(NODE_MIX, mix_type, compiler.stack_assign(color_out)); if (use_clamp) { compiler.add_node(NODE_MIX, 0, compiler.stack_assign(color_out)); compiler.add_node(NODE_MIX, NODE_MIX_CLAMP, compiler.stack_assign(color_out)); } } void MixNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "mix_type"); compiler.parameter(this, "use_clamp"); compiler.add(this, "node_mix"); } void MixNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { folder.make_constant_clamp(svm_mix_clamped_factor(mix_type, fac, color1, color2), use_clamp); } else { folder.fold_mix(mix_type, use_clamp); } } bool MixNode::is_linear_operation() { switch (mix_type) { case NODE_MIX_BLEND: case NODE_MIX_ADD: case NODE_MIX_MUL: case NODE_MIX_SUB: break; default: return false; } return use_clamp == false && input("Factor")->link == nullptr; } /* Mix Color */ NODE_DEFINE(MixColorNode) { NodeType *type = NodeType::add("mix_color", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("mix", NODE_MIX_BLEND); type_enum.insert("add", NODE_MIX_ADD); type_enum.insert("multiply", NODE_MIX_MUL); type_enum.insert("screen", NODE_MIX_SCREEN); type_enum.insert("overlay", NODE_MIX_OVERLAY); type_enum.insert("subtract", NODE_MIX_SUB); type_enum.insert("divide", NODE_MIX_DIV); type_enum.insert("difference", NODE_MIX_DIFF); type_enum.insert("darken", NODE_MIX_DARK); type_enum.insert("lighten", NODE_MIX_LIGHT); type_enum.insert("dodge", NODE_MIX_DODGE); type_enum.insert("burn", NODE_MIX_BURN); type_enum.insert("hue", NODE_MIX_HUE); type_enum.insert("saturation", NODE_MIX_SAT); type_enum.insert("value", NODE_MIX_VAL); type_enum.insert("color", NODE_MIX_COL); type_enum.insert("soft_light", NODE_MIX_SOFT); type_enum.insert("linear_light", NODE_MIX_LINEAR); type_enum.insert("exclusion", NODE_MIX_EXCLUSION); SOCKET_ENUM(blend_type, "Type", type_enum, NODE_MIX_BLEND); SOCKET_IN_FLOAT(fac, "Factor", 0.5f); SOCKET_IN_COLOR(a, "A", zero_float3()); SOCKET_IN_COLOR(b, "B", zero_float3()); SOCKET_BOOLEAN(use_clamp_result, "Use Clamp Result", false); SOCKET_BOOLEAN(use_clamp, "Use Clamp", true); SOCKET_OUT_COLOR(result, "Result"); return type; } MixColorNode::MixColorNode() : ShaderNode(get_node_type()) {} void MixColorNode::compile(SVMCompiler &compiler) { ShaderInput *fac_in = input("Factor"); ShaderInput *a_in = input("A"); ShaderInput *b_in = input("B"); ShaderOutput *result_out = output("Result"); const int fac_in_stack_offset = compiler.stack_assign(fac_in); const int a_in_stack_offset = compiler.stack_assign(a_in); const int b_in_stack_offset = compiler.stack_assign(b_in); compiler.add_node( NODE_MIX_COLOR, compiler.encode_uchar4(use_clamp, blend_type, use_clamp_result), compiler.encode_uchar4(fac_in_stack_offset, a_in_stack_offset, b_in_stack_offset), compiler.stack_assign(result_out)); } void MixColorNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "blend_type"); compiler.parameter(this, "use_clamp"); compiler.parameter(this, "use_clamp_result"); compiler.add(this, "node_mix_color"); } void MixColorNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { if (use_clamp) { fac = clamp(fac, 0.0f, 1.0f); } folder.make_constant_clamp(svm_mix(blend_type, fac, a, b), use_clamp_result); } else { folder.fold_mix_color(blend_type, use_clamp, use_clamp_result); } } bool MixColorNode::is_linear_operation() { switch (blend_type) { case NODE_MIX_BLEND: case NODE_MIX_ADD: case NODE_MIX_MUL: case NODE_MIX_SUB: break; default: return false; } return use_clamp == false && use_clamp_result == false && input("Factor")->link == nullptr; } /* Mix Float */ NODE_DEFINE(MixFloatNode) { NodeType *type = NodeType::add("mix_float", create, NodeType::SHADER); SOCKET_IN_FLOAT(fac, "Factor", 0.5f); SOCKET_IN_FLOAT(a, "A", 0.0f); SOCKET_IN_FLOAT(b, "B", 0.0f); SOCKET_BOOLEAN(use_clamp, "Use Clamp", true); SOCKET_OUT_FLOAT(result, "Result"); return type; } MixFloatNode::MixFloatNode() : ShaderNode(get_node_type()) {} void MixFloatNode::compile(SVMCompiler &compiler) { ShaderInput *fac_in = input("Factor"); ShaderInput *a_in = input("A"); ShaderInput *b_in = input("B"); ShaderOutput *result_out = output("Result"); const int fac_in_stack_offset = compiler.stack_assign(fac_in); const int a_in_stack_offset = compiler.stack_assign(a_in); const int b_in_stack_offset = compiler.stack_assign(b_in); compiler.add_node( NODE_MIX_FLOAT, use_clamp, compiler.encode_uchar4(fac_in_stack_offset, a_in_stack_offset, b_in_stack_offset), compiler.stack_assign(result_out)); } void MixFloatNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "use_clamp"); compiler.add(this, "node_mix_float"); } void MixFloatNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { if (use_clamp) { fac = clamp(fac, 0.0f, 1.0f); } folder.make_constant(a * (1 - fac) + b * fac); } else { folder.fold_mix_float(use_clamp, false); } } bool MixFloatNode::is_linear_operation() { return use_clamp == false && input("Factor")->link == nullptr; } /* Mix Vector */ NODE_DEFINE(MixVectorNode) { NodeType *type = NodeType::add("mix_vector", create, NodeType::SHADER); SOCKET_IN_FLOAT(fac, "Factor", 0.5f); SOCKET_IN_VECTOR(a, "A", zero_float3()); SOCKET_IN_VECTOR(b, "B", zero_float3()); SOCKET_BOOLEAN(use_clamp, "Use Clamp", true); SOCKET_OUT_VECTOR(result, "Result"); return type; } MixVectorNode::MixVectorNode() : ShaderNode(get_node_type()) {} void MixVectorNode::compile(SVMCompiler &compiler) { ShaderInput *fac_in = input("Factor"); ShaderInput *a_in = input("A"); ShaderInput *b_in = input("B"); ShaderOutput *result_out = output("Result"); const int fac_in_stack_offset = compiler.stack_assign(fac_in); const int a_in_stack_offset = compiler.stack_assign(a_in); const int b_in_stack_offset = compiler.stack_assign(b_in); compiler.add_node( NODE_MIX_VECTOR, compiler.encode_uchar4(use_clamp, fac_in_stack_offset, a_in_stack_offset, b_in_stack_offset), compiler.stack_assign(result_out)); } void MixVectorNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "use_clamp"); compiler.add(this, "node_mix_vector"); } void MixVectorNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { if (use_clamp) { fac = clamp(fac, 0.0f, 1.0f); } folder.make_constant(a * (one_float3() - fac) + b * fac); } else { folder.fold_mix_color(NODE_MIX_BLEND, use_clamp, false); } } bool MixVectorNode::is_linear_operation() { return use_clamp == false && input("Factor")->link == nullptr; } /* Mix Vector Non Uniform */ NODE_DEFINE(MixVectorNonUniformNode) { NodeType *type = NodeType::add("mix_vector_non_uniform", create, NodeType::SHADER); SOCKET_IN_VECTOR(fac, "Factor", make_float3(0.5f, 0.5f, 0.5f)); SOCKET_IN_VECTOR(a, "A", zero_float3()); SOCKET_IN_VECTOR(b, "B", zero_float3()); SOCKET_BOOLEAN(use_clamp, "Use Clamp", true); SOCKET_OUT_VECTOR(result, "Result"); return type; } MixVectorNonUniformNode::MixVectorNonUniformNode() : ShaderNode(get_node_type()) {} void MixVectorNonUniformNode::compile(SVMCompiler &compiler) { ShaderInput *fac_in = input("Factor"); ShaderInput *a_in = input("A"); ShaderInput *b_in = input("B"); ShaderOutput *result_out = output("Result"); const int fac_in_stack_offset = compiler.stack_assign(fac_in); const int a_in_stack_offset = compiler.stack_assign(a_in); const int b_in_stack_offset = compiler.stack_assign(b_in); compiler.add_node( NODE_MIX_VECTOR_NON_UNIFORM, compiler.encode_uchar4(use_clamp, fac_in_stack_offset, a_in_stack_offset, b_in_stack_offset), compiler.stack_assign(result_out)); } void MixVectorNonUniformNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "use_clamp"); compiler.add(this, "node_mix_vector_non_uniform"); } void MixVectorNonUniformNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { if (use_clamp) { fac = saturate(fac); } folder.make_constant(a * (one_float3() - fac) + b * fac); } } bool MixVectorNonUniformNode::is_linear_operation() { return use_clamp == false && input("Factor")->link == nullptr; } /* Combine Color */ NODE_DEFINE(CombineColorNode) { NodeType *type = NodeType::add("combine_color", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("rgb", NODE_COMBSEP_COLOR_RGB); type_enum.insert("hsv", NODE_COMBSEP_COLOR_HSV); type_enum.insert("hsl", NODE_COMBSEP_COLOR_HSL); SOCKET_ENUM(color_type, "Type", type_enum, NODE_COMBSEP_COLOR_RGB); SOCKET_IN_FLOAT(r, "Red", 0.0f); SOCKET_IN_FLOAT(g, "Green", 0.0f); SOCKET_IN_FLOAT(b, "Blue", 0.0f); SOCKET_OUT_COLOR(color, "Color"); return type; } CombineColorNode::CombineColorNode() : ShaderNode(get_node_type()) {} void CombineColorNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { folder.make_constant(svm_combine_color(color_type, make_float3(r, g, b))); } } void CombineColorNode::compile(SVMCompiler &compiler) { ShaderInput *red_in = input("Red"); ShaderInput *green_in = input("Green"); ShaderInput *blue_in = input("Blue"); ShaderOutput *color_out = output("Color"); const int red_stack_offset = compiler.stack_assign(red_in); const int green_stack_offset = compiler.stack_assign(green_in); const int blue_stack_offset = compiler.stack_assign(blue_in); const int color_stack_offset = compiler.stack_assign(color_out); compiler.add_node( NODE_COMBINE_COLOR, color_type, compiler.encode_uchar4(red_stack_offset, green_stack_offset, blue_stack_offset), color_stack_offset); } void CombineColorNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "color_type"); compiler.add(this, "node_combine_color"); } /* Combine XYZ */ NODE_DEFINE(CombineXYZNode) { NodeType *type = NodeType::add("combine_xyz", create, NodeType::SHADER); SOCKET_IN_FLOAT(x, "X", 0.0f); SOCKET_IN_FLOAT(y, "Y", 0.0f); SOCKET_IN_FLOAT(z, "Z", 0.0f); SOCKET_OUT_VECTOR(vector, "Vector"); return type; } CombineXYZNode::CombineXYZNode() : ShaderNode(get_node_type()) {} void CombineXYZNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { folder.make_constant(make_float3(x, y, z)); } } void CombineXYZNode::compile(SVMCompiler &compiler) { ShaderInput *x_in = input("X"); ShaderInput *y_in = input("Y"); ShaderInput *z_in = input("Z"); ShaderOutput *vector_out = output("Vector"); compiler.add_node( NODE_COMBINE_VECTOR, compiler.stack_assign(x_in), 0, compiler.stack_assign(vector_out)); compiler.add_node( NODE_COMBINE_VECTOR, compiler.stack_assign(y_in), 1, compiler.stack_assign(vector_out)); compiler.add_node( NODE_COMBINE_VECTOR, compiler.stack_assign(z_in), 2, compiler.stack_assign(vector_out)); } void CombineXYZNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_combine_xyz"); } /* Gamma */ NODE_DEFINE(GammaNode) { NodeType *type = NodeType::add("gamma", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", zero_float3()); SOCKET_IN_FLOAT(gamma, "Gamma", 1.0f); SOCKET_OUT_COLOR(color, "Color"); return type; } GammaNode::GammaNode() : ShaderNode(get_node_type()) {} void GammaNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { folder.make_constant(svm_math_gamma_color(color, gamma)); } else { ShaderInput *color_in = input("Color"); ShaderInput *gamma_in = input("Gamma"); /* 1 ^ X == X ^ 0 == 1 */ if (folder.is_one(color_in) || folder.is_zero(gamma_in)) { folder.make_one(); } /* X ^ 1 == X */ else if (folder.is_one(gamma_in)) { folder.try_bypass_or_make_constant(color_in, false); } } } void GammaNode::compile(SVMCompiler &compiler) { ShaderInput *color_in = input("Color"); ShaderInput *gamma_in = input("Gamma"); ShaderOutput *color_out = output("Color"); compiler.add_node(NODE_GAMMA, compiler.stack_assign(gamma_in), compiler.stack_assign(color_in), compiler.stack_assign(color_out)); } void GammaNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_gamma"); } /* Bright Contrast */ NODE_DEFINE(BrightContrastNode) { NodeType *type = NodeType::add("brightness_contrast", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", zero_float3()); SOCKET_IN_FLOAT(bright, "Bright", 0.0f); SOCKET_IN_FLOAT(contrast, "Contrast", 0.0f); SOCKET_OUT_COLOR(color, "Color"); return type; } BrightContrastNode::BrightContrastNode() : ShaderNode(get_node_type()) {} void BrightContrastNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { folder.make_constant(svm_brightness_contrast(color, bright, contrast)); } } void BrightContrastNode::compile(SVMCompiler &compiler) { ShaderInput *color_in = input("Color"); ShaderInput *bright_in = input("Bright"); ShaderInput *contrast_in = input("Contrast"); ShaderOutput *color_out = output("Color"); compiler.add_node(NODE_BRIGHTCONTRAST, compiler.stack_assign(color_in), compiler.stack_assign(color_out), compiler.encode_uchar4(compiler.stack_assign(bright_in), compiler.stack_assign(contrast_in))); } void BrightContrastNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_brightness"); } /* Separate Color */ NODE_DEFINE(SeparateColorNode) { NodeType *type = NodeType::add("separate_color", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("rgb", NODE_COMBSEP_COLOR_RGB); type_enum.insert("hsv", NODE_COMBSEP_COLOR_HSV); type_enum.insert("hsl", NODE_COMBSEP_COLOR_HSL); SOCKET_ENUM(color_type, "Type", type_enum, NODE_COMBSEP_COLOR_RGB); SOCKET_IN_COLOR(color, "Color", zero_float3()); SOCKET_OUT_FLOAT(r, "Red"); SOCKET_OUT_FLOAT(g, "Green"); SOCKET_OUT_FLOAT(b, "Blue"); return type; } SeparateColorNode::SeparateColorNode() : ShaderNode(get_node_type()) {} void SeparateColorNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { float3 col = svm_separate_color(color_type, color); for (int channel = 0; channel < 3; channel++) { if (outputs[channel] == folder.output) { folder.make_constant(col[channel]); return; } } } } void SeparateColorNode::compile(SVMCompiler &compiler) { ShaderInput *color_in = input("Color"); ShaderOutput *red_out = output("Red"); ShaderOutput *green_out = output("Green"); ShaderOutput *blue_out = output("Blue"); const int color_stack_offset = compiler.stack_assign(color_in); const int red_stack_offset = compiler.stack_assign(red_out); const int green_stack_offset = compiler.stack_assign(green_out); const int blue_stack_offset = compiler.stack_assign(blue_out); compiler.add_node( NODE_SEPARATE_COLOR, color_type, color_stack_offset, compiler.encode_uchar4(red_stack_offset, green_stack_offset, blue_stack_offset)); } void SeparateColorNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "color_type"); compiler.add(this, "node_separate_color"); } /* Separate XYZ */ NODE_DEFINE(SeparateXYZNode) { NodeType *type = NodeType::add("separate_xyz", create, NodeType::SHADER); SOCKET_IN_COLOR(vector, "Vector", zero_float3()); SOCKET_OUT_FLOAT(x, "X"); SOCKET_OUT_FLOAT(y, "Y"); SOCKET_OUT_FLOAT(z, "Z"); return type; } SeparateXYZNode::SeparateXYZNode() : ShaderNode(get_node_type()) {} void SeparateXYZNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { for (int channel = 0; channel < 3; channel++) { if (outputs[channel] == folder.output) { folder.make_constant(vector[channel]); return; } } } } void SeparateXYZNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderOutput *x_out = output("X"); ShaderOutput *y_out = output("Y"); ShaderOutput *z_out = output("Z"); compiler.add_node( NODE_SEPARATE_VECTOR, compiler.stack_assign(vector_in), 0, compiler.stack_assign(x_out)); compiler.add_node( NODE_SEPARATE_VECTOR, compiler.stack_assign(vector_in), 1, compiler.stack_assign(y_out)); compiler.add_node( NODE_SEPARATE_VECTOR, compiler.stack_assign(vector_in), 2, compiler.stack_assign(z_out)); } void SeparateXYZNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_separate_xyz"); } /* Hue/Saturation/Value */ NODE_DEFINE(HSVNode) { NodeType *type = NodeType::add("hsv", create, NodeType::SHADER); SOCKET_IN_FLOAT(hue, "Hue", 0.5f); SOCKET_IN_FLOAT(saturation, "Saturation", 1.0f); SOCKET_IN_FLOAT(value, "Value", 1.0f); SOCKET_IN_FLOAT(fac, "Fac", 1.0f); SOCKET_IN_COLOR(color, "Color", zero_float3()); SOCKET_OUT_COLOR(color, "Color"); return type; } HSVNode::HSVNode() : ShaderNode(get_node_type()) {} void HSVNode::compile(SVMCompiler &compiler) { ShaderInput *hue_in = input("Hue"); ShaderInput *saturation_in = input("Saturation"); ShaderInput *value_in = input("Value"); ShaderInput *fac_in = input("Fac"); ShaderInput *color_in = input("Color"); ShaderOutput *color_out = output("Color"); compiler.add_node(NODE_HSV, compiler.encode_uchar4(compiler.stack_assign(color_in), compiler.stack_assign(fac_in), compiler.stack_assign(color_out)), compiler.encode_uchar4(compiler.stack_assign(hue_in), compiler.stack_assign(saturation_in), compiler.stack_assign(value_in))); } void HSVNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_hsv"); } /* Attribute */ NODE_DEFINE(AttributeNode) { NodeType *type = NodeType::add("attribute", create, NodeType::SHADER); SOCKET_STRING(attribute, "Attribute", ustring()); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_VECTOR(vector, "Vector"); SOCKET_OUT_FLOAT(fac, "Fac"); SOCKET_OUT_FLOAT(alpha, "Alpha"); return type; } AttributeNode::AttributeNode() : ShaderNode(get_node_type()) {} void AttributeNode::attributes(Shader *shader, AttributeRequestSet *attributes) { ShaderOutput *color_out = output("Color"); ShaderOutput *vector_out = output("Vector"); ShaderOutput *fac_out = output("Fac"); ShaderOutput *alpha_out = output("Alpha"); if (!color_out->links.empty() || !vector_out->links.empty() || !fac_out->links.empty() || !alpha_out->links.empty()) { attributes->add_standard(attribute); } if (shader->has_volume) { attributes->add(ATTR_STD_GENERATED_TRANSFORM); } ShaderNode::attributes(shader, attributes); } void AttributeNode::compile(SVMCompiler &compiler) { ShaderOutput *color_out = output("Color"); ShaderOutput *vector_out = output("Vector"); ShaderOutput *fac_out = output("Fac"); ShaderOutput *alpha_out = output("Alpha"); ShaderNodeType attr_node = NODE_ATTR; const int attr = compiler.attribute_standard(attribute); const uint bump_filter_or_stochastic = (compiler.output_type() == SHADER_TYPE_VOLUME) ? stochastic_sample : __float_as_uint(bump_filter_width); if (bump == SHADER_BUMP_DX) { attr_node = NODE_ATTR_BUMP_DX; } else if (bump == SHADER_BUMP_DY) { attr_node = NODE_ATTR_BUMP_DY; } if (!color_out->links.empty() || !vector_out->links.empty()) { if (!color_out->links.empty()) { compiler.add_node( attr_node, attr, compiler.encode_uchar4(compiler.stack_assign(color_out), NODE_ATTR_OUTPUT_FLOAT3), bump_filter_or_stochastic); } if (!vector_out->links.empty()) { compiler.add_node( attr_node, attr, compiler.encode_uchar4(compiler.stack_assign(vector_out), NODE_ATTR_OUTPUT_FLOAT3), bump_filter_or_stochastic); } } if (!fac_out->links.empty()) { compiler.add_node( attr_node, attr, compiler.encode_uchar4(compiler.stack_assign(fac_out), NODE_ATTR_OUTPUT_FLOAT), bump_filter_or_stochastic); } if (!alpha_out->links.empty()) { compiler.add_node( attr_node, attr, compiler.encode_uchar4(compiler.stack_assign(alpha_out), NODE_ATTR_OUTPUT_FLOAT_ALPHA), bump_filter_or_stochastic); } } void AttributeNode::compile(OSLCompiler &compiler) { if (bump == SHADER_BUMP_DX) { compiler.parameter("bump_offset", "dx"); } else if (bump == SHADER_BUMP_DY) { compiler.parameter("bump_offset", "dy"); } else { compiler.parameter("bump_offset", "center"); } compiler.parameter("bump_filter_width", bump_filter_width); if (Attribute::name_standard(attribute.c_str()) != ATTR_STD_NONE) { compiler.parameter("name", (string("geom:") + attribute.c_str()).c_str()); } else { compiler.parameter("name", attribute.c_str()); } compiler.add(this, "node_attribute"); } /* Camera */ NODE_DEFINE(CameraNode) { NodeType *type = NodeType::add("camera_info", create, NodeType::SHADER); SOCKET_OUT_VECTOR(view_vector, "View Vector"); SOCKET_OUT_FLOAT(view_z_depth, "View Z Depth"); SOCKET_OUT_FLOAT(view_distance, "View Distance"); return type; } CameraNode::CameraNode() : ShaderNode(get_node_type()) {} void CameraNode::compile(SVMCompiler &compiler) { ShaderOutput *vector_out = output("View Vector"); ShaderOutput *z_depth_out = output("View Z Depth"); ShaderOutput *distance_out = output("View Distance"); compiler.add_node(NODE_CAMERA, compiler.stack_assign(vector_out), compiler.stack_assign(z_depth_out), compiler.stack_assign(distance_out)); } void CameraNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_camera"); } /* Fresnel */ NODE_DEFINE(FresnelNode) { NodeType *type = NodeType::add("fresnel", create, NodeType::SHADER); SOCKET_IN_NORMAL( normal, "Normal", zero_float3(), SocketType::LINK_NORMAL | SocketType::OSL_INTERNAL); SOCKET_IN_FLOAT(IOR, "IOR", 1.5f); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } FresnelNode::FresnelNode() : ShaderNode(get_node_type()) {} void FresnelNode::compile(SVMCompiler &compiler) { ShaderInput *normal_in = input("Normal"); ShaderInput *IOR_in = input("IOR"); ShaderOutput *fac_out = output("Fac"); compiler.add_node(NODE_FRESNEL, compiler.stack_assign(IOR_in), __float_as_int(IOR), compiler.encode_uchar4(compiler.stack_assign_if_linked(normal_in), compiler.stack_assign(fac_out))); } void FresnelNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_fresnel"); } /* Layer Weight */ NODE_DEFINE(LayerWeightNode) { NodeType *type = NodeType::add("layer_weight", create, NodeType::SHADER); SOCKET_IN_NORMAL( normal, "Normal", zero_float3(), SocketType::LINK_NORMAL | SocketType::OSL_INTERNAL); SOCKET_IN_FLOAT(blend, "Blend", 0.5f); SOCKET_OUT_FLOAT(fresnel, "Fresnel"); SOCKET_OUT_FLOAT(facing, "Facing"); return type; } LayerWeightNode::LayerWeightNode() : ShaderNode(get_node_type()) {} void LayerWeightNode::compile(SVMCompiler &compiler) { ShaderInput *normal_in = input("Normal"); ShaderInput *blend_in = input("Blend"); ShaderOutput *fresnel_out = output("Fresnel"); ShaderOutput *facing_out = output("Facing"); if (!fresnel_out->links.empty()) { compiler.add_node(NODE_LAYER_WEIGHT, compiler.stack_assign_if_linked(blend_in), __float_as_int(blend), compiler.encode_uchar4(NODE_LAYER_WEIGHT_FRESNEL, compiler.stack_assign_if_linked(normal_in), compiler.stack_assign(fresnel_out))); } if (!facing_out->links.empty()) { compiler.add_node(NODE_LAYER_WEIGHT, compiler.stack_assign_if_linked(blend_in), __float_as_int(blend), compiler.encode_uchar4(NODE_LAYER_WEIGHT_FACING, compiler.stack_assign_if_linked(normal_in), compiler.stack_assign(facing_out))); } } void LayerWeightNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_layer_weight"); } /* Wireframe */ NODE_DEFINE(WireframeNode) { NodeType *type = NodeType::add("wireframe", create, NodeType::SHADER); SOCKET_BOOLEAN(use_pixel_size, "Use Pixel Size", false); SOCKET_IN_FLOAT(size, "Size", 0.01f); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } WireframeNode::WireframeNode() : ShaderNode(get_node_type()) {} void WireframeNode::compile(SVMCompiler &compiler) { ShaderInput *size_in = input("Size"); ShaderOutput *fac_out = output("Fac"); NodeBumpOffset bump_offset = NODE_BUMP_OFFSET_CENTER; if (bump == SHADER_BUMP_DX) { bump_offset = NODE_BUMP_OFFSET_DX; } else if (bump == SHADER_BUMP_DY) { bump_offset = NODE_BUMP_OFFSET_DY; } compiler.add_node( NODE_WIREFRAME, compiler.stack_assign(size_in), __float_as_uint(bump_filter_width), compiler.encode_uchar4(use_pixel_size, bump_offset, compiler.stack_assign(fac_out), 0)); } void WireframeNode::compile(OSLCompiler &compiler) { if (bump == SHADER_BUMP_DX) { compiler.parameter("bump_offset", "dx"); } else if (bump == SHADER_BUMP_DY) { compiler.parameter("bump_offset", "dy"); } else { compiler.parameter("bump_offset", "center"); } compiler.parameter("bump_filter_width", bump_filter_width); compiler.parameter(this, "use_pixel_size"); compiler.add(this, "node_wireframe"); } /* Wavelength */ NODE_DEFINE(WavelengthNode) { NodeType *type = NodeType::add("wavelength", create, NodeType::SHADER); SOCKET_IN_FLOAT(wavelength, "Wavelength", 500.0f); SOCKET_OUT_COLOR(color, "Color"); return type; } WavelengthNode::WavelengthNode() : ShaderNode(get_node_type()) {} void WavelengthNode::compile(SVMCompiler &compiler) { ShaderInput *wavelength_in = input("Wavelength"); ShaderOutput *color_out = output("Color"); compiler.add_node( NODE_WAVELENGTH, compiler.stack_assign(wavelength_in), compiler.stack_assign(color_out)); } void WavelengthNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_wavelength"); } /* Blackbody */ NODE_DEFINE(BlackbodyNode) { NodeType *type = NodeType::add("blackbody", create, NodeType::SHADER); SOCKET_IN_FLOAT(temperature, "Temperature", 1200.0f); SOCKET_OUT_COLOR(color, "Color"); return type; } BlackbodyNode::BlackbodyNode() : ShaderNode(get_node_type()) {} void BlackbodyNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { const float3 rgb_rec709 = svm_math_blackbody_color_rec709(temperature); const float3 rgb = folder.scene->shader_manager->rec709_to_scene_linear(rgb_rec709); folder.make_constant(max(rgb, zero_float3())); } } void BlackbodyNode::compile(SVMCompiler &compiler) { ShaderInput *temperature_in = input("Temperature"); ShaderOutput *color_out = output("Color"); compiler.add_node( NODE_BLACKBODY, compiler.stack_assign(temperature_in), compiler.stack_assign(color_out)); } void BlackbodyNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_blackbody"); } /* Output */ NODE_DEFINE(OutputNode) { NodeType *type = NodeType::add("output", create, NodeType::SHADER); SOCKET_IN_CLOSURE(surface, "Surface"); SOCKET_IN_CLOSURE(volume, "Volume"); SOCKET_IN_VECTOR(displacement, "Displacement", zero_float3()); SOCKET_IN_NORMAL(normal, "Normal", zero_float3()); return type; } OutputNode::OutputNode() : ShaderNode(get_node_type()) { special_type = SHADER_SPECIAL_TYPE_OUTPUT; } void OutputNode::compile(SVMCompiler &compiler) { if (compiler.output_type() == SHADER_TYPE_DISPLACEMENT) { ShaderInput *displacement_in = input("Displacement"); if (displacement_in->link) { compiler.add_node(NODE_SET_DISPLACEMENT, compiler.stack_assign(displacement_in)); } } } void OutputNode::compile(OSLCompiler &compiler) { if (compiler.output_type() == SHADER_TYPE_SURFACE) { compiler.add(this, "node_output_surface"); } else if (compiler.output_type() == SHADER_TYPE_VOLUME) { compiler.add(this, "node_output_volume"); } else if (compiler.output_type() == SHADER_TYPE_DISPLACEMENT) { compiler.add(this, "node_output_displacement"); } } /* Map Range Node */ NODE_DEFINE(MapRangeNode) { NodeType *type = NodeType::add("map_range", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("linear", NODE_MAP_RANGE_LINEAR); type_enum.insert("stepped", NODE_MAP_RANGE_STEPPED); type_enum.insert("smoothstep", NODE_MAP_RANGE_SMOOTHSTEP); type_enum.insert("smootherstep", NODE_MAP_RANGE_SMOOTHERSTEP); SOCKET_ENUM(range_type, "Type", type_enum, NODE_MAP_RANGE_LINEAR); SOCKET_IN_FLOAT(value, "Value", 1.0f); SOCKET_IN_FLOAT(from_min, "From Min", 0.0f); SOCKET_IN_FLOAT(from_max, "From Max", 1.0f); SOCKET_IN_FLOAT(to_min, "To Min", 0.0f); SOCKET_IN_FLOAT(to_max, "To Max", 1.0f); SOCKET_IN_FLOAT(steps, "Steps", 4.0f); SOCKET_IN_BOOLEAN(clamp, "Clamp", false); SOCKET_OUT_FLOAT(result, "Result"); return type; } MapRangeNode::MapRangeNode() : ShaderNode(get_node_type()) {} void MapRangeNode::expand(ShaderGraph *graph) { if (clamp) { ShaderOutput *result_out = output("Result"); if (!result_out->links.empty()) { ClampNode *clamp_node = graph->create_node(); clamp_node->set_clamp_type(NODE_CLAMP_RANGE); graph->relink(result_out, clamp_node->output("Result")); graph->connect(result_out, clamp_node->input("Value")); if (input("To Min")->link) { graph->connect(input("To Min")->link, clamp_node->input("Min")); } else { clamp_node->set_min(to_min); } if (input("To Max")->link) { graph->connect(input("To Max")->link, clamp_node->input("Max")); } else { clamp_node->set_max(to_max); } } } } bool MapRangeNode::is_linear_operation() { if (range_type != NODE_MAP_RANGE_LINEAR) { return false; } ShaderInput *from_min_in = input("To Min"); ShaderInput *from_max_in = input("To Max"); ShaderInput *to_min_in = input("To Min"); ShaderInput *to_max_in = input("To Max"); return from_min_in->link == nullptr && from_max_in->link == nullptr && to_min_in->link == nullptr && to_max_in->link == nullptr; } void MapRangeNode::compile(SVMCompiler &compiler) { ShaderInput *value_in = input("Value"); ShaderInput *from_min_in = input("From Min"); ShaderInput *from_max_in = input("From Max"); ShaderInput *to_min_in = input("To Min"); ShaderInput *to_max_in = input("To Max"); ShaderInput *steps_in = input("Steps"); ShaderOutput *result_out = output("Result"); const int value_stack_offset = compiler.stack_assign(value_in); const int from_min_stack_offset = compiler.stack_assign_if_linked(from_min_in); const int from_max_stack_offset = compiler.stack_assign_if_linked(from_max_in); const int to_min_stack_offset = compiler.stack_assign_if_linked(to_min_in); const int to_max_stack_offset = compiler.stack_assign_if_linked(to_max_in); const int steps_stack_offset = compiler.stack_assign(steps_in); const int result_stack_offset = compiler.stack_assign(result_out); compiler.add_node( NODE_MAP_RANGE, value_stack_offset, compiler.encode_uchar4( from_min_stack_offset, from_max_stack_offset, to_min_stack_offset, to_max_stack_offset), compiler.encode_uchar4(range_type, steps_stack_offset, result_stack_offset)); compiler.add_node(__float_as_int(from_min), __float_as_int(from_max), __float_as_int(to_min), __float_as_int(to_max)); compiler.add_node(__float_as_int(steps)); } void MapRangeNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "range_type"); compiler.add(this, "node_map_range"); } /* Vector Map Range Node */ NODE_DEFINE(VectorMapRangeNode) { NodeType *type = NodeType::add("vector_map_range", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("linear", NODE_MAP_RANGE_LINEAR); type_enum.insert("stepped", NODE_MAP_RANGE_STEPPED); type_enum.insert("smoothstep", NODE_MAP_RANGE_SMOOTHSTEP); type_enum.insert("smootherstep", NODE_MAP_RANGE_SMOOTHERSTEP); SOCKET_ENUM(range_type, "Type", type_enum, NODE_MAP_RANGE_LINEAR); SOCKET_IN_VECTOR(vector, "Vector", zero_float3()); SOCKET_IN_VECTOR(from_min, "From_Min_FLOAT3", zero_float3()); SOCKET_IN_VECTOR(from_max, "From_Max_FLOAT3", one_float3()); SOCKET_IN_VECTOR(to_min, "To_Min_FLOAT3", zero_float3()); SOCKET_IN_VECTOR(to_max, "To_Max_FLOAT3", one_float3()); SOCKET_IN_VECTOR(steps, "Steps_FLOAT3", make_float3(4.0f)); SOCKET_BOOLEAN(use_clamp, "Use Clamp", false); SOCKET_OUT_VECTOR(vector, "Vector"); return type; } VectorMapRangeNode::VectorMapRangeNode() : ShaderNode(get_node_type()) {} void VectorMapRangeNode::expand(ShaderGraph * /*graph*/) {} bool VectorMapRangeNode::is_linear_operation() { if (range_type != NODE_MAP_RANGE_LINEAR) { return false; } ShaderInput *from_min_in = input("From_Min_FLOAT3"); ShaderInput *from_max_in = input("From_Max_FLOAT3"); ShaderInput *to_min_in = input("To_Min_FLOAT3"); ShaderInput *to_max_in = input("To_Max_FLOAT3"); return from_min_in->link == nullptr && from_max_in->link == nullptr && to_min_in->link == nullptr && to_max_in->link == nullptr; } void VectorMapRangeNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *from_min_in = input("From_Min_FLOAT3"); ShaderInput *from_max_in = input("From_Max_FLOAT3"); ShaderInput *to_min_in = input("To_Min_FLOAT3"); ShaderInput *to_max_in = input("To_Max_FLOAT3"); ShaderInput *steps_in = input("Steps_FLOAT3"); ShaderOutput *vector_out = output("Vector"); const int value_stack_offset = compiler.stack_assign(vector_in); const int from_min_stack_offset = compiler.stack_assign(from_min_in); const int from_max_stack_offset = compiler.stack_assign(from_max_in); const int to_min_stack_offset = compiler.stack_assign(to_min_in); const int to_max_stack_offset = compiler.stack_assign(to_max_in); const int steps_stack_offset = compiler.stack_assign(steps_in); const int result_stack_offset = compiler.stack_assign(vector_out); compiler.add_node( NODE_VECTOR_MAP_RANGE, value_stack_offset, compiler.encode_uchar4( from_min_stack_offset, from_max_stack_offset, to_min_stack_offset, to_max_stack_offset), compiler.encode_uchar4(steps_stack_offset, use_clamp, range_type, result_stack_offset)); } void VectorMapRangeNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "range_type"); compiler.parameter(this, "use_clamp"); compiler.add(this, "node_vector_map_range"); } /* Clamp Node */ NODE_DEFINE(ClampNode) { NodeType *type = NodeType::add("clamp", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("minmax", NODE_CLAMP_MINMAX); type_enum.insert("range", NODE_CLAMP_RANGE); SOCKET_ENUM(clamp_type, "Type", type_enum, NODE_CLAMP_MINMAX); SOCKET_IN_FLOAT(value, "Value", 1.0f); SOCKET_IN_FLOAT(min, "Min", 0.0f); SOCKET_IN_FLOAT(max, "Max", 1.0f); SOCKET_OUT_FLOAT(result, "Result"); return type; } ClampNode::ClampNode() : ShaderNode(get_node_type()) {} void ClampNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { if (clamp_type == NODE_CLAMP_RANGE && (min > max)) { folder.make_constant(clamp(value, max, min)); } else { folder.make_constant(clamp(value, min, max)); } } } void ClampNode::compile(SVMCompiler &compiler) { ShaderInput *value_in = input("Value"); ShaderInput *min_in = input("Min"); ShaderInput *max_in = input("Max"); ShaderOutput *result_out = output("Result"); const int value_stack_offset = compiler.stack_assign(value_in); const int min_stack_offset = compiler.stack_assign(min_in); const int max_stack_offset = compiler.stack_assign(max_in); const int result_stack_offset = compiler.stack_assign(result_out); compiler.add_node(NODE_CLAMP, value_stack_offset, compiler.encode_uchar4(min_stack_offset, max_stack_offset, clamp_type), result_stack_offset); compiler.add_node(__float_as_int(min), __float_as_int(max)); } void ClampNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "clamp_type"); compiler.add(this, "node_clamp"); } /* AOV Output */ NODE_DEFINE(OutputAOVNode) { NodeType *type = NodeType::add("aov_output", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", zero_float3()); SOCKET_IN_FLOAT(value, "Value", 0.0f); SOCKET_STRING(name, "AOV Name", ustring("")); return type; } OutputAOVNode::OutputAOVNode() : ShaderNode(get_node_type()) { special_type = SHADER_SPECIAL_TYPE_OUTPUT_AOV; offset = -1; } void OutputAOVNode::simplify_settings(Scene *scene) { offset = scene->film->get_aov_offset(scene, name.string(), is_color); if (offset == -1) { offset = scene->film->get_aov_offset(scene, name.string(), is_color); } if (offset == -1 || is_color) { input("Value")->disconnect(); } if (offset == -1 || !is_color) { input("Color")->disconnect(); } } void OutputAOVNode::compile(SVMCompiler &compiler) { assert(offset >= 0); if (is_color) { compiler.add_node(NODE_AOV_COLOR, compiler.stack_assign(input("Color")), offset); } else { compiler.add_node(NODE_AOV_VALUE, compiler.stack_assign(input("Value")), offset); } } void OutputAOVNode::compile(OSLCompiler & /*compiler*/) { /* TODO */ } /* Math */ NODE_DEFINE(MathNode) { NodeType *type = NodeType::add("math", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("add", NODE_MATH_ADD); type_enum.insert("subtract", NODE_MATH_SUBTRACT); type_enum.insert("multiply", NODE_MATH_MULTIPLY); type_enum.insert("divide", NODE_MATH_DIVIDE); type_enum.insert("multiply_add", NODE_MATH_MULTIPLY_ADD); type_enum.insert("sine", NODE_MATH_SINE); type_enum.insert("cosine", NODE_MATH_COSINE); type_enum.insert("tangent", NODE_MATH_TANGENT); type_enum.insert("sinh", NODE_MATH_SINH); type_enum.insert("cosh", NODE_MATH_COSH); type_enum.insert("tanh", NODE_MATH_TANH); type_enum.insert("arcsine", NODE_MATH_ARCSINE); type_enum.insert("arccosine", NODE_MATH_ARCCOSINE); type_enum.insert("arctangent", NODE_MATH_ARCTANGENT); type_enum.insert("power", NODE_MATH_POWER); type_enum.insert("logarithm", NODE_MATH_LOGARITHM); type_enum.insert("minimum", NODE_MATH_MINIMUM); type_enum.insert("maximum", NODE_MATH_MAXIMUM); type_enum.insert("round", NODE_MATH_ROUND); type_enum.insert("less_than", NODE_MATH_LESS_THAN); type_enum.insert("greater_than", NODE_MATH_GREATER_THAN); type_enum.insert("modulo", NODE_MATH_MODULO); type_enum.insert("floored_modulo", NODE_MATH_FLOORED_MODULO); type_enum.insert("absolute", NODE_MATH_ABSOLUTE); type_enum.insert("arctan2", NODE_MATH_ARCTAN2); type_enum.insert("floor", NODE_MATH_FLOOR); type_enum.insert("ceil", NODE_MATH_CEIL); type_enum.insert("fraction", NODE_MATH_FRACTION); type_enum.insert("trunc", NODE_MATH_TRUNC); type_enum.insert("snap", NODE_MATH_SNAP); type_enum.insert("wrap", NODE_MATH_WRAP); type_enum.insert("pingpong", NODE_MATH_PINGPONG); type_enum.insert("sqrt", NODE_MATH_SQRT); type_enum.insert("inversesqrt", NODE_MATH_INV_SQRT); type_enum.insert("sign", NODE_MATH_SIGN); type_enum.insert("exponent", NODE_MATH_EXPONENT); type_enum.insert("radians", NODE_MATH_RADIANS); type_enum.insert("degrees", NODE_MATH_DEGREES); type_enum.insert("smoothmin", NODE_MATH_SMOOTH_MIN); type_enum.insert("smoothmax", NODE_MATH_SMOOTH_MAX); type_enum.insert("compare", NODE_MATH_COMPARE); SOCKET_ENUM(math_type, "Type", type_enum, NODE_MATH_ADD); SOCKET_BOOLEAN(use_clamp, "Use Clamp", false); SOCKET_IN_FLOAT(value1, "Value1", 0.5f); SOCKET_IN_FLOAT(value2, "Value2", 0.5f); SOCKET_IN_FLOAT(value3, "Value3", 0.0f); SOCKET_OUT_FLOAT(value, "Value"); return type; } MathNode::MathNode() : ShaderNode(get_node_type()) {} void MathNode::expand(ShaderGraph *graph) { if (use_clamp) { ShaderOutput *result_out = output("Value"); if (!result_out->links.empty()) { ClampNode *clamp_node = graph->create_node(); clamp_node->set_clamp_type(NODE_CLAMP_MINMAX); clamp_node->set_min(0.0f); clamp_node->set_max(1.0f); graph->relink(result_out, clamp_node->output("Result")); graph->connect(result_out, clamp_node->input("Value")); } } } void MathNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { folder.make_constant(svm_math(math_type, value1, value2, value3)); } else { folder.fold_math(math_type); } } bool MathNode::is_linear_operation() { switch (math_type) { case NODE_MATH_ADD: case NODE_MATH_SUBTRACT: case NODE_MATH_MULTIPLY: case NODE_MATH_MULTIPLY_ADD: break; case NODE_MATH_DIVIDE: return input("Value2")->link == nullptr; default: return false; } int num_variable_inputs = 0; for (ShaderInput *input : inputs) { num_variable_inputs += (input->link) ? 1 : 0; } return num_variable_inputs <= 1; } void MathNode::compile(SVMCompiler &compiler) { ShaderInput *value1_in = input("Value1"); ShaderInput *value2_in = input("Value2"); ShaderInput *value3_in = input("Value3"); ShaderOutput *value_out = output("Value"); const int value1_stack_offset = compiler.stack_assign(value1_in); const int value2_stack_offset = compiler.stack_assign(value2_in); const int value3_stack_offset = compiler.stack_assign(value3_in); const int value_stack_offset = compiler.stack_assign(value_out); compiler.add_node( NODE_MATH, math_type, compiler.encode_uchar4(value1_stack_offset, value2_stack_offset, value3_stack_offset), value_stack_offset); } void MathNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "math_type"); compiler.add(this, "node_math"); } /* VectorMath */ NODE_DEFINE(VectorMathNode) { NodeType *type = NodeType::add("vector_math", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("add", NODE_VECTOR_MATH_ADD); type_enum.insert("subtract", NODE_VECTOR_MATH_SUBTRACT); type_enum.insert("multiply", NODE_VECTOR_MATH_MULTIPLY); type_enum.insert("divide", NODE_VECTOR_MATH_DIVIDE); type_enum.insert("cross_product", NODE_VECTOR_MATH_CROSS_PRODUCT); type_enum.insert("project", NODE_VECTOR_MATH_PROJECT); type_enum.insert("reflect", NODE_VECTOR_MATH_REFLECT); type_enum.insert("refract", NODE_VECTOR_MATH_REFRACT); type_enum.insert("faceforward", NODE_VECTOR_MATH_FACEFORWARD); type_enum.insert("multiply_add", NODE_VECTOR_MATH_MULTIPLY_ADD); type_enum.insert("dot_product", NODE_VECTOR_MATH_DOT_PRODUCT); type_enum.insert("distance", NODE_VECTOR_MATH_DISTANCE); type_enum.insert("length", NODE_VECTOR_MATH_LENGTH); type_enum.insert("scale", NODE_VECTOR_MATH_SCALE); type_enum.insert("normalize", NODE_VECTOR_MATH_NORMALIZE); type_enum.insert("snap", NODE_VECTOR_MATH_SNAP); type_enum.insert("floor", NODE_VECTOR_MATH_FLOOR); type_enum.insert("ceil", NODE_VECTOR_MATH_CEIL); type_enum.insert("modulo", NODE_VECTOR_MATH_MODULO); type_enum.insert("wrap", NODE_VECTOR_MATH_WRAP); type_enum.insert("fraction", NODE_VECTOR_MATH_FRACTION); type_enum.insert("absolute", NODE_VECTOR_MATH_ABSOLUTE); type_enum.insert("power", NODE_VECTOR_MATH_POWER); type_enum.insert("sign", NODE_VECTOR_MATH_SIGN); type_enum.insert("minimum", NODE_VECTOR_MATH_MINIMUM); type_enum.insert("maximum", NODE_VECTOR_MATH_MAXIMUM); type_enum.insert("sine", NODE_VECTOR_MATH_SINE); type_enum.insert("cosine", NODE_VECTOR_MATH_COSINE); type_enum.insert("tangent", NODE_VECTOR_MATH_TANGENT); SOCKET_ENUM(math_type, "Type", type_enum, NODE_VECTOR_MATH_ADD); SOCKET_IN_VECTOR(vector1, "Vector1", zero_float3()); SOCKET_IN_VECTOR(vector2, "Vector2", zero_float3()); SOCKET_IN_VECTOR(vector3, "Vector3", zero_float3()); SOCKET_IN_FLOAT(scale, "Scale", 1.0f); SOCKET_OUT_FLOAT(value, "Value"); SOCKET_OUT_VECTOR(vector, "Vector"); return type; } VectorMathNode::VectorMathNode() : ShaderNode(get_node_type()) {} void VectorMathNode::constant_fold(const ConstantFolder &folder) { float value = 0.0f; float3 vector = zero_float3(); if (folder.all_inputs_constant()) { svm_vector_math(&value, &vector, math_type, vector1, vector2, vector3, scale); if (folder.output == output("Value")) { folder.make_constant(value); } else if (folder.output == output("Vector")) { folder.make_constant(vector); } } else { folder.fold_vector_math(math_type); } } bool VectorMathNode::is_linear_operation() { switch (math_type) { case NODE_VECTOR_MATH_ADD: case NODE_VECTOR_MATH_SUBTRACT: case NODE_VECTOR_MATH_MULTIPLY: case NODE_VECTOR_MATH_MULTIPLY_ADD: break; case NODE_VECTOR_MATH_DIVIDE: return input("Vector2")->link == nullptr; default: return false; } int num_variable_inputs = 0; for (ShaderInput *input : inputs) { num_variable_inputs += (input->link) ? 1 : 0; } return num_variable_inputs <= 1; } void VectorMathNode::compile(SVMCompiler &compiler) { ShaderInput *vector1_in = input("Vector1"); ShaderInput *vector2_in = input("Vector2"); ShaderInput *param1_in = input("Scale"); ShaderOutput *value_out = output("Value"); ShaderOutput *vector_out = output("Vector"); const int vector1_stack_offset = compiler.stack_assign(vector1_in); const int vector2_stack_offset = compiler.stack_assign(vector2_in); const int param1_stack_offset = compiler.stack_assign(param1_in); const int value_stack_offset = compiler.stack_assign_if_linked(value_out); const int vector_stack_offset = compiler.stack_assign_if_linked(vector_out); /* 3 Vector Operators */ if (math_type == NODE_VECTOR_MATH_WRAP || math_type == NODE_VECTOR_MATH_FACEFORWARD || math_type == NODE_VECTOR_MATH_MULTIPLY_ADD) { ShaderInput *vector3_in = input("Vector3"); const int vector3_stack_offset = compiler.stack_assign(vector3_in); compiler.add_node( NODE_VECTOR_MATH, math_type, compiler.encode_uchar4(vector1_stack_offset, vector2_stack_offset, param1_stack_offset), compiler.encode_uchar4(value_stack_offset, vector_stack_offset)); compiler.add_node(vector3_stack_offset); } else { compiler.add_node( NODE_VECTOR_MATH, math_type, compiler.encode_uchar4(vector1_stack_offset, vector2_stack_offset, param1_stack_offset), compiler.encode_uchar4(value_stack_offset, vector_stack_offset)); } } void VectorMathNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "math_type"); compiler.add(this, "node_vector_math"); } /* Vector Rotate */ NODE_DEFINE(VectorRotateNode) { NodeType *type = NodeType::add("vector_rotate", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("axis", NODE_VECTOR_ROTATE_TYPE_AXIS); type_enum.insert("x_axis", NODE_VECTOR_ROTATE_TYPE_AXIS_X); type_enum.insert("y_axis", NODE_VECTOR_ROTATE_TYPE_AXIS_Y); type_enum.insert("z_axis", NODE_VECTOR_ROTATE_TYPE_AXIS_Z); type_enum.insert("euler_xyz", NODE_VECTOR_ROTATE_TYPE_EULER_XYZ); SOCKET_ENUM(rotate_type, "Type", type_enum, NODE_VECTOR_ROTATE_TYPE_AXIS); SOCKET_BOOLEAN(invert, "Invert", false); SOCKET_IN_VECTOR(vector, "Vector", zero_float3()); SOCKET_IN_POINT(rotation, "Rotation", zero_float3()); SOCKET_IN_POINT(center, "Center", zero_float3()); SOCKET_IN_VECTOR(axis, "Axis", make_float3(0.0f, 0.0f, 1.0f)); SOCKET_IN_FLOAT(angle, "Angle", 0.0f); SOCKET_OUT_VECTOR(vector, "Vector"); return type; } VectorRotateNode::VectorRotateNode() : ShaderNode(get_node_type()) {} void VectorRotateNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *rotation_in = input("Rotation"); ShaderInput *center_in = input("Center"); ShaderInput *axis_in = input("Axis"); ShaderInput *angle_in = input("Angle"); ShaderOutput *vector_out = output("Vector"); compiler.add_node(NODE_VECTOR_ROTATE, compiler.encode_uchar4(rotate_type, compiler.stack_assign(vector_in), compiler.stack_assign(rotation_in), invert), compiler.encode_uchar4(compiler.stack_assign(center_in), compiler.stack_assign(axis_in), compiler.stack_assign(angle_in)), compiler.stack_assign(vector_out)); } void VectorRotateNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "rotate_type"); compiler.parameter(this, "invert"); compiler.add(this, "node_vector_rotate"); } /* VectorTransform */ NODE_DEFINE(VectorTransformNode) { NodeType *type = NodeType::add("vector_transform", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("vector", NODE_VECTOR_TRANSFORM_TYPE_VECTOR); type_enum.insert("point", NODE_VECTOR_TRANSFORM_TYPE_POINT); type_enum.insert("normal", NODE_VECTOR_TRANSFORM_TYPE_NORMAL); SOCKET_ENUM(transform_type, "Type", type_enum, NODE_VECTOR_TRANSFORM_TYPE_VECTOR); static NodeEnum space_enum; space_enum.insert("world", NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD); space_enum.insert("object", NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT); space_enum.insert("camera", NODE_VECTOR_TRANSFORM_CONVERT_SPACE_CAMERA); SOCKET_ENUM(convert_from, "Convert From", space_enum, NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD); SOCKET_ENUM(convert_to, "Convert To", space_enum, NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT); SOCKET_IN_VECTOR(vector, "Vector", zero_float3()); SOCKET_OUT_VECTOR(vector, "Vector"); return type; } VectorTransformNode::VectorTransformNode() : ShaderNode(get_node_type()) {} void VectorTransformNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderOutput *vector_out = output("Vector"); compiler.add_node( NODE_VECTOR_TRANSFORM, compiler.encode_uchar4(transform_type, convert_from, convert_to), compiler.encode_uchar4(compiler.stack_assign(vector_in), compiler.stack_assign(vector_out))); } void VectorTransformNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "transform_type"); compiler.parameter(this, "convert_from"); compiler.parameter(this, "convert_to"); compiler.add(this, "node_vector_transform"); } /* BumpNode */ NODE_DEFINE(BumpNode) { NodeType *type = NodeType::add("bump", create, NodeType::SHADER); SOCKET_BOOLEAN(invert, "Invert", false); SOCKET_BOOLEAN(use_object_space, "UseObjectSpace", false); /* this input is used by the user, but after graph transform it is no longer * used and moved to sampler center/x/y instead */ SOCKET_IN_FLOAT(height, "Height", 1.0f); SOCKET_IN_FLOAT(sample_center, "SampleCenter", 0.0f); SOCKET_IN_FLOAT(sample_x, "SampleX", 0.0f); SOCKET_IN_FLOAT(sample_y, "SampleY", 0.0f); SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(strength, "Strength", 1.0f); SOCKET_IN_FLOAT(distance, "Distance", 0.1f); SOCKET_IN_FLOAT(filter_width, "Filter Width", 0.1f); SOCKET_OUT_NORMAL(normal, "Normal"); return type; } BumpNode::BumpNode() : ShaderNode(get_node_type()) { special_type = SHADER_SPECIAL_TYPE_BUMP; } void BumpNode::compile(SVMCompiler &compiler) { ShaderInput *center_in = input("SampleCenter"); ShaderInput *dx_in = input("SampleX"); ShaderInput *dy_in = input("SampleY"); ShaderInput *normal_in = input("Normal"); ShaderInput *strength_in = input("Strength"); ShaderInput *distance_in = input("Distance"); ShaderOutput *normal_out = output("Normal"); /* pack all parameters in the node */ compiler.add_node( NODE_SET_BUMP, compiler.encode_uchar4(compiler.stack_assign_if_linked(normal_in), compiler.stack_assign(distance_in), invert, use_object_space), compiler.encode_uchar4(compiler.stack_assign(center_in), compiler.stack_assign(dx_in), compiler.stack_assign(dy_in), compiler.stack_assign(strength_in)), compiler.encode_uchar4(compiler.stack_assign(normal_out), compiler.get_bump_state_offset())); compiler.add_node(__float_as_uint(filter_width)); } void BumpNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "invert"); compiler.parameter(this, "use_object_space"); compiler.add(this, "node_bump"); } void BumpNode::constant_fold(const ConstantFolder &folder) { ShaderInput *height_in = input("Height"); ShaderInput *normal_in = input("Normal"); if (height_in->link == nullptr) { if (normal_in->link == nullptr) { GeometryNode *geom = folder.graph->create_node(); folder.bypass(geom->output("Normal")); } else { folder.bypass(normal_in->link); } } /* TODO(sergey): Ignore bump with zero strength. */ } /* Curves node */ CurvesNode::CurvesNode(const NodeType *node_type) : ShaderNode(node_type) {} void CurvesNode::constant_fold(const ConstantFolder &folder, ShaderInput *value_in) { ShaderInput *fac_in = input("Fac"); /* evaluate fully constant node */ if (folder.all_inputs_constant()) { if (curves.size() == 0) { return; } float3 pos = (value - make_float3(min_x, min_x, min_x)) / (max_x - min_x); float3 result; result[0] = rgb_ramp_lookup(curves.data(), pos[0], true, extrapolate, curves.size()).x; result[1] = rgb_ramp_lookup(curves.data(), pos[1], true, extrapolate, curves.size()).y; result[2] = rgb_ramp_lookup(curves.data(), pos[2], true, extrapolate, curves.size()).z; folder.make_constant(interp(value, result, fac)); } /* remove no-op node */ else if (!fac_in->link && fac == 0.0f) { /* link is not null because otherwise all inputs are constant */ folder.bypass(value_in->link); } } void CurvesNode::compile(SVMCompiler &compiler, const int type, ShaderInput *value_in, ShaderOutput *value_out) { if (curves.size() == 0) { return; } ShaderInput *fac_in = input("Fac"); compiler.add_node(ShaderNodeType(type), compiler.encode_uchar4(compiler.stack_assign(fac_in), compiler.stack_assign(value_in), compiler.stack_assign(value_out), extrapolate), __float_as_int(min_x), __float_as_int(max_x)); compiler.add_node(curves.size()); for (int i = 0; i < curves.size(); i++) { compiler.add_node(make_float4(curves[i])); } } void CurvesNode::compile(OSLCompiler &compiler, const char *name) { if (curves.size() == 0) { return; } compiler.parameter_color_array("ramp", curves); compiler.parameter(this, "min_x"); compiler.parameter(this, "max_x"); compiler.parameter(this, "extrapolate"); compiler.add(this, name); } void CurvesNode::compile(SVMCompiler & /*compiler*/) { assert(0); } void CurvesNode::compile(OSLCompiler & /*compiler*/) { assert(0); } /* RGBCurvesNode */ NODE_DEFINE(RGBCurvesNode) { NodeType *type = NodeType::add("rgb_curves", create, NodeType::SHADER); SOCKET_COLOR_ARRAY(curves, "Curves", array()); SOCKET_FLOAT(min_x, "Min X", 0.0f); SOCKET_FLOAT(max_x, "Max X", 1.0f); SOCKET_BOOLEAN(extrapolate, "Extrapolate", true); SOCKET_IN_FLOAT(fac, "Fac", 0.0f); SOCKET_IN_COLOR(value, "Color", zero_float3()); SOCKET_OUT_COLOR(value, "Color"); return type; } RGBCurvesNode::RGBCurvesNode() : CurvesNode(get_node_type()) {} void RGBCurvesNode::constant_fold(const ConstantFolder &folder) { CurvesNode::constant_fold(folder, input("Color")); } void RGBCurvesNode::compile(SVMCompiler &compiler) { CurvesNode::compile(compiler, NODE_CURVES, input("Color"), output("Color")); } void RGBCurvesNode::compile(OSLCompiler &compiler) { CurvesNode::compile(compiler, "node_rgb_curves"); } /* VectorCurvesNode */ NODE_DEFINE(VectorCurvesNode) { NodeType *type = NodeType::add("vector_curves", create, NodeType::SHADER); SOCKET_VECTOR_ARRAY(curves, "Curves", array()); SOCKET_FLOAT(min_x, "Min X", 0.0f); SOCKET_FLOAT(max_x, "Max X", 1.0f); SOCKET_BOOLEAN(extrapolate, "Extrapolate", true); SOCKET_IN_FLOAT(fac, "Fac", 0.0f); SOCKET_IN_VECTOR(value, "Vector", zero_float3()); SOCKET_OUT_VECTOR(value, "Vector"); return type; } VectorCurvesNode::VectorCurvesNode() : CurvesNode(get_node_type()) {} void VectorCurvesNode::constant_fold(const ConstantFolder &folder) { CurvesNode::constant_fold(folder, input("Vector")); } void VectorCurvesNode::compile(SVMCompiler &compiler) { CurvesNode::compile(compiler, NODE_CURVES, input("Vector"), output("Vector")); } void VectorCurvesNode::compile(OSLCompiler &compiler) { CurvesNode::compile(compiler, "node_vector_curves"); } /* FloatCurveNode */ NODE_DEFINE(FloatCurveNode) { NodeType *type = NodeType::add("float_curve", create, NodeType::SHADER); SOCKET_FLOAT_ARRAY(curve, "Curve", array()); SOCKET_FLOAT(min_x, "Min X", 0.0f); SOCKET_FLOAT(max_x, "Max X", 1.0f); SOCKET_BOOLEAN(extrapolate, "Extrapolate", true); SOCKET_IN_FLOAT(fac, "Factor", 0.0f); SOCKET_IN_FLOAT(value, "Value", 0.0f); SOCKET_OUT_FLOAT(value, "Value"); return type; } FloatCurveNode::FloatCurveNode() : ShaderNode(get_node_type()) {} void FloatCurveNode::constant_fold(const ConstantFolder &folder) { ShaderInput *value_in = input("Value"); ShaderInput *fac_in = input("Factor"); /* evaluate fully constant node */ if (folder.all_inputs_constant()) { if (curve.size() == 0) { return; } const float pos = (value - min_x) / (max_x - min_x); const float result = float_ramp_lookup(curve.data(), pos, true, extrapolate, curve.size()); folder.make_constant(value + fac * (result - value)); } /* remove no-op node */ else if (!fac_in->link && fac == 0.0f) { /* link is not null because otherwise all inputs are constant */ folder.bypass(value_in->link); } } void FloatCurveNode::compile(SVMCompiler &compiler) { if (curve.size() == 0) { return; } ShaderInput *value_in = input("Value"); ShaderInput *fac_in = input("Factor"); ShaderOutput *value_out = output("Value"); compiler.add_node(NODE_FLOAT_CURVE, compiler.encode_uchar4(compiler.stack_assign(fac_in), compiler.stack_assign(value_in), compiler.stack_assign(value_out), extrapolate), __float_as_int(min_x), __float_as_int(max_x)); compiler.add_node(curve.size()); for (int i = 0; i < curve.size(); i++) { compiler.add_node(make_float4(curve[i])); } } void FloatCurveNode::compile(OSLCompiler &compiler) { if (curve.size() == 0) { return; } compiler.parameter_array("ramp", curve.data(), curve.size()); compiler.parameter(this, "min_x"); compiler.parameter(this, "max_x"); compiler.parameter(this, "extrapolate"); compiler.add(this, "node_float_curve"); } /* RGBRampNode */ NODE_DEFINE(RGBRampNode) { NodeType *type = NodeType::add("rgb_ramp", create, NodeType::SHADER); SOCKET_COLOR_ARRAY(ramp, "Ramp", array()); SOCKET_FLOAT_ARRAY(ramp_alpha, "Ramp Alpha", array()); SOCKET_BOOLEAN(interpolate, "Interpolate", true); SOCKET_IN_FLOAT(fac, "Fac", 0.0f); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(alpha, "Alpha"); return type; } RGBRampNode::RGBRampNode() : ShaderNode(get_node_type()) {} void RGBRampNode::constant_fold(const ConstantFolder &folder) { if (ramp.size() == 0 || ramp.size() != ramp_alpha.size()) { return; } if (folder.all_inputs_constant()) { const float f = clamp(fac, 0.0f, 1.0f) * (ramp.size() - 1); /* clamp int as well in case of NaN */ const int i = clamp((int)f, 0, ramp.size() - 1); const float t = f - (float)i; const bool use_lerp = interpolate && t > 0.0f; if (folder.output == output("Color")) { const float3 color = rgb_ramp_lookup(ramp.data(), fac, use_lerp, false, ramp.size()); folder.make_constant(color); } else if (folder.output == output("Alpha")) { const float alpha = float_ramp_lookup( ramp_alpha.data(), fac, use_lerp, false, ramp_alpha.size()); folder.make_constant(alpha); } } } void RGBRampNode::compile(SVMCompiler &compiler) { if (ramp.size() == 0 || ramp.size() != ramp_alpha.size()) { return; } ShaderInput *fac_in = input("Fac"); ShaderOutput *color_out = output("Color"); ShaderOutput *alpha_out = output("Alpha"); compiler.add_node(NODE_RGB_RAMP, compiler.encode_uchar4(compiler.stack_assign(fac_in), compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out)), interpolate); compiler.add_node(ramp.size()); for (int i = 0; i < ramp.size(); i++) { compiler.add_node(make_float4(ramp[i], ramp_alpha[i])); } } void RGBRampNode::compile(OSLCompiler &compiler) { if (ramp.size() == 0 || ramp.size() != ramp_alpha.size()) { return; } compiler.parameter_color_array("ramp_color", ramp); compiler.parameter_array("ramp_alpha", ramp_alpha.data(), ramp_alpha.size()); compiler.parameter(this, "interpolate"); compiler.add(this, "node_rgb_ramp"); } /* Set Normal Node */ NODE_DEFINE(SetNormalNode) { NodeType *type = NodeType::add("set_normal", create, NodeType::SHADER); SOCKET_IN_VECTOR(direction, "Direction", zero_float3()); SOCKET_OUT_NORMAL(normal, "Normal"); return type; } SetNormalNode::SetNormalNode() : ShaderNode(get_node_type()) {} void SetNormalNode::compile(SVMCompiler &compiler) { ShaderInput *direction_in = input("Direction"); ShaderOutput *normal_out = output("Normal"); compiler.add_node(NODE_CLOSURE_SET_NORMAL, compiler.stack_assign(direction_in), compiler.stack_assign(normal_out)); } void SetNormalNode::compile(OSLCompiler &compiler) { compiler.add(this, "node_set_normal"); } /* OSLNode */ OSLNode::OSLNode() : ShaderNode(new NodeType(NodeType::SHADER)) { special_type = SHADER_SPECIAL_TYPE_OSL; has_emission = false; } OSLNode::~OSLNode() { delete type; } ShaderNode *OSLNode::clone(ShaderGraph *graph) const { return OSLNode::create(graph, this->inputs.size(), this); } void OSLNode::attributes(Shader *shader, AttributeRequestSet *attributes) { /* the added geometry node's attributes function unfortunately doesn't * request the need for ATTR_STD_GENERATED in-time somehow, so we request it * here if there are any sockets that have LINK_TANGENT or * LINK_TEXTURE_GENERATED flags */ if (shader->has_surface_link()) { for (const ShaderInput *in : inputs) { if (!in->link && (in->flags() & SocketType::LINK_TANGENT || in->flags() & SocketType::LINK_TEXTURE_GENERATED)) { attributes->add(ATTR_STD_GENERATED); break; } } } ShaderNode::attributes(shader, attributes); } OSLNode *OSLNode::create(ShaderGraph *graph, const size_t num_inputs, const OSLNode *from) { /* allocate space for the node itself and parameters, aligned to 16 bytes * assuming that's the most parameter types need */ const size_t node_size = align_up(sizeof(OSLNode), 16); const size_t inputs_size = align_up(SocketType::max_size(), 16) * num_inputs; char *node_memory = (char *)operator new(node_size + inputs_size); memset(node_memory, 0, node_size + inputs_size); if (!from) { return graph->create_osl_node(node_memory); } /* copy input default values and node type for cloning */ memcpy(node_memory + node_size, (char *)from + node_size, inputs_size); OSLNode *node = graph->create_osl_node(node_memory, *from); node->type = new NodeType(*(from->type)); return node; } char *OSLNode::input_default_value() { /* pointer to default value storage, which is the same as our actual value */ const size_t num_inputs = type->inputs.size(); const size_t inputs_size = align_up(SocketType::max_size(), 16) * num_inputs; return (char *)this + align_up(sizeof(OSLNode), 16) + inputs_size; } void OSLNode::add_input(ustring name, SocketType::Type socket_type, const int flags) { char *memory = input_default_value(); const size_t offset = memory - (char *)this; const_cast(type)->register_input( name, name, socket_type, offset, memory, nullptr, nullptr, flags | SocketType::LINKABLE); } void OSLNode::add_output(ustring name, SocketType::Type socket_type) { const_cast(type)->register_output(name, name, socket_type); } void OSLNode::compile(SVMCompiler & /*compiler*/) { /* doesn't work for SVM, obviously ... */ } void OSLNode::compile(OSLCompiler &compiler) { if (!filepath.empty()) { compiler.add(this, filepath.c_str(), true); } else { compiler.add(this, bytecode_hash.c_str(), false); } } /* Normal Map */ NODE_DEFINE(NormalMapNode) { NodeType *type = NodeType::add("normal_map", create, NodeType::SHADER); static NodeEnum space_enum; space_enum.insert("tangent", NODE_NORMAL_MAP_TANGENT); space_enum.insert("object", NODE_NORMAL_MAP_OBJECT); space_enum.insert("world", NODE_NORMAL_MAP_WORLD); space_enum.insert("blender_object", NODE_NORMAL_MAP_BLENDER_OBJECT); space_enum.insert("blender_world", NODE_NORMAL_MAP_BLENDER_WORLD); SOCKET_ENUM(space, "Space", space_enum, NODE_NORMAL_MAP_TANGENT); SOCKET_STRING(attribute, "Attribute", ustring()); SOCKET_IN_FLOAT(strength, "Strength", 1.0f); SOCKET_IN_COLOR(color, "Color", make_float3(0.5f, 0.5f, 1.0f)); SOCKET_OUT_NORMAL(normal, "Normal"); return type; } NormalMapNode::NormalMapNode() : ShaderNode(get_node_type()) {} void NormalMapNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (shader->has_surface_link() && space == NODE_NORMAL_MAP_TANGENT) { if (attribute.empty()) { /* We don't need the UV ourselves, but we need to compute the tangent from it. */ attributes->add(ATTR_STD_UV); attributes->add(ATTR_STD_UV_TANGENT_UNDISPLACED); attributes->add(ATTR_STD_UV_TANGENT_SIGN_UNDISPLACED); } else { attributes->add(attribute); attributes->add(ustring((string(attribute.c_str()) + ".undisplaced_tangent").c_str())); attributes->add(ustring((string(attribute.c_str()) + ".undisplaced_tangent_sign").c_str())); } attributes->add(ATTR_STD_NORMAL_UNDISPLACED); } ShaderNode::attributes(shader, attributes); } void NormalMapNode::compile(SVMCompiler &compiler) { ShaderInput *color_in = input("Color"); ShaderInput *strength_in = input("Strength"); ShaderOutput *normal_out = output("Normal"); int attr = 0; int attr_sign = 0; if (space == NODE_NORMAL_MAP_TANGENT) { if (attribute.empty()) { attr = compiler.attribute(ATTR_STD_UV_TANGENT_UNDISPLACED); attr_sign = compiler.attribute(ATTR_STD_UV_TANGENT_SIGN_UNDISPLACED); } else { attr = compiler.attribute( ustring((string(attribute.c_str()) + ".undisplaced_tangent").c_str())); attr_sign = compiler.attribute( ustring((string(attribute.c_str()) + ".undisplaced_tangent_sign").c_str())); } } compiler.add_node(NODE_NORMAL_MAP, compiler.encode_uchar4(compiler.stack_assign(color_in), compiler.stack_assign(strength_in), compiler.stack_assign(normal_out), space), attr, attr_sign); } void NormalMapNode::compile(OSLCompiler &compiler) { if (space == NODE_NORMAL_MAP_TANGENT) { if (attribute.empty()) { compiler.parameter("attr_name", ustring("geom:undisplaced_tangent")); compiler.parameter("attr_sign_name", ustring("geom:undisplaced_tangent_sign")); } else { compiler.parameter("attr_name", ustring((string(attribute.c_str()) + ".undisplaced_tangent").c_str())); compiler.parameter( "attr_sign_name", ustring((string(attribute.c_str()) + ".undisplaced_tangent_sign").c_str())); } } compiler.parameter(this, "space"); compiler.add(this, "node_normal_map"); } /* Radial Tiling */ NODE_DEFINE(RadialTilingNode) { NodeType *type = NodeType::add("radial_tiling", create, NodeType::SHADER); SOCKET_BOOLEAN(use_normalize, "Normalize", false); SOCKET_IN_POINT(vector, "Vector", zero_float3()); SOCKET_IN_FLOAT(r_gon_sides, "Sides", 5.0f); SOCKET_IN_FLOAT(r_gon_roundness, "Roundness", 0.0f); SOCKET_OUT_POINT(segment_coordinates, "Segment Coordinates"); SOCKET_OUT_FLOAT(segment_id, "Segment ID"); SOCKET_OUT_FLOAT(max_unit_parameter, "Segment Width"); SOCKET_OUT_FLOAT(x_axis_A_angle_bisector, "Segment Rotation"); return type; } RadialTilingNode::RadialTilingNode() : ShaderNode(get_node_type()) {} void RadialTilingNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *r_gon_sides_in = input("Sides"); ShaderInput *r_gon_roundness_in = input("Roundness"); ShaderOutput *segment_coordinates_out = output("Segment Coordinates"); ShaderOutput *segment_id_out = output("Segment ID"); ShaderOutput *max_unit_parameter_out = output("Segment Width"); ShaderOutput *x_axis_A_angle_bisector_out = output("Segment Rotation"); compiler.add_node(NODE_RADIAL_TILING, use_normalize, compiler.encode_uchar4(compiler.stack_assign(vector_in), compiler.stack_assign(r_gon_sides_in), compiler.stack_assign(r_gon_roundness_in), compiler.stack_assign(segment_coordinates_out)), compiler.encode_uchar4(compiler.stack_assign(segment_id_out), compiler.stack_assign(max_unit_parameter_out), compiler.stack_assign(x_axis_A_angle_bisector_out))); } void RadialTilingNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "use_normalize"); compiler.add(this, "node_radial_tiling"); } /* Tangent */ NODE_DEFINE(TangentNode) { NodeType *type = NodeType::add("tangent", create, NodeType::SHADER); static NodeEnum direction_type_enum; direction_type_enum.insert("radial", NODE_TANGENT_RADIAL); direction_type_enum.insert("uv_map", NODE_TANGENT_UVMAP); SOCKET_ENUM(direction_type, "Direction Type", direction_type_enum, NODE_TANGENT_RADIAL); static NodeEnum axis_enum; axis_enum.insert("x", NODE_TANGENT_AXIS_X); axis_enum.insert("y", NODE_TANGENT_AXIS_Y); axis_enum.insert("z", NODE_TANGENT_AXIS_Z); SOCKET_ENUM(axis, "Axis", axis_enum, NODE_TANGENT_AXIS_X); SOCKET_STRING(attribute, "Attribute", ustring()); SOCKET_OUT_NORMAL(tangent, "Tangent"); return type; } TangentNode::TangentNode() : ShaderNode(get_node_type()) {} void TangentNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (shader->has_surface_link()) { if (direction_type == NODE_TANGENT_UVMAP) { if (attribute.empty()) { /* We don't need the UV ourselves, but we need to compute the tangent from it. */ attributes->add(ATTR_STD_UV); attributes->add(ATTR_STD_UV_TANGENT); } else { attributes->add(attribute); attributes->add(ustring((string(attribute.c_str()) + ".tangent").c_str())); } } else { attributes->add(ATTR_STD_GENERATED); } } ShaderNode::attributes(shader, attributes); } void TangentNode::compile(SVMCompiler &compiler) { ShaderOutput *tangent_out = output("Tangent"); int attr; if (direction_type == NODE_TANGENT_UVMAP) { if (attribute.empty()) { attr = compiler.attribute(ATTR_STD_UV_TANGENT); } else { attr = compiler.attribute(ustring((string(attribute.c_str()) + ".tangent").c_str())); } } else { attr = compiler.attribute(ATTR_STD_GENERATED); } compiler.add_node( NODE_TANGENT, compiler.encode_uchar4(compiler.stack_assign(tangent_out), direction_type, axis), attr); } void TangentNode::compile(OSLCompiler &compiler) { if (direction_type == NODE_TANGENT_UVMAP) { if (attribute.empty()) { compiler.parameter("attr_name", ustring("geom:tangent")); } else { compiler.parameter("attr_name", ustring((string(attribute.c_str()) + ".tangent").c_str())); } } compiler.parameter(this, "direction_type"); compiler.parameter(this, "axis"); compiler.add(this, "node_tangent"); } /* Bevel */ NODE_DEFINE(BevelNode) { NodeType *type = NodeType::add("bevel", create, NodeType::SHADER); SOCKET_INT(samples, "Samples", 4); SOCKET_IN_FLOAT(radius, "Radius", 0.05f); SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_OUT_NORMAL(bevel, "Normal"); return type; } BevelNode::BevelNode() : ShaderNode(get_node_type()) {} void BevelNode::compile(SVMCompiler &compiler) { ShaderInput *radius_in = input("Radius"); ShaderInput *normal_in = input("Normal"); ShaderOutput *normal_out = output("Normal"); compiler.add_node(NODE_BEVEL, compiler.encode_uchar4(samples, compiler.stack_assign(radius_in), compiler.stack_assign_if_linked(normal_in), compiler.stack_assign(normal_out))); } void BevelNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "samples"); compiler.add(this, "node_bevel"); } /* Displacement */ NODE_DEFINE(DisplacementNode) { NodeType *type = NodeType::add("displacement", create, NodeType::SHADER); static NodeEnum space_enum; space_enum.insert("object", NODE_NORMAL_MAP_OBJECT); space_enum.insert("world", NODE_NORMAL_MAP_WORLD); SOCKET_ENUM(space, "Space", space_enum, NODE_NORMAL_MAP_OBJECT); SOCKET_IN_FLOAT(height, "Height", 0.0f); SOCKET_IN_FLOAT(midlevel, "Midlevel", 0.5f); SOCKET_IN_FLOAT(scale, "Scale", 1.0f); SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_OUT_VECTOR(displacement, "Displacement"); return type; } DisplacementNode::DisplacementNode() : ShaderNode(get_node_type()) {} void DisplacementNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { if ((height - midlevel == 0.0f) || (scale == 0.0f)) { folder.make_zero(); } } } void DisplacementNode::compile(SVMCompiler &compiler) { ShaderInput *height_in = input("Height"); ShaderInput *midlevel_in = input("Midlevel"); ShaderInput *scale_in = input("Scale"); ShaderInput *normal_in = input("Normal"); ShaderOutput *displacement_out = output("Displacement"); compiler.add_node(NODE_DISPLACEMENT, compiler.encode_uchar4(compiler.stack_assign(height_in), compiler.stack_assign(midlevel_in), compiler.stack_assign(scale_in), compiler.stack_assign_if_linked(normal_in)), compiler.stack_assign(displacement_out), space); } void DisplacementNode::compile(OSLCompiler &compiler) { compiler.parameter(this, "space"); compiler.add(this, "node_displacement"); } /* Vector Displacement */ NODE_DEFINE(VectorDisplacementNode) { NodeType *type = NodeType::add("vector_displacement", create, NodeType::SHADER); static NodeEnum space_enum; space_enum.insert("tangent", NODE_NORMAL_MAP_TANGENT); space_enum.insert("object", NODE_NORMAL_MAP_OBJECT); space_enum.insert("world", NODE_NORMAL_MAP_WORLD); SOCKET_ENUM(space, "Space", space_enum, NODE_NORMAL_MAP_TANGENT); SOCKET_STRING(attribute, "Attribute", ustring()); SOCKET_IN_COLOR(vector, "Vector", zero_float3()); SOCKET_IN_FLOAT(midlevel, "Midlevel", 0.0f); SOCKET_IN_FLOAT(scale, "Scale", 1.0f); SOCKET_OUT_VECTOR(displacement, "Displacement"); return type; } VectorDisplacementNode::VectorDisplacementNode() : ShaderNode(get_node_type()) {} void VectorDisplacementNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { if ((vector == zero_float3() && midlevel == 0.0f) || (scale == 0.0f)) { folder.make_zero(); } } } void VectorDisplacementNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if (shader->has_surface_link() && space == NODE_NORMAL_MAP_TANGENT) { if (attribute.empty()) { attributes->add(ATTR_STD_UV); attributes->add(ATTR_STD_UV_TANGENT_UNDISPLACED); attributes->add(ATTR_STD_UV_TANGENT_SIGN_UNDISPLACED); } else { attributes->add(attribute); attributes->add(ustring((string(attribute.c_str()) + ".undisplaced_tangent").c_str())); attributes->add(ustring((string(attribute.c_str()) + ".undisplaced_tangent_sign").c_str())); } } ShaderNode::attributes(shader, attributes); } void VectorDisplacementNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *midlevel_in = input("Midlevel"); ShaderInput *scale_in = input("Scale"); ShaderOutput *displacement_out = output("Displacement"); int attr = 0; int attr_sign = 0; if (space == NODE_NORMAL_MAP_TANGENT) { if (attribute.empty()) { attr = compiler.attribute(ATTR_STD_UV_TANGENT_UNDISPLACED); attr_sign = compiler.attribute(ATTR_STD_UV_TANGENT_SIGN_UNDISPLACED); } else { attr = compiler.attribute( ustring((string(attribute.c_str()) + ".undisplaced_tangent").c_str())); attr_sign = compiler.attribute( ustring((string(attribute.c_str()) + ".undisplaced_tangent_sign").c_str())); } } compiler.add_node(NODE_VECTOR_DISPLACEMENT, compiler.encode_uchar4(compiler.stack_assign(vector_in), compiler.stack_assign(midlevel_in), compiler.stack_assign(scale_in), compiler.stack_assign(displacement_out)), attr, attr_sign); compiler.add_node(space); } void VectorDisplacementNode::compile(OSLCompiler &compiler) { if (space == NODE_NORMAL_MAP_TANGENT) { if (attribute.empty()) { compiler.parameter("attr_name", ustring("geom:undisplaced_tangent")); compiler.parameter("attr_sign_name", ustring("geom:undisplaced_tangent_sign")); } else { compiler.parameter("attr_name", ustring((string(attribute.c_str()) + ".undisplaced_tangent").c_str())); compiler.parameter( "attr_sign_name", ustring((string(attribute.c_str()) + ".undisplaced_tangent_sign").c_str())); } } compiler.parameter(this, "space"); compiler.add(this, "node_vector_displacement"); } CCL_NAMESPACE_END