Source code for fury.material

import os
import warnings

from fury.decorators import warn_on_args_to_kwargs
from fury.lib import VTK_OBJECT, calldata_type
from fury.shaders import (
    add_shader_callback,
    compose_shader,
    import_fury_shader,
    shader_to_actor,
)


class __PBRParams:
    """Helper class to manage PBR parameters.

    Attributes
    ----------
    actor_properties : vtkProperty
        The actor properties.

    Parameters
    ----------
    metallic : float
        Metallic or non-metallic (dielectric) shading computation value. Values
        must be between 0.0 and 1.0.
    roughness : float
        Parameter used to specify how glossy the actor should be. Values must
        be between 0.0 and 1.0.
    anisotropy : float
        Isotropic or anisotropic material parameter. Values must be between
        0.0 and 1.0.
    anisotropy_rotation : float
        Rotation of the anisotropy around the normal in a counter-clockwise
        fashion. Values must be between 0.0 and 1.0. A value of 1.0 means a
        rotation of 2 * pi.
    coat_strength : float
        Strength of the coat layer. Values must be between 0.0 and 1.0 (0.0
        means no clear coat will be modeled).
    coat_roughness : float
        Roughness of the coat layer. Values must be between 0.0 and 1.0.
    base_ior : float
        Index of refraction of the base material. Default is 1.5. Values must
        be between 1.0 and 2.3.
    coat_ior : float
        Index of refraction of the coat material. Default is 1.5. Values must
        be between 1.0 and 2.3.

    """

    def __init__(
        self,
        actor_properties,
        metallic,
        roughness,
        anisotropy,
        anisotropy_rotation,
        coat_strength,
        coat_roughness,
        base_ior,
        coat_ior,
    ):
        self.__actor_properties = actor_properties
        self.__actor_properties.SetMetallic(metallic)
        self.__actor_properties.SetRoughness(roughness)
        self.__actor_properties.SetAnisotropy(anisotropy)
        self.__actor_properties.SetAnisotropyRotation(anisotropy_rotation)
        self.__actor_properties.SetCoatStrength(coat_strength)
        self.__actor_properties.SetCoatRoughness(coat_roughness)
        self.__actor_properties.SetBaseIOR(base_ior)
        self.__actor_properties.SetCoatIOR(coat_ior)

    @property
    def metallic(self):
        return self.__actor_properties.GetMetallic()

    @metallic.setter
    def metallic(self, metallic):
        self.__actor_properties.SetMetallic(metallic)

    @property
    def roughness(self):
        return self.__actor_properties.GetRoughness()

    @roughness.setter
    def roughness(self, roughness):
        self.__actor_properties.SetRoughness(roughness)

    @property
    def anisotropy(self):
        return self.__actor_properties.GetAnisotropy()

    @anisotropy.setter
    def anisotropy(self, anisotropy):
        self.__actor_properties.SetAnisotropy(anisotropy)

    @property
    def anisotropy_rotation(self):
        return self.__actor_properties.GetAnisotropyRotation()

    @anisotropy_rotation.setter
    def anisotropy_rotation(self, anisotropy_rotation):
        self.__actor_properties.SetAnisotropyRotation(anisotropy_rotation)

    @property
    def coat_strength(self):
        return self.__actor_properties.GetCoatStrength()

    @coat_strength.setter
    def coat_strength(self, coat_strength):
        self.__actor_properties.SetCoatStrength(coat_strength)

    @property
    def coat_roughness(self):
        return self.__actor_properties.GetCoatRoughness()

    @coat_roughness.setter
    def coat_roughness(self, coat_roughness):
        self.__actor_properties.SetCoatRoughness(coat_roughness)

    @property
    def base_ior(self):
        return self.__actor_properties.GetBaseIOR()

    @base_ior.setter
    def base_ior(self, base_ior):
        self.__actor_properties.SetBaseIOR(base_ior)

    @property
    def coat_ior(self):
        return self.__actor_properties.GetCoatIOR()

    @coat_ior.setter
    def coat_ior(self, coat_ior):
        self.__actor_properties.SetCoatIOR(coat_ior)


