Skip to content
Snippets Groups Projects
shading.py 19.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • # SPDX-License-Identifier: GPL-2.0-or-later
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    """Translate complex shaders to exported POV textures."""
    
    
    def write_object_material_interior(file, material, ob, tab_write):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Translate some object level material from Blender UI (VS data level)
    
        to POV interior{} syntax and write it to exported file.
    
        This is called in model_all.objects_loop
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """
        # DH - modified some variables to be function local, avoiding RNA write
        # this should be checked to see if it is functionally correct
    
        # Commented out: always write IOR to be able to use it for SSS, Fresnel reflections...
    
        # if material and material.pov.transparency_method == 'RAYTRACE':
        if not material:
            return
        # implicit if material:
        # But there can be only one ior!
        if material.pov_subsurface_scattering.use:  # SSS IOR get highest priority
            tab_write(file, "interior {\n")
            tab_write(file, "ior %.6f\n" % material.pov_subsurface_scattering.ior)
        # Then the raytrace IOR taken from raytrace transparency properties and used for
        # reflections if IOR Mirror option is checked.
        elif material.pov.mirror_use_IOR or material.pov.transparency_method != "Z_TRANSPARENCY":
            tab_write(file, "interior {\n")
            tab_write(file, "ior %.6f\n" % material.pov_raytrace_transparency.ior)
        else:
            tab_write(file, "interior {\n")
            tab_write(file, "ior 1.0\n")
    
        pov_fake_caustics = False
        pov_photons_refraction = False
        pov_photons_reflection = bool(material.pov.photons_reflection)
        if not material.pov.refraction_caustics:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            pov_fake_caustics = False
            pov_photons_refraction = False
    
        elif material.pov.refraction_type == "1":
            pov_fake_caustics = True
            pov_photons_refraction = False
        elif material.pov.refraction_type == "2":
            pov_fake_caustics = False
            pov_photons_refraction = True
    
        # If only Raytrace transparency is set, its IOR will be used for refraction, but user
        # can set up 'un-physical' fresnel reflections in raytrace mirror parameters.
        # Last, if none of the above is specified, user can set up 'un-physical' fresnel
        # reflections in raytrace mirror parameters. And pov IOR defaults to 1.
        if material.pov.caustics_enable:
            if pov_fake_caustics:
                tab_write(file, "caustics %.3g\n" % material.pov.fake_caustics_power)
            if pov_photons_refraction:
                # Default of 1 means no dispersion
                tab_write(file, "dispersion %.6f\n" % material.pov.photons_dispersion)
                tab_write(file, "dispersion_samples %.d\n" % material.pov.photons_dispersion_samples)
        # TODO
        # Other interior args
        if material.pov.use_transparency and material.pov.transparency_method == "RAYTRACE":
            # fade_distance
            # In Blender this value has always been reversed compared to what tooltip says.
            # 100.001 rather than 100 so that it does not get to 0
            # which deactivates the feature in POV
            tab_write(
                file, "fade_distance %.3g\n" % (100.001 - material.pov_raytrace_transparency.depth_max)
            )
            # fade_power
            tab_write(file, "fade_power %.3g\n" % material.pov_raytrace_transparency.falloff)
            # fade_color
            tab_write(file, "fade_color <%.3g, %.3g, %.3g>\n" % material.pov.interior_fade_color[:])
    
        # (variable) dispersion_samples (constant count for now)
        tab_write(file, "}\n")
        if material.pov.photons_reflection or material.pov.refraction_type == "2":
            tab_write(file, "photons{")
            tab_write(file, "target %.3g\n" % ob.pov.spacing_multiplier)
            if not ob.pov.collect_photons:
                tab_write(file, "collect off\n")
            if pov_photons_refraction:
                tab_write(file, "refraction on\n")
            if pov_photons_reflection:
                tab_write(file, "reflection on\n")
            tab_write(file, "}\n")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
    
    def write_material(
    
        using_uberpov,
        DEF_MAT_NAME,
        tab_write,
        comments,
        unique_name,
        material_names_dictionary,
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    ):
    
        """Translate Blender material to POV texture{} block and write in exported file."""
    
        # Assumes only called once on each material
    
        if material:
            name_orig = material.name
    
            name = material_names_dictionary[name_orig] = unique_name(
                bpy.path.clean_name(name_orig), material_names_dictionary
    
            # If saturation(.s) is not zero, then color is not grey, and has a tint
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            colored_specular_found = (material.pov.specular_color.s > 0.0) and (
                material.pov.diffuse_shader != "MINNAERT"
            )
    
        else:
            name = name_orig = DEF_MAT_NAME
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        # Several versions of the finish: ref_level_bound conditions are variations for specular/Mirror
    
        # texture channel map with alternative finish of 0 specular and no mirror reflection.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        # ref_level_bound=1 Means No specular nor Mirror reflection
        # ref_level_bound=2 Means translation of spec and mir levels for when no map influences them
        # ref_level_bound=3 Means Maximum Spec and Mirror
    
        def pov_has_no_specular_maps(file, ref_level_bound):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """Translate Blender specular map influence to POV finish map trick and write to file."""
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if ref_level_bound == 1:
    
                    tab_write(file, "//--No specular nor Mirror reflection--\n")
    
                    tab_write(file, "\n")
                tab_write(file, "#declare %s = finish {\n" % safety(name, ref_level_bound=1))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            elif ref_level_bound == 2:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    tab_write(
    
                        file,
                        "//--translation of spec and mir levels for when no map " "influences them--\n",
    
                    tab_write(file, "\n")
                tab_write(file, "#declare %s = finish {\n" % safety(name, ref_level_bound=2))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            elif ref_level_bound == 3:
    
                    tab_write(file, "//--Maximum Spec and Mirror--\n")
    
                    tab_write(file, "\n")
                tab_write(file, "#declare %s = finish {\n" % safety(name, ref_level_bound=3))
    
            if material:
                # POV-Ray 3.7 now uses two diffuse values respectively for front and back shading
                # (the back diffuse is like blender translucency)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                front_diffuse = material.pov.diffuse_intensity
                back_diffuse = material.pov.translucency
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    # Total should not go above one
                    if (front_diffuse + back_diffuse) <= 1.0:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    elif front_diffuse == back_diffuse:
    
                        # Try to respect the user's 'intention' by comparing the two values but
                        # bringing the total back to one.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        front_diffuse = back_diffuse = 0.5
    
                    # Let the highest value stay the highest value.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    elif front_diffuse > back_diffuse:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        back_diffuse = min(back_diffuse, (1.0 - front_diffuse))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        front_diffuse = min(front_diffuse, (1.0 - back_diffuse))
    
    
                # map hardness between 0.0 and 1.0
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                roughness = 1.0 - ((material.pov.specular_hardness - 1.0) / 510.0)
    
                ## scale from 0.0 to 0.1
                roughness *= 0.1
                # add a small value because 0.0 is invalid.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                roughness += 1.0 / 511.0
    
                # ------------------------------ Diffuse Shader ------------------------------ #
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                # Not used for Full spec (ref_level_bound=3) of the shader.
                if material.pov.diffuse_shader == "OREN_NAYAR" and ref_level_bound != 3:
    
                    # Blender roughness is what is generally called oren nayar Sigma,
                    # and brilliance in POV-Ray.
    
                    tab_write(file, "brilliance %.3g\n" % (0.9 + material.roughness))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                if material.pov.diffuse_shader == "TOON" and ref_level_bound != 3:
    
                    tab_write(file, "brilliance %.3g\n" % (0.01 + material.diffuse_toon_smooth * 0.25))
    
                    # Lower diffuse and increase specular for toon effect seems to look better
                    # in POV-Ray.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    front_diffuse *= 0.5
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                if material.pov.diffuse_shader == "MINNAERT" and ref_level_bound != 3:
    
                    # tab_write(file, "aoi %.3g\n" % material.pov.darkness)
    
                    pass  # let's keep things simple for now
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                if material.pov.diffuse_shader == "FRESNEL" and ref_level_bound != 3:
    
                    # tab_write(file, "aoi %.3g\n" % material.pov.diffuse_fresnel_factor)
    
                    pass  # let's keep things simple for now
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                if material.pov.diffuse_shader == "LAMBERT" and ref_level_bound != 3:
    
                    # trying to best match lambert attenuation by that constant brilliance value
    
                    tab_write(file, "brilliance 1\n")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                if ref_level_bound == 2:
    
                    # ------------------------------ Specular Shader ------------------------------ #
    
                    # No difference between phong and cook torrence in blender HaHa!
    
                    if material.pov.specular_shader in ["COOKTORR", "PHONG"]:
                        tab_write(file, "phong %.3g\n" % material.pov.specular_intensity)
                        tab_write(file, "phong_size %.3g\n" % (material.pov.specular_hardness / 3.14))
    
    
                    # POV-Ray 'specular' keyword corresponds to a Blinn model, without the ior.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    elif material.pov.specular_shader == "BLINN":
    
                        # Use blender Blinn's IOR just as some factor for spec intensity
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        tab_write(
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            "specular %.3g\n"
    
                            % (material.pov.specular_intensity * (material.pov.specular_ior / 4.0)),
    
                        tab_write(file, "roughness %.3g\n" % roughness)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        # Could use brilliance 2(or varying around 2 depending on ior or factor) too.
    
                    elif material.pov.specular_shader == "TOON":
    
                        tab_write(file, "phong %.3g\n" % (material.pov.specular_intensity * 2.0))
    
                        tab_write(
                            file, "phong_size %.3g\n" % (0.1 + material.pov.specular_toon_smooth / 2.0)
                        )
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    elif material.pov.specular_shader == "WARDISO":
    
                        # find best suited default constant for brilliance Use both phong and
                        # specular for some values.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        tab_write(
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            "specular %.3g\n"
    
                            % (
                                material.pov.specular_intensity / (material.pov.specular_slope + 0.0005)
                            ),
    
                        # find best suited default constant for brilliance Use both phong and
                        # specular for some values.
    
                        tab_write(
                            file, "roughness %.4g\n" % (0.0005 + material.pov.specular_slope / 10.0)
                        )
    
                        # find best suited default constant for brilliance Use both phong and
                        # specular for some values.
    
                        tab_write(file, "brilliance %.4g\n" % (1.8 - material.pov.specular_slope * 1.8))
    
                # -------------------------------------------------------------------------------- #
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                elif ref_level_bound == 1:
    
                    if material.pov.specular_shader in ["COOKTORR", "PHONG"]:
                        tab_write(file, "phong 0\n")  # %.3g\n" % (material.pov.specular_intensity/5))
                        tab_write(file, "phong_size %.3g\n" % (material.pov.specular_hardness / 3.14))
    
    
                    # POV-Ray 'specular' keyword corresponds to a Blinn model, without the ior.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    elif material.pov.specular_shader == "BLINN":
    
                        # Use blender Blinn's IOR just as some factor for spec intensity
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        tab_write(
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            "specular %.3g\n"
    
                            % (material.pov.specular_intensity * (material.pov.specular_ior / 4.0)),
    
                        tab_write(file, "roughness %.3g\n" % roughness)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        # Could use brilliance 2(or varying around 2 depending on ior or factor) too.
    
                    elif material.pov.specular_shader == "TOON":
    
                        tab_write(file, "phong %.3g\n" % (material.pov.specular_intensity * 2.0))
    
                        # use extreme phong_size
    
                        tab_write(
                            file, "phong_size %.3g\n" % (0.1 + material.pov.specular_toon_smooth / 2.0)
                        )
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    elif material.pov.specular_shader == "WARDISO":
    
                        # find best suited default constant for brilliance Use both phong and
                        # specular for some values.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        tab_write(
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            "specular %.3g\n"
    
                            % (
                                material.pov.specular_intensity / (material.pov.specular_slope + 0.0005)
                            ),
    
                        # find best suited default constant for brilliance Use both phong and
                        # specular for some values.
    
                        tab_write(
                            file, "roughness %.4g\n" % (0.0005 + material.pov.specular_slope / 10.0)
                        )
    
                        # find best suited default constant for brilliance Use both phong and
                        # specular for some values.
    
                        tab_write(file, "brilliance %.4g\n" % (1.8 - material.pov.specular_slope * 1.8))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                elif ref_level_bound == 3:
                    # Spec must be Max at ref_level_bound 3 so that white of mixing texture always shows specularity
    
                    # That's why it's multiplied by 255. maybe replace by texture's brightest pixel value?
    
                    if material.pov_texture_slots:
                        max_spec_factor = (
    
                            material.pov.specular_intensity
                            * material.pov.specular_color.v
                            * 255
                            * slot.specular_factor
    
                            material.pov.specular_intensity * material.pov.specular_color.v * 255
    
                    tab_write(file, "specular %.3g\n" % max_spec_factor)
                    tab_write(file, "roughness %.3g\n" % (1 / material.pov.specular_hardness))
                tab_write(file, "diffuse %.3g, %.3g\n" % (front_diffuse, back_diffuse))
    
                tab_write(file, "ambient %.3g\n" % material.pov.ambient)
    
                # POV-Ray blends the global value
    
                # tab_write(file, "ambient rgb <%.3g, %.3g, %.3g>\n" % \
    
                #         tuple([c*material.pov.ambient for c in world.ambient_color]))
    
                tab_write(file, "emission %.3g\n" % material.pov.emit)  # New in POV-Ray 3.7
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                # POV-Ray just ignores roughness if there's no specular keyword
    
                # tab_write(file, "roughness %.3g\n" % roughness)
    
    
                if material.pov.conserve_energy:
                    # added for more realistic shading. Needs some checking to see if it
                    # really works. --Maurice.
    
                    tab_write(file, "conserve_energy\n")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                if colored_specular_found:
    
                if ref_level_bound != 1 and material.pov_raytrace_mirror.use:
                    raytrace_mirror = material.pov_raytrace_mirror
                    if raytrace_mirror.reflect_factor:
                        tab_write(file, "reflection {\n")
                        tab_write(file, "rgb <%.3g, %.3g, %.3g>\n" % material.pov.mirror_color[:])
                        if material.metallic:
                            tab_write(file, "metallic %.3g\n" % material.metallic)
                        # Blurry reflections for UberPOV
                        if using_uberpov and raytrace_mirror.gloss_factor < 1.0:
                            # tab_write(file, "#ifdef(unofficial) #if(unofficial = \"patch\") #if(patch(\"upov-reflection-roughness\") > 0)\n")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            tab_write(
    
                                file, "roughness %.6f\n" % (0.000001 / raytrace_mirror.gloss_factor)
    
                            # tab_write(file, "#end #end #end\n") # This and previous comment for backward compatibility, messier pov code
                        if material.pov.mirror_use_IOR:  # WORKING ?
                            # Removed from the line below: gives a more physically correct
                            # material but needs proper IOR. --Maurice
                            tab_write(file, "fresnel 1 ")
                        tab_write(
                            file,
                            "falloff %.3g exponent %.3g} "
                            % (raytrace_mirror.fresnel, raytrace_mirror.fresnel_factor),
                        )
    
                if material.pov_subsurface_scattering.use:
                    subsurface_scattering = material.pov_subsurface_scattering
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    tab_write(
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        "subsurface { translucency <%.3g, %.3g, %.3g> }\n"
                        % (
                            (subsurface_scattering.radius[0]),
                            (subsurface_scattering.radius[1]),
                            (subsurface_scattering.radius[2]),
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    tab_write(
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        "irid { %.4g thickness %.4g turbulence %.4g }"
                        % (
                            material.pov.irid_amount,
                            material.pov.irid_thickness,
                            material.pov.irid_turbulence,
    
                tab_write(file, "diffuse 0.8\n")
                tab_write(file, "phong 70.0\n")
    
                # tab_write(file, "specular 0.2\n")
    
    
            # This is written into the object
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """
    
            if material and material.pov.transparency_method=='RAYTRACE':
    
                'interior { ior %.3g} ' % material.raytrace_transparency.ior
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """
    
            # tab_write(file, "crand 1.0\n") # Sand granyness
            # tab_write(file, "metallic %.6f\n" % material.metallic)
            # tab_write(file, "phong %.6f\n" % material.spec)
            # tab_write(file, "phong_size %.6f\n" % material.spec)
            # tab_write(file, "brilliance %.6f " % (material.pov.specular_hardness/256.0) # Like hardness
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        # ref_level_bound=2 Means translation of spec and mir levels for when no map influences them
    
        pov_has_no_specular_maps(file, ref_level_bound=2)
    
    
        if material:
            special_texture_found = False
    
            for t in material.pov_texture_slots:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                # index = material.pov.active_texture_index
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                slot = material.pov_texture_slots[tmpidx]  # [index]
                povtex = slot.texture  # slot.name
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                tex = bpy.data.textures[povtex]
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                if t and t.use and tex is not None:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    if (tex.type == "IMAGE" and tex.image) or tex.type != "IMAGE":
                        # validPath
                        if (
                            t
                            and t.use
                            and (
                                t.use_map_specular
                                or t.use_map_raymir
                                or t.use_map_normal
                                or t.use_map_alpha
                            )
                        ):
    
                            special_texture_found = True
    
                            continue  # Some texture found
    
    
            if special_texture_found or colored_specular_found:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                # ref_level_bound=1 Means No specular nor Mirror reflection
    
                pov_has_no_specular_maps(file, ref_level_bound=1)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
                # ref_level_bound=3 Means Maximum Spec and Mirror
    
                pov_has_no_specular_maps(file, ref_level_bound=3)