Created
May 25, 2023 12:51
-
-
Save blender8r/3ea047af33381d0e41b8009115ccd0b8 to your computer and use it in GitHub Desktop.
Creates a Blender cel shader with the controls gathered into one group
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 | |
| 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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment