-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
import bpy
import math
import random
from mathutils import Vector, noise
===========================================
Creative 3D Fluid Prompt with Wow Effect
Hyper-realistic oil & gas cosmic fluid sim
For Blender 3.5+ (Mantaflow)
===========================================
Clear the scene
bpy.ops.wm.read_factory_settings(use_empty=True)
-----------------------------------------------------
1. Scene Setup: Volumetric World & Cosmic Lighting
-----------------------------------------------------
scene = bpy.context.scene
scene.render.engine = 'CYCLES'
scene.render.resolution_x = 3840
scene.render.resolution_y = 2160
scene.render.resolution_percentage = 100
scene.cycles.samples = 1024
scene.cycles.use_denoising = True
scene.view_settings.view_transform = 'Filmic'
scene.view_settings.look = 'Very High Contrast'
World with starry nebula texture
world = bpy.data.worlds.new("CosmicWorld")
world.use_nodes = True
world_nodes = world.node_tree.nodes
world_links = world.node_tree.links
world_nodes.clear()
Background node with procedural stars
tex_coord = world_nodes.new(type='ShaderNodeTexCoord')
mapping = world_nodes.new(type='ShaderNodeMapping')
mapping.vector_type = 'POINT'
mapping.scale[0] = 2.5
mapping.scale[1] = 2.5
mapping.scale[2] = 2.5
noise_tex = world_nodes.new(type='ShaderNodeTexNoise')
noise_tex.inputs['Scale'].default_value = 1.2
noise_tex.inputs['Detail'].default_value = 8.0
noise_tex.inputs['Roughness'].default_value = 0.7
color_ramp = world_nodes.new(type='ShaderNodeValToRGB')
color_ramp.color_ramp.elements[0].position = 0.4
color_ramp.color_ramp.elements[0].color = (0.01, 0.0, 0.05, 1.0) # deep space
color_ramp.color_ramp.elements[1].position = 0.7
color_ramp.color_ramp.elements[1].color = (0.3, 0.1, 0.6, 1.0) # nebulae purple
bg = world_nodes.new(type='ShaderNodeBackground')
bg.inputs['Strength'].default_value = 0.8
output = world_nodes.new(type='ShaderNodeOutputWorld')
Link: mapping -> noise -> color ramp -> background
world_links.new(tex_coord.outputs['Generated'], mapping.inputs['Vector'])
world_links.new(mapping.outputs['Vector'], noise_tex.inputs['Vector'])
world_links.new(noise_tex.outputs['Fac'], color_ramp.inputs['Fac'])
world_links.new(color_ramp.outputs['Color'], bg.inputs['Color'])
world_links.new(bg.outputs['Background'], output.inputs['Surface'])
bpy.context.scene.world = world
-----------------------------------------------------
2. Camera & Cinematic Composition
-----------------------------------------------------
bpy.ops.object.camera_add(location=(15, -20, 10), rotation=(1.2, 0.0, 0.8))
camera = bpy.context.active_object
camera.data.lens = 35
scene.camera = camera
Empty for depth of field focus
bpy.ops.object.empty_add(type='PLAIN_AXES', location=(0, 0, 2))
focus_target = bpy.context.active_object
camera.data.dof.use_dof = True
camera.data.dof.focus_object = focus_target
camera.data.dof.aperture_fstop = 1.8
Add lights: cosmic ambient + electric sparks
Main area light (blue tone)
bpy.ops.object.light_add(type='AREA', location=(5, -5, 8), rotation=(0.8, 0.2, 0.5))
light1 = bpy.context.active_object
light1.data.energy = 300
light1.data.color = (0.3, 0.5, 1.0) # electric blue
light1.data.shape = 'DISK'
light1.data.size = 5
Fill light (orange sparks)
bpy.ops.object.light_add(type='POINT', location=(-8, 4, 3))
light2 = bpy.context.active_object
light2.data.energy = 200
light2.data.color = (1.0, 0.5, 0.1) # fiery orange
Back rim light (green gas)
bpy.ops.object.light_add(type='AREA', location=(2, 12, 5), rotation=(-0.5, 0.0, -2.0))
light3 = bpy.context.active_object
light3.data.energy = 250
light3.data.color = (0.2, 0.9, 0.3) # neon green
light3.data.shape = 'RECTANGLE'
light3.data.size = 8
light3.data.size_y = 4
-----------------------------------------------------
3. Fluid Domain and Effectors (Mantaflow)
-----------------------------------------------------
bpy.ops.mesh.primitive_cube_add(size=12, location=(0, 0, 2))
domain_obj = bpy.context.active_object
domain_obj.name = "FluidDomain"
domain_obj.hide_viewport = True
domain_obj.hide_render = True
Add fluid modifier
bpy.ops.object.modifier_add(type='FLUID')
domain_obj.modifiers["Fluid"].fluid_type = 'DOMAIN'
domain_settings = domain_obj.modifiers["Fluid"].domain_settings
domain_settings.resolution_max = 128 # high detail for fluid sim
domain_settings.use_mesh = True
domain_settings.use_speed_vectors = True
domain_settings.cache_type = 'ALL'
domain_settings.cache_frame_start = 1
domain_settings.cache_frame_end = 120
domain_settings.alpha = 0.1 # transparency for volumetric feel
Enable particles (for bubbles/sparks)
domain_settings.use_spray_particles = True
domain_settings.use_bubble_particles = True
domain_settings.use_foam_particles = True
domain_settings.use_tracer_particles = True
domain_settings.particle_max = 8000
domain_settings.particle_bandwidth = 0.3
domain_settings.particle_scale = 0.2
domain_settings.particle_scale_max = 0.5
-----------------------------------------------------
4. Fluid Inflow: OIL (viscous black) & GAS (neon green)
-----------------------------------------------------
---- Oil inflow (thick black ribbons) ----
bpy.ops.mesh.primitive_torus_add(major_radius=0.8, minor_radius=0.2, location=(0, 0, 1.5))
oil_inflow = bpy.context.active_object
oil_inflow.name = "Oil_Inflow"
oil_inflow.rotation_euler = (0.5, 0.2, 0.0)
bpy.ops.object.modifier_add(type='FLUID')
oil_inflow.modifiers["Fluid"].fluid_type = 'FLOW'
flow_oil = oil_inflow.modifiers["Fluid"].flow_settings
flow_oil.flow_behavior = 'INFLOW'
flow_oil.flow_type = 'LIQUID'
flow_oil.flow_source = 'PARTICLES'
flow_oil.use_initial_velocity = True
flow_oil.velocity_factor = 1.8
flow_oil.velocity_normal = 2.0
flow_oil.velocity_random = 0.6
flow_oil.particle_system = None # will be added
Add particle system for oil drip effect
bpy.ops.object.select_all(action='DESELECT')
oil_inflow.select_set(True)
bpy.context.view_layer.objects.active = oil_inflow
bpy.ops.object.particle_system_add()
psys_oil = oil_inflow.particle_systems[0]
psys_oil.name = "OilParticles"
settings_oil = psys_oil.settings
settings_oil.type = 'HAIR'
settings_oil.emit_from = 'VERT'
settings_oil.count = 300
settings_oil.hair_length = 0.8
settings_oil.hair_step = 5
settings_oil.use_hair_dynamics = True
settings_oil.mass = 2.0
settings_oil.brownian_factor = 0.5
settings_oil.use_rotations = True
settings_oil.rotation_mode = 'VELOCITY'
settings_oil.random_rotation = 0.2
Assign material later
---- Gas inflow (neon green, turbulent) ----
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.6, location=(2, 1, 2.5))
gas_inflow = bpy.context.active_object
gas_inflow.name = "Gas_Inflow"
bpy.ops.object.modifier_add(type='FLUID')
gas_inflow.modifiers["Fluid"].fluid_type = 'FLOW'
flow_gas = gas_inflow.modifiers["Fluid"].flow_settings
flow_gas.flow_behavior = 'INFLOW'
flow_gas.flow_type = 'GAS' # smoke / gas
flow_gas.surface_distance = 0.3
flow_gas.flow_source = 'PARTICLES'
flow_gas.velocity_factor = 2.5
flow_gas.velocity_normal = 3.0
flow_gas.velocity_random = 1.2
flow_gas.use_initial_velocity = True
bpy.ops.object.particle_system_add()
psys_gas = gas_inflow.particle_systems[0]
psys_gas.name = "GasParticles"
settings_gas = psys_gas.settings
settings_gas.type = 'EMITTER'
settings_gas.count = 500
settings_gas.frame_start = 1
settings_gas.frame_end = 100
settings_gas.lifetime = 60
settings_gas.emit_from = 'FACE'
settings_gas.distribution = 'JIT'
settings_gas.normal_factor = 2.5
settings_gas.factor_random = 1.0
settings_gas.use_rotations = True
settings_gas.rotation_factor = 0.3
-----------------------------------------------------
5. Effectors: Lightning arcs & vortex force
-----------------------------------------------------
Create electric arcs with simple curves + force field
bpy.ops.object.select_all(action='DESELECT')
for i in range(8):
x = random.uniform(-6, 6)
y = random.uniform(-6, 6)
z = random.uniform(1, 5)
bpy.ops.curve.primitive_bezier_curve_add(location=(x, y, z))
arc = bpy.context.active_object
arc.name = f"LightningArc_{i}"
arc.scale = (0.5, 0.5, 0.5)
arc.data.bevel_depth = 0.03
arc.data.bevel_resolution = 4
# Add turbulence field for electric feel
bpy.ops.object.select_all(action='DESELECT')
arc.select_set(True)
bpy.context.view_layer.objects.active = arc
bpy.ops.object.effector_add(type='TURBULENCE', enter_editmode=False, location=arc.location)
turb = bpy.context.active_object
turb.name = f"Turbulence_{i}"
turb.field.strength = 12.0
turb.field.size = 1.5
turb.field.flow = 0.5
turb.field.noise = 2.0
# Material will be added later
Vortex force for the mesmerizing pull
bpy.ops.object.effector_add(type='VORTEX', location=(0, 0, 4))
vortex = bpy.context.active_object
vortex.name = "CosmicVortex"
vortex.rotation_euler = (0.5, 0.2, 0.0)
vortex.field.strength = 25.0
vortex.field.flow = 0.8
vortex.field.noise = 0.3
vortex.field.distance = 10.0
-----------------------------------------------------
6. Advanced Materials: Iridescence, Oil, Gas, Sparks
-----------------------------------------------------
---- Oil material: viscous black with iridescent sheen ----
oil_mat = bpy.data.materials.new(name="IridescentOil")
oil_mat.use_nodes = True
oil_nodes = oil_mat.node_tree.nodes
oil_links = oil_mat.node_tree.links
oil_nodes.clear()
Mix Shader: glossy + translucent
mix1 = oil_nodes.new(type='ShaderNodeMixShader')
mix2 = oil_nodes.new(type='ShaderNodeMixShader')
glossy = oil_nodes.new(type='ShaderNodeBsdfGlossy')
glossy.inputs['Color'].default_value = (0.02, 0.01, 0.01, 1.0)
glossy.inputs['Roughness'].default_value = 0.15
translucent = oil_nodes.new(type='ShaderNodeBsdfTranslucent')
translucent.inputs['Color'].default_value = (0.05, 0.03, 0.03, 1.0)
Iridescent layer via fresnel + noise
fresnel = oil_nodes.new(type='ShaderNodeFresnel')
fresnel.inputs['IOR'].default_value = 1.8
noise_tex_oil = oil_nodes.new(type='ShaderTexNoise')
noise_tex_oil.inputs['Scale'].default_value = 8.0
noise_tex_oil.inputs['Detail'].default_value = 5.0
color_ramp_oil = oil_nodes.new(type='ShaderNodeValToRGB')
color_ramp_oil.color_ramp.elements[0].color = (0.1, 0.6, 0.7, 1.0) # cyan
color_ramp_oil.color_ramp.elements[1].color = (0.8, 0.2, 0.5, 1.0) # magenta
output_oil = oil_nodes.new(type='ShaderNodeOutputMaterial')
Links
oil_links.new(noise_tex_oil.outputs['Fac'], color_ramp_oil.inputs['Fac'])
oil_links.new(fresnel.outputs['Fac'], mix1.inputs[0])
oil_links.new(color_ramp_oil.outputs['Color'], mix1.inputs[2])
oil_links.new(glossy.outputs['BSDF'], mix1.inputs[1])
oil_links.new(mix1.outputs['Shader'], mix2.inputs[1])
oil_links.new(translucent.outputs['BSDF'], mix2.inputs[2])
oil_links.new(mix2.outputs['Shader'], output_oil.inputs['Surface'])
Assign oil material
oil_inflow.data.materials.append(oil_mat)
---- Gas material: neon green, volumetric, glowing ----
gas_mat = bpy.data.materials.new(name="NeonGas")
gas_mat.use_nodes = True
gas_nodes = gas_mat.node_tree.nodes
gas_links = gas_mat.node_tree.links
gas_nodes.clear()
vol_absorb = gas_nodes.new(type='ShaderNodeVolumeAbsorption')
vol_absorb.inputs['Color'].default_value = (0.1, 0.8, 0.2, 1.0)
vol_absorb.inputs['Density'].default_value = 0.3
vol_scatter = gas_nodes.new(type='ShaderNodeVolumeScatter')
vol_scatter.inputs['Color'].default_value = (0.2, 0.9, 0.2, 1.0)
vol_scatter.inputs['Density'].default_value = 0.2
vol_scatter.inputs['Anisotropy'].default_value = 0.0
mix_vol = gas_nodes.new(type='ShaderNodeMixShader')
emission = gas_nodes.new(type='ShaderNodeEmission')
emission.inputs['Color'].default_value = (0.3, 1.0, 0.4, 1.0)
emission.inputs['Strength'].default_value = 0.8
output_gas = gas_nodes.new(type='ShaderNodeOutputMaterial')
Links
gas_links.new(vol_absorb.outputs['Volume'], mix_vol.inputs[1])
gas_links.new(vol_scatter.outputs['Volume'], mix_vol.inputs[2])
gas_links.new(emission.outputs['Emission'], mix_vol.inputs[2])
gas_links.new(mix_vol.outputs['Shader'], output_gas.inputs['Volume'])
gas_inflow.data.materials.append(gas_mat)
---- Lightning material: electric blue emission ----
lightning_mat = bpy.data.materials.new(name="ElectricBlue")
lightning_mat.use_nodes = True
lightning_nodes = lightning_mat.node_tree.nodes
lightning_links = lightning_mat.node_tree.links
lightning_nodes.clear()
emit = lightning_nodes.new(type='ShaderNodeEmission')
emit.inputs['Color'].default_value = (0.0, 0.5, 1.0, 1.0)
emit.inputs['Strength'].default_value = 15.0
output_light = lightning_nodes.new(type='ShaderNodeOutputMaterial')
lightning_links.new(emit.outputs['Emission'], output_light.inputs['Surface'])
for obj in bpy.data.objects:
if "LightningArc" in obj.name:
obj.data.materials.append(lightning_mat)
---- Bubble material: iridescent, glass-like ----
bubble_mat = bpy.data.materials.new(name="IridescentBubble")
bubble_mat.use_nodes = True
bubble_nodes = bubble_mat.node_tree.nodes
bubble_links = bubble_mat.node_tree.links
bubble_nodes.clear()
glass = bubble_nodes.new(type='ShaderNodeBsdfGlass')
glass.inputs['Color'].default_value = (0.9, 0.9, 1.0, 1.0)
glass.inputs['Roughness'].default_value = 0.05
glass.inputs['IOR'].default_value = 1.33
Add thin film interference (fresnel + noise)
fresnel_bubble = bubble_nodes.new(type='ShaderNodeFresnel')
noise_bubble = bubble_nodes.new(type='ShaderTexNoise')
noise_bubble.inputs['Scale'].default_value = 25.0
color_ramp_bubble = bubble_nodes.new(type='ShaderNodeValToRGB')
color_ramp_bubble.color_ramp.elements[0].color = (0.8, 0.2, 0.6, 1.0)
color_ramp_bubble.color_ramp.elements[1].color = (0.2, 0.9, 0.8, 1.0)
mix_bubble = bubble_nodes.new(type='ShaderNodeMixShader')
output_bubble = bubble_nodes.new(type='ShaderNodeOutputMaterial')
bubble_links.new(noise_bubble.outputs['Fac'], color_ramp_bubble.inputs['Fac'])
bubble_links.new(color_ramp_bubble.outputs['Color'], mix_bubble.inputs[2])
bubble_links.new(fresnel_bubble.outputs['Fac'], mix_bubble.inputs[0])
bubble_links.new(glass.outputs['BSDF'], mix_bubble.inputs[1])
bubble_links.new(mix_bubble.outputs['Shader'], output_bubble.inputs['Surface'])
Assign to fluid particle system later if needed (domain particles inherit material via domain)
---- Spark material: fiery orange ----
spark_mat = bpy.data.materials.new(name="FierySpark")
spark_mat.use_nodes = True
spark_nodes = spark_mat.node_tree.nodes
spark_links = spark_mat.node_tree.links
spark_nodes.clear()
spark_emit = spark_nodes.new(type='ShaderNodeEmission')
spark_emit.inputs['Color'].default_value = (1.0, 0.4, 0.0, 1.0)
spark_emit.inputs['Strength'].default_value = 12.0
spark_output = spark_nodes.new(type='ShaderNodeOutputMaterial')
spark_links.new(spark_emit.outputs['Emission'], spark_output.inputs['Surface'])
-----------------------------------------------------
7. Background Cosmic Nebulae (Distant)
-----------------------------------------------------
bpy.ops.mesh.primitive_uv_sphere_add(radius=30, location=(0, 0, 10))
nebula_sphere = bpy.context.active_object
nebula_sphere.name = "CosmicNebula"
nebula_sphere.scale = (2.0, 2.0, 0.8)
nebula_sphere.hide_viewport = False
nebula_sphere.hide_render = False
nebula_mat = bpy.data.materials.new(name="NebulaBackground")
nebula_mat.use_nodes = True
nebula_nodes = nebula_mat.node_tree.nodes
nebula_links = nebula_mat.node_tree.links
nebula_nodes.clear()
Use volume emission for soft glow
vol_emiss = nebula_nodes.new(type='ShaderNodeVolumeEmission')
vol_emiss.inputs['Color'].default_value = (0.6, 0.2, 0.8, 1.0)
vol_emiss.inputs['Density'].default_value = 0.02
out_vol = nebula_nodes.new(type='ShaderNodeOutputMaterial')
nebula_links.new(vol_emiss.outputs['Volume'], out_vol.inputs['Volume'])
nebula_sphere.data.materials.append(nebula_mat)
-----------------------------------------------------
8. Animation & Baking Setup
-----------------------------------------------------
Set timeline
scene.frame_start = 1
scene.frame_end = 120
Add keyframes for dynamic inflow motion (oil swirl)
oil_inflow.location = (0, 0, 1.5)
oil_inflow.keyframe_insert(data_path="location", frame=1)
oil_inflow.location = (-1, 1, 3)
oil_inflow.keyframe_insert(data_path="location", frame=60)
oil_inflow.location = (1, -0.5, 2)
oil_inflow.keyframe_insert(data_path="location", frame=120)
gas_inflow.location = (2, 1, 2.5)
gas_inflow.keyframe_insert(data_path="location", frame=1)
gas_inflow.location = (3, -1, 4)
gas_inflow.keyframe_insert(data_path="location", frame=90)
Bake fluid simulation (user may need to press 'Bake All' in UI or run this line)
bpy.ops.fluid.bake_all() # uncomment to start baking (takes time)
-----------------------------------------------------
9. Post-processing: Volumetric fog in domain
-----------------------------------------------------
domain_mat = bpy.data.materials.new(name="DomainVolume")
domain_mat.use_nodes = True
domain_nodes = domain_mat.node_tree.nodes
domain_links = domain_mat.node_tree.links
domain_nodes.clear()
domain_vol = domain_nodes.new(type='ShaderNodeVolumeScatter')
domain_vol.inputs['Color'].default_value = (0.4, 0.6, 1.0, 1.0)
domain_vol.inputs['Density'].default_value = 0.02
domain_out = domain_nodes.new(type='ShaderNodeOutputMaterial')
domain_links.new(domain_vol.outputs['Volume'], domain_out.inputs['Volume'])
if domain_obj.data.materials:
domain_obj.data.materials[0] = domain_mat
else:
domain_obj.data.materials.append(domain_mat)
print("=== SCENE READY ===")
print("Fluid simulation and cosmic oil/gas vortex created.")
print("Adjust camera, bake fluid, and render to see the 'wow' effect!")