Mostrando entradas con la etiqueta 3d. Mostrar todas las entradas
Mostrando entradas con la etiqueta 3d. Mostrar todas las entradas

jueves, 2 de marzo de 2017

Simple VFX animation Rig

Some day back in my old days, in the beginning of my new 3d life i was exposed to help riggers and VFX department on how to rig physical properties like vector forces etc with control curves. And i was surprised what an easy task this was and how much of a problem this meant for some people. Although i understand it could have been ages for them since they left school (yes, this is not even university level maths!) you probably didnt get a scientific path in high school. Anyways, im far from being a math nerd myself, and if you are an artist not very familiar with vectors and matrices you will probably discover how surprisingly easy this is.

What we want is basically to control the direction of a vector, for example nucleus' gravity by means of the rotation controlled by a handle/curve control.

So basically this corresponds to rotating a vector by a rotation matrix!

vr = [M].vo

being "vo" the original vector direction and "vr" the rotated vector.


Basically you perform this operation with the vector product node and hook its output in this case right into the axis unit vector of a vortex field.

In the outliner you have this marvellous, beautiful arrow that serves as the possible curve control of a hipothetically more complex part of a rig, which indicates the initial direction of the vector.

And the results are here in a demo video using nParticles and field forces!!
God! that was quick!! i think this is the shortest blog entry ive done so far!!!! and in the middle of a working week!!!!

Hope you enjoyed!

 

sábado, 11 de junio de 2016

Simple Procedural Texture Generator and Visualizer

INTRODUCTION
I've been thinking about coding something related with perlin noise, just something that could be used as a justification. Normally i would have coded it in C++ with Qt but since ive been digging into the guts of python and PySide/PyQt for the last year, together with the fact that python GUI with PyQt is not that hard like in C++ (something it really does not have much interest once you get how the layouts, widgets, etc, work).

My only concern was performance because i wanted to do all the calculation and send the vertices data to the gpu each time you changed any of the parameter values governing the shape of the noise, the size, the visualization,..etc. I was willing to accept a little lag.

I wont explain deeply how Perlin noise works. For this you can have a look at the wikipedia or in a book i consider very useful: Texturing & Modeling: A Procedural Approach 

My approach basically consists of a function that generates values for a given octave. Then the final result will be a superposition of those octaves depending on the number specified.


INTERPOLATION

One of the options i wanted to explore was to obtain a more organic feel to the noise. With linear interpolation you can get some artifacts horizontally and vertically which really doesnt look well.

Here are the three interpolation methods:

1. linear
2. cosine
3. cubic 

All of the form "interpolate(x0,x1,t)"



 def Linear(a,b,t):  
   return a * (1 - t) + b * t  
 def Cosine(a,b,t):  
   t2 = (1 - math.cos(t * math.pi)) / 2.0  
   return (a * (1 - t2) + b * t2);  
 def Spline(x0,x1,t):  
   a = x0 - x1  
   b = -1.5 * x0 + 1.5 * x1  
   c = -0.5 * x0 + 0.5 * x1  
   d = x0  
   t2= t * t  
   return a * t2 * t + b * t2 + c * t + d  

The spline or cubic interpolation was used in a simplified manner. Normally the cubic interpolation formula uses information of 4 points: the two in the middle plus the rightmost and leftmost of them. For coding purposes, just to simplify, we assumed p0=p1 and p2=p3, hence the above code.

I will quote this page for the cubic interpolation just in case it disappears.

If the values of a function f(x) and its derivative are known at x=0 and x=1, then the function can be interpolated on the interval [0,1] using a third degree polynomial. This is called cubic interpolation. The formula of this polynomial can be easily derived.
A third degree polynomial and its derivative:
f(x) = ax^3 + bx^2 + cx + d
f'(x) = 3ax^2 + 2bx + c
plot

For the green curve:
a = -\tfrac{1}{2}\cdot2 + \tfrac{3}{2}\cdot4 - \tfrac{3}{2}\cdot2 + \tfrac{1}{2}\cdot3 = \tfrac{7}{2}
b = 2 - \tfrac{5}{2}\cdot4 + 2\cdot2 - \tfrac{1}{2}\cdot3 = -\tfrac{11}{2}
c = -\tfrac{1}{2}\cdot2 + \tfrac{1}{2}\cdot2 = 0
d = 4
f(x) = \tfrac{7}{2}(x-2)^3 - \tfrac{11}{2}(x-2)^2 + 4
The values of the polynomial and its derivative at x=0 and x=1:
f(0) = d
f(1) = a + b + c + d
f'(0) = c
f'(1) = 3a + 2b + c
The four equations above can be rewritten to this:
a = 2f(0) - 2f(1) + f'(0) + f'(1)
b = -3f(0) + 3f(1) - 2f'(0) - f'(1)
c = f'(0)
d = f(0)
And there we have our cubic interpolation formula.
Interpolation is often used to interpolate between a list of values. In that case we don't know the derivative of the function. We could simply use derivative 0 at every point, but we obtain smoother curves when we use the slope of a line between the previous and the next point as the derivative at a point. In that case the resulting polynomial is called a Catmull-Rom spline. Suppose you have the values p0, p1, p2 and p3 at respectively x=-1, x=0, x=1, and x=2. Then we can assign the values of f(0), f(1), f'(0) and f'(1) using the formulas below to interpolate between p1 and p2.
f(0) = p_1
f(1) = p_2
f'(0) = \dfrac{p_2 - p_0}{2}
f'(1) = \dfrac{p_3 - p_1}{2}
Combining the last four formulas and the preceding four, we get:

a = -\tfrac{1}{2}p_0 + \tfrac{3}{2}p_1 - \tfrac{3}{2}p_2 + \tfrac{1}{2}p_3

b = p_0 - \tfrac{5}{2}p_1 + 2p_2 - \tfrac{1}{2}p_3

c = -\tfrac{1}{2}p_0 + \tfrac{1}{2}p_2
d = p_1


OPENGL and PYTHON

One of the most time consuming aspects of dealing with PyOpenGL is that OpenGL is a C library and hence, if you code in C++ you share the same basic data types specially things like (void *) pointers, C arrays and the casting operation between types... But Python has its own data types!

1) I'll give you an example: Vertex Buffer Objects need to be passed a C array of GL_FLOAT values in order to specify vertex data. I was managing vertex data but in python lists. I discovered i had two options here: whether i used another dependency library such as Numpy with their immediate conversion between lists and arrays...or i could just use the "array" type. I finally chose this last option.


 from array import array  
 vertex_array = array('f', vertex_list)  
 index_array = array('i', index_list)  

where 'f' stands for float and 'i' for integer.

2) Another big problem i faced is how on earth i could update the vertex data sent to the buffer instead of deleting/creating/sending everything again as if i restarted the app.

 glBindBuffer(GL_ARRAY_BUFFER, self.vboId)  
 c_void_ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE)  
 c_float_array_ptr = cast(c_void_ptr, POINTER(c_float))  
 # change vertex data    
 for i in range(len(vertex_list)):  
    c_float_array_ptr[i] = vertex_list[i]  
 glUnmapBuffer(GL_ARRAY_BUFFER)  


I discovered the buffer in video memory could be mapped to a chunk in RAM so that when changing one, it immediately applies to GPU. This is using "glMapBuffer/glUnmapBuffer".

But this function returns a "C void pointer" which in python terms is just an integer refering to some memory address.

We need a way to cast this void pointer to a float pointer (float array). That is the raison d'être of the next line. Needless to say i needed to import the ctypes module.

Then we can access finally the c_float_array_ptr as an iterable assigning float values from the python vertex_list!

Here is a video snippet of how the app works.











domingo, 13 de diciembre de 2015

Driving A Vector's Direction By Euler With Maya Node Editor

Introduction

