Import_3ds: Improved distance cue node setup
[blender-addons.git] / render_povray / shading.py
blob2cc09842225982484df74a411b90479f1c08a904
1 # SPDX-FileCopyrightText: 2015-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 """Translate complex shaders to exported POV textures."""
7 import bpy
10 def write_object_material_interior(file, material, ob, tab_write):
11 """Translate some object level material from Blender UI (VS data level)
13 to POV interior{} syntax and write it to exported file.
14 This is called in model_all.objects_loop
15 """
16 # DH - modified some variables to be function local, avoiding RNA write
17 # this should be checked to see if it is functionally correct
19 # Commented out: always write IOR to be able to use it for SSS, Fresnel reflections...
20 # if material and material.pov.transparency_method == 'RAYTRACE':
21 if not material:
22 return
23 # implicit if material:
24 # But there can be only one ior!
25 if material.pov_subsurface_scattering.use: # SSS IOR get highest priority
26 tab_write(file, "interior {\n")
27 tab_write(file, "ior %.6f\n" % material.pov_subsurface_scattering.ior)
28 # Then the raytrace IOR taken from raytrace transparency properties and used for
29 # reflections if IOR Mirror option is checked.
30 elif material.pov.mirror_use_IOR or material.pov.transparency_method != "Z_TRANSPARENCY":
31 tab_write(file, "interior {\n")
32 tab_write(file, "ior %.6f\n" % material.pov_raytrace_transparency.ior)
33 else:
34 tab_write(file, "interior {\n")
35 tab_write(file, "ior 1.0\n")
37 pov_fake_caustics = False
38 pov_photons_refraction = False
39 pov_photons_reflection = bool(material.pov.photons_reflection)
40 if not material.pov.refraction_caustics:
41 pov_fake_caustics = False
42 pov_photons_refraction = False
43 elif material.pov.refraction_type == "1":
44 pov_fake_caustics = True
45 pov_photons_refraction = False
46 elif material.pov.refraction_type == "2":
47 pov_fake_caustics = False
48 pov_photons_refraction = True
50 # If only Raytrace transparency is set, its IOR will be used for refraction, but user
51 # can set up 'un-physical' fresnel reflections in raytrace mirror parameters.
52 # Last, if none of the above is specified, user can set up 'un-physical' fresnel
53 # reflections in raytrace mirror parameters. And pov IOR defaults to 1.
54 if material.pov.caustics_enable:
55 if pov_fake_caustics:
56 tab_write(file, "caustics %.3g\n" % material.pov.fake_caustics_power)
57 if pov_photons_refraction:
58 # Default of 1 means no dispersion
59 tab_write(file, "dispersion %.6f\n" % material.pov.photons_dispersion)
60 tab_write(file, "dispersion_samples %.d\n" % material.pov.photons_dispersion_samples)
61 # TODO
62 # Other interior args
63 if material.pov.use_transparency and material.pov.transparency_method == "RAYTRACE":
64 # fade_distance
65 # In Blender this value has always been reversed compared to what tooltip says.
66 # 100.001 rather than 100 so that it does not get to 0
67 # which deactivates the feature in POV
68 tab_write(
69 file, "fade_distance %.3g\n" % (100.001 - material.pov_raytrace_transparency.depth_max)
71 # fade_power
72 tab_write(file, "fade_power %.3g\n" % material.pov_raytrace_transparency.falloff)
73 # fade_color
74 tab_write(file, "fade_color <%.3g, %.3g, %.3g>\n" % material.pov.interior_fade_color[:])
76 # (variable) dispersion_samples (constant count for now)
77 tab_write(file, "}\n")
78 if material.pov.photons_reflection or material.pov.refraction_type == "2":
79 tab_write(file, "photons{")
80 tab_write(file, "target %.3g\n" % ob.pov.spacing_multiplier)
81 if not ob.pov.collect_photons:
82 tab_write(file, "collect off\n")
83 if pov_photons_refraction:
84 tab_write(file, "refraction on\n")
85 if pov_photons_reflection:
86 tab_write(file, "reflection on\n")
87 tab_write(file, "}\n")
90 def write_material(
91 file,
92 using_uberpov,
93 DEF_MAT_NAME,
94 tab_write,
95 comments,
96 unique_name,
97 material_names_dictionary,
98 material,
100 """Translate Blender material to POV texture{} block and write in exported file."""
101 # Assumes only called once on each material
103 from .render import safety
105 if material:
106 name_orig = material.name
107 name = material_names_dictionary[name_orig] = unique_name(
108 bpy.path.clean_name(name_orig), material_names_dictionary
110 # If saturation(.s) is not zero, then color is not grey, and has a tint
111 colored_specular_found = (material.pov.specular_color.s > 0.0) and (
112 material.pov.diffuse_shader != "MINNAERT"
114 else:
115 name = name_orig = DEF_MAT_NAME
117 ##################
118 # Several versions of the finish: ref_level_bound conditions are variations for specular/Mirror
119 # texture channel map with alternative finish of 0 specular and no mirror reflection.
120 # ref_level_bound=1 Means No specular nor Mirror reflection
121 # ref_level_bound=2 Means translation of spec and mir levels for when no map influences them
122 # ref_level_bound=3 Means Maximum Spec and Mirror
124 def pov_has_no_specular_maps(file, ref_level_bound):
125 """Translate Blender specular map influence to POV finish map trick and write to file."""
126 if ref_level_bound == 1:
127 if comments:
128 tab_write(file, "//--No specular nor Mirror reflection--\n")
129 else:
130 tab_write(file, "\n")
131 tab_write(file, "#declare %s = finish {\n" % safety(name, ref_level_bound=1))
133 elif ref_level_bound == 2:
134 if comments:
135 tab_write(
136 file,
137 "//--translation of spec and mir levels for when no map " "influences them--\n",
139 else:
140 tab_write(file, "\n")
141 tab_write(file, "#declare %s = finish {\n" % safety(name, ref_level_bound=2))
143 elif ref_level_bound == 3:
144 if comments:
145 tab_write(file, "//--Maximum Spec and Mirror--\n")
146 else:
147 tab_write(file, "\n")
148 tab_write(file, "#declare %s = finish {\n" % safety(name, ref_level_bound=3))
149 if material:
150 # POV-Ray 3.7 now uses two diffuse values respectively for front and back shading
151 # (the back diffuse is like blender translucency)
152 front_diffuse = material.pov.diffuse_intensity
153 back_diffuse = material.pov.translucency
155 if material.pov.conserve_energy:
157 # Total should not go above one
158 if (front_diffuse + back_diffuse) <= 1.0:
159 pass
160 elif front_diffuse == back_diffuse:
161 # Try to respect the user's 'intention' by comparing the two values but
162 # bringing the total back to one.
163 front_diffuse = back_diffuse = 0.5
164 # Let the highest value stay the highest value.
165 elif front_diffuse > back_diffuse:
166 # clamps the sum below 1
167 back_diffuse = min(back_diffuse, (1.0 - front_diffuse))
168 else:
169 front_diffuse = min(front_diffuse, (1.0 - back_diffuse))
171 # map hardness between 0.0 and 1.0
172 roughness = 1.0 - ((material.pov.specular_hardness - 1.0) / 510.0)
173 ## scale from 0.0 to 0.1
174 roughness *= 0.1
175 # add a small value because 0.0 is invalid.
176 roughness += 1.0 / 511.0
178 # ------------------------------ Diffuse Shader ------------------------------ #
179 # Not used for Full spec (ref_level_bound=3) of the shader.
180 if material.pov.diffuse_shader == "OREN_NAYAR" and ref_level_bound != 3:
181 # Blender roughness is what is generally called oren nayar Sigma,
182 # and brilliance in POV-Ray.
183 tab_write(file, "brilliance %.3g\n" % (0.9 + material.roughness))
185 if material.pov.diffuse_shader == "TOON" and ref_level_bound != 3:
186 tab_write(file, "brilliance %.3g\n" % (0.01 + material.diffuse_toon_smooth * 0.25))
187 # Lower diffuse and increase specular for toon effect seems to look better
188 # in POV-Ray.
189 front_diffuse *= 0.5
191 if material.pov.diffuse_shader == "MINNAERT" and ref_level_bound != 3:
192 # tab_write(file, "aoi %.3g\n" % material.pov.darkness)
193 pass # let's keep things simple for now
194 if material.pov.diffuse_shader == "FRESNEL" and ref_level_bound != 3:
195 # tab_write(file, "aoi %.3g\n" % material.pov.diffuse_fresnel_factor)
196 pass # let's keep things simple for now
197 if material.pov.diffuse_shader == "LAMBERT" and ref_level_bound != 3:
198 # trying to best match lambert attenuation by that constant brilliance value
199 tab_write(file, "brilliance 1\n")
201 if ref_level_bound == 2:
202 # ------------------------------ Specular Shader ------------------------------ #
203 # No difference between phong and cook torrence in blender HaHa!
204 if material.pov.specular_shader in ["COOKTORR", "PHONG"]:
205 tab_write(file, "phong %.3g\n" % material.pov.specular_intensity)
206 tab_write(file, "phong_size %.3g\n" % (material.pov.specular_hardness / 3.14))
208 # POV-Ray 'specular' keyword corresponds to a Blinn model, without the ior.
209 elif material.pov.specular_shader == "BLINN":
210 # Use blender Blinn's IOR just as some factor for spec intensity
211 tab_write(
212 file,
213 "specular %.3g\n"
214 % (material.pov.specular_intensity * (material.pov.specular_ior / 4.0)),
216 tab_write(file, "roughness %.3g\n" % roughness)
217 # Could use brilliance 2(or varying around 2 depending on ior or factor) too.
219 elif material.pov.specular_shader == "TOON":
220 tab_write(file, "phong %.3g\n" % (material.pov.specular_intensity * 2.0))
221 # use extreme phong_size
222 tab_write(
223 file, "phong_size %.3g\n" % (0.1 + material.pov.specular_toon_smooth / 2.0)
226 elif material.pov.specular_shader == "WARDISO":
227 # find best suited default constant for brilliance Use both phong and
228 # specular for some values.
229 tab_write(
230 file,
231 "specular %.3g\n"
233 material.pov.specular_intensity / (material.pov.specular_slope + 0.0005)
236 # find best suited default constant for brilliance Use both phong and
237 # specular for some values.
238 tab_write(
239 file, "roughness %.4g\n" % (0.0005 + material.pov.specular_slope / 10.0)
241 # find best suited default constant for brilliance Use both phong and
242 # specular for some values.
243 tab_write(file, "brilliance %.4g\n" % (1.8 - material.pov.specular_slope * 1.8))
245 # -------------------------------------------------------------------------------- #
246 elif ref_level_bound == 1:
247 if material.pov.specular_shader in ["COOKTORR", "PHONG"]:
248 tab_write(file, "phong 0\n") # %.3g\n" % (material.pov.specular_intensity/5))
249 tab_write(file, "phong_size %.3g\n" % (material.pov.specular_hardness / 3.14))
251 # POV-Ray 'specular' keyword corresponds to a Blinn model, without the ior.
252 elif material.pov.specular_shader == "BLINN":
253 # Use blender Blinn's IOR just as some factor for spec intensity
254 tab_write(
255 file,
256 "specular %.3g\n"
257 % (material.pov.specular_intensity * (material.pov.specular_ior / 4.0)),
259 tab_write(file, "roughness %.3g\n" % roughness)
260 # Could use brilliance 2(or varying around 2 depending on ior or factor) too.
262 elif material.pov.specular_shader == "TOON":
263 tab_write(file, "phong %.3g\n" % (material.pov.specular_intensity * 2.0))
264 # use extreme phong_size
265 tab_write(
266 file, "phong_size %.3g\n" % (0.1 + material.pov.specular_toon_smooth / 2.0)
269 elif material.pov.specular_shader == "WARDISO":
270 # find best suited default constant for brilliance Use both phong and
271 # specular for some values.
272 tab_write(
273 file,
274 "specular %.3g\n"
276 material.pov.specular_intensity / (material.pov.specular_slope + 0.0005)
279 # find best suited default constant for brilliance Use both phong and
280 # specular for some values.
281 tab_write(
282 file, "roughness %.4g\n" % (0.0005 + material.pov.specular_slope / 10.0)
284 # find best suited default constant for brilliance Use both phong and
285 # specular for some values.
286 tab_write(file, "brilliance %.4g\n" % (1.8 - material.pov.specular_slope * 1.8))
287 elif ref_level_bound == 3:
288 # Spec must be Max at ref_level_bound 3 so that white of mixing texture always shows specularity
289 # That's why it's multiplied by 255. maybe replace by texture's brightest pixel value?
290 if material.pov_texture_slots:
291 max_spec_factor = (
292 material.pov.specular_intensity
293 * material.pov.specular_color.v
294 * 255
295 * slot.specular_factor
297 else:
298 max_spec_factor = (
299 material.pov.specular_intensity * material.pov.specular_color.v * 255
301 tab_write(file, "specular %.3g\n" % max_spec_factor)
302 tab_write(file, "roughness %.3g\n" % (1 / material.pov.specular_hardness))
303 tab_write(file, "diffuse %.3g, %.3g\n" % (front_diffuse, back_diffuse))
305 tab_write(file, "ambient %.3g\n" % material.pov.ambient)
306 # POV-Ray blends the global value
307 # tab_write(file, "ambient rgb <%.3g, %.3g, %.3g>\n" % \
308 # tuple([c*material.pov.ambient for c in world.ambient_color]))
309 tab_write(file, "emission %.3g\n" % material.pov.emit) # New in POV-Ray 3.7
311 # POV-Ray just ignores roughness if there's no specular keyword
312 # tab_write(file, "roughness %.3g\n" % roughness)
314 if material.pov.conserve_energy:
315 # added for more realistic shading. Needs some checking to see if it
316 # really works. --Maurice.
317 tab_write(file, "conserve_energy\n")
319 if colored_specular_found:
320 tab_write(file, "metallic\n")
322 # 'phong 70.0 '
323 if ref_level_bound != 1 and material.pov_raytrace_mirror.use:
324 raytrace_mirror = material.pov_raytrace_mirror
325 if raytrace_mirror.reflect_factor:
326 tab_write(file, "reflection {\n")
327 tab_write(file, "rgb <%.3g, %.3g, %.3g>\n" % material.pov.mirror_color[:])
328 if material.metallic:
329 tab_write(file, "metallic %.3g\n" % material.metallic)
330 # Blurry reflections for UberPOV
331 if using_uberpov and raytrace_mirror.gloss_factor < 1.0:
332 # tab_write(file, "#ifdef(unofficial) #if(unofficial = \"patch\") #if(patch(\"upov-reflection-roughness\") > 0)\n")
333 tab_write(
334 file, "roughness %.6f\n" % (0.000001 / raytrace_mirror.gloss_factor)
336 # tab_write(file, "#end #end #end\n") # This and previous comment for backward compatibility, messier pov code
337 if material.pov.mirror_use_IOR: # WORKING ?
338 # Removed from the line below: gives a more physically correct
339 # material but needs proper IOR. --Maurice
340 tab_write(file, "fresnel 1 ")
341 tab_write(
342 file,
343 "falloff %.3g exponent %.3g} "
344 % (raytrace_mirror.fresnel, raytrace_mirror.fresnel_factor),
347 if material.pov_subsurface_scattering.use:
348 subsurface_scattering = material.pov_subsurface_scattering
349 tab_write(
350 file,
351 "subsurface { translucency <%.3g, %.3g, %.3g> }\n"
353 (subsurface_scattering.radius[0]),
354 (subsurface_scattering.radius[1]),
355 (subsurface_scattering.radius[2]),
359 if material.pov.irid_enable:
360 tab_write(
361 file,
362 "irid { %.4g thickness %.4g turbulence %.4g }"
364 material.pov.irid_amount,
365 material.pov.irid_thickness,
366 material.pov.irid_turbulence,
370 else:
371 tab_write(file, "diffuse 0.8\n")
372 tab_write(file, "phong 70.0\n")
374 # tab_write(file, "specular 0.2\n")
376 # This is written into the object
378 if material and material.pov.transparency_method=='RAYTRACE':
379 'interior { ior %.3g} ' % material.raytrace_transparency.ior
382 # tab_write(file, "crand 1.0\n") # Sand granyness
383 # tab_write(file, "metallic %.6f\n" % material.metallic)
384 # tab_write(file, "phong %.6f\n" % material.spec)
385 # tab_write(file, "phong_size %.6f\n" % material.spec)
386 # tab_write(file, "brilliance %.6f " % (material.pov.specular_hardness/256.0) # Like hardness
388 tab_write(file, "}\n\n")
390 # ref_level_bound=2 Means translation of spec and mir levels for when no map influences them
391 pov_has_no_specular_maps(file, ref_level_bound=2)
393 if material:
394 special_texture_found = False
395 tmpidx = -1
396 slot = None
397 for t in material.pov_texture_slots:
398 tmpidx += 1
399 # index = material.pov.active_texture_index
400 slot = material.pov_texture_slots[tmpidx] # [index]
401 povtex = slot.texture # slot.name
402 tex = bpy.data.textures[povtex]
404 if t and t.use and tex is not None:
406 if (tex.type == "IMAGE" and tex.image) or tex.type != "IMAGE":
407 # validPath
408 if (
410 and t.use
411 and (
412 t.use_map_specular
413 or t.use_map_raymir
414 or t.use_map_normal
415 or t.use_map_alpha
418 special_texture_found = True
420 continue # Some texture found
422 if special_texture_found or colored_specular_found:
423 # ref_level_bound=1 Means No specular nor Mirror reflection
424 pov_has_no_specular_maps(file, ref_level_bound=1)
426 # ref_level_bound=3 Means Maximum Spec and Mirror
427 pov_has_no_specular_maps(file, ref_level_bound=3)