[docs] @warn_on_args_to_kwargs() def manifest_pbr( actor, *, metallic=0, roughness=0.5, anisotropy=0, anisotropy_rotation=0, coat_strength=0, coat_roughness=0, base_ior=1.5, coat_ior=2, ): """Apply VTK's Physically Based Rendering properties to the selected actor. Parameters ---------- actor : actor metallic : float, optional Metallic or non-metallic (dielectric) shading computation value. Values must be between 0.0 and 1.0. roughness : float, optional Parameter used to specify how glossy the actor should be. Values must be between 0.0 and 1.0. anisotropy : float, optional Isotropic or anisotropic material parameter. Values must be between 0.0 and 1.0. anisotropy_rotation : float, optional Rotation of the anisotropy around the normal in a counter-clockwise fashion. Values must be between 0.0 and 1.0. A value of 1.0 means a rotation of 2 * pi. coat_strength : float, optional Strength of the coat layer. Values must be between 0.0 and 1.0 (0.0 means no clear coat will be modeled). coat_roughness : float, optional Roughness of the coat layer. Values must be between 0.0 and 1.0. base_ior : float, optional Index of refraction of the base material. Default is 1.5. Values must be between 1.0 and 2.3. coat_ior : float, optional Index of refraction of the coat material. Default is 1.5. Values must be between 1.0 and 2.3. """ try: prop = actor.GetProperty() try: prop.SetInterpolationToPBR() pbr_params = __PBRParams( prop, metallic, roughness, anisotropy, anisotropy_rotation, coat_strength, coat_roughness, base_ior, coat_ior, ) return pbr_params except AttributeError: warnings.warn( "PBR interpolation cannot be applied to this actor. The " "material will not be applied.", stacklevel=2, ) return None except AttributeError: warnings.warn( "Actor does not have the attribute property. This " "material will not be applied.", stacklevel=2, ) return None
[docs] @warn_on_args_to_kwargs() def manifest_principled( actor, *, subsurface=0, metallic=0, specular=0, specular_tint=0, roughness=0, anisotropic=0, anisotropic_direction=None, sheen=0, sheen_tint=0, clearcoat=0, clearcoat_gloss=0, ): """Apply the Principled Shading properties to the selected actor. Parameters ---------- actor : actor subsurface : float, optional Subsurface scattering computation value. Values must be between 0.0 and 1.0. metallic : float, optional Metallic or non-metallic (dielectric) shading computation value. Values must be between 0.0 and 1.0. specular : float, optional Specular lighting coefficient. Value must be between 0.0 and 1.0. specular_tint : float, optional Specular tint coefficient value. Values must be between 0.0 and 1.0. roughness : float, optional Parameter used to specify how glossy the actor should be. Values must be between 0.0 and 1.0. anisotropic : float, optional Anisotropy coefficient. Values must be between 0.0 and 1.0. anisotropic_direction : list, optional Anisotropy direction where X, Y and Z should be in the range [-1, 1]. sheen : float, optional Sheen coefficient. Values must be between 0.0 and 1.0. sheen_tint : float, optional Sheen tint coefficient value. Values must be between 0.0 and 1.0. clearcoat : float, optional Clearcoat coefficient. Values must be between 0.0 and 1.0. clearcoat_gloss : float, optional Clearcoat gloss coefficient value. Values must be between 0.0 and 1.0. Returns ------- principled_params : dict Dictionary containing the Principled Shading parameters. """ if anisotropic_direction is None: anisotropic_direction = [0, 1, 0.5] try: prop = actor.GetProperty() principled_params = { "subsurface": subsurface, "metallic": metallic, "specular": specular, "specular_tint": specular_tint, "roughness": roughness, "anisotropic": anisotropic, "anisotropic_direction": anisotropic_direction, "sheen": sheen, "sheen_tint": sheen_tint, "clearcoat": clearcoat, "clearcoat_gloss": clearcoat_gloss, } prop.SetSpecular(specular) @calldata_type(VTK_OBJECT) def uniforms_callback(_caller, _event, calldata=None): if calldata is not None: calldata.SetUniformf( "subsurface", principled_params["subsurface"], ) calldata.SetUniformf("metallic", principled_params["metallic"]) calldata.SetUniformf( "specularTint", principled_params["specular_tint"], ) calldata.SetUniformf( "roughness", principled_params["roughness"], ) calldata.SetUniformf( "anisotropic", principled_params["anisotropic"], ) calldata.SetUniformf("sheen", principled_params["sheen"]) calldata.SetUniformf( "sheenTint", principled_params["sheen_tint"], ) calldata.SetUniformf( "clearcoat", principled_params["clearcoat"], ) calldata.SetUniformf( "clearcoatGloss", principled_params["clearcoat_gloss"], ) calldata.SetUniform3f( "anisotropicDirection", principled_params["anisotropic_direction"], ) add_shader_callback(actor, uniforms_callback) # Start of shader implementation # Adding required constants pi = "#define PI 3.14159265359" # Adding uniforms uniforms = """ uniform float subsurface; uniform float metallic; uniform float specularTint; uniform float roughness; uniform float anisotropic; uniform float sheen; uniform float sheenTint; uniform float clearcoat; uniform float clearcoatGloss; uniform vec3 anisotropicDirection; """ # Importing functions in order # Importing utility functions square = import_fury_shader(os.path.join("utils", "square.glsl")) pow5 = import_fury_shader(os.path.join("utils", "pow5.glsl")) # Importing utility function to update the tangent and bitangent # vectors given a direction of anisotropy update_tan_bitan = import_fury_shader( os.path.join("utils", "update_tan_bitan.glsl") ) # Importing color conversion gamma to linear space function gamma_to_linear = import_fury_shader( os.path.join("lighting", "gamma_to_linear.frag") ) # Importing color conversion linear to gamma space function linear_to_gamma = import_fury_shader( os.path.join("lighting", "linear_to_gamma.frag") ) # Importing linear-space CIE luminance tint approximation function cie_color_tint = import_fury_shader( os.path.join("lighting", "cie_color_tint.frag") ) # Importing Schlick's weight approximation of the Fresnel equation schlick_weight = import_fury_shader( os.path.join("lighting", "schlick_weight.frag") ) # Importing Normal Distribution Function (NDF): Generalized # Trowbridge-Reitz with param gamma=1 (D_{GTR_1}) needed for the Clear # Coat lobe gtr1 = import_fury_shader(os.path.join("lighting", "ndf", "gtr1.frag")) # Importing Normal Distribution Function (NDF): Generalized # Trowbridge-Reitz with param gamma=2 (D_{GTR_2}) needed for the # Isotropic Specular lobe gtr2 = import_fury_shader(os.path.join("lighting", "ndf", "gtr2.frag")) # Importing Normal Distribution Function (NDF): Anisotropic form of the # Generalized Trowbridge-Reitz with param gamma=2 # (D_{GTR_2anisotropic}) needed for the respective Specular lobe gtr2_anisotropic = import_fury_shader( os.path.join("lighting", "ndf", "gtr2_anisotropic.frag") ) # Importing Geometry Shadowing and Masking Function (GF): Smith Ground # Glass Unknown (G_{GGX}) needed for the Isotropic Specular and Clear # Coat lobes smith_ggx = import_fury_shader( os.path.join( "lighting", "gf", "smith_ggx.frag", ) ) # Importing Geometry Shadowing and Masking Function (GF): Anisotropic # form of the Smith Ground Glass Unknown (G_{GGXanisotropic}) needed # for the respective Specular lobe smith_ggx_anisotropic = import_fury_shader( os.path.join("lighting", "gf", "smith_ggx_anisotropic.frag") ) # Importing Principled components functions diffuse = import_fury_shader( os.path.join("lighting", "principled", "diffuse.frag") ) subsurface = import_fury_shader( os.path.join("lighting", "principled", "subsurface.frag") ) sheen = import_fury_shader( os.path.join( "lighting", "principled", "sheen.frag", ) ) specular_isotropic = import_fury_shader( os.path.join("lighting", "principled", "specular_isotropic.frag") ) specular_anisotropic = import_fury_shader( os.path.join("lighting", "principled", "specular_anisotropic.frag") ) clearcoat = import_fury_shader( os.path.join("lighting", "principled", "clearcoat.frag") ) # Putting all the functions together before passing them to the actor fs_dec = compose_shader( [ pi, uniforms, square, pow5, update_tan_bitan, gamma_to_linear, linear_to_gamma, cie_color_tint, schlick_weight, gtr1, gtr2, gtr2_anisotropic, smith_ggx, smith_ggx_anisotropic, diffuse, subsurface, sheen, specular_isotropic, specular_anisotropic, clearcoat, ] ) # Adding shader functions to actor shader_to_actor(actor, "fragment", decl_code=fs_dec) # Start of the implementation code start_comment = "//Disney's Principled BRDF" # Preparing vectors and values normal = "vec3 normal = normalVCVSOutput;" # VTK's default system is retroreflective, which means view = light view = "vec3 view = normalize(-vertexVC.xyz);" # Since VTK's default setup is retroreflective we only need to # calculate one single dot product dot_n_v = "float dotNV = clamp(dot(normal, view), 1e-5, 1);" dot_n_v_validation = """ if(dotNV < 0) fragOutput0 = vec4(vec3(0), opacity); """ # To work with anisotropic distributions is necessary to have a tangent # and bitangent vector per point on the surface tangent = "vec3 tangent = vec3(.0);" bitangent = "vec3 bitangent = vec3(.0);" # The shader function updateTanBitan aligns tangents and bitangents # according to a direction of anisotropy update_aniso_vecs = """ updateTanBitan(normal, anisotropicDirection, tangent, bitangent); """ # Calculating dot products with tangent and bitangent dot_t_v = "float dotTV = dot(tangent, view);" dot_b_v = "float dotBV = dot(bitangent, view);" # Converting color to linear space linear_color = "vec3 linColor = gamma2Linear(diffuseColor);" # Calculating linear-space CIE luminance tint approximation tint = "vec3 tint = calculateTint(linColor);" # Since VTK's default setup is retroreflective we only need to # calculate one single Schlick's weight fsw = "float fsw = schlickWeight(dotNV);" # Calculating the diffuse coefficient diff_coeff = """ float diffCoeff = evaluateDiffuse(roughness, fsw, fsw, dotNV); """ # Calculating the subsurface coefficient subsurf_coeff = """ float subsurfCoeff = evaluateSubsurface(roughness, fsw, fsw, dotNV, dotNV, dotNV); """ # Calculating the sheen irradiance sheen_rad = """ vec3 sheenRad = evaluateSheen(sheen, sheenTint, tint, fsw); """ # Calculating the specular irradiance spec_rad = """ vec3 specRad = evaluateSpecularAnisotropic(specularIntensity, specularTint, metallic, anisotropic, roughness, tint, linColor, fsw, dotNV, dotTV, dotBV, dotNV, dotTV, dotBV, dotNV, dotTV, dotBV); """ # Calculating the clear coat coefficient clear_coat_coef = """ float coatCoeff = evaluateClearcoat(clearcoat, clearcoatGloss, fsw, dotNV, dotNV, dotNV); """ # Starting to put all together # Initializing the radiance vector radiance = "vec3 rad = (1 / PI) * linColor;" # Adding mix between the diffuse and the subsurface coefficients # controlled by the subsurface parameter diff_subsurf_mix = "rad *= mix(diffCoeff, subsurfCoeff, subsurface);" # Adding sheen radiance sheen_add = "rad += sheenRad;" # Balancing energy using metallic metallic_balance = "rad *= (1 - metallic);" # Adding specular radiance specular_add = "rad += specRad;" # Adding clear coat coefficient clearcoat_add = "rad += coatCoeff;" # Initializing the color vector using the final radiance and VTK's # additional information color = "vec3 color = rad * lightColor0;" # Converting color back to gamma space gamma_color = "color = linear2Gamma(color);" # Clamping color values color_clamp = "color = clamp(color, vec3(0), vec3(1));" # Fragment shader output frag_output = "fragOutput0 = vec4(color, opacity);" # Putting all the implementation together before passing it to the # actor fs_impl = compose_shader( [ start_comment, normal, view, dot_n_v, dot_n_v_validation, tangent, bitangent, update_aniso_vecs, dot_t_v, dot_b_v, linear_color, tint, fsw, diff_coeff, subsurf_coeff, sheen_rad, spec_rad, clear_coat_coef, radiance, diff_subsurf_mix, sheen_add, metallic_balance, specular_add, clearcoat_add, color, gamma_color, color_clamp, frag_output, ] ) # Adding shader implementation to actor shader_to_actor(actor, "fragment", impl_code=fs_impl, block="light") return principled_params except AttributeError: warnings.warn( "Actor does not have the attribute property. This " "material will not be applied.", stacklevel=2, ) return None
[docs] @warn_on_args_to_kwargs() def manifest_standard( actor, *, ambient_level=0, ambient_color=(1, 1, 1), diffuse_level=1, diffuse_color=(1, 1, 1), specular_level=0, specular_color=(1, 1, 1), specular_power=1, interpolation="gouraud", ): """Apply the standard material to the selected actor. Parameters ---------- actor : actor ambient_level : float, optional Ambient lighting coefficient. Value must be between 0.0 and 1.0. ambient_color : tuple (3,), optional Ambient RGB color where R, G and B should be in the range [0, 1]. diffuse_level : float, optional Diffuse lighting coefficient. Value must be between 0.0 and 1.0. diffuse_color : tuple (3,), optional Diffuse RGB color where R, G and B should be in the range [0, 1]. specular_level : float, optional Specular lighting coefficient. Value must be between 0.0 and 1.0. specular_color : tuple (3,), optional Specular RGB color where R, G and B should be in the range [0, 1]. specular_power : float, optional Parameter used to specify the intensity of the specular reflection. Value must be between 0.0 and 128.0. interpolation : string, optional If 'flat', the actor will be shaded using flat interpolation. If 'gouraud' (default), then the shading will be calculated at the vertex level. If 'phong', then the shading will be calculated at the fragment level. """ try: prop = actor.GetProperty() interpolation = interpolation.lower() if interpolation == "flat": prop.SetInterpolationToFlat() elif interpolation == "gouraud": prop.SetInterpolationToGouraud() elif interpolation == "phong": prop.SetInterpolationToPhong() else: message = ( 'Unknown interpolation. Ignoring "{}" interpolation ' 'option and using the default ("{}") option.' ) message = message.format(interpolation, "gouraud") warnings.warn(message, stacklevel=2) prop.SetAmbient(ambient_level) prop.SetAmbientColor(ambient_color) prop.SetDiffuse(diffuse_level) prop.SetDiffuseColor(diffuse_color) prop.SetSpecular(specular_level) prop.SetSpecularColor(specular_color) prop.SetSpecularPower(specular_power) except AttributeError: warnings.warn( "Actor does not have the attribute property. This " "material will not be applied.", stacklevel=2, ) return