# Difference between revisions of "JoyOfVex9"

MattEstela (talk | contribs) |
MattEstela (talk | contribs) |
||

(22 intermediate revisions by the same user not shown) | |||

Line 1: | Line 1: | ||

− | '' | + | ''Dot and cross product, fake lighting, combing normals to a surface'' |

=== Day 9 === | === Day 9 === | ||

==== Dot product ==== | ==== Dot product ==== | ||

− | Switch to a more complex model now, like tommy, | + | Switch to a more complex model now, like tommy, make sure his textures are disabled so we're not distracted by that. |

+ | <source lang="javascript" > | ||

@Cd = @N.y; | @Cd = @N.y; | ||

+ | </source> | ||

That looks like he's lit from above. If you use @N.x or @N.z, he looks lit from the right or front. Can negate to make him look like he's lit from underneath/left/behind. | That looks like he's lit from above. If you use @N.x or @N.z, he looks lit from the right or front. Can negate to make him look like he's lit from underneath/left/behind. | ||

+ | <source lang="javascript" > | ||

@Cd = -@N.y; | @Cd = -@N.y; | ||

+ | </source> | ||

− | So thats fine, but what if we want him lit from an arbitrary direction? This is where a '''dot''' product comes in handy. It compares the directions of vectors, if they are pointing the same way (ie, parallel), it returns | + | So thats fine, but what if we want him lit from an arbitrary direction? This is where a '''dot''' product comes in handy. It compares the directions of vectors, if they are pointing the same way (ie, parallel), it returns 1. If they're perpendicular, 0, if facing in exact opposite directions, -1, and angles in between those extreme positions will smoothly vary between 1, 0, -1. |

Eg, compare the normals to a vector pointing along the y axis {0,1,0}: | Eg, compare the normals to a vector pointing along the y axis {0,1,0}: | ||

+ | <source lang="javascript" > | ||

@Cd = dot(@N, {0,1,0}); | @Cd = dot(@N, {0,1,0}); | ||

+ | </source> | ||

Replace that hard coded vector with a chv slider, now we can light from any angle: | Replace that hard coded vector with a chv slider, now we can light from any angle: | ||

+ | <source lang="javascript" > | ||

@Cd = dot(@N, chv('angle')); | @Cd = dot(@N, chv('angle')); | ||

+ | </source> | ||

Adjusting angles with vectors can be a little tricky if you're not used to it, think of it as 'how much x, how much y, how much z', so for a light to come from the left, slightly up, and to the front, would be a value like {1, 0.1, 1}. | Adjusting angles with vectors can be a little tricky if you're not used to it, think of it as 'how much x, how much y, how much z', so for a light to come from the left, slightly up, and to the front, would be a value like {1, 0.1, 1}. | ||

Line 26: | Line 34: | ||

An alternative way to think about this is if you had a point in space. You move that where you want, draw a line from the origin to the point, thats your light vector. You can do exactly this; make a single point with an add sop, feed it to the second input, retrieve its @P, and treat it as a vector to do the dot product test: | An alternative way to think about this is if you had a point in space. You move that where you want, draw a line from the origin to the point, thats your light vector. You can do exactly this; make a single point with an add sop, feed it to the second input, retrieve its @P, and treat it as a vector to do the dot product test: | ||

+ | <source lang="javascript" > | ||

vector pos = point(1,'P',0); | vector pos = point(1,'P',0); | ||

@Cd = dot(@N, pos); | @Cd = dot(@N, pos); | ||

+ | </source> | ||

