Campbell Barton
e8da6131fd
Move copyright text to SPDX-FileCopyrightText or set to the Blender Foundation so "make check_licenses" now runs without warnings.
428 lines
20 KiB
Python
Executable File
428 lines
20 KiB
Python
Executable File
# SPDX-FileCopyrightText: 2015-2022 Blender Foundation
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
"""Translate complex shaders to exported POV textures."""
|
|
|
|
import bpy
|
|
|
|
|
|
def write_object_material_interior(file, material, ob, tab_write):
|
|
"""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
|
|
"""
|
|
# 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:
|
|
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")
|
|
|
|
|
|
def write_material(
|
|
file,
|
|
using_uberpov,
|
|
DEF_MAT_NAME,
|
|
tab_write,
|
|
comments,
|
|
unique_name,
|
|
material_names_dictionary,
|
|
material,
|
|
):
|
|
"""Translate Blender material to POV texture{} block and write in exported file."""
|
|
# Assumes only called once on each material
|
|
|
|
from .render import safety
|
|
|
|
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
|
|
colored_specular_found = (material.pov.specular_color.s > 0.0) and (
|
|
material.pov.diffuse_shader != "MINNAERT"
|
|
)
|
|
else:
|
|
name = name_orig = DEF_MAT_NAME
|
|
|
|
##################
|
|
# 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.
|
|
# 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):
|
|
"""Translate Blender specular map influence to POV finish map trick and write to file."""
|
|
if ref_level_bound == 1:
|
|
if comments:
|
|
tab_write(file, "//--No specular nor Mirror reflection--\n")
|
|
else:
|
|
tab_write(file, "\n")
|
|
tab_write(file, "#declare %s = finish {\n" % safety(name, ref_level_bound=1))
|
|
|
|
elif ref_level_bound == 2:
|
|
if comments:
|
|
tab_write(
|
|
file,
|
|
"//--translation of spec and mir levels for when no map " "influences them--\n",
|
|
)
|
|
else:
|
|
tab_write(file, "\n")
|
|
tab_write(file, "#declare %s = finish {\n" % safety(name, ref_level_bound=2))
|
|
|
|
elif ref_level_bound == 3:
|
|
if comments:
|
|
tab_write(file, "//--Maximum Spec and Mirror--\n")
|
|
else:
|
|
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)
|
|
front_diffuse = material.pov.diffuse_intensity
|
|
back_diffuse = material.pov.translucency
|
|
|
|
if material.pov.conserve_energy:
|
|
|
|
# Total should not go above one
|
|
if (front_diffuse + back_diffuse) <= 1.0:
|
|
pass
|
|
elif front_diffuse == back_diffuse:
|
|
# Try to respect the user's 'intention' by comparing the two values but
|
|
# bringing the total back to one.
|
|
front_diffuse = back_diffuse = 0.5
|
|
# Let the highest value stay the highest value.
|
|
elif front_diffuse > back_diffuse:
|
|
# clamps the sum below 1
|
|
back_diffuse = min(back_diffuse, (1.0 - front_diffuse))
|
|
else:
|
|
front_diffuse = min(front_diffuse, (1.0 - back_diffuse))
|
|
|
|
# map hardness between 0.0 and 1.0
|
|
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.
|
|
roughness += 1.0 / 511.0
|
|
|
|
# ------------------------------ Diffuse Shader ------------------------------ #
|
|
# 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))
|
|
|
|
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.
|
|
front_diffuse *= 0.5
|
|
|
|
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
|
|
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
|
|
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")
|
|
|
|
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.
|
|
elif material.pov.specular_shader == "BLINN":
|
|
# Use blender Blinn's IOR just as some factor for spec intensity
|
|
tab_write(
|
|
file,
|
|
"specular %.3g\n"
|
|
% (material.pov.specular_intensity * (material.pov.specular_ior / 4.0)),
|
|
)
|
|
tab_write(file, "roughness %.3g\n" % roughness)
|
|
# 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)
|
|
)
|
|
|
|
elif material.pov.specular_shader == "WARDISO":
|
|
# find best suited default constant for brilliance Use both phong and
|
|
# specular for some values.
|
|
tab_write(
|
|
file,
|
|
"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))
|
|
|
|
# -------------------------------------------------------------------------------- #
|
|
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.
|
|
elif material.pov.specular_shader == "BLINN":
|
|
# Use blender Blinn's IOR just as some factor for spec intensity
|
|
tab_write(
|
|
file,
|
|
"specular %.3g\n"
|
|
% (material.pov.specular_intensity * (material.pov.specular_ior / 4.0)),
|
|
)
|
|
tab_write(file, "roughness %.3g\n" % roughness)
|
|
# 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)
|
|
)
|
|
|
|
elif material.pov.specular_shader == "WARDISO":
|
|
# find best suited default constant for brilliance Use both phong and
|
|
# specular for some values.
|
|
tab_write(
|
|
file,
|
|
"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))
|
|
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
|
|
)
|
|
else:
|
|
max_spec_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
|
|
|
|
# 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")
|
|
|
|
if colored_specular_found:
|
|
tab_write(file, "metallic\n")
|
|
|
|
# 'phong 70.0 '
|
|
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")
|
|
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
|
|
tab_write(
|
|
file,
|
|
"subsurface { translucency <%.3g, %.3g, %.3g> }\n"
|
|
% (
|
|
(subsurface_scattering.radius[0]),
|
|
(subsurface_scattering.radius[1]),
|
|
(subsurface_scattering.radius[2]),
|
|
),
|
|
)
|
|
|
|
if material.pov.irid_enable:
|
|
tab_write(
|
|
file,
|
|
"irid { %.4g thickness %.4g turbulence %.4g }"
|
|
% (
|
|
material.pov.irid_amount,
|
|
material.pov.irid_thickness,
|
|
material.pov.irid_turbulence,
|
|
),
|
|
)
|
|
|
|
else:
|
|
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
|
|
"""
|
|
if material and material.pov.transparency_method=='RAYTRACE':
|
|
'interior { ior %.3g} ' % material.raytrace_transparency.ior
|
|
"""
|
|
|
|
# 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
|
|
|
|
tab_write(file, "}\n\n")
|
|
|
|
# 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
|
|
tmpidx = -1
|
|
slot = None
|
|
for t in material.pov_texture_slots:
|
|
tmpidx += 1
|
|
# index = material.pov.active_texture_index
|
|
slot = material.pov_texture_slots[tmpidx] # [index]
|
|
povtex = slot.texture # slot.name
|
|
tex = bpy.data.textures[povtex]
|
|
|
|
if t and t.use and tex is not None:
|
|
|
|
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:
|
|
# ref_level_bound=1 Means No specular nor Mirror reflection
|
|
pov_has_no_specular_maps(file, ref_level_bound=1)
|
|
|
|
# ref_level_bound=3 Means Maximum Spec and Mirror
|
|
pov_has_no_specular_maps(file, ref_level_bound=3)
|