Skip to content

Instantly share code, notes, and snippets.

@blender8r
Created January 11, 2023 14:43
Show Gist options
  • Select an option

  • Save blender8r/65e3e5cd2aae8fcd0f9223f101a82ca4 to your computer and use it in GitHub Desktop.

Select an option

Save blender8r/65e3e5cd2aae8fcd0f9223f101a82ca4 to your computer and use it in GitHub Desktop.
Simulates volumetric fog on Blender grease pencil objects
import bpy
from mathutils import Vector
min_dist = 10.0
max_dist = 120.0
fog_color = (1.0, 0.9, 0.7, 1.0)
fog_mult = 1.0
final_fog = (fog_color[0]*fog_mult, fog_color[1]*fog_mult, fog_color[2]*fog_mult)
falloff = 3.0
grease_pencils = []
cameras = []
camera = None
error = ""
def norm(value, min, max):
return (value - min) / (max - min)
def get_distance(pos1, pos2):
return (pos2 - pos1).length
def get_center_pivot(gp, stroke):
cx, cy, cz = 0.0, 0.0, 0.0
num_pts = 0
for p in stroke.points:
p_local = p.co
p_global = gp.matrix_world @ p_local
cx += p_global[0]
cy += p_global[1]
cz += p_global[2]
num_pts += 1
return (cx/num_pts, cy/num_pts, cz/num_pts)
def add_fog(gp, gp_stroke, cam_pos, min_dist, max_dist, fog_color, falloff):
stroke_pos = get_center_pivot(gp, gp_stroke)
dist_to_cam = get_distance(Vector(cam_pos), Vector(stroke_pos))
norm_dist = norm(dist_to_cam, min_dist, max_dist)
# add fog to vertex colors
for p in gp_stroke.points:
cur_color = Vector(p.vertex_color)
vc_fog_r = cur_color[0] + ((final_fog[0] - cur_color[0]) * (norm_dist**falloff))
vc_fog_g = cur_color[1] + ((final_fog[1] - cur_color[1]) * (norm_dist**falloff))
vc_fog_b = cur_color[2] + ((final_fog[2] - cur_color[2]) * (norm_dist**falloff))
p.vertex_color = (vc_fog_r, vc_fog_g, vc_fog_b, cur_color[3])
# add fog to vertex fill colors
vcf = Vector(gp_stroke.vertex_color_fill)
vcf_fog_r = vcf[0] + ((final_fog[0] - vcf[0]) * (norm_dist**falloff))
vcf_fog_g = vcf[1] + ((final_fog[1] - vcf[1]) * (norm_dist**falloff))
vcf_fog_b = vcf[2] + ((final_fog[2] - vcf[2]) * (norm_dist**falloff))
gp_stroke.vertex_color_fill = (vcf_fog_r, vcf_fog_g, vcf_fog_b, vcf[3])
# add fog to material strokes
mat = gpencil.materials[gp_stroke.material_index]
mat_color = mat.grease_pencil.color
mc_fog_r = mat_color[0] + ((final_fog[0] - mat_color[0]) * (norm_dist**falloff))
mc_fog_g = mat_color[1] + ((final_fog[1] - mat_color[1]) * (norm_dist**falloff))
mc_fog_b = mat_color[2] + ((final_fog[2] - mat_color[2]) * (norm_dist**falloff))
mat.grease_pencil.color = (mc_fog_r, mc_fog_g, mc_fog_b, mat_color[3])
# add fog to material fills
mat_fill_color = mat.grease_pencil.fill_color
mcf_fog_r = mat_fill_color[0] + ((final_fog[0] - mat_fill_color[0]) * (norm_dist**falloff))
mcf_fog_g = mat_fill_color[1] + ((final_fog[1] - mat_fill_color[1]) * (norm_dist**falloff))
mcf_fog_b = mat_fill_color[2] + ((final_fog[2] - mat_fill_color[2]) * (norm_dist**falloff))
mat.grease_pencil.fill_color = (mcf_fog_r, mcf_fog_g, mcf_fog_b, mat_fill_color[3])
def get_cam_position(camera, frame):
loc = [0.0, 0.0, 0.0]
if camera.animation_data:
action_name = camera.animation_data.action.name
action = bpy.data.actions[action_name]
for i, fcurve in enumerate(action.fcurves):
v = fcurve.evaluate(frame)
if fcurve.data_path == "location":
loc[fcurve.array_index] = v
else:
loc = camera.location
return loc
def duplicate(obj, data=True, actions=True, collection=None):
obj_copy = obj.copy()
if data:
obj_copy.data = obj_copy.data.copy()
if actions and obj_copy.animation_data:
obj_copy.animation_data.action = obj_copy.animation_data.action.copy()
collection.objects.link(obj_copy)
obj_copy.name = obj.name + "_fog"
materials = obj_copy.data.materials
for i, mat in enumerate(materials):
new_mat = mat.copy()
new_mat.name = mat.name + "_fog"
obj_copy.data.materials[i] = new_mat
return obj_copy
sel_obj = None
sel_objs = bpy.context.selected_objects
if sel_objs:
for sel in sel_objs:
if sel.type == 'GPENCIL':
grease_pencils.append(sel)
elif sel.type == 'CAMERA':
cameras.append(sel)
if grease_pencils and cameras:
if len(cameras) > 1:
error = "More than one camera selected!"
else:
camera = cameras[0]
else:
error = "You must select at least one grease pencil object and one camera!"
if error:
print(error)
else:
for gp in grease_pencils:
gp_copy = duplicate(gp, collection=gp.users_collection[0])
gpencil = gp_copy.data
gp.hide_viewport = True
gp.hide_render = True
for gp_layer in gpencil.layers:
for gp_frame in gp_layer.frames:
cam_pos = get_cam_position(camera, gp_frame.frame_number)
for gp_stroke in gp_frame.strokes:
add_fog(gp, gp_stroke, cam_pos, min_dist, max_dist, fog_color, falloff)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment