This tutorial comes with implementation files in the download section.
Boundaries force us to adapt which then opens up the door for new ideas. This also applies to discrete exterior calculus (DEC). In this course, we have often avoided boundaries due to the added complications that we get through them but today we will shed some light on this topic which will allow us to create waves and heat simulations that realistically deal with boundaries.
So how should our DEC build deal with boundaries? The answer is surprisingly simple. The exterior derivatives $d$ does not change at all while the Hodge-star operators $\star$ only have to adjust their values at the boundaries a bit.
Always keep this image in your mind:
Let $\sigma_k$ be the k-measure used for the Hodge-star computation. In our euclidean settings this means that:
The discrete Hodge-star $\star_k$ is then defined as a diagonal matrix with the ratio between the dual measure and the primal measure.
$$ (\star_k)_{ii}(e) = \frac{\sigma_{n-k}(\text{dual}(e))}{\sigma_k(e)}$$
Where $e$ is a either a point, edge or face.
For our 2D surface case you can now look at the above image and see the following:
Implementations of $\star$ with boundaries now only have to compute slightly different measures at the boundaries. That’s it!
Nothing! That is actually crazy.
For the 2D surface case we still have the computationally super convenient formula
$$d^*_1 = d_0^{\intercal}$$
$$d^*_0 = d_1^{\intercal}$$
And the boundary respecting Laplacian still obeys the old formula as usual
$$\Delta = \star d \star d + d \star d \star$$
In fact, the DEC build assets you received earlier already do this.
Ok cool. We have now a DEC setup with a Laplacian but how do we actually solve the heat equation or the wave equation with this?
Solving a differential equation on a domain $M$ with a boundary will require boundary conditions, possibly multiple distinct conditions for different parts of the boundary. The choice of this boundary condition strongly depends on the context. This page has a nice list of possible conditions. The main conditions that show up again and again in graphics are the following:
How do I implement these conditions into my favorite DEC setup? We will stick to the geometric interpretation of the heat flow to describe what happens.
Ok, simple example. Let’s say you want to solve $\Delta f = 0$ with prescribed $f|_D = g_D$. Example: you have some square shape with blue and red marked areas where constant heating/cooling is applied. We want to compute the steady-state heat distribution that the heat equation converges to.
In this case the Dirichlet boundary condition is set on points and we are trying to solve the heat $f$ as a 0-form.
The way to approach this is through matrix slicing. We already know that some points are going to be fixed. In our case we will call these points boundary points. Let B be the set of boundary points and I the set of interior points. All the DEC operators can be permuted since their order only dependent on the enumeration of the points, edges and faces. These enumerations are arbitrary. Imagine now that we would re-enumerate all our points in such a way that we first count all the interiour points I and then count all the boundary points B. Then our equation would look like this:
$$\Delta f = \begin{bmatrix}
\Delta_{II} & \Delta_{BI}\\
\Delta_{IB} & \Delta_{BB}
\end{bmatrix} \begin{bmatrix}
f_I\\
f_B
\end{bmatrix} = 0$$
Where the I and B mark what parts of the matrix act on the interior and boundary points respectively. Since $f_B$ is already directed by the Dirichlet condition we only want to solve for $f_I$. This is actually quite straight forward. Just solve the following sliced equation for $f_I$:
$$\Delta_{II} f_I = -\Delta_{BI}f_B$$.
Then stich $f_I$ and $f_B$ together on $M$ and that’s it! Solving this equation for the above example then gives us this heat distribution:
This is what this solver would give us if we had only specified half of the top and bottom boundary to have a fixed temperature value:
Here is an example of a spiral where only the tips are heated or cooled respectively:
And here a strange slice of cheese where certain corners got marked with the Dirichlet condition:
Note!: Solving this type of problem with prescribed values will happen again and again, and as you might have noticed, we have not used the fact that our boundary points are actually on the boundary. You could use this to fix values on the inside of the set too. However, the good thing about having these conditions on boundaries is that you are guaranteed a solution as long as your set $M$ is compact and connected. If your Dirichlet conditions draws a closed curve inside your set then you are effectively splitting the set and solving multiple Dirichlet problems at once.
In the examples above we marked some boundaries with conditions, but not all of them! What happened there? Next, we will show that not specifying anything at the boundary is equivalent to setting a zero Neumann boundary condition.
Let’s solve for the steady-state of the heat equation again but this time without any prescribed temperatures. Instead, we will fix the amount of heat traveling in to and out of the surface. For example, we take a grid and watch how the heat enters from the sides.
The heat flow corresponding to this steady-state that we are looking for looks like this:
$$ \partial_t u_t= \Delta u_t + \star d g $$
where $g$ is a flux 1-form along the dual-edge (normal to the boundary) specifying the flux with the boundary. $g=0$ on the interior.
Since we work with temperatures on points only we can go ahead and replace $\star d g$ by $\tilde{g}$ specifying the incoming/outcoming temperature at that point.
$$ \partial_t u_t= \Delta u_t + \tilde{g} $$
This time we don’t have to slice any matrices. The Neuman steady-state equation is the following:
$$ \star d \star d f = -\tilde{g} $$
This is just a Poisson problem. We move the $\star$ to the other side for computational convenience and then solve for $f$. This is what it looks like for the test case constructed above:
But wait! When can we assume this equation to have a solution? The answer is geometrically simple: When the mean boundary flux is 0. Why? because if you continuously pump heat in and out of a system, stability can only occur if the amount of heat coming in and the amount of heat going out is the same. If this is not the case, the system will have infinite heat or negative infinite heat as the heat flow continues. This condition also guarantees that the divergence theorem holds:
$$ 0 = \int_M \Delta f = \int_M \nabla \cdot \nabla f = \int_{\partial M} n \cdot \nabla f = \int_{\partial M} \frac{\partial f}{\partial u } $$
Here is another example of flux entering and leaving a system.
Notes!: This result is really cool because it means that if $g=0$ on the boundary (no flux leaving the system) we have to implement exactly nothing! This is actually how we got away in the Dirichlet solutions above without specifying Dirichlet conditions on all the boundary points.
This is simple. Just set up the Neuman problem and start slicing. Let us solve $\Delta f = -g$ by looking at
$$L f = -\star g$$
($L = d\star d$) Let us split the boundary $\partial M$ into a Dirichlet part $\partial M_D$ and a Neuman part $\partial M_N$ (these two sets do not intersect).
$$\partial M = \partial M_D \cup \partial M_N$$
First, we slice our matrix as above for the Dirichlet boundary. We define $I_D:=M – \partial M_D$ as the interior points with respect to the Dirichlet boundary and $B_D$ as the Dirichlet boundary itself. This will give us this sliced version of our equations:
$$ L_{II_D} f_{I_D} = -L_{IB_D}f_{I_B} – \star_{II_D} g_I$$
Then we just solve this.
This is where matrix slicing gets its name from. We are solving for $\Delta f = -g$ with Dirichlet and Neuman conditions. This is equivalent to solving $L f = -\star g$ with $L = d \star d$. Let $\text{bdy_d}\in \mathbb{Z}^n$ be a vector such that
$$\text{bdy_d}_i=\left\{ \begin{array}{ll} 1 \in \partial M_D \\0, \text{else} \end{array} \right\}$$
Let $\text{bdy_d_val}$ and $\text{bdy_n_val}$ be the vectors containing the Dirichlet and Neuman boundary data respectively. Then we can get $L_{II}$ and $L_{IB}$ by eliminating rows and colums that are unwanted. We create two complementary numpy vectors
# index slice instructions indB = np.flatnonzero(bdy_d) indI = np.flatnonzero(bdy_d==0)
and use them for sclicing
# slice the matrix LI = L[indI,:] # delete non-interior rows LIB = LI[:,indB] # delete non-boundary cols LII = LI[:,indI] # delete non-interior cols
The same slicing has to be done for star0. The right hand side (rhs) also has to be sliced correctly with the Dirichlet and Neuman boundary data.
rhs = - LIB.dot(bdy_d_val[indB]) - star0II.dot(bdy_n_val[indI])
We can then solve
# linear solve out = la.cg(LII,rhs)
and then combine this solution with the Dirichlet boundary data
# assign solution solI = out[0] f[indB] = bdy_d_val[indB] f[indI] = solI
Let’s solve $\partial_t f = \Delta f + g$ with Dirichlet and Neuman boundary conditions. Previously we solved this equation without (g=0) boundaries using
$$(\star – hL)f_{k+1} = \star f_k$$
Now this derivation can also be performed using slicing like this:
$$(\star_{II} – hL_{II})f_{I,k+1} = \star f_{I,k} – h(L_{IB}f_D + \star_{II} g_I)$$
For example:
For waves, a Dirichlet condition does not really make sense. The most reasonable boundary condition is to prevent the wave from leaving the domain. This corresponds to a zero Neuman condition. As we have learned above having $g=0$ means that we have to change absolutely nothing! As long as your DEC build has the right dual edge weights and point weights everything works fine out of the box. We still solve
$$f_{t+h} = h^2\Delta f_t + 2f_t – f_{t-h}$$
and get valid reflections at the boundary.
]]>
But first a note on assets.
How do you reuse code in Houdin? If you don’t value your time in this life you could always copy and paste your nodes. A faster approach is to create your own digital assets.
Creating a digital asset is like creating a node of your own. This new node will consist of a collection of sub-nodes and can have exchangeable parameters of its own.
To create an asset you first have to set up a couple of nodes that create the function that defines your asset. For example, you might want to reuse this simple ball and stick model network
To create a digital asset from these nodes, highlight them all and create a subnetwork
Now you can right-click on this subnet and “create digital asset“
You can now store your digital asset in a .hda (.hdanc for non-comercial) file. One such file can contain multiple assets.
That is it! You can now drop a ball and stick node wherever you like
To import digital assets in your next project click on assets->install asset library
Notice the cute little lock when using assets. It remains locked when the assets not being edited. Right-click on the node to unlock it by “allow editing of content”
Then you can change whatever you want locally. However, in order to save the changes onto the asset and thus update all instances of this assets you must right-click and select Save Node Type.
If instead, you click on Match Current Definition you will reset your edits to the current .hda version.
Let’s say that your ball and stick method needs a handle to edit the size of the spheres. To expose an editable parameter simply right click on the node after unlocking it and hit Type Properties.
Here you can create and edit parameters to be exposed in the interface and even edit the icon of your node.
Once you have created a float parameter that you call sphere_size you can now edit it when clicking on the node.
Right-clicking on the node will allow you to copy the parameter. This is extremely useful because now you can paste the reference to the parameter where the size of your sphere is determined. Use paste relative references to be functional in other files as well.
It will now say ch(“../sphere_size”) in the place where you have pasted it. Now don’t forget to save your asset again. Now you can slide the sphere size with you mouse directly from the asset:
Your Homework 06 will make use of this.
We are merciful and allow you to implement this yourself. The lecture notes 06_Conformal.pdf contain the description of the algorithm that you will be using for your homework.
Lucky for you, we provide you with DEC assets that will reduce your implementation effort. The first node called DEC_build will compute point weights, cotan weights, lengths, areas, label boundaries, source points, and destination points, reference opposite edges and so on. It’s two outputs will be the original mesh as well as the primal edge graph used for matrix building.
The second node, DEC_matrices, builds all your favorite matrices that you love so much. It even allows you to specify the name you want these matrices to have. This asset needs two inputs, which are the two outputs of the DEC_build.
Go ahead and read the notes from 06_Conformal.pdf and implement it yourself as your homework.
Note: In the visualization industry, an “asset” can be any sharable, reusable item. Algorithms, textures, models, sounds, music, materials, environmental lights etc… Online stores can be used to distribute and sell these assets. A robust solid algorithm can be worth a lot online and now you already have the tools to build a user-friendly one.
As you might have noticed the algorithm for conformal mappings requires complex numbers in python. Theoretically, you can work well with two float values, which is what you should do on a VEX level. In Python, however, complex numbers can be defined directly.
Look how easy python works with complex numbers. Just work with j
z = 2 + 3j
w = 0.5 + 0.1j
print z*w
( this will print 0.7+1.7j )
This is how you create an array of complex numbers of size n
w = np.zeros( n , dtype=np.complex_)
This is how you can get the real and imaginary parts from an array
np.real( … ), np.imag( … )
scipy.sparse.linalg has eigenvalue and eigenvector methods also for the generalized eigenvalue problem. You can also Cholesky factorization and use the inverse power method.
By default, you have seen node = hou.pwd(). pwd is the present working directory and this call gives you a handle to your node. Then geo = node.geometry() creates a handle to access the geometry of the node.
You can input the edge skeleton (primal graph) as your second input. In python you can then access the second input using node_edge = node.inputs()[1]. Using this handle you can access geo_edge = node_edge.geometry() and now read attributes from the second input into python.
Using such a geometry node handle also allows you to iterate over all elements of a node. For example:
for prim in geo_edge.prims():
# will iterate over every primitive of the node geo_edge
Now you are ready for the homework!
]]>Please check out the download folder and check out the .hip file of this tutorial. It will contain a functional DEC build with a heat flow example. I have noticed that many of you used for loops to build the sparse matrix. The file shows that this is not necessary.
Entropy
Given a 0-form $u_t:M\rightarrow \mathbb{R}$ that represents temperature at time $t$ on our surface $M$, the dynamics of the heat is given by the heat equation:
$$\partial_t u_t = \Delta u_t = \star_0^{-1} d_1^*\star_1 d_0 u_t$$
we can define entropy to just be the n-form
$$\sigma_t:=\star_0 \log u_t$$
The interpretation of this function does not matter at all for the math and its implications that follow from it. We can define the total entropy as
$$ S(t):= \int_M \sigma_t$$
The Amazing Thing about Entropy
In a closed system ($\partial M = \emptyset$), the total entropy’s derivative is always positive, which is also often referred to as the second law of thermodynamics.
$$\frac{d}{dt}S(t) \geq 0$$
We show this now:
$$ \frac{d}{dt}S(t) = \int_M \partial_t \sigma_t = \int_M \star_0 \partial_t \ln u_t$$
$$
= \int_M \star_0 \frac{1}{u_t}\partial_t u_t
= \int_M \star_0 \frac{1}{u_t} \Delta u_t $$
$$
= \int_M \star_0 \frac{1}{u_t} \star_0^{-1} d_1^*\star_1 d_0 u_t
= \int_M \frac{1}{u_t} d_1^*\star_1 d_0 u_t$$
$$
= \int_M d_1^*\left( \frac{1}{u_t} \star_1 d_0 u_t \right ) – d_0\left( \frac{1}{u_t}\right)\wedge \star_1 d_0 u_t$$
$$
= \int_M d_0\left( -\frac{1}{u_t}\right)\wedge \star_1 d_0 u_t
= \int_M \frac{1}{u_t^2} d_0u_t \wedge \star_1 d_0 u_t $$
$$
= \int_M \star_1\frac{1}{u_t^2} |d_0u_t|^2 dp \geq 0$$
Here we used integration by parts together with stokes theorem.
The Implications
So! Why should we care? Well actually we don’t have to, but if we assume that our universe $M$ is an isolated system with finite energy and we believe the laws of thermodynamics to be true, then the following is true in our universe:
The fact that $S$ can only increase in time shows us that the system has no loops. This is a profound statement to make about $u$ on $M$. Think of it this way: no matter how the dynamics turn out, the state as it was before can never ever return at a later time because this would imply that the total entropy would reach the same value again.
When does $\frac{d}{dt}S(t) = 0$? From the equations in our derivation above, we see that this is indeed the case when $d_0u_t = 0$. In that case, $M$ is at thermal equilibrium, meaning that no heat difference exists anymore.
At the thermal equilibrium, everything will come to an end. All energy available for mechanics will have dissipated into heat and no more life will be able to exist. This is called the heat death of the universe.
The Discrete Version
The discrete version can also be computed in Houdini.
$$ S(t):= \sum_i \sigma(p_i,t) = \sum_i \text{f@point_weight*log(f@u)}$$
For the heat flow on the bunny, you will see this term rise all the time. However, if you initialize your $u$ with many 0 values then log(0) will cause problems. You can get around this by adding a mini-mini-mini amount of heat everywhere. This is ok because true 0° Kelvin temperature can never be reached.
If our universe was bunny-surface shaped, then the heat flow together with this computation would be a valid simulation of entropy.
Splish Splash Waves
Let $u_t:M\rightarrow \mathbb{R}$ be a 0-form. The wave equation is given by
$$\partial_t^2u_t = \Delta u_t$$
We have not yet given $u$ a specific physical meaning. This is because many things in life obey the wave equation in different contexts. For this class’s sake, we can simply assume that this is the hight of the water on a surface $M$.
We have our DEC build ready, the only thing left to do is to find a suitable discretization of this equation to simulate it.
The simplest approach will actually be a really good one (Verlet integration). We use a simple discretisation of the second derivative:
$$\partial_t^2 u_t = \frac{1}{h}\left( \frac{u_{t+h} – u_{t}}{h} – \frac{u_{t} – u_{t-h}}{h} \right)$$
$$ = \frac{u_{t+h} -2 u_{t} +u_{t-h} }{h^2}$$
Set this equal to $\Delta u_t$ and solve for $u_{t+h}$:
$$u_{t+h} = h^2\Delta u_t + 2u_t – u_{t-h}$$
That’s it! Since this is a second order differential equation we need to track $u_t$ and $u_{t-h}$. This is simple in our case because we just have to store the value of $u_t$ from the previous time step.
Also, keep in mind that for the initial values for these simulations it is not enough to just define $u_t$, you have to define $u_{t-h}$ somehow too.
You will have fun with this in your homework.
Painting in Houdini. Painting attributes.
To design your initial condition for $u$ on the surface we can abuse the paint tool. First we initialize every $f@u = 0.5$ on all points. Then simply place a paint node and select the white color (value 1) and check “overwrite color” and set it “u”. Then with the paint node selected, however over the scene view and hit enter to activate paint mode and start painting. Press escape to finish paint mode. Repeat this with a black paint node (value 0). Then use a point node to map these values form -1 to +1.
In this case, later down the nodes we use the “u” attribute to recolor and displace the surface. This is why the painting of the attribute “u” is visible.
the final visualization looks like this.
]]>
Environmental lights
First of all a small note on environmental lights. Environmental lightmaps can really help to make your objects look realistic. They basically simulate a weak light source from all over the place, just like in real life where everything in the surrounding casts/reflects/manipulates light somehow on your object. Everything matters, just like you.
You have already learned to use Houdini’s skylight nodes. They give you a sun for a light source as well as an environmental map with the colors of the sky.
However, you can really go online and search for these 360° photos and use them as environmental lights. Here is a really professional website with free lightmaps.
Inside Houdini you can then import this map in the environment light node.
Textures
Textures are the go-to method to apply high-resolution data on lower resolution meshes. It is a fantastic method to work in between the triangles. The main application is for coloring surfaces.
Imagine a triangle. Each vertex of the triangle is given a texture coordinate (uv). These coordinates are then used to find the triangle on an image that is to be projected onto the triangle we are working with. See this image for clarification:
Textures in Houdini
It is really simple. Any image can be a texture. Just create a material (e.g. principle shader) and go to the texture tab and select what texture it should have.
Later we will also add more special textures (e.g. reflectivity texture, bump texture). All kinds of texture are applied through the material options. Multiple textures can be used on one material.
Example: Bunny
Here we have a bunny .obj mesh and a texture of it. This .obj file already came with uv-coordinates for each point and this texture image was explicitly made for this .obj. Applying the texture onto the surface then colors in the bunny nicely. So cute! ( this is the LSCM Standford bunny by the way.)
+
=
(Note: for some reason we had to flip over the v-coordinate to make sense.)
Example: Earth
Textures can do so much more than just bring in colors. Here, for example, we have multiple property maps. We have (left to right, top to bottom):
Each of these textures can now manipulate our surface during render time in order to get really realistic images. This is very powerful, which is why there is a lot of research for generating nice texture layouts from geometry. Conformal mappings for example often speak of texture mapping with as little shear as possible.
In order to place the uv-coordinates onto your sphere, we use the UV texture node. This will allow us to select “polar” coordinates as the method to compute uv-coordinates. After that, we can directly apply the texture.
This is what these textures can look like in action:
This is the source page for the textures of this earth model. You can google many free textures and also use this handy site to find nice material textures.
Example: flat texture
Let us use a texture from the site above as our floor. This texture comes packed with an ambient occlusion map (secondary reflections), a diffuse map (color), a displacement map (height), a normal map (fine details), and a roughness map (reflection property).
We can then apply all these materials to a single square grid with orthogonally projected texture coordinates and thus get a beautiful tiling of the plane!
But bunnies don’t belong on the street!
Exercise 1: Render the bunny on some nicer surface than this street above. Go to texturehaven.com and find a more suitable surface for the bunny and apply it.
Exercise 2: Go online and find some free mesh with textures, download it and render it to understand it.
Exercise 3: Download another planet from the astronomy texture page linked above and make the planet orbit around the bunny.
SciPy and sparse matrices
Scipy exist and is really good! Its main feature that we are going to use a lot is its sprase matrix library:
https://docs.scipy.org/doc/scipy/reference/sparse.html
Look at this picture to understand what sparse matrix data is about:
With sparse matrices, we only focus on the non-zero values of the matrices. For our discrete exterior calculus differential operator $d_0\in\mathbb{R}^{nEdges\times nPoints}$ for example (d on 0-forms) we only care about entries $(i,k)$ where point $i$ is connected to edge $k$. This way we reduce the amount of memory we need from $\mathcal{O}(n^2)$ down to $\mathcal{O}(n)$, awesome!
The way to build these matrices will be the following:
But wait! We are working with half edges in Houdini, not edges. Do we have to change anything? Surprisingly, for now, we don’t! This is because it is kind of handy that half edges already come with an orientation.
Your next homework will be to build exactly this differential operator so that it becomes used in the heat flow simulation.
]]>Volumes
The most intuitive 3D mesh to fill up space is to dissect space into many little cubes. Such a box of dissected cells can be placed using the volume node.
Each cell can now carry an attribute. Normal meshes fill up surfaces only but 3D mesh fills up volume.
Volumes and Velocity
One can initialize this volume to carry vectors that we call velocity in our case.
The thing is, volumes are a bit special (like all of us). They are not points, vertices, primitives. They are their own special kind and get their own volume wrangle node for manipulations. Inside the volume wrangle, you can then access attributes such as v@P for the cell position and v@velocity for the vector we just defined. This means that you can use v@P as your input to a function to evaluate a velocity field.
Here is an example of a spinning vector field. We then use a few nodes to sample points inside the volume and then trail out the velocity field using these points as starters. Learn to use the volume trail node for volume inspection.
We can also animate motion in the velocity field using solver nodes! For that, we must first convert the volume into the OpenVDB volume format. The sampled points from above are then added to the solver and advected using the VDB advect points node. Straight forward. VDB is also cool because it lets us compute gradients and curls easily using the VDB analysis node.
Volume and Scalar Functions
The velocity vector above was actually just 3 scalar functions. Kind of like three kids stacked up and wearing a big trench coat to appear like an adult. If we, however, stick to just scalar functions we can build surfaces with that!
For example, we create a scalar f@f and assign the function f@f = $1-4r^2$; to it where r is the distance to the origin. We can then create isosurfaces by meshing the level set to a given value of f@f using the convert volume node. For example, tracing out the values of f@f = 0.5 will give us a small sphere of radius $\frac{1}{\sqrt{2}}$. However, choosing negative values will give us something that looks like the cut out of a sphere from a box.
Volumes Being Useful
Each volume cell (Voxel!) has its address as i@ix, i@iy, i@iz. You can read useful calls here. What is important is that even though you only have 10x10x10 voxels like in this example, you can sample interpolated values in between these voxels. For example attribute from volume node lets you do this. This can also be done in code:
float volumesample(<geometry>geometry, string volumename, vector pos)
Volumes from Surfaces
We just saw how to create a surface from a volume, but we can also create a volume using a surface using the VDB from polygon node. Given for example a banana mesh with a tangent vector field on it, we can build a volume around the mesh and advect points on the surface. The node sequence below shows you how to do this. This is fantastic for visualizing vector fields. Volumes can also be generated to get a signed distance field for your mesh.
Python and Houdini
Python is strong because it comes with many great operators that we would like to use. SciPy, for example, comes with sparse matrix builders and solvers that we absolutely need for exterior calculus applications.
Head to the download section and get the hip files of this Tutorial 04. There, python is used with careful documentation. In this lesson, we will not make use of SciPy yet.
Instead, you will learn how to read attributes in Python and save attributes and matrices for later access.
Installing SciPy for Next Tutorial
We will need SciPy soon and for that I wish you to install it. Things work differently for Windows, Mac, and Linux so I can’t just give a simple answer on how to do so. Sadly, the newer versions of Houdini really mess up when it comes to custom python packages. You can try to use it with your version but if Houdini fails to find the SciPy package then try it using an older version.
Here is an old tutorial that worked well with Houdini 16.5 and below. (be sure to read it carefully)
I have noticed that I could not check out a free license for 16.5 after installing it now. However, I managed to install the newest version, then check out the license, and then I was able to continue using Houdini 16.5 as usual (both versions can coexist on your machine). Perhaps try that before following the tutorial above if all else fails.
Here you find the previous version of Houdini:
https://www.sidefx.com/login/?next=/download/daily-builds/
You will know that SciPy works as soon as the import SciPy command does not fail.
]]>
Go into the download folder and get the Tutorial 03 folder!
Controlling Time
Time in Houdini is controlled using the time bar at the bottom of your screen. In its settings, you can specify how many frames represent one second. This will be very important for making videos. You then press “play” on the bar to smoothly increase the frame count.
Typical frames per seconds:
In your code (inside the Wrangle nodes) Houdini uses the VEX language. In it, you can access time through:
However, when you are typing into the parameter fields of some node, Houdini uses the language Hscript, in which time can be accessed as:
We recommend:
Solving this Life’s Misteries
The previous section only taught you how to read time in your code. What comes next will actually be much more important. The solver node is the go-to node if you want repeat operations on previous output.
The inside of a solver node can look like this:
The Prev_Fram node takes the output of the solver node and feeds it back into the solver.
The above screenshot of a solver node does the following: Given a banana with large random hue values to it, we average out the hue value at each point with its neighbors. Repeat at every frame. The result will look like this:
We can also run a random walk on the banana, darkening every triangle we meet on the path. We can use that for a rotting effect.
Large Render Tasks
We have learned that for single image rendering it is enough to right-click on the render view and “save frame” as a .png. For large render tasks, we will now learn how to use render nodes.
Your network view can also show you the outputs. Click on the address bar on top and switch to the out view like in the image below.
In this view, you can place mantra nodes. Mantra is the name of the renderer used by Houdini. Click on it to access many render options.
The most important settings for you are the following.
The output will be a sequence of images. The raw material to create videos.
You can then use these images in your favorite video editing program. Import, edit, export.
EXERCISE 1:
Recreate the following using @Time (choose any coloring):
EXERCISE 2:
Recreate this:hint: black regions grow. Use the previous code as well.
EXERCISE 3:
Render a video.
]]>
The Data Structure in Houdini
Everything in Houdini is made out of points, primitives and vertices. A quick overview:
Bonus: For each vertex there is also a half edge. Half edges show how the vertices are linked to each other and are very important for many tasks.
The Code Nodes
Houdini comes with its own programing language called VEX which is similar to java and c++. It is optimized to handle operations on all points, primitives, vertices in parallel and is easy to use. All code will be written inside special nodes called attribute wranglers.
Clicking on these nodes reveals the options with a special field where code can be written into the. Notice that each wrangler can be set to run over each point, each primitive, each vertex or a single time only on the global parameters of the attributes (Detail).
The Attributes of Houdini
Any relevant piece of information is stored as an attribute. Position, Normal vector, color, transparency, uv texture coordinate, index, arrays, matrices etc. Attributes are always initialized and accessed using
type@name
The relevant types are the following:
These attributes then live where they are used. For example, every point has v@P as its position vector. Once you start working with v@Cd, you make Houdini use that vector as an RGB color definition. Once you start working with v@N, Houdini uses that as its normal vector definition.
Notice that if a non-existant attribute is called, then Houdini initializes it with zero by default. Be aware of that when checking for typos.
Looking at Attributes
There is a fantastic tool to overview the attribute values. It is called the Geometry Spreadsheet. Open its tab and you will find 4 Icons on top for each of the following: points, vertices, primitives, detail. This will allow you to observe the attributes at every element and you will quickly be able to see errors here if something is wrong with your code.
If you are debugging your code and wondering what the intermediate values of a variable are, just throw it into the geometry spreadsheet by creating an attribute for a quick check.
Getting Started with the Exercises
Head over again to the download section and get the folder 02 Getting Even More Started with Houdini. Open up the .hip file located inside the folder.
EXERCISE 1
The demo file you downloaded is thoroughly documented. Go through the nodes, read the code with its comments, and visualize each step to see the node’s effect.
EXERCISE 2
Find a parametrization of a Möbius strip online and implement it! Use a grid node as the starting domain.
The Möbius strip is a bit spooky because it is not orientable. This can cause problems with some algorithms that depend on the normal vector.
EXERCISE 3
Be creative! Do something fun (or spooky)!
I will do my very best to make your entry as smooth as possible. It will be like sitting in a cockpit with the fear of crashing the plane by hitting the wrong button but rest assured that we will bit by bit learn to understand the software. It will be worth it and you will get a nice insight behind the scenes of Hollywood production on the way.
Get to the downloads folders and get the content from “01 Getting Started with Houdini” from the Houdini folder. Then open up the .hip file.
To get this starting screen.
EXERCISE 1:
The comments in the Network view will do their best to explain what is going on. Carefully study what they say and click on the nodes to take a look a the parameters.
After looking at all the nodes you should be able to see this:
We can also remove the faces (How would you do that quickly?). Then after making multiple translated copies and taking a different camera angle we can get this:
Rendering:
Click on the render view tab and click the render button to see results. You can toggle auto rerender after changes (on by default). For faster previews, you can lower the resolution. Only your final images need a high resolution.
If you are wondering how the rendering works, check out this video:
EXERCISE 2:
Use your newly gained knowledge to build a scene to render these images.
note: the second image is from the same scene as the first image but has an orthographic camera projection.
EXERCISE 3: (highly recommended for your sanity)
The default window arrangement in Houdini really sucks. Please take 10 minutes of your time to rearrange this to your liking.
Panes:
Each of these small windows is called a pane and each of these panes can be split vertically and horizontally to fit your needs. You can also grab the edges between panes and drag them to resize the window. Figure out a finite set of pane splittings and edge draggings to get a favorable setup (do not care yet about their content). Try to mimic the setup from the image above.
Tabs:
Now that you have the panes set in place we can fill them with life. Click on the + sign to open a new tab and click the x sign to close a tab. There are many, many tabs and we will list all the relevant tabs for your life as a mathematician.
We now write down a possible tab layout with little explanations.
Saving the Layout:
Finally, once you are happy with your layout, do not forget to save it! Click on the bar on top and “Save Current Desktop As…”. You can save multiple layouts like this and switch between them.
EXERCISE 4: (Take your time to explore!)
Place a skylight node and then play around with geometry nodes. Have a look at all the possible nodes you could get.
Please check out the daily builds site and download Houdini 16.5. You will have to create an account to download the necessary file.
https://www.sidefx.com/login/?next=/download/daily-builds/
After the installation please go to the download section of this site and download the “00 Try to Open This” folder and open the “Open_this_file.hip” file in there.
You will be asked about a license when first opening Houdini. Just accept the free apprentice license which is absolutely fine for our cause. The terms and conditions don’t involve losing your dignity.
When you open the file, you should see this geometry (don’t ask why)
If this worked then you are well prepared for the tutorials. Don’t be scared if Houdini seems a bit overwhelming with options. It will be like entering a cockpit without knowing how to operate it. Bit by bit we will explore all the possibilities.
Oh yeah! A mouse with a middle button is highly recommended for navigating this software!
]]>