import bpy import sys import os def create_tex_node(nodes, img_path, label, color_space, location): img = bpy.data.images.load(img_path) tex = nodes.new(type='ShaderNodeTexImage') tex.image = img tex.label = label tex.location = location tex.image.colorspace_settings.name = color_space return tex def setup_environment_lighting(hdri_path): if not bpy.data.worlds: bpy.data.worlds.new(name="World") if bpy.context.scene.world is None: bpy.context.scene.world = bpy.data.worlds[0] world = bpy.context.scene.world world.use_nodes = True nodes = world.node_tree.nodes links = world.node_tree.links nodes.clear() env_tex = nodes.new(type="ShaderNodeTexEnvironment") env_tex.image = bpy.data.images.load(hdri_path) env_tex.location = (-300, 0) bg = nodes.new(type="ShaderNodeBackground") bg.location = (0, 0) output = nodes.new(type="ShaderNodeOutputWorld") output.location = (300, 0) links.new(env_tex.outputs["Color"], bg.inputs["Color"]) links.new(bg.outputs["Background"], output.inputs["Surface"]) def setup_gpu_rendering(): bpy.context.scene.render.engine = 'CYCLES' prefs = bpy.context.preferences cprefs = prefs.addons['cycles'].preferences # Choose backend depending on GPU type: 'CUDA', 'OPTIX', 'HIP', 'METAL' cprefs.compute_device_type = 'CUDA' bpy.context.scene.cycles.device = 'GPU' def generate_blend(obj_path, base_color_path, normal_map_path, roughness_path, metallic_path, output_blend): # Reset scene bpy.ops.wm.read_factory_settings(use_empty=True) # Import OBJ bpy.ops.import_scene.obj(filepath=obj_path) obj = bpy.context.selected_objects[0] # Create material mat = bpy.data.materials.new(name="BRDF_Material") mat.use_nodes = True nodes = mat.node_tree.nodes links = mat.node_tree.links nodes.clear() output = nodes.new(type='ShaderNodeOutputMaterial') output.location = (400, 0) principled = nodes.new(type='ShaderNodeBsdfPrincipled') principled.location = (100, 0) links.new(principled.outputs['BSDF'], output.inputs['Surface']) # Base Color base_color = create_tex_node(nodes, base_color_path, "Base Color", 'sRGB', (-600, 200)) links.new(base_color.outputs['Color'], principled.inputs['Base Color']) # Roughness rough = create_tex_node(nodes, roughness_path, "Roughness", 'Non-Color', (-600, 0)) links.new(rough.outputs['Color'], principled.inputs['Roughness']) # Metallic metal = create_tex_node(nodes, metallic_path, "Metallic", 'Non-Color', (-600, -200)) links.new(metal.outputs['Color'], principled.inputs['Metallic']) # Normal Map normal_tex = create_tex_node(nodes, normal_map_path, "Normal Map", 'Non-Color', (-800, -400)) normal_map = nodes.new(type='ShaderNodeNormalMap') normal_map.location = (-400, -400) links.new(normal_tex.outputs['Color'], normal_map.inputs['Color']) links.new(normal_map.outputs['Normal'], principled.inputs['Normal']) # Assign material if obj.data.materials: obj.data.materials[0] = mat else: obj.data.materials.append(mat) # Global Illumination using Blender's default forest HDRI blender_data_path = bpy.utils.resource_path('LOCAL') forest_hdri_path = os.path.join(blender_data_path, "datafiles", "studiolights", "world", "forest.exr") print(f"Using HDRI: {forest_hdri_path}") setup_environment_lighting(forest_hdri_path) # GPU rendering setup setup_gpu_rendering() # Pack textures into .blend bpy.ops.file.pack_all() # Set the 3D View to Rendered mode and focus on object for area in bpy.context.screen.areas: if area.type == 'VIEW_3D': for space in area.spaces: if space.type == 'VIEW_3D': space.shading.type = 'RENDERED' # Set viewport shading to Rendered for region in area.regions: if region.type == 'WINDOW': override = {'area': area, 'region': region, 'scene': bpy.context.scene} bpy.ops.view3d.view_all(override, center=True) elif area.type == 'NODE_EDITOR': for space in area.spaces: if space.type == 'NODE_EDITOR': space.tree_type = 'ShaderNodeTree' # Switch to Shader Editor space.shader_type = 'OBJECT' # Optional: Switch active workspace to Shading (if it exists) for workspace in bpy.data.workspaces: if workspace.name == 'Shading': bpy.context.window.workspace = workspace break # Save the .blend file bpy.ops.wm.save_as_mainfile(filepath=output_blend) print(f"✅ Saved .blend file with BRDF, HDRI, GPU: {output_blend}") if __name__ == "__main__": argv = sys.argv argv = argv[argv.index("--") + 1:] # Only use args after "--" if len(argv) != 6: print("Usage:\n blender --background --python generate_blend.py -- obj base_color normal roughness metallic output.blend") sys.exit(1) generate_blend(*argv)