Past thursday one my fellow mates at the office in charge of refining the shots and animating dynamics asked me to help him with a problem he was facing: he needed to map the euler rotation angles from a CTRL transform to the nucleus' gravity direction vector. In short: control with angles the direction of a vector.

He was trying with the "angleBetween" node looking for some way to solve his problem. I have to say that at first he wouldnt explain  to me correctly what he needed but that's because he was striving for it himself.

The next day, this is last friday, while at the bus in the morning to the office i remembered the conversation he had a couple of days earlier asking for this to my fellow riggers. They seemed to fill the expectations of my comrade in need with their answers. It wasnt clear for me but i was too busy with something else and didnt want to intercede.... Until now, where this was something that was puzzling him for quite a few days now.

So i started to suppose what was the problem and started to think how i would solve it with nodes.

Controlling Vectors through Rotation

In fact, once aware of the problem the solution is just applying some simple math. We have a curve/CTRL's transform that represents rotations with Euler angles.

Well, we need to apply the rotation of the control to the vector. In terms of maths, we need to get the matrix representation of the rotation. Maya's node editor has a ComposeMatrix node that generates a matrix with information on translation, rotation, scale, shear depending on what the input is.

Next step is to multiply this matrix by the vector we want to transform. Again, Maya has VectorProduct node for this that can do different operations such as Dot, Cross, VectorMatrix and PointMatrix.

v' = M * v

All that is left is just attach the output of the operation to the nucleus's gravity vector....and voilà!!!

Voilà????? Hold on a second, there is a little problem.

One little problem

The way i approached for the first time the node network i was convinced it had to work from the beginning since these are simple maths. But i was committing one error that stems from the fact that i was a little unaware of how nodes work.

I was using the current gravity vector direction to feed the operation and then the result attached back to the nucleus as input..... And i tried to test with known trigonometric values and i was getting a strange behaviour: the numbers just didn't correspond to a valid result.

My network suffered from a "cycle" that never stopped: i'm feeding the input of the attribute with the output of the same attribute after doing some calculation....

But i found one workaround for this.



Workaround

After trying inefficiently to break that cycle using an intermediate transform where i would store the result and pass it on again to nucleus i decided to write a post in a Maya forum convinced of the fact that this can be made without using a transform or any other supplemental entities.

But i kept thinking after the post, enbraved by the fact that i wasnt getting a quick question and i was stuck. Well, if all i need is the starting value of the nucleus how about putting that same initial value into a transform and use it instead of the nucleus to feed the calculation? That way  i can attach directly the result to the nucleus gravity vector. All i have to make sure is that both vector values: nucleus initial value and transform's value are the same!! It doesnt matter really which channels of the transfrom i use provided they represent the three vector componentes X, Y,Z. I decided to use the translate.






Additional Comments

It's worth noting that since the initial value of the vector is such that the module is 1.0, the results after the matrix multiplication must be also of module 1.0. Since a rotation matrix doesnt change the module of a vector. 

This was helpful to rapidly notice wrong numbers and therefore there was a problem. Also, testing with cosine and sine values of most known angles such as 0,30,45,60,90 degrees.... One is used to see numbers like 0.707 or 0.5 and 0.866 etc....

Also, note that because we are using the rotation the translation of the control/curve doesnt matter.

jueves, 17 de septiembre de 2015

Nested References in Maya

I've found myself in the need at work to be able to get all the references a shot had. What you can get with file(q = True, r = True) are only the top level references leaving behind the nested ones.





I've read somewhere that is a good practice trying to avoid such levels of depth, but in this case we were in a closet.

So after a while searching the web and the official Maya python documentation i hit with referenceQuery and the "children" flag.

Finally i developed this simple method that fills a python list with reference filenames:


    def add_nested_references(self,parent_ref_list):
       
        for parent_ref in parent_ref_list:
            child_list = mc.referenceQuery(parent_ref, f = True, ch = True)
            if child_list != None:
                   self.add_nested_references(child_list)
                   parent_ref_list.extend(child_list)