Bella uses a node-based scene model, which is very generic and flexible, to facilitate interoperation with a wide range of third-party applications. Its Scene SDK is written in c++, and provides safe, simple, and error-resistant usage by means of a ref-counted hidden-implementation design.
This document discusses core concepts and functionalities of Bella at a fairly technical level. It will be useful if you wish to understand how Bella scenes are constructed, and how various core Bella nodes are defined, and why.
Bella supports running on 64-bit machines and operating systems only. It is recommended to have at least 8GB of memory, but generally preferable to have 16GB or more. Bella itself is very compact, but using it may involve large files, so you should have plenty of disk space (preferably SSD) available.
On Windows, we support running on version 7 and higher. Bella may work fine on earlier versions, but we do not officially support them.
On MacOS, we support running on MacOS Mojave, installed on a genuine Apple computer (i.e. we do not officially support running on a Hackintosh). Bella may work fine on earlier versions, or on a Hackintosh, but these are not officially supported.
Note that we do not officially support running in virtual machines, or otherwise-emulated environments. For example, we support running under Bootcamp on MacOS, but not under Parallels, VMware, VirtualBox, or similar. Bella may work fine in these environments, but we do not officially support them.
A node is a data structure which is defined at runtime, to contain a collection of input and output attributes. In the following diagram, we consider inputs to be on the left, and outputs to to be on the right, and we adopt the convention that output names always begin with the string "out".
A node may be just a container for data, in which case it has no outputs, or it may be a "black box" for computing output values, based on the values of its inputs. In the latter case there is expected to be code associated with the node, to perform the computation; this may be code contained in the Bella engine itself, for internal Bella nodes, or in a shared library, for third-party nodes.
The node itself also has some inherent properties which are neither inputs nor outputs, the foremost being a permanent name (a string). A node's name will never change, and will persist through file IO. To facilitate user-friendly usage of the node, it is possible to set its displayName, which itself is implemented internally using a "name" input (which is therefore common to all nodes).
Nodes are defined using a .bnd file, which is a json file that is parsed at runtime, to create node types, and endow them with their inputs and outputs. It is also possible to entirely define a node type at runtime, using c++ (or any supported interop language). Nodes in Bella are allowed to inherit the attributes of multiple other nodes.
An attribute represents a named value of a given type, and may be either an input or an output attribute. The supported attribute types are as follows:
|Int:||Signed 64-bit integer.|
|UInt:||Unsigned 64-bit integer.|
|Vec2:||2-component real vector|
|Vec3:||3-component real vector|
|Vec4:||4-component real vector|
|Pos2:||2-component real position vector (i.e. point).|
|Pos3:||3-component real position vector (i.e. point)|
|Quat:||Quaternion (4 reals).|
|Rgba:||RGBA vector (4 reals, range 0-1).|
|Mat3:||3x3 matrix (9 reals).|
|Mat4:||4x4 matrix (16 reals).|
|String:||Null-terminated UTF-8 string.|
|Node:||Reference to a node. Input only.|
|Buffer:||Buffer of primitive values.|
|Array:||Array of input attributes. Input only.|
|Object:||Container of child input attributes. Input only.|
Input attributes have an immediate value -- that is to say, if you are working with a Vec3 input, and you set it to (1,2,3), this value is stored in the attribute, and may be read back out at a later time. Output attributes have no such immediate value, and can only return values computed by the node from its inputs.
As mentioned above, it is possible to add inputs to a node at runtime -- it is also possible to remove them, but care should be taken in doing so, since code may exist which depends on the names and types of inputs for known nodes.
Such a system would be of limited usefulness if it were only possible to set input values, and compute output values -- what we would prefer is the ability to drive input values with computed output values, which themselves are used to drive other inputs, and so forth. This is accomplished by allowing connections between inputs and outputs:
This raises the question of how to know when to read an input's immediate value, and when to follow its connection (if connected) to get the value of a connected output. And the answer is: it depends.
If you are (for example) writing a UI element that allows showing an input's immediate value while it is connected, then you would obviously need to query it for its immediate value. But if you are writing the internal implementation of a node, then it is almost always the case that you would want to follow, or evaluate, the connection.
Attributes, both input and output, provide a full set of both is and as methods -- isInt()/asInt() and so forth, for the above-listed attribute types -- which work according to their type.
Calling asInt() on an output necessarily calls code in the output's node, which may in turn cause inputs of that node to be evaluated, in a chain of evaluation. The system ensures that cycles cannot be created, and that consequently, the evaluation will halt at some point.
For an input, however, the corresponding as methods take an optional eval (bool) argument, to allow either retrieving the input's immediate value, or evaluating the output to which it is connected, if connected.
One fundamental node type in the system is the transform, or xform node. The xform node is defined to have an array of children, which is an attribute of type node, and which accepts nodes deriving from xform itself, from camera, or from the abstract node, geometry. It also has an array of (4x4 matrix) transforms, each associated with a particular time value.
Together with input/output connections, xform therefore describes a DAG structure, and in Bella, this structure has its root in the so-called world xform, which is the singular xform to which all other xforms and geometry in the visible scene are ultimately parented:
Other xforms or geometry may exist in the scene, which are not ultimately parented to the world xform, but these will not be part of the visual, rendered scene.
Through the ability to parent one xform to another, it is made possible to create as many instances of a particular piece of geometry as desired, or even of entire sub-hierarchies of xforms and geometry.
As such, another key related concept is that of a path, which describes a particular path from the world xform, to a given instance. The need for such a concept is implied by the fact that no piece of geometry necessarily has a single parent, and may indeed be parented to an arbitrary number of xforms, whether directly or indirectly.
The smart node is an abstract base node type, inherited by other nodes, to indicate that they present one (generally simplified) set of inputs, and output a node (generally more complicated, and internal to the engine) built using those inputs, at render-time.
A Bella scene consists of a list of nodes, arranged and connected in a particular way; at minimum it must have a certain configuration of global, state, settings, and world xform nodes.
The node list is a simple, flat list of all of the nodes currently contained in the scene. Any nodes in the list may have input/output connections between them. Nodes in the list are not allowed to have duplicate names, so when creating new nodes, the requested name may be altered by the scene, by appending a numeric suffix.
For programmers, note that it can be expensive to search names in some export scenarios, and that you may improve export performance by ensuring that names are already unique when you request the scene to create a node.
The global node serves as the root of a Bella scene. It contains a list of state nodes, one of which is current at any given time. It is not supported to "switch" the global node, and indeed, it exists mainly to avoid hard-coding similar functionality into the Bella scene code.
In general, a user will rarely have reason to interact with the global node, since switching state may involve much rebuilding of (likely custom) GUI structure.
The state node holds references to a settings node, and a world xform. Its purpose is to allow a Bella scene to contain multiple sets of settings/world pairs. Though there is not yet any use of this functionality, it may be exposed in the future, for example, by the Bella CLI allowing to select from among multiple states, for rendering.
NOTE: as of the time of this writing (build 19.3.0), the Bella GUI is not yet prepared to deal with switching states.
A settings node holds references to a camera, environment, beautyPass, default & override materials, and other attributes (base output path, and so forth) that define how the scene is rendered.
NOTE: as of the time of this writing (build 19.3.0), the Bella GUI is not yet prepared to deal with switching settings.
The world xform is a particular xform, which as described above, has been designated as the root of the visual scene by being referenced by the current state node.
Bella contains functions neither for setting global scene scale, nor axis (left-handed YXZ, right-handed YZX, etc) configuration -- these are both encoded in the world xform, which is therefore expected to be an axis-aligned orthogonal matrix, by which all children will be transformed for rendering.
NOTE: as of the time of this writing (build 19.3.0), the Bella GUI is not yet prepared to deal with switching the world xform.
An environment is a node optionally linked in the current settings node, which produces a dome of color with gradient from horizon to zenith (colorDome node), an image dome that uses an HDRI for lighting (imageDome node), or a sky dome to simulate a physical sky & sun (skyDome node).
It is also supported to render with no environment, though you will encounter an error if there are also no other light sources found in the scene.
Output images in Bella are created using a render pass node, of which there are several types:
|Albedo Pass||The albedo (diffuse reflectance) of the first impacted material.|
|Alpha Pass||The opacity of the first impacted object.|
|Beauty Pass||The traditional production "beauty" output.|
|Material Pass||The material ID color of the first impacted material.|
|Normal Pass||The normalized surface normal per pixel, in world coords.|
|Object Pass||The color ID of the first impacted object.|
|Shadow Pass||The shadows for objects with "Capture Shadows" render flag enabled.|
|Tangent Pass||The normalized surface x-axis tangent per pixel, in world coords.|
|Z Pass||The projected-to-film distance from the first impact to the camera position, normalized to the specified range.|
Each pass type uses a particular solver to produce its output image, and multiple passes may be rendered by adding them to settings:extraPasses.
Usually, there is a beautyPass referenced at settings:beautyPass, and it is rendered before extra passes. However, you may also opt to render extra passes first, by also adding your beautyPass to the extra passes (you do not need to remove it from settings:beautyPass).
It is also supported to render with no beautyPass at all (except in IPR) if desired.
The Bella material system is based on rendering materials composed of an optional substrate (conductor, dielectric, or complex IOR), along with a layer, which essentially simulates a very thin layer of dielectric material. With optional coating and scattering nodes, a cross-section of the model looks like this:
All Bella materials share a common set of basic attributes:
|Opacity:||A global opacity applied to the material as a whole.|
|Normal:||A bump or normal map applied to the material as a whole.|
|Priority:||Decides which geometry takes precedence, when dielectric volumes are nested.|
|Material ID:||The color used for objects assigned this material, in the materialPass.|
The Bella substrate nodes are these:
|Conductor:||Represents an opaque surface, with reflectance color.|
|Dielectric:||Represents a refractive surface, with transmittance, IOR, dispersion.|
|Complex Material:||A substrate defined through complex IOR data.|
In addition to these type-specific attributes, all of the substrates also share a common set of surface-related (BSDF-related, to be precise) attributes:
|Coating:||An optional coating node to be applied over the substrate.|
|Roughness:||The micro-geometrical roughness of the surface.|
|Anisotropy:||Determines to what degree roughness is biased in one direction (produces a brushed look).|
|Rotation:||Rotates the tangent frame (essentially, rotates anisotropy effect).|
Over a Bella substrate, we may optionally apply a layer, which simulates a thin dielectric layer. Like the dielectric, a layer also has the common surface-related attributes (Coating, Roughness, Anisotropy, and Rotation), as well as the medium-related ones (IOR, transmittance).
Additionally, the layer supports referencing a scattering node, to control scattering of light within the layer medium (as shown in the diagram).
A coating represents a very thin (nanometer scale) layer over a substrate or layer surface, by which thin-film effects are simulated. As depicted in the diagram above, the coating follows the micro-geometrical form (i.e. roughness) of the surface to which it is applied.
Currently supported in the layer, the scattering node is used to simulate scattering of light within the layer medium.
The sheet material is essentially a layer used on its own as a material, with no substrate at all. It is therefore useful for simulating thin surfaces like paper or leaves, or for obtaining a glass-like appearance from window panes that have no volume.
As it inherits both the material and layer nodes, it may be used anywhere that either of those types may be used.
The Bella emitter material is used to emit light from arbitrary geometry. It allows setting output power using one of several different units, for convenience in matching real-world light sources. It also supports some advanced rendering-related features:
|Caustics:||Connect an optional filterCaustics node, to control caustics for this emitter.|
|Radius:||Connect an optional rampInOut node, to define the distances (from geometry) at which light begins and ends for this emitter.|
|Diffuse:||Reduce the contribution of this emitter to diffuse reflections.|
|Specular:||Reduce the contribution of this emitter to specular reflections.|
Bella supports using complex IOR data through the complexMaterial and complexLayer nodes, which function as substrate, and layer, respectively.
In the case of complexLayer, a thickness value is provided, for controlling the simulated virtual thickness of the layer.
Several of the Bella materials derive from the smart node:
|Ceramic:||Produces a ceramic material based on color, roughness, and specularity.|
|Glass:||Produces a simple glass material with given color and IOR.|
|Metal:||Produces a simple metal material, either complex IOR-based, or based on color.|
|Plastic:||Produces a simple plastic material based on color, roughness, and specularity.|
|Urethane:||Produces a urethane-like material, based on color, translucence, and roughness.|
|Water:||Produces a water material, with the given color.|
These are by no means special types of materials, in the sense that they build other materials internally using the standard conductor, dielectric, and other standard Bella nodes. They exist mainly for the purpose of simplifying material usage, and saving time, when the full expressiveness of the core material system is not required.
Bella uses several file formats, to hold Bella scenes, rendering data, and node defintions.
BSA stands for Bella Scene ASCII, the text-based Bella scene file format. Technically it is UTF-8, not strictly ASCII.
BSX does not stand for anything specific; it is generally a binary-format Bella scene, but it is also allowed to hold a text-based scene. We generally refer to any scene file as "BSX" in common conversation, whether it is actually BSA, BSX, or BSZ.
BSZ stands for Bella Scene Zip, and is a zip archive containing a BSX file, along with all of the external file resources it references.
Anywhere you can open a BSA or BSX file, you can also open a BSZ file, as the scene's read function will handle unzipping it. When it does so, it will write (or overwrite, so be careful, and warn users if necessary) the zipped BSX file, as well as the BSZ file's res directory, next to the BSZ.
BSI stands for Bella Spectral Image, and it is our internal format for holding rendered data. It is currently used to support resume render.
BND stands for Bella Node Definitions, and it is our json-format file for defining Bella nodes. It is read at runtime by the Bella scene, which then provides access to all of its definitions (nodes like any other, except stored in a special list, and used as prototypes for creating nodes in the scene) through the scene's definitions list.