Obi supports two fluid rendering techniques: ellipsoidal impostors and isosurface meshing, implemented by
ObiParticleRenderer and ObiFluidSurfaceMesher, respectively.
Check out custom particle rendering for more details on how particles and fluid are rendered, alternative rendering methods, and info on how to write your own renderer.
Obi Particle Renderer
Impostors are a special type of billboards (camera-facing quads) which look like perfectly-lit, highly tesselated 3D ellipsoids, at a fraction of the cost. The ObiParticleRenderer component can be used to
render your fluid particles as tiny ellipsoids using this technique. While typically not suitable for final rendering, this is useful to visualize and debug the simulation. It can also come in handy to render granulars.
In order to do this, just add the ObiParticleRenderer component to any actor (for instance an ObiEmitter, but it can also be used to draw cloth or ropes, see particle rendering). You can also
set the color of the particles, and scale the radius used when rendering them.
The default material used for particle rendering has been created using Shader Graph. You can find it Obi/Resources/ObiMaterials/Common/ParticleShader.shadergraph.
Obi Fluid Surface Mesher
This component renders the surface of the fluid as a mesh. Setting up fluid rendering requires three steps:
Add a ObiFluidSurfaceMesher component to the emitters we want to render. Surface meshers take a ObiFluidRenderingPass as input.
Create a ObiFluidRenderingPass asset, which contains rendering parameters for all meshers using it.
Create a ObiFluidRendererFeature, which is used to render additional data for transparent fluids.
1) Creating a surface mesher
By default, ObiEmitters created from the Object menu already have a ObiFluidSurfaceMesher component in them. Should you need to create one manually, go to Component→Physics→Obi→ObiFluidSurfaceMesher.
Pass
Rendering pass used. Note: Make sure the pass you use is also assigned to a ObiFluidRendererFeature, otherwise no renderer using this pass will be visible.
2) Creating a fluid rendering pass
ObiFluidSurfaceMesher requires a fluid rendering pass asset as input. You can create one by clicking the "Create" button in the mesher's inspector (the newly created pass will then be automatically assigned to the mesher), or by right clicking on a project folder and selecting Create→Obi→Fluid Rendering Pass and then assigning it manually to the mesher.
Passes determine how fluid meshers are grouped together to form a single fluid surface: meshers sharing the same pass will be merged and produce a single mesh, meshers using different passes will generate separate meshes.
Note that passes have no impact on fluid physics, they just control surface mesh generation. Specifically, they won't affect whether fluids mix with each other or not. That's controlled by the blueprint's polarity and miscibility parameters.
Passes expose a set of parameters that determine how the mesh is generated and rendered. Let's take a look at them:
Mesh generation
These parameters control how the surface mesh is built from the particle-based fluid.
Voxel size
Controls overall mesh quality. Small voxel sizes lead to highly detailed surfaces that are costly to generate - while large voxel sizes lead to cheaper, less detailed surfaces. If your voxel size is too small performance will be negatively affected, and if it's too large, small drops and other fine details will be lost.
A good starting value for the voxel size is the size of your fluid particles. You can check the size of a particle in the info bubble that appears at the bottom of your fluid blueprint's inspector. If you are using
the same pass to render multiple emitters with different blueprint resolutions, you typically want to use the particle size of your highest resolution blueprint (as it will produce the smallest particles).
Using a small voxel size has a huge impact on performance, so keep this parameter at the highest value that yields acceptable results.
Isosurface
Offsets the fluid surface, blending particle properties together to obtain a smooth surface. At a value of 0, the fluid surface sits at the border of each particle's smoothing radius, where fluid density reaches 0. Increasing the isosurface value will offset the surface inwards, towards areas of greater density.
Descent iterations
Amount of gradient descent iterations. Once the initial isosurface has been sampled at the specified voxel size, surface vertices are further moved towards higher density regions using gradient descent. The resulting effect is denser sampling at virtually no cost. This parameter controls the amount of descent iterations.
Descent isosurface
Target isosurface value to descend towards. This is added to the Isosurface value to determine how much vertices are shrunk during gradient descent.
Descent speed
How much each descent iteration moves vertices towards the descent isosurface.
Smoothing
Amount of laplacian smoothing applied to the surface mesh. More iterations/higher intensity yield smoother surfaces. Higher smoothing tends to shrink the mesh, obtaining thinner tendril and sheet-like structures.
Bevel (2D only)
When the solver is in 2D mode, controls the amount of beveling applied to the fluid edges to obtain a 3D effect.
Rendering
This controls how the mesh is rendered.
Material type
Determines the material used to render the fluid. Obi comes with two materials intended for opaque and transparent fluids, and you can use your own custom material too.
The included opaque material works in all render pipelines, provided that you add your pipeline as a target in ShaderGraph. The transparent one works in both the built-in pipeline and URP, however HDRP has much better transparent shading capabilities out of the box so you should use a custom transparent material in HDRP. For information on how to
write a custom shader, see Custom fluid shaders at the end of this page.
When using transparent material in URP, you need to enable opaque and depth buffer generation in your pipeline asset:
Smoothness
Controls overall surface smoothness of the fluid. Higher values lead to more pronounced specular highlights.
Thickness
How much light is absorbed by the fluid, as light travels trough it: higher values will tint the background with the fluid color.
Thickness downsample
Downsampling of the thickness buffer. Increase it to use a lower-resolution buffer.
Refraction intensity
Amount of light refraction. This effect is more noticeable in thicker regions of the fluid.
Thickness downsample
Downsampling of the refraction buffer. Increase it to use a lower-resolution buffer.
3) Creating a fluid renderer feature
Creating a renderer feature is done in a slightly different way depending on which scriptable render pipeline you're using. Note the HDRP rendering pipeline is not currently supported:
Built-in
Add a ObiFluidBuiltInRendererFeature component to any object in the scene.
Universal (URP)
Add a ObiFluidRendererFeature to the feature list of your pipeline's Renderer asset, see the following picture:
The minimum URP version supported by the fluid renderer as of Obi 7.0 is URP 7.4.1.
Memory budget
Obi uses an algorithm called surface nets to build the fluid surface mesh. This algorithm is based on voxels, Obi stores these voxels in groups of 64 voxels called chunks. You can set a memory budget for storing surface chunks in the ObiSolver component, under the "Memory budget" foldout:
It is possible for fluid rendering to run out of chunks, in that case the simulation will slow down considerably and you'll notice holes and missing chunks in the fluid mesh (see the following image for an example of how this looks like). If this is your case, increase the surface memory budget.
Custom fluid shaders
You can use ShaderGraph to build custom fluid shaders. The only difference with a regular shader is that fluid meshes use a custom compressed vertex format, so you must add a specific node to your graph to uncompress vertex data. Which node to use depends on whether your shader is intended for the Burst or Compute backends
When using the Burst backend, fluid mesh vertices are sent from the CPU to the GPU. Vertex normals use octahedral encoding, so you need the DecodeNormal node to decode them. Here's a minimal fluid shader for the Burst backend:
When using the Compute backend, fluid mesh vertices are generated and rendered by the GPU. You need to use the GetIndirectVertex node to retrieve vertex data. Here's a minimal fluid shader for the Compute backend: