How to create a liquid simulation in Blender with Python
Introduction
Fluid graphics are crucial in realistic 3D animations, and Blender, with its built-in
Python interface (BPY), provides the means to streamline and adapt fluid graphics.
You'll know the simple ideas about water moving in simulations and how to write
the code to do it faster by reading this piece.
What is Blender?
Blender is a no-cost, publicly accessible 3D software package featuring extensive
utilities for functions such as the creation of models, motion sequences, physical
effects, image synthesis, and motion picture crafting techniques. It's favoured across
both professionals and enthusiasts for crafting animated films, visual effects, 3D
components, and interactive programs.
Blender provides two primary ways to interact with its features.
A window displays tools & actions visually.
Automation interface - Work process customisation- 3D object creation
(procedural)
What is BPY?
Blender's BPY lets users add special commands to manage Blender easily. Here's
what you can do with it.
Construct shapes, light sources, view devices, and extra features with code.
Do repetitive work faster by automating.
Adjust liquid or motion effects and enhance colour effects without touching the
main panel.
Introduce new tools, features, or custom interfaces to improve suitability.
BPY connects imagination in 3D art with the accuracy of coding. It’s an essential aid
for creators and designers to fully explore Blender’s features.
This article will cover the following steps.
- Setting up the fluid domain.
- Adding a fluid source.
- Applying transformations.
- Adding realistic water material.
- Baking the simulation.
- Denoising and rendering.
Step 1: Initial Scene Setup in Blender
At this point, a new scene is opened in Blender. By default, the following objects are
already present in a new scene.
Spot in the middle (Box thing) is at 0, 0, 0. This object is often used as a base for
further modeling. In the future, it will be transformed into a section for flow
depictions.
Tilted slightly to see cubes when drawing the view.
Light: A point of light needed to illuminate objects in the scene.
Step 2: Splitting the Screen in Blender
Now you've divided the screen space in Blender into two parts. This facilitates
simultaneous multitasking, enabling activities like coding on one monitor and 3D
rendering on another.
How to split the screen in Blender.
Slide your click-point over the dividing line till it shows the divide symbol.
Right-click and select “Split Area”.
Moving the boundary line where you want and releasing it allows you to secure the
division fixed in place.
Step 3: Opening a Text Editor for Scripting.
Next, you've selected one of the window sections to utilize Blender's text space. This
allows you to write and run Python scripts directly in Blender.
How to open a text editor in Blender.
Selecting a window: Select one of the split areas of the screen.
Push the button in the top left corner and select "Words in Paper" from the choices
provided. This will switch the selected window to text editor mode.
Now you can write or paste Python scripts for running jobs automatically.
Step 4: Make a fresh text file in Blender's text editor
In this step, we create a new text file in Blender's text tool for writing a Python
script.
Steps to create a new text document.
Go to a text-write window. Ensure one of the windows is configured to text editor
operational setting from the preceding action.
Make a clean file and click the "New" button at the top right corner of the text
editor. This switch generates a fresh document, allowing one to create code scripts.
Step 5: Import the BPY library
At this point, the first line of code is added to the textual content editor — import
BPY. This is a preferred step to get commenced with Python in Blender, permitting
you to get right off entry to the Blender API.
import bpy
Step 6: Writing the first a part of the code to create the area
In this step, the script to create the fluid simulation domain is written in a textual content editor. This code converts the item (cube) into a domain that defines the bounds within which the simulation will take place.
import bpy
# Create the domain
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
domain = bpy.context.object
domain.name = "Domain"
bpy.ops.object.modifier_add(type='FLUID')
domain.modifiers["Fluid"].fluid_type = 'DOMAIN'
domain.modifiers["Fluid"].domain_settings.domain_type = 'LIQUID'
domain.modifiers["Fluid"].domain_settings.resolution_max = 32
domain.modifiers["Fluid"].domain_settings.cache_directory = "//cache_fluid"
Creating a domain object:
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
Creates a cube in the center of the scene (coordinates 0, 0, 0) with a size of 2 Blender units.
This cube will act as a domain, which will enclose the area of the fluid simulation.
Renaming a domain object:
domain.name = "Domain"
Gives the cube the name "Domain", which makes it easier to work with.
Adding a fluid modifier:
bpy.ops.object.modifier_add(type='FLUID')
Adds a fluid simulation modifier to the object.
domain.modifiers["Fluid"].fluid_type = 'DOMAIN'
Specifies that the object will be used as a simulation domain.
Setting up domain parameters:
domain.modifiers["Fluid"].domain_settings.domain_type = 'LIQUID'
Sets the domain type for working with liquid.
domain.modifiers["Fluid"].domain_settings.resolution_max = 32
Determines the quality of the simulation. The higher the value, the more details, but it increases the calculation time.
domain.modifiers["Fluid"].domain_settings.cache_directory = "//cache_fluid"
Specifies the folder for saving the simulation data. The prefix "//" denotes the relative path to the project file.
Helpful tips:
Resolution (resolution_max):
For tests, a value of 32 is sufficient.
For final rendering, it is recommended to use 64 or higher.
Caching: Setting the cache path is important for reusing the simulation.
Here is the result of this script if we Press Alt+P
After executing the primary part of the code, an object appears in the scene, converted to a fluid simulation area. In the photo, you could see that the object (dice) now has the name "Domain" defined in the script.
What happened after executing the code:
An area object is created: The cube turned into added to the scene the usage of “bpy.Ops.Mesh.Primitive_cube_add()” and converted to a website with the "Fluid" modifier.
Domain type is configured: The domain kind is ready to LIQUID, which makes the object ready for fluid simulation.
Caching: The area settings are configured to shop the simulation information in the //cache_fluid folder.
Step 7: Writing the second one part of the code to feature the fluid source
At this factor, the code that creates the fluid source, so one can generate fluid inside the domain, is introduced within the textual content editor.
# Create the fluid source
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.2, location=(0, 0, 1))
source = bpy.context.object
source.name = "Source"
bpy.ops.object.modifier_add(type='FLUID')
source.modifiers["Fluid"].fluid_type = 'FLOW'
source.modifiers["Fluid"].flow_settings.flow_type = 'LIQUID'
source.modifiers["Fluid"].flow_settings.flow_behavior = 'INFLOW'
Creating a source object:
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.2, location=(0, 0, 1))
Creates a sphere with a radius of 0.2 Blender units at coordinates (0, 0, 1).
This sphere will emit fluid into the domain.
Renaming the source object:
source.name = "Source"
Sets the object's name to "Source".
Adding a fluid modifier:
bpy.ops.object.modifier_add(type='FLUID')
Adds a fluid simulation modifier.
source.modifiers["Fluid"].fluid_type = 'FLOW'
Specifies that the object will be used as a fluid source.
Setting the source parameters:
source.modifiers["Fluid"].flow_settings.flow_type =
Sets the emitted material type to liquid.
source.modifiers["Fluid"].flow_settings.flow_behavior = 'INFLOW'
Specifies continuous emission of fluid.
Helpful tips:
The source can be used to create effects such as water jets, pouring, or dripping.
The flow_behavior parameter can be used to set the source to emit a single burst (OUTFLOW) or a continuous flow (INFLOW).
Result of the second part of the code: including a fluid source
After executing the code, a new item seemed in the scene - a sphere located above the domain. This object is ready as a fluid source, so one can generate a float of fluid into the area.
What happened within the scene:
A fluid source became created:
A sphere placed at coordinates (0, 0, 1) became delivered the use of the “bpy.Ops.Mesh.Primitive_uv_sphere_add()” command and named "Source".
This item is ready as a fluid source way to the "Fluid" modifier with the Flow kind.
Continuous fluid generation:
The supply is about to INFLOW, which means continuous emission of fluid into the area.
Object interactions:
The sphere is inside the area, which allows the simulation to efficaciously deal with the physical interactions of the fluid.
Step 8: Applying Transforms to Objects
At this point, we've introduced code to use transforms to all objects in the scene. This is an essential step to make certain the simulation works efficiently.
# Apply transformations
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
Selecting all objects:
bpy.ops.object.select_all(action='SELECT')
Selects all objects in the scene.
Applying transforms:
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
"Bake" the current transforms of objects (position, rotation, scale) into their base properties.
Why is so vital?
Blender uses transformation matrices to determine the size, position, and orientation of objects. Applying transforms ensures that the simulation interprets these values correctly.
Step 9: Create and assign a sensible water fabric
In this step, code is delivered to create a material on the way to simulate the houses of water. This material includes color and refraction settings appropriate for realistically rendering liquid.
# Create a new material
material = bpy.data.materials.new(name="RealisticWaterMaterial")
material.use_nodes = True
nodes = material.node_tree.nodes
# Clear defualt nodes
for node in nodes:
nodes.remove(node)
# Add new nodes
output_node = nodes.new(type='ShaderNodeOutputMaterial')
output_node.location = (300, 0)
glass_bsdf = nodes.new(type='ShaderNodeBsdfGlass')
glass_bsdf.location = (0, 0)
# Set water properties
glass_bsdf.inputs['Color'].default_value = (0.2, 0.5, 0.8, 1.0) оттенок
glass_bsdf.inputs['IOR'].default_value = 1.33
#Connect nodes
material.node_tree.links.new(glass_bsdf.outputs['BSDF'], output_node.inputs['Surface'])
# Assign the material to the domain
domain.data.materials.append(material)
Creating a new material:
bpy.data.materials.new(name="RealisticWaterMaterial")
Creates a new material named "RealisticWaterMaterial".
material.use_nodes = True
Enables nodes for the material.
Cleaning up default nodes:
Removing default material nodes to create a custom shader network.
Adding nodes:
The "ShaderNodeBsdfGlass" node simulates transparency and refraction of water.
The index of refraction (IOR) is set to 1.33 to realistically display water.
Assigning a material:
The material is assigned to the fluid domain for rendering.
Helpful tips:
The color can be adjusted to create the effect of colored water.
Add textures or noise for more realism.
10 Step: Baking the Fluid Simulation
This step provides the simulation baking code, which creates the very last records to show the fluid movement. Baking the simulation allows you to visualize how the fluid will interact with the area and source.
bpy.context.view_layer.objects.active = domain # Set domain as active object
bpy.ops.fluid.bake_all()
Setting the active object:
bpy.context.view_layer.objects.active = domain
Makes the domain object active, which is necessary for baking the simulation.
Bake the simulation:
bpy.ops.fluid.bake_all()
Starts the process of baking all the simulation data.
Helpful tips:
Baking may take some time depending on the complexity of the simulation.
Make sure the cache path is set up correctly to save the data.
Final end result of jogging the code: a custom-designed fluid simulation scene
The image indicates the final end result of strolling the code. The scene incorporates the subsequent elements:
Domain: A large dice representing the place wherein the fluid simulation occurs.
Source: A small sphere placed above the area that generates the fluid.
Now we are going to switch the display mode in Material Preview to see the color and material assigned to objects.
The domain, which turned into formerly a regular cube, now seems as a container with water cloth, having a sensible blue-green tint and reflections.
Step 11: Running the Fluid Simulation Animation
At this factor, you are prepared to play the fluid simulation animation in Blender to peer how the fluid is generated with the aid of the source and fills the domain.
This is the result we should get (gif)
This photograph shows the key objects in the scene and visualizes the first level of the fluid simulation. We can see the interaction between the source and the domain, creating a fluid flow internal a restrained space.
In the animation, we see that the liquid is broken into many fragments, and it is not as smooth and flexible as real water. To fix this, we should add antialiasing to the domain geometry to make the fluid animation smoother and more realistic. To do this, use the Shade Auto Smooth function.
This is the result we should get (gif)
As you can see in the gif, there are colored pixels in the animation - they are called noise.
Fortunately, Blender has a lot of tools to help us remove them.
First of all, we go into the render section, and change the EEVEE to cycles.
Cycles presents ray tracing, which makes reflections, refractions, and shadows a lot higher.
Step 12: Setting up Denoising for Cycles
In this step, we permit Denoising to take away noise from the final render. This feature greatly improves photographing first-class without having to boom the number of samples to excessive values.
Next, we have to enable Denoising Data within the View Layer settings to skip extra information for denoising in publish-processing. This is useful while the usage of Cycles renders to make the end result look cleaner and extra super.
And now the final and most important touch
At this point, you have moved directly to the Compositing section to refine the final render, in the use of the node system. This lets you use additional outcomes, consisting of noise reduction, progressed color correction, and put-up-processing.
Add a Denoise node to the compositing to remove noise in the final render.
We connect the nodes as shown in the picture
To visualize the rendered result without delay within the Blender workspace we upload a Viewer node.
At this stage, the nodes inside the composition editor are linked in this sort of way as to make certain correct noise discount, visualization of intermediate consequences, and final output.
After rendering, all 250 frames will be saved in this folder;
You can rename it as you wish.
The very last step in growing an animation is rendering all the frames and saving them. At this level, you begin the system of rendering the entire scene, contemplating all the formerly set parameters.
Since there are a lot of frames, the rendering process can take quite a while.
Be patient - the result is worth it!!!
In the directory you specified before rendering, you can see all the rendered frames of your animation with denoising applied. These frames can now be used to create video.
In Blender's video sequencer, you can gather all rendered frames right into a finished animation.
At this factor, you pick out the Image/Sequence option to upload your rendered pictures to Blender's sequencer.
Next, you've got selected the rendered images within the tmp folder and clicked the Add Image Strip button. Your photos will now be introduced as a chain of frames to the sequencer timeline.
To do this, you need to
Press Shift, A → Image/Sequence.
Select all 250 frames of the render.
Now, you have got successfully delivered a rendered photograph collection to the Video Sequencer.
There's only a little bit left...
Installation of the render parameters to create the video and setting the output directory.
As proven in the screenshot - this could provide us with a more pleasant video.
Press Ctrl + F12 to export video.
Animation creation process!
Congratulations on efficiently growing an animation!
Now you could enjoy your video determined within the specific directory.
import bpy
# Create the domain
bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 0))
domain = bpy.context.object
domain.name = "Domain"
bpy.ops.object.modifier_add(type='FLUID')
domain.modifiers["Fluid"].fluid_type = 'DOMAIN'
domain.modifiers["Fluid"].domain_settings.domain_type = 'LIQUID'
domain.modifiers["Fluid"].domain_settings.resolution_max = 32
domain.modifiers["Fluid"].domain_settings.cache_directory = "//cache_fluid"
# Create the fluid source
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.2, location=(0, 0, 1))
source = bpy.context.object
source.name = "Source"
bpy.ops.object.modifier_add(type='FLUID')
source.modifiers["Fluid"].fluid_type = 'FLOW'
source.modifiers["Fluid"].flow_settings.flow_type = 'LIQUID'
source.modifiers["Fluid"].flow_settings.flow_behavior = 'INFLOW'
# Apply transformations
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
# Create a new material
material = bpy.data.materials.new(name="RealisticWaterMaterial")
material.use_nodes = True
nodes = material.node_tree.nodes
# Clear defualt nodes
for node in nodes:
nodes.remove(node)
# Add new nodes
output_node = nodes.new(type='ShaderNodeOutputMaterial')
output_node.location = (300, 0)
glass_bsdf = nodes.new(type='ShaderNodeBsdfGlass')
glass_bsdf.location = (0, 0)
# Set water properties
glass_bsdf.inputs['Color'].default_value = (0.2, 0.5, 0.8, 1.0)
glass_bsdf.inputs['IOR'].default_value = 1.33
#Connece nodes
material.node_tree.links.new(glass_bsdf.outputs['BSDF'], output_node.inputs['Surface'])
# Assign the material to the domain
domain.data.materials.append(material)
bpy.context.view_layer.objects.active = domain # Set domain as active object
bpy.ops.fluid.bake_all()
Conclusion
Blender combined with Python is a powerful tool that allows you to create complex simulations and automate labor-intensive processes. In the example provided, you can develop your programming skills and work on more complex projects, including multi-layer modeling or integration with other 3D tools.

