Skip to content

Instantly share code, notes, and snippets.

@blender8r
Created May 25, 2023 12:51
Show Gist options
  • Select an option

  • Save blender8r/3ea047af33381d0e41b8009115ccd0b8 to your computer and use it in GitHub Desktop.

Select an option

Save blender8r/3ea047af33381d0e41b8009115ccd0b8 to your computer and use it in GitHub Desktop.

Revisions

  1. blender8r created this gist May 25, 2023.
    233 changes: 233 additions & 0 deletions ghibli_shader_full.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,233 @@
    import bpy


    IMAGE = None
    TEXTURE_PATH = 'D:\\images\\textures\\hatch_1.jpg'

    for i in bpy.data.images:
    if i.filepath == TEXTURE_PATH:
    IMAGE = i

    if not IMAGE:
    IMAGE = bpy.data.images.load(TEXTURE_PATH)


    def add_group_controls(group, input_type, input_name, def_val, min_val=0.0, max_val=1.0):
    ctrl = group.inputs.new(input_type, input_name)
    ctrl.default_value = def_val
    if input_type == 'NodeSocketFloat' or input_type == 'NodeSocketFloatFactor':
    ctrl.min_value = min_val
    ctrl.max_value = max_val

    # based on TLousky's function posted in this thread
    # https://blender.stackexchange.com/questions/39127/how-to-put-together-a-driver-with-python/39129#39129

    def add_driver(source, target, prop, dataPath, index = -1, negative = False, func = ''):
    if index != -1:
    d = source.driver_add(prop, index).driver
    else:
    d = source.driver_add(prop).driver

    v = d.variables.new()
    v.name = prop
    v.targets[0].id_type = 'MATERIAL'
    v.targets[0].id = target
    v.targets[0].data_path = dataPath

    d.expression = func + "(" + v.name + ")" if func else v.name
    d.expression = d.expression if not negative else "-1 * " + d.expression

    return d


    # create an ico sphere and set to smooth shade
    bpy.ops.mesh.primitive_ico_sphere_add()
    sphere = bpy.context.active_object
    for p in sphere.data.polygons:
    p.use_smooth = True

    # add subd modifier
    subd = sphere.modifiers.new('subsurf', type='SUBSURF')
    subd.levels = 3

    # create new material
    mat_name = 'Ghibli Hatch Shader'
    mat = bpy.data.materials.new(mat_name)
    mat.use_nodes = True
    node_tree = mat.node_tree
    #nodes = node_tree.nodes
    links = node_tree.links

    sphere.data.materials.append(mat)

    # create control group
    group = bpy.data.node_groups.new(type='ShaderNodeTree', name='Ghibli Shader')
    nodes = group.nodes

    # add inputs with calls to our add input function
    add_group_controls(group, 'NodeSocketColor', 'Color A', (0.015, 0.125, 0.013, 1.0))
    add_group_controls(group, 'NodeSocketColor', 'Color B', (0.481, 0.464, 0.153, 1.0))
    add_group_controls(group, 'NodeSocketFloatFactor', 'Color A Clamp', 0.0)
    add_group_controls(group, 'NodeSocketFloatFactor', 'Color B Clamp', 0.7)
    add_group_controls(group, 'NodeSocketFloatFactor', 'Noise Strength', 0.75)
    add_group_controls(group, 'NodeSocketFloat', 'Noise Scale', 5.0, 0.0, 200.0)
    add_group_controls(group, 'NodeSocketFloatFactor', 'Noise Variation', 0.8)
    add_group_controls(group, 'NodeSocketFloatFactor', 'Noise Min', 0.0)
    add_group_controls(group, 'NodeSocketFloatFactor', 'Noise Max', 1.0)
    add_group_controls(group, 'NodeSocketFloatFactor', 'Noise Soft Min', 0.0)
    add_group_controls(group, 'NodeSocketFloatFactor', 'Color Soft Max', 0.9)
    add_group_controls(group, 'NodeSocketFloatFactor', 'Speckle Strength', 0.8)
    add_group_controls(group, 'NodeSocketFloat', 'Speckle Scale', 25.0, 0.0, 50.0)
    add_group_controls(group, 'NodeSocketFloatFactor', 'Shadow Blend', 1.0)
    add_group_controls(group, 'NodeSocketFloatFactor', 'Hatching Blend', 1.0)
    add_group_controls(group, 'NodeSocketVector', 'Hatching Scale', (8.0, 8.0, 8.0))
    add_group_controls(group, 'NodeSocketFloatFactor', 'Hatching Falloff Min', 0.136)
    add_group_controls(group, 'NodeSocketFloatFactor', 'Hatching Falloff Max', 0.291)


    # create group inputs and outputs
    group.outputs.new('NodeSocketShader', 'Output1')
    output_node = group.nodes.new('NodeGroupOutput')
    input_node = group.nodes.new('NodeGroupInput')

    # assign the group to the main node tree
    group_node = node_tree.nodes.new('ShaderNodeGroup')
    group_node.node_tree = group

    ShaderNodeOutputMaterial_node = node_tree.nodes['Material Output']
    links.new(group_node.outputs[0], ShaderNodeOutputMaterial_node.inputs[0])

    # get main shader material output node
    node_mat_output = node_tree.nodes['Material Output']

    # delete default bsdf node
    node_tree.nodes.remove(node_tree.nodes['Principled BSDF'])

    # create new nodes and set their values
    node_emission = nodes.new(type='ShaderNodeEmission')

    node_mix1 = nodes.new(type='ShaderNodeMixRGB')
    node_mix1.blend_type = 'MULTIPLY'
    node_mix1.inputs['Fac'].default_value = 1.0
    node_mix1.label = 'Ink Effect Blend'

    node_mix2 = nodes.new(type='ShaderNodeMixRGB')
    node_mix2.blend_type = 'SOFT_LIGHT'
    node_mix2.inputs['Fac'].default_value = 0.75
    node_mix2.label = 'Secondary Noise Strength'

    node_mix3 = nodes.new(type='ShaderNodeMixRGB')
    node_mix3.blend_type = 'SOFT_LIGHT'
    node_mix3.label = 'Hatching Effect Blend'

    node_mix4 = nodes.new(type='ShaderNodeMixRGB')
    node_mix4.blend_type = 'MIX'
    node_mix4.inputs['Fac'].default_value = 0.8
    node_mix4.label = 'Speckle Effect Blend'

    node_color_ramp1 = nodes.new(type='ShaderNodeValToRGB')
    node_color_ramp1.color_ramp.elements[0].position = 0.0
    node_color_ramp1.color_ramp.elements[0].color = (0.0373, 0.0752, 0.0305, 1.0)
    node_color_ramp1.color_ramp.elements[1].position = 0.7
    node_color_ramp1.color_ramp.elements[1].color = (0.4351, 0.4297, 0.0953, 1.0)
    node_color_ramp1.label = 'Secondary Color Blend'

    node_color_ramp2 = nodes.new(type='ShaderNodeValToRGB')
    node_color_ramp2.color_ramp.elements[0].position = 0.15
    node_color_ramp2.color_ramp.elements[1].position = 0.3
    node_color_ramp2.label = 'Hatching Falloff'

    node_color_ramp3 = nodes.new(type='ShaderNodeValToRGB')
    node_color_ramp3.label = 'Voronoi Strength'

    node_color_ramp4 = nodes.new(type='ShaderNodeValToRGB')
    node_color_ramp4.color_ramp.elements[0].position = 0.5
    node_color_ramp4.color_ramp.elements[1].position = 0.7
    node_color_ramp4.label = 'Speckle Falloff'

    node_color_ramp5 = nodes.new(type='ShaderNodeValToRGB')
    node_color_ramp5.color_ramp.elements[0].position = 0.0
    node_color_ramp5.color_ramp.elements[1].position = 0.9
    node_color_ramp5.label = 'Voronoi Softness Variation'

    node_img_tex = nodes.new(type='ShaderNodeTexImage')
    node_img_tex.image = IMAGE
    node_img_tex.label = 'Hatching Texture Map'

    node_shader2RGB = nodes.new(type='ShaderNodeShaderToRGB')

    node_mapping = nodes.new(type='ShaderNodeMapping')
    node_mapping.inputs[3].default_value = (8.0, 8.0, 8.0)

    node_tex_coords = nodes.new(type='ShaderNodeTexCoord')

    node_diff_bsdf = nodes.new(type='ShaderNodeBsdfDiffuse')

    node_voronoi = nodes.new(type='ShaderNodeTexVoronoi')
    node_voronoi.feature = 'SMOOTH_F1'
    node_voronoi.inputs[2].default_value = 10.0

    node_musgrave = nodes.new(type='ShaderNodeTexMusgrave')
    node_musgrave.inputs[2].default_value = 25.0

    node_noise = nodes.new(type='ShaderNodeTexNoise')
    node_noise.inputs[2].default_value = 5.0
    node_noise.inputs[4].default_value = 0.5

    # link nodes together

    group.links.new(output_node.inputs['Output1'], node_emission.outputs['Emission'])
    group.links.new(node_emission.inputs['Color'], node_mix1.outputs['Color'])
    group.links.new(node_mix1.inputs['Color1'], node_color_ramp1.outputs['Color'])
    group.links.new(node_mix1.inputs['Color2'], node_color_ramp2.outputs['Color'])
    group.links.new(node_color_ramp1.inputs['Fac'], node_mix2.outputs['Color'])
    group.links.new(node_color_ramp2.inputs['Fac'], node_mix3.outputs['Color'])
    group.links.new(node_mix2.inputs['Color2'], node_mix4.outputs['Color'])
    group.links.new(node_mix4.inputs['Color2'], node_color_ramp4.outputs['Color'])
    group.links.new(node_color_ramp4.inputs['Fac'], node_musgrave.outputs['Fac'])
    group.links.new(node_mix4.inputs['Color1'], node_color_ramp3.outputs['Color'])
    group.links.new(node_color_ramp3.inputs['Fac'], node_voronoi.outputs['Color'])
    group.links.new(node_voronoi.inputs['Smoothness'], node_color_ramp5.outputs['Color'])
    group.links.new(node_color_ramp5.inputs['Fac'], node_noise.outputs['Color'])
    group.links.new(node_mix2.inputs['Color1'], node_shader2RGB.outputs['Color'])
    group.links.new(node_mix3.inputs['Color1'], node_shader2RGB.outputs['Color'])
    group.links.new(node_shader2RGB.inputs['Shader'], node_diff_bsdf.outputs['BSDF'])
    group.links.new(node_mix3.inputs['Color2'], node_img_tex.outputs['Color'])
    group.links.new(node_img_tex.inputs['Vector'], node_mapping.outputs['Vector'])
    group.links.new(node_mapping.inputs['Vector'], node_tex_coords.outputs['UV'])

    # link group controls to shader node inputs
    group.links.new(node_mix2.inputs['Fac'], input_node.outputs['Noise Strength'])
    group.links.new(node_noise.inputs['Scale'], input_node.outputs['Noise Variation'])
    group.links.new(node_voronoi.inputs['Scale'], input_node.outputs['Noise Scale'])
    group.links.new(node_mix4.inputs['Fac'], input_node.outputs['Speckle Strength'])
    group.links.new(node_musgrave.inputs['Scale'], input_node.outputs['Speckle Scale'])
    group.links.new(node_mix1.inputs['Fac'], input_node.outputs['Shadow Blend'])
    group.links.new(node_mix3.inputs['Fac'], input_node.outputs['Hatching Blend'])
    group.links.new(node_mapping.inputs['Scale'], input_node.outputs['Hatching Scale'])

    # add drivers for color ramps

    driver = add_driver(node_color_ramp1.color_ramp.elements[0], mat, 'position', 'node_tree.nodes["Group"].inputs["Color A Clamp"].default_value')
    driver = add_driver(node_color_ramp1.color_ramp.elements[1], mat, 'position', 'node_tree.nodes["Group"].inputs["Color B Clamp"].default_value')

    driver = add_driver(node_color_ramp3.color_ramp.elements[0], mat, 'position', 'node_tree.nodes["Group"].inputs["Noise Min"].default_value')
    driver = add_driver(node_color_ramp3.color_ramp.elements[1], mat, 'position', 'node_tree.nodes["Group"].inputs["Noise Max"].default_value')

    driver = add_driver(node_color_ramp5.color_ramp.elements[0], mat, 'position', 'node_tree.nodes["Group"].inputs["Noise Soft Min"].default_value')
    driver = add_driver(node_color_ramp5.color_ramp.elements[1], mat, 'position', 'node_tree.nodes["Group"].inputs["Noise Soft Max"].default_value')

    driver = add_driver(node_color_ramp2.color_ramp.elements[0], mat, 'position', 'node_tree.nodes["Group"].inputs["Hatching Falloff Min"].default_value')
    driver = add_driver(node_color_ramp2.color_ramp.elements[1], mat, 'position', 'node_tree.nodes["Group"].inputs["Hatching Falloff Max"].default_value')

    # add drivers for color ramp 1 colors per channel

    driver = add_driver(node_color_ramp1.color_ramp.elements[0], mat, 'color', 'node_tree.nodes["Group"].inputs["Color A"].default_value[0]', 0)
    driver = add_driver(node_color_ramp1.color_ramp.elements[0], mat, 'color', 'node_tree.nodes["Group"].inputs["Color A"].default_value[1]', 1)
    driver = add_driver(node_color_ramp1.color_ramp.elements[0], mat, 'color', 'node_tree.nodes["Group"].inputs["Color A"].default_value[2]', 2)
    driver = add_driver(node_color_ramp1.color_ramp.elements[0], mat, 'color', 'node_tree.nodes["Group"].inputs["Color A"].default_value[3]', 3)

    driver = add_driver(node_color_ramp1.color_ramp.elements[1], mat, 'color', 'node_tree.nodes["Group"].inputs["Color B"].default_value[0]', 0)
    driver = add_driver(node_color_ramp1.color_ramp.elements[1], mat, 'color', 'node_tree.nodes["Group"].inputs["Color B"].default_value[1]', 1)
    driver = add_driver(node_color_ramp1.color_ramp.elements[1], mat, 'color', 'node_tree.nodes["Group"].inputs["Color B"].default_value[2]', 2)
    driver = add_driver(node_color_ramp1.color_ramp.elements[1], mat, 'color', 'node_tree.nodes["Group"].inputs["Color B"].default_value[3]', 3)