# SPDX-License-Identifier: Apache-2.0 # Copyright 2018-2022 The glTF-Blender-IO authors. import bpy import numpy as np from .gltf2_blender_gather_image import StoreImage, StoreData from .gltf2_blender_image import TmpImageGuard, make_temp_image_copy def specular_calculation(stored): # See https://gist.github.com/proog128/d627c692a6bbe584d66789a5a6437a33 # Find all Blender images used images = [] for fill in stored.values(): if isinstance(fill, StoreImage): if fill.image not in images: images.append(fill.image) if not images: # No ImageFills; use a 1x1 white pixel pixels = np.array([1.0, 1.0, 1.0, 1.0], np.float32) return pixels, 1, 1 width = max(image.size[0] for image in images) height = max(image.size[1] for image in images) buffers = {} for identifier, image in [(ident, store.image) for (ident, store) in stored.items() if isinstance(store, StoreImage)]: tmp_buf = np.empty(width * height * 4, np.float32) if image.size[0] == width and image.size[1] == height: image.pixels.foreach_get(tmp_buf) else: # Image is the wrong size; make a temp copy and scale it. with TmpImageGuard() as guard: make_temp_image_copy(guard, src_image=image) tmp_image = guard.image tmp_image.scale(width, height) tmp_image.pixels.foreach_get(tmp_buf) buffers[identifier] = np.reshape(tmp_buf, [width, height, 4]) # keep only needed channels ## scalar for i in ['specular', 'specular_tint', 'transmission']: if i in buffers.keys(): buffers[i] = buffers[i][:,:,stored[i + "_channel"].data] else: buffers[i] = np.full((width, height, 1), stored[i].data) # Vector 3 for i in ['base_color']: if i in buffers.keys(): if i + "_channel" not in stored.keys(): buffers[i] = buffers[i][:,:,:3] else: # keep only needed channel for c in range(3): if c != stored[i+"_channel"].data: buffers[i][:, :, c] = 0.0 buffers[i] = buffers[i][:,:,:3] else: buffers[i] = np.full((width, height, 3), stored[i].data[0:3]) ior = stored['ior'].data # calculation stack3 = lambda v: np.dstack([v]*3) def normalize(c): luminance = lambda c: 0.3 * c[:,:,0] + 0.6 * c[:,:,1] + 0.1 * c[:,:,2] l = luminance(c) # TODOExt Manage all 0 return c / stack3(l) f0_from_ior = ((ior - 1)/(ior + 1))**2 tint_strength = (1 - stack3(buffers['specular_tint'])) + normalize(buffers['base_color']) * stack3(buffers['specular_tint']) out_buf = (1 - stack3(buffers['transmission'])) * (1 / f0_from_ior) * 0.08 * stack3(buffers['specular']) * tint_strength + stack3(buffers['transmission']) * tint_strength # Manage values > 1.0 -> Need to apply factor factor = None factors = [np.amax(out_buf[:, :, i]) for i in range(3)] if any([f > 1.0 for f in factors]): factor = [1.0 if f < 1.0 else f for f in factors] out_buf /= factor out_buf = np.dstack((out_buf, np.ones((width, height)))) # Set alpha (glTF specular) to 1 out_buf = np.reshape(out_buf, (width * height * 4)) return np.float32(out_buf), width, height, [float(f) for f in factor] if factor else None