For this challenge, I wanna make this:
I followed along this tutorial:
and made a shader graph in unity that read the texture via uv scrolling. (The scrolling is controlled by a custom vertex stream in the particle system, my graph is specifically for making mesh particles animate.)
Here’s the skull in unity with the working animation texture shader
and the skull in blender
I’m not sure why the forehead flashes green? that doesn’t show up in my gif editor or in the softwares
Draft before timing pass and texture and stuff, just breaking the effect into piece I can work with
it looks very cool so far!
1st Timing Pass
WIP, Made a new super shader are closer to the one they described in this video: Technical Artist Bootcamp: The VFX of Diablo - YouTube
so it’d look closer to the original vid hopefully.
Kinda want to make a nose a sneeze version but not sure if I got the time.
import bpy
import bmesh
import mathutils
from mathutils import Vector, Color
#go to Properties Panel -> Render Properties -> Color Management -> View Transform -> Standard
#export the image with no compression, no non-uniform scaling, and no mip maps, use bilinear filter
def create_morph_us_set(obj):
bm = bmesh.new()
bm.from_mesh(obj.data)
uv_layer = bm.loops.layers.uv.new("VAT_UV")
pixel_size = 1.0 / len(bm.verts)
i = 0
for v in bm.verts:
for l in v.link_loops:
uv_data = l[uv_layer]
uv_data.uv = mathutils.Vector((i * pixel_size, 0.0))
i += 1
bm.to_mesh(obj.data)
# Replace 'YourObjectName' with the name of your object
object_name = "Cube"
# Get the object by its name
obj = bpy.data.objects.get(object_name)
#define scale use to scale down the vertex movement when they exceed 1
scale = 6.0
if obj and obj.type == 'MESH':
#generate the UV
create_morph_us_set(obj)
# Access the mesh data of the object
mesh_data = obj.data
# Get the number of frames in the animation
start_frame = bpy.context.scene.frame_start
end_frame = bpy.context.scene.frame_end
#create lists for position and normal
#initialize width
pixels_coords = []
pixels_normal = []
width = 0
# Iterate through the frames in the animation
for frame in range(start_frame, end_frame + 1):
# Set the current frame
bpy.context.scene.frame_set(frame)
# Get the dependency graph and evaluate the object
depsgraph = bpy.context.evaluated_depsgraph_get()
evaluated_object = obj.evaluated_get(depsgraph)
# Get the mesh data of the evaluated object
mesh_data = evaluated_object.data
# Get the world transformation matrix of the object for the current frame
world_matrix = obj.matrix_world
# Iterate through the vertices and get their global positions for the current frame
for vertex in mesh_data.vertices:
# Get the local coordinates of the vertex
local_coords = vertex.co.copy()
local_normal = vertex.normal.copy()
# Convert the local coordinates to global coordinates using the world transformation matrix
global_coords = world_matrix @ local_coords
global_normal = world_matrix @ local_normal
# Get the index of the vertex
#vertex_index = vertex.index
#store the vector values into a list of floats
for coord in global_coords:
pixels_coords.append(((coord / scale) + 1) / 2)
for coord in global_normal:
pixels_normal.append((coord + 1) / 2)
#add 1 for the alpha of each pixel
pixels_coords.append(1.0)
pixels_normal.append(1.0)
#print("Frame:", frame, "Vertex", vertex_index, "Global Coordinates:", global_coords)
#get width
width = len(obj.data.vertices)
#initalize image
image = bpy.data.images.new(object_name, width, end_frame + 1 - start_frame)
image.colorspace_settings.is_data = True
#save image
image.pixels = pixels_coords
image.save_render(bpy.path.abspath("//") + object_name + '_position' + ".png", scene = bpy.context.scene)
image.pixels = pixels_normal
image.save_render(bpy.path.abspath("//") + object_name + '_normal' + ".png", scene = bpy.context.scene)
else:
print("Object", object_name, "not found or not a mesh.")