Created
January 11, 2023 14:43
-
-
Save blender8r/65e3e5cd2aae8fcd0f9223f101a82ca4 to your computer and use it in GitHub Desktop.
Simulates volumetric fog on Blender grease pencil objects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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