− | The point function here goes to input 1, and retrieves the @P attribute of point 0. | + | The point function here goes to input 1, and retrieves the @P attribute of point 0. (Remember that the point function doesn't need the @ prefix). |

− | This works, but the light gets brighter as the point gets further away. As I mentioned a while back, a lot of functions expect the input vectors to be normalised, this is one of those cases. | + | This works, but the light gets brighter as the point gets further away. As I mentioned a while back, a lot of functions expect the input vectors to be normalised, this is one of those cases. To do that you just use the '''normalize''' function. |

+ | <source lang="javascript" > | ||

vector pos = point(1,'P',0); | vector pos = point(1,'P',0); | ||

pos = normalize(pos); | pos = normalize(pos); | ||

@Cd = dot(@N, pos); | @Cd = dot(@N, pos); | ||

+ | </source> | ||

+ | |||

+ | [[File:joyofvex9_dot_lighting.gif]] | ||

==== Cross product ==== | ==== Cross product ==== | ||

Line 41: | Line 55: | ||

Switch to a sphere now. Turn on 'view point normals' in the viewport. You see a ball with spikes pointing away from it. Check what happens here: | Switch to a sphere now. Turn on 'view point normals' in the viewport. You see a ball with spikes pointing away from it. Check what happens here: | ||

+ | <source lang="javascript" > | ||

@N = cross(@N, {0,1,0}); | @N = cross(@N, {0,1,0}); | ||

+ | </source> | ||

It can be a little hard to see against the mesh, append an add sop, turn on 'delete geometry but keep points', to make it easier to see. The normals are now rotating around the surface of the sphere. | It can be a little hard to see against the mesh, append an add sop, turn on 'delete geometry but keep points', to make it easier to see. The normals are now rotating around the surface of the sphere. | ||

− | The cross product will take 2 vectors, and generate a 3rd perpendicular vector. Easiest to think about with normals at the equator, and doing a vector vulcan hand shape | + | [[File:joyofvex9_cross_basic.gif]] |

+ | |||

+ | The cross product will take 2 vectors, and generate a 3rd perpendicular vector. Easiest to think about with normals at the equator, and doing a vector vulcan hand shape: | ||

+ | |||

+ | https://study.com/cimages/videopreview/5zsq17tjsc.jpg | ||

+ | |||

+ | Point your thumb up along the y axis, middle finger along a normal at the equator, your index finger now points along the equator. Turn your hand to represent normals at various points around the equator, you can see that it will swirl the normals (or 'combs' the normals as this operation is sometimes called) flat to the surface. | ||

Try giving it different values to cross against, see what happens. | Try giving it different values to cross against, see what happens. | ||

Line 51: | Line 73: | ||

What can be interesting is to cross the normal with an axis, save it in a temp vector, and then cross the normal with that temp vector: | What can be interesting is to cross the normal with an axis, save it in a temp vector, and then cross the normal with that temp vector: | ||

+ | <source lang="javascript" > | ||

vector tmp = cross(@N, {0,1,0}); | vector tmp = cross(@N, {0,1,0}); | ||

@N = cross(@N, tmp); | @N = cross(@N, tmp); | ||

+ | </source> | ||

+ | |||

+ | [[File:joyofvex9_cross_double.gif]] | ||

This 'double cross' operation as its usually called really combs the normals along the surface. A lot of grooming tools will start with this kind of operation to force hairs to flow along the surface. You can keep doing this operation, and it will rotate the vectors 90 degrees: | This 'double cross' operation as its usually called really combs the normals along the surface. A lot of grooming tools will start with this kind of operation to force hairs to flow along the surface. You can keep doing this operation, and it will rotate the vectors 90 degrees: | ||

+ | <source lang="javascript" > | ||

vector cross1 = cross(@N, {0,1,0}); | vector cross1 = cross(@N, {0,1,0}); | ||

cross1 = cross(@N, cross1); | cross1 = cross(@N, cross1); | ||

@N = cross(@N, cross1); | @N = cross(@N, cross1); | ||

+ | </source> | ||

vs | vs | ||

+ | <source lang="javascript" > | ||

vector cross1 = cross(@N, {0,1,0}); | vector cross1 = cross(@N, {0,1,0}); | ||

cross1 = cross(@N, cross1); | cross1 = cross(@N, cross1); | ||

cross1 = cross(@N, cross1); | cross1 = cross(@N, cross1); | ||

@N = cross(@N, cross1); | @N = cross(@N, cross1); | ||

+ | </source> | ||

vs | vs | ||

+ | <source lang="javascript" > | ||

vector cross1 = cross(@N, {0,1,0}); | vector cross1 = cross(@N, {0,1,0}); | ||

cross1 = cross(@N, cross1); | cross1 = cross(@N, cross1); | ||

Line 74: | Line 105: | ||

cross1 = cross(@N, cross1); | cross1 = cross(@N, cross1); | ||

@N = cross(@N, cross1); | @N = cross(@N, cross1); | ||

+ | </source> | ||

vs | vs | ||

+ | <source lang="javascript" > | ||

vector cross1 = cross(@N, {0,1,0}); | vector cross1 = cross(@N, {0,1,0}); | ||

cross1 = cross(@N, cross1); | cross1 = cross(@N, cross1); | ||

Line 83: | Line 116: | ||

cross1 = cross(@N, cross1); | cross1 = cross(@N, cross1); | ||

@N = cross(@N, cross1); | @N = cross(@N, cross1); | ||

+ | </source> | ||

will get you back to where you started. | will get you back to where you started. | ||

− | Exercises | + | === Vector maths === |

+ | |||

+ | This is a good time to have a quick refresher on some vector maths basics. It's way easier than it seems (especially if you half remember stuff from high school or first year uni), with Houdini you get direct visual feedback which is great, and direct useful applications. Once you know how to add, subtract, multiply, dot and cross vectors, that's pretty much all you need to know for 90% of your Houdini needs! | ||

+ | |||

+ | ==== Vector addition ==== | ||

+ | |||

+ | The maths textbook definition of adding vectors is to lie the vectors tip-to-tail, then draw a new vector from the start of the first to the end of the last. This is much easier to understand visually with a wrangle. I define 2 vector parameters, and set @N as the addition of both. Adding {1,0,0} (ie a vector pointing along x) and {0,1,0} (a vector pointing along y) will result in {1,1,0} (a vector pointing 45 degrees up and along): | ||

+ | |||

+ | <source lang="javascript" > | ||

+ | vector a = chv('a'); | ||

+ | vector b = chv('b'); | ||

+ | @N = a+b; | ||

+ | </source> | ||

+ | |||

+ | [[File:jov9_addvector_simple.gif]] | ||

+ | |||

+ | Here's a similar thing, but this time taking the normals from a sphere and adding a vector to it; you can see it bends the normals towards whatever new vector I specify. Handy for steering and adjusting initial velocities for particles, for example: | ||

+ | |||

+ | <source lang="javascript" > | ||

+ | vector a = chv('a'); | ||

+ | @N += a; | ||

+ | </source> | ||

+ | |||

+ | [[File:jov9_addvector_sphere.gif]] | ||

+ | |||

+ | ==== Vector subtraction ==== | ||

+ | |||

+ | The textbook definition of vector subtraction is.... something? Frankly I don't remember. In Houdini, I know it as a handy way to create a vector from 2 points. Store @P of the first point as 'a', @P of the second point as 'b', and set @N of the first point as 'a-b'. Now its normal aims at the second point: | ||

+ | |||

+ | <source lang="javascript" > | ||

+ | vector a = point(0,'P',0); | ||

+ | vector b = point(1,'P',0); | ||

+ | |||

+ | @N = b-a; | ||

+ | </source> | ||

+ | |||

+ | [[File:jov9_subtract_vectors.gif]] | ||

+ | |||

+ | Notice that I stated its 'a-b' in the intro paragraph, but the vex code is 'b-a'? That's because I can never remember the order; I'll try it, find my vectors are pointing in the opposite direction to what I expect, swear, and reverse the terms. You'll do the same. :) | ||

+ | |||

+ | Extending this to a sphere again, we can make every normal on the sphere aim at a point: | ||

+ | |||

+ | <source lang="javascript" > | ||

+ | vector a = @P; | ||

+ | vector b = point(1,'P',0); | ||

+ | |||

+ | @N = b-a; | ||

+ | </source> | ||

+ | |||

+ | [[File:jov9_subtract_vectors_sphere.gif]] | ||

+ | |||

+ | If you reverse the terms, this can be a way to set the initial velocity for an explosion for example, where the point defines the center of the explosion: | ||

+ | |||

+ | <source lang="javascript" > | ||

+ | vector origin = point(1,'P',0); | ||

+ | @v = @P-origin; | ||

+ | </source> | ||

+ | |||

+ | [[File:jov9_rbd_example.gif]] | ||

+ | |||

+ | Download hip: [[:File:jov9_rbd_v_origin.hip]] | ||

+ | |||

+ | ==== Vector multiplication ==== | ||

+ | |||

+ | Multiplying a vector by a single number just multiplies each number inside the vector by the single number. This has the effect of scaling the vector, making it longer or shorter, but maintaining it's direction. Multiplying by 0 cancels out the vector completely. | ||

+ | |||

+ | <source lang="javascript" > | ||

+ | @N *= ch('scale'); | ||

+ | </source> | ||

+ | |||

+ | [[File:jov9_mult_sphere.gif]] | ||

+ | |||

+ | Multiplying by a negative number will reverse the direction of a vector: | ||

+ | |||

+ | [[File:jov9_mult_neg1.gif]] | ||

+ | |||

+ | Multiplying 2 vectors together will multiply the x components together, the y, the z. In practice, its a way to scale or even cancel out an axis of vectors. Eg, I have a sphere with normals, if I multiply it by {1,0,1}, I cancel out all the vertical parts of the vectors. Increasing the x component so that its {2,0,1} will mean the vectors will be twice as long in that axis: | ||

+ | |||

+ | <source lang="javascript" > | ||

+ | @N *= chv('scalevec'); | ||

+ | </source> | ||

+ | |||

+ | [[File:jov9_mult_sphere2.gif]] | ||

+ | |||

+ | === Exercises === | ||

# Cross against @N and noise, what happens? | # Cross against @N and noise, what happens? | ||

# double cross @N and noise? | # double cross @N and noise? | ||

# Against @P? | # Against @P? | ||

− | # Can you fix the colours going negative in the shadowed areas of the dot product examples? | + | # Can you fix the colours going negative in the shadowed areas of the dot product examples? (Hint: You'll need to clamp colours to stay within a valid range) |

+ | ---- | ||

+ | |||

+ | prev: [[JoyOfVex8]] this: [[JoyOfVex9]] next: [[JoyOfVex10]] <br> | ||

+ | main menu: [[JoyOfVex]] |

## Latest revision as of 22:27, 28 July 2020

*Dot and cross product, fake lighting, combing normals to a surface*

### Day 9

#### Dot product

Switch to a more complex model now, like tommy, make sure his textures are disabled so we're not distracted by that.

```
@Cd = @N.y;
```

That looks like he's lit from above. If you use @N.x or @N.z, he looks lit from the right or front. Can negate to make him look like he's lit from underneath/left/behind.

```
@Cd = -@N.y;
```

So thats fine, but what if we want him lit from an arbitrary direction? This is where a **dot** product comes in handy. It compares the directions of vectors, if they are pointing the same way (ie, parallel), it returns 1. If they're perpendicular, 0, if facing in exact opposite directions, -1, and angles in between those extreme positions will smoothly vary between 1, 0, -1.

Eg, compare the normals to a vector pointing along the y axis {0,1,0}:

```
@Cd = dot(@N, {0,1,0});
```

Replace that hard coded vector with a chv slider, now we can light from any angle:

```
@Cd = dot(@N, chv('angle'));
```

Adjusting angles with vectors can be a little tricky if you're not used to it, think of it as 'how much x, how much y, how much z', so for a light to come from the left, slightly up, and to the front, would be a value like {1, 0.1, 1}.

An alternative way to think about this is if you had a point in space. You move that where you want, draw a line from the origin to the point, thats your light vector. You can do exactly this; make a single point with an add sop, feed it to the second input, retrieve its @P, and treat it as a vector to do the dot product test:

```
vector pos = point(1,'P',0);
@Cd = dot(@N, pos);
```

The point function here goes to input 1, and retrieves the @P attribute of point 0. (Remember that the point function doesn't need the @ prefix).

This works, but the light gets brighter as the point gets further away. As I mentioned a while back, a lot of functions expect the input vectors to be normalised, this is one of those cases. To do that you just use the **normalize** function.

```
vector pos = point(1,'P',0);
pos = normalize(pos);
@Cd = dot(@N, pos);
```

#### Cross product

Switch to a sphere now. Turn on 'view point normals' in the viewport. You see a ball with spikes pointing away from it. Check what happens here:

```
@N = cross(@N, {0,1,0});
```

It can be a little hard to see against the mesh, append an add sop, turn on 'delete geometry but keep points', to make it easier to see. The normals are now rotating around the surface of the sphere.

The cross product will take 2 vectors, and generate a 3rd perpendicular vector. Easiest to think about with normals at the equator, and doing a vector vulcan hand shape:

Point your thumb up along the y axis, middle finger along a normal at the equator, your index finger now points along the equator. Turn your hand to represent normals at various points around the equator, you can see that it will swirl the normals (or 'combs' the normals as this operation is sometimes called) flat to the surface.

Try giving it different values to cross against, see what happens.

What can be interesting is to cross the normal with an axis, save it in a temp vector, and then cross the normal with that temp vector:

```
vector tmp = cross(@N, {0,1,0});
@N = cross(@N, tmp);
```

This 'double cross' operation as its usually called really combs the normals along the surface. A lot of grooming tools will start with this kind of operation to force hairs to flow along the surface. You can keep doing this operation, and it will rotate the vectors 90 degrees:

```
vector cross1 = cross(@N, {0,1,0});
cross1 = cross(@N, cross1);
@N = cross(@N, cross1);
```

vs

```
vector cross1 = cross(@N, {0,1,0});
cross1 = cross(@N, cross1);
cross1 = cross(@N, cross1);
@N = cross(@N, cross1);
```

vs

```
vector cross1 = cross(@N, {0,1,0});
cross1 = cross(@N, cross1);
cross1 = cross(@N, cross1);
cross1 = cross(@N, cross1);
@N = cross(@N, cross1);
```

vs

```
vector cross1 = cross(@N, {0,1,0});
cross1 = cross(@N, cross1);
cross1 = cross(@N, cross1);
cross1 = cross(@N, cross1);
cross1 = cross(@N, cross1);
@N = cross(@N, cross1);
```

will get you back to where you started.

### Vector maths

This is a good time to have a quick refresher on some vector maths basics. It's way easier than it seems (especially if you half remember stuff from high school or first year uni), with Houdini you get direct visual feedback which is great, and direct useful applications. Once you know how to add, subtract, multiply, dot and cross vectors, that's pretty much all you need to know for 90% of your Houdini needs!

#### Vector addition

The maths textbook definition of adding vectors is to lie the vectors tip-to-tail, then draw a new vector from the start of the first to the end of the last. This is much easier to understand visually with a wrangle. I define 2 vector parameters, and set @N as the addition of both. Adding {1,0,0} (ie a vector pointing along x) and {0,1,0} (a vector pointing along y) will result in {1,1,0} (a vector pointing 45 degrees up and along):

```
vector a = chv('a');
vector b = chv('b');
@N = a+b;
```

Here's a similar thing, but this time taking the normals from a sphere and adding a vector to it; you can see it bends the normals towards whatever new vector I specify. Handy for steering and adjusting initial velocities for particles, for example:

```
vector a = chv('a');
@N += a;
```

#### Vector subtraction

The textbook definition of vector subtraction is.... something? Frankly I don't remember. In Houdini, I know it as a handy way to create a vector from 2 points. Store @P of the first point as 'a', @P of the second point as 'b', and set @N of the first point as 'a-b'. Now its normal aims at the second point:

```
vector a = point(0,'P',0);
vector b = point(1,'P',0);
@N = b-a;
```

Notice that I stated its 'a-b' in the intro paragraph, but the vex code is 'b-a'? That's because I can never remember the order; I'll try it, find my vectors are pointing in the opposite direction to what I expect, swear, and reverse the terms. You'll do the same. :)

Extending this to a sphere again, we can make every normal on the sphere aim at a point:

```
vector a = @P;
vector b = point(1,'P',0);
@N = b-a;
```

If you reverse the terms, this can be a way to set the initial velocity for an explosion for example, where the point defines the center of the explosion:

```
vector origin = point(1,'P',0);
@v = @P-origin;
```

Download hip: File:jov9_rbd_v_origin.hip

#### Vector multiplication

Multiplying a vector by a single number just multiplies each number inside the vector by the single number. This has the effect of scaling the vector, making it longer or shorter, but maintaining it's direction. Multiplying by 0 cancels out the vector completely.

```
@N *= ch('scale');
```

Multiplying by a negative number will reverse the direction of a vector:

Multiplying 2 vectors together will multiply the x components together, the y, the z. In practice, its a way to scale or even cancel out an axis of vectors. Eg, I have a sphere with normals, if I multiply it by {1,0,1}, I cancel out all the vertical parts of the vectors. Increasing the x component so that its {2,0,1} will mean the vectors will be twice as long in that axis:

```
@N *= chv('scalevec');
```

### Exercises

- Cross against @N and noise, what happens?
- double cross @N and noise?
- Against @P?
- Can you fix the colours going negative in the shadowed areas of the dot product examples? (Hint: You'll need to clamp colours to stay within a valid range)

prev: JoyOfVex8 this: JoyOfVex9 next: JoyOfVex10

main menu: JoyOfVex