Skip to content

Instantly share code, notes, and snippets.

@dskjal
Last active February 18, 2018 05:21
Show Gist options
  • Save dskjal/23d11a6262b04caff311003dcc6d8bf5 to your computer and use it in GitHub Desktop.
Save dskjal/23d11a6262b04caff311003dcc6d8bf5 to your computer and use it in GitHub Desktop.
Create a normal texture from in Blender image.
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
import bpy
import numpy as np
import mathutils
img_name = 'bump.png'
strength = 10
sample = 24 # 8 or 24
useEase = True
exponent = 6 # [2, 6] higher value, higher contrast
# [min, max)
def clamp(x, min, max):
if x < 0:
x = 0
elif x >= width:
x = width-1
return x
def get_pixel(img, width, height, x, y):
x = clamp(x, 0, width)
y = clamp(y, 0, height)
idx = 4*(y*width + x)
return img[idx], img[idx + 1], img[idx + 2]
def get_r(img, width, height, x, y):
x = clamp(x, 0, width)
y = clamp(y, 0, height)
return img[4*(y*width + x)]
def set_pixel(img, width, height, x, y, r, g, b):
idx = 4*(y*width + x)
img[idx]=r
img[idx+1] = g
img[idx+2] = b
img[idx+3] = 1.0
def calc_normal(l, r, t, b):
du = (r-l)*strength
dv = (b-t)*strength
n = mathutils.Vector((dv, du, 1))
n.normalize()
return n
img_src = bpy.data.images[img_name]
img = np.array(img_src.pixels[:], dtype=np.float16)
# to grayscale
gray = 0.299 * img[0::4] + 0.587 * img[1::4] + 0.114 * img[2::4]
#easing
def ease(gray):
if gray < 0.5:
return 0.5*((2*gray)**exponent)
else:
return 1-0.5*((2-2*gray)**exponent)
if useEase:
ease_func = np.frompyfunc(ease, 1, 1)
gray = ease_func(gray)
img[0::4] = gray
#calc
width, height = img_src.size
dst = np.empty(4*width*height, dtype=np.float16)
if sample == 8:
#8 sample
for y in range(height):
for x in range(width):
l = get_r(img, width, height, x-1, y)
r = get_r(img, width, height, x+1, y)
t = get_r(img, width, height, x, y-1)
b = get_r(img, width, height, x, y+1)
n1 = calc_normal(l, r, t, b)
l = get_r(img, width, height, x-1, y-1)
r = get_r(img, width, height, x+1, y+1)
t = get_r(img, width, height, x+1, y-1)
b = get_r(img, width, height, x-1, y+1)
n2 = calc_normal(l, r, t, b)
n = 0.25*(n1 + n2)
n = n + mathutils.Vector((0.5, 0.5, 0.5))
set_pixel(dst, width, height, x, y, n.x, n.y, n.z)
elif sample == 24:
# 24 sample
for y in range(height):
for x in range(width):
l = get_r(img, width, height, x-1, y)
r = get_r(img, width, height, x+1, y)
t = get_r(img, width, height, x, y-1)
b = get_r(img, width, height, x, y+1)
n1 = calc_normal(l, r, t, b)
l = get_r(img, width, height, x-1, y-1)
r = get_r(img, width, height, x+1, y+1)
t = get_r(img, width, height, x+1, y-1)
b = get_r(img, width, height, x-1, y+1)
n2 = calc_normal(l, r, t, b)
l = get_r(img, width, height, x-2, y-2)
r = get_r(img, width, height, x+2, y+2)
t = get_r(img, width, height, x+2, y-2)
b = get_r(img, width, height, x-2, y+2)
n3 = calc_normal(l, r, t, b)
l = get_r(img, width, height, x-2, y)
r = get_r(img, width, height, x+2, y)
t = get_r(img, width, height, x, y-2)
b = get_r(img, width, height, x, y+2)
n4 = calc_normal(l, r, t, b)
l = get_r(img, width, height, x-2, y-1)
r = get_r(img, width, height, x+2, y+1)
t = get_r(img, width, height, x+2, y-1)
b = get_r(img, width, height, x-2, y+1)
n5 = calc_normal(l, r, t, b)
l = get_r(img, width, height, x-1, y-2)
r = get_r(img, width, height, x+1, y+2)
t = get_r(img, width, height, x+1, y-2)
b = get_r(img, width, height, x-1, y+2)
n6 = calc_normal(l, r, t, b)
n = 0.08333333*(n1 + n2 + n3 + n4 + n5 + n6)
n = n + mathutils.Vector((0.5, 0.5, 0.5))
set_pixel(dst, width, height, x, y, n.x, n.y, n.z)
#to image
new = bpy.data.images.new('normal_'+img_name, width, height, alpha=True)
new.pixels = dst
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment