import bpy
import os
import random
def cleanup_scene():
bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.delete(use_global=False)
def setup_random_seed(seed=42):
random.seed(seed)
def create_background_pipes():
for i in range(20):
radius = 0.5
depth = random.uniform(4, 6)
x = random.uniform(-8, 8)
y = random.uniform(13, 17) z = random.uniform(-20, -10)
bpy.ops.mesh.primitive_cylinder_add(
radius=radius, depth=depth, location=(x, y, z)
)
outer_pipe = bpy.context.object
outer_pipe.name = f"DimPipe_{i}"
inner_radius = radius - 0.15 inner_depth = depth + 0.2 bpy.ops.mesh.primitive_cylinder_add(
radius=inner_radius, depth=inner_depth, location=(x, y, z)
)
inner_pipe = bpy.context.object
inner_pipe.name = f"DimPipeInner_{i}"
bool_mod = outer_pipe.modifiers.new(name="PipeHole", type="BOOLEAN")
bool_mod.operation = "DIFFERENCE"
bool_mod.object = inner_pipe
bpy.context.view_layer.objects.active = outer_pipe
bpy.ops.object.modifier_apply(modifier=bool_mod.name)
bpy.data.objects.remove(inner_pipe, do_unlink=True)
dim_mat = bpy.data.materials.new(name=f"DimPipeMat_{i}")
dim_mat.use_nodes = True
dnodes = dim_mat.node_tree.nodes
dprincipled = dnodes.get("Principled BSDF")
dprincipled.inputs["Base Color"].default_value = (
0.2,
0.2,
0.3,
1,
) dprincipled.inputs["Metallic"].default_value = 0.2
dprincipled.inputs["Roughness"].default_value = 0.8
outer_pipe.data.materials.append(dim_mat)
def create_clouds():
for i in range(13):
base_x = random.uniform(-20, 20)
base_y = random.uniform(10, 50)
base_z = random.uniform(-30, -20)
num_parts = random.randint(1, 3)
cloud_parts = []
for j in range(num_parts):
offset_x = random.uniform(-1.0, 1.0)
offset_y = random.uniform(-1.0, 1.0)
offset_z = random.uniform(-0.2, 0.2)
pos_x = base_x + offset_x
pos_y = base_y + offset_y
pos_z = base_z + offset_z
bpy.ops.mesh.primitive_circle_add(
vertices=32, radius=1, fill_type="NGON", location=(pos_x, pos_y, pos_z)
)
ellipse = bpy.context.object
ellipse.name = f"Cloud_{i}_{j}"
scale_x = random.uniform(2.0, 3.0)
scale_y = random.uniform(1.0, 1.5)
scale_z = random.uniform(0.3, 0.5)
ellipse.scale = (scale_x, scale_y, scale_z)
ellipse.rotation_euler[2] = random.uniform(0, 6.28319)
cloud_mat = bpy.data.materials.new(name=f"CloudMaterial_{i}_{j}")
cloud_mat.use_nodes = True
c_nodes = cloud_mat.node_tree.nodes
c_principled = c_nodes.get("Principled BSDF")
c_principled.inputs["Base Color"].default_value = (0.9, 0.9, 0.9, 1)
c_principled.inputs["Roughness"].default_value = 1.0
emission = c_nodes.new(type="ShaderNodeEmission")
emission.inputs["Color"].default_value = (1, 1, 1, 1)
emission.inputs["Strength"].default_value = 0.2
mix_shader = c_nodes.new(type="ShaderNodeMixShader")
links = cloud_mat.node_tree.links
links.new(c_principled.outputs["BSDF"], mix_shader.inputs[1])
links.new(emission.outputs["Emission"], mix_shader.inputs[2])
mix_shader.inputs["Fac"].default_value = 0.3
output = c_nodes.get("Material Output")
links.new(mix_shader.outputs["Shader"], output.inputs["Surface"])
ellipse.data.materials.append(cloud_mat)
cloud_parts.append(ellipse)
if len(cloud_parts) > 1:
bpy.context.view_layer.objects.active = cloud_parts[0]
for obj in cloud_parts:
obj.select_set(True)
bpy.ops.object.join()
cloud_parts[0].name = f"Cloud_{i}"
def create_hollow_pipe():
bpy.ops.mesh.primitive_cylinder_add(radius=1, depth=5, location=(0, 0, 0))
outer_pipe = bpy.context.object
outer_pipe.name = "OuterPipe"
bpy.ops.mesh.primitive_cylinder_add(radius=0.8, depth=5.1, location=(0, 0, 0))
inner_pipe = bpy.context.object
inner_pipe.name = "InnerPipe"
bool_mod = outer_pipe.modifiers.new(name="PipeHole", type="BOOLEAN")
bool_mod.operation = "DIFFERENCE"
bool_mod.object = inner_pipe
bpy.context.view_layer.objects.active = outer_pipe
bpy.ops.object.modifier_apply(modifier=bool_mod.name)
bpy.data.objects.remove(inner_pipe, do_unlink=True)
return outer_pipe
def assign_pipe_material(pipe):
pipe_mat = bpy.data.materials.new(name="PipeMaterial")
pipe_mat.use_nodes = True
nodes = pipe_mat.node_tree.nodes
principled = nodes.get("Principled BSDF")
principled.inputs["Base Color"].default_value = (0.0, 0.6, 0.0, 1)
principled.inputs["Metallic"].default_value = 0.3
principled.inputs["Roughness"].default_value = 0.2
pipe.data.materials.append(pipe_mat)
def create_text():
bpy.ops.object.text_add(location=(0, -1.5, 1))
text_obj = bpy.context.object
text_obj.name = "LogoText"
text_data = text_obj.data
text_data.body = "PortRedirect"
text_data.align_x = "CENTER"
text_data.align_y = "CENTER"
text_data.extrude = 0.15
text_data.size = 1.6
font_path = os.path.join(
os.path.expanduser("~"), "Library", "Fonts", "OleoScriptSwashCaps-Regular.ttf"
)
if os.path.exists(font_path):
text_data.font = bpy.data.fonts.load(font_path)
bpy.ops.object.convert(target="MESH")
return bpy.context.object
def assign_text_material(text_obj):
text_mat = bpy.data.materials.new(name="TextMaterial")
text_mat.use_nodes = True
nodes = text_mat.node_tree.nodes
links = text_mat.node_tree.links
principled = nodes.get("Principled BSDF")
principled.inputs["Base Color"].default_value = (1.0, 0.84, 0.0, 1)
principled.inputs["Metallic"].default_value = 0.7
principled.inputs["Roughness"].default_value = 0.1
noise_tex = nodes.new(type="ShaderNodeTexNoise")
noise_tex.inputs["Scale"].default_value = 200.0 noise_tex.inputs["Detail"].default_value = 16.0
bump_node = nodes.new(type="ShaderNodeBump")
bump_node.inputs["Strength"].default_value = 0.05
links.new(noise_tex.outputs["Fac"], bump_node.inputs["Height"])
links.new(bump_node.outputs["Normal"], principled.inputs["Normal"])
text_obj.data.materials.append(text_mat)
def create_skybox():
bpy.ops.mesh.primitive_cube_add(location=(0, 0, 50))
skybox = bpy.context.object
skybox.name = "Skybox"
skybox.scale = (100, 100, 1)
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.select_all(action="SELECT")
bpy.ops.mesh.flip_normals()
bpy.ops.object.mode_set(mode="OBJECT")
skybox_mat = bpy.data.materials.new(name="SkyboxMaterial")
skybox_mat.use_nodes = True
nodes = skybox_mat.node_tree.nodes
links = skybox_mat.node_tree.links
for node in nodes:
nodes.remove(node)
emission_node = nodes.new(type="ShaderNodeEmission")
emission_node.location = (400, 0)
emission_node.inputs["Strength"].default_value = 1.0
noise_node = nodes.new(type="ShaderNodeTexNoise")
noise_node.location = (-600, 0)
noise_node.inputs["Scale"].default_value = 20.0
noise_node.inputs["Detail"].default_value = 2.0
color_ramp = nodes.new(type="ShaderNodeValToRGB")
color_ramp.location = (-400, 0)
color_ramp.color_ramp.elements[0].position = 0.4
color_ramp.color_ramp.elements[0].color = (0.8, 0.8, 0.8, 1)
color_ramp.color_ramp.elements[1].position = 0.6
color_ramp.color_ramp.elements[1].color = (0.0, 0.3, 0.0, 1)
mix_node = nodes.new(type="ShaderNodeMixRGB")
mix_node.location = (0, 0)
mix_node.blend_type = "MIX"
mix_node.inputs["Color1"].default_value = (0.6, 0.8, 1.0, 1)
mix_node.inputs["Fac"].default_value = 0.3
output_node = nodes.new(type="ShaderNodeOutputMaterial")
output_node.location = (600, 0)
links.new(noise_node.outputs["Fac"], color_ramp.inputs["Fac"])
links.new(color_ramp.outputs["Color"], mix_node.inputs["Color2"])
links.new(mix_node.outputs["Color"], emission_node.inputs["Color"])
links.new(emission_node.outputs["Emission"], output_node.inputs["Surface"])
skybox.data.materials.append(skybox_mat)
def setup_lighting():
bpy.ops.object.light_add(type="AREA", location=(0, 0, 10))
light_area = bpy.context.object
light_area.data.energy = 2000
light_area.data.size = 100
bpy.ops.object.light_add(type="AREA", location=(0, -5, 3))
light_area = bpy.context.object
light_area.data.energy = 100
light_area.data.size = 10
bpy.ops.object.light_add(type="AREA", location=(0, -5, -3))
light_area = bpy.context.object
light_area.data.energy = 50
light_area.data.size = 10
bpy.ops.object.light_add(type="AREA", location=(-2, -1.5, 2))
light_area = bpy.context.object
light_area.data.energy = 30
light_area.data.size = 5
bpy.ops.object.light_add(type="AREA", location=(2, -1.5, 2))
light_area = bpy.context.object
light_area.data.energy = 50
light_area.data.size = 5
bpy.ops.object.light_add(type="POINT", location=(-20, -7, 10))
light_point = bpy.context.object
light_point.data.energy = 9000
def setup_camera():
bpy.ops.object.camera_add(location=(0, -10, 10))
camera = bpy.context.object
camera.rotation_euler = (0.785, 0, 0) bpy.context.scene.camera = camera
def setup_world_background():
if bpy.data.worlds:
world = bpy.data.worlds[0]
else:
world = bpy.data.worlds.new("World")
bpy.context.scene.world = world
world.use_nodes = True
bg_node = world.node_tree.nodes.get("Background")
if bg_node:
bg_node.inputs[0].default_value = (0.18, 0.44, 0.70, 1)
bg_node.inputs[1].default_value = (
0.2 )
else:
raise Exception("i need a Background node")
def setup_render_settings(output_path):
scene = bpy.context.scene
scene.render.engine = "BLENDER_EEVEE_NEXT"
scene.view_settings.view_transform = "Filmic"
scene.view_settings.look = "None"
scene.view_settings.exposure = 1.2
scene.render.resolution_x = 1920
scene.render.resolution_y = 1080
scene.render.image_settings.file_format = "PNG"
scene.render.filepath = output_path
def main():
cleanup_scene()
setup_random_seed(42)
create_skybox()
create_background_pipes()
create_clouds()
pipe = create_hollow_pipe()
assign_pipe_material(pipe)
logo_text = create_text()
assign_text_material(logo_text)
setup_lighting()
setup_camera()
setup_world_background()
output_path = os.path.join(bpy.path.abspath("//"), "portredirect_logo.png")
setup_render_settings(output_path)
bpy.ops.render.render(write_still=True)
print("Logo rendered and saved to", output_path)
main()