Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finished wing #1

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 51 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,82 +1,89 @@
# [Project2: Toolbox Functions](https://github.com/CIS700-Procedural-Graphics/Project2-Toolbox-Functions)

Find the final product at: https://tabathah.github.io/Project2-Toolbox-Functions/

## Overview

The objective of this assignment is to procedurally model and animate a bird wing. Let's get creative!
I procedurally created an animated wing of a parrot.

Start by forking and then cloning [this repository](https://github.com/CIS700-Procedural-Graphics/Project2-Toolbox-Functions)
Here are some reference images I started out with:

## Modeling
![](./references/parrot01.jpg)

##### Reference images
![](./references/parrot02.jpg)

Search for three or more images of a bird wing (or any flying creature, really) in order to provide yourself reference material, as you're going to base your modeling and animation from these images. For the more artistic minds, feel free to sketch your own concept.
![](./references/parrot03.jpg)

##### Make wing curve
## Model

Begin with a 3D curve for your basic wing shape. Three.js provides classes to create many different types of curves, so you may use whatever type of curve you prefer.
I was given this basic model of a feather:

##### Distribute feathers
![](./progressShots/original-feather.PNG)

We have provided a simple feather model from which to begin. You are not required to use this model if you have others that you prefer. From this base, you must duplicate the feather to model a complete wing, and your wing should consist of at least thirty feathers. Distribute points along the curve you created previously; you will append the feather primitives to the curve at these points. Make sure that you modify the size, orientation, and color of your feathers depending on their location on the wing.
The first thing I did was create a curve that would outline the basic shape of the wing. Here is my curve displayed as a tube geometry so I could see and work on it:

Feel free to diversify your wings by using multiple base feather models.
![](./progressShots/curve.PNG)

## Animation
The next thing I did was evenly distribute feathers along the positions on the curve:

Add a wind force to your scene, and parameterize its direction and speed. You will use this wind force to animate the feathers of your wing by vibrating them slightly. Using Dat.GUI, allow the user to modify these wind parameters. Please note that we don't care about your feather motion being physically accurate, as long as it looks nice.
![](./progressShots/placed-feathers-on-curve.PNG)

Additionally, animate the control points of your wing curve to make the wing flap, and allow the user to control the speed of the wing flapping.
I then began scaling the feathers. To do this, I simply interpolated between the size I wanted the left most feather to have and the size I wanted the right most feather to have. To make this more realistic, I incorporated bias and gain to make the transition less linear. The bias on the scale favored the left feathers so that the left most feathers would be noticably larger than the rest. Also, the gain favored the middle of the feather distribution, so there would be many feathers of the same size in the middle. The result was the following:

## Interactivity
![](./progressShots/with-scale-distribution.PNG)

After doing this, I realized I wanted three layers of feathers on my wing to provide added realism. I made two more layers of feathers, displacing there depth by a bit to separate them from other layers, with limited positions on the wing curve and different ranges of scale from the first layer. This gave the following result:

![](./progressShots/layers-added.PNG)

The last thing to do in the modeling was to interpolate rotation of the feathers. The left most feathers had a sharp angle to the left in comparison to the rest and the right most feathers had a slight angle to the right. I provided similar bias and gain in this interpolation to the scaling one because again I wanted the left-most feathers to stand out from the rest and I wanted the middle ones to be pretty uniform. This addition provided the following result:

Using Dat.GUI and the examples provided in the reference code, allow the user to adjust the following controls:
![](./progressShots/rotation-added.PNG)

1. The curvature of the wing's basic shape
2. Feather distribution
3. Feather size
4. Feather color
5. Feather orientation
6. Flapping speed
7. Flapping motion
Finally, I added color to the feathers. I created my own shaders which took into account the index of each feather for interpolating and deciding on color based on layer, as well as the light position in the scene and an integer indicating the color pallete. First I started with a flat shader, that merely chose color based on the layer the feather was on.

## For the Overachievers
![](./progressShots/color-added.PNG)

Suggestions:
- Make a pretty iridescent or otherwise feather appropriate shader.
- Otherwise, going the extra mile for this assignment is really in the polish!
I then added lamertian shading and iridescence, as well as changing the colors I was interpolating in order to make the colors more realistic.

## Submission
![](./progressShots/improved-color-with-lambert-and-iridescence.PNG)

- Create a folder called `references` to include your reference images.
## Animation

I animated the wing so that it would flap at a certain speed and so that the feathers would be affected by a theoretical wind with a speed and a direction.

The flapping part of the animation was simply a rotation about the x-axis that is linearly interpolated to create the continuous motion.

- Update `README.md` to contain a solid description of your project
I applied the wind by adding a rotation to each feather that was based on the angle of the wind direction and a simple pseudo-noise value based on the feather's index. This noise value was limited based on the speed of the wind, where low speed caused the noise to be very limited and high speed caused the noise to be unlimited. The following is a shot of the wing while high wind speed is on and feathers are being displaced.

![](./progressShots/wind-displacement.PNG)

## Interactivity

- Publish your project to gh-pages. `npm run deploy`. It should now be visible at http://username.github.io/repo-name
I added several sliders on the GUI to change the parameters of the wing for this project. Some pictures have been added for particularly interesting changes that can occur.

- Create a [pull request](https://help.github.com/articles/creating-a-pull-request/) to this repository, and in the comment, include a link to your published project.
The curvature one affects the y positions of the original curve the wing is based on, so that increasing curvature creates a bendy wing and decreasing it make the wing more flat.

- Submit the link to your pull request on Canvas.
![](./progressShots/high-curvature.PNG)

## Getting Started
![](./progressShots/low-curvature.PNG)

1. [Install Node.js](https://nodejs.org/en/download/). Node.js is a JavaScript runtime. It basically allows you to run JavaScript when not in a browser. For our purposes, this is not necessary. The important part is that with it comes `npm`, the Node Package Manager. This allows us to easily declare and install external dependencies such as [three.js](https://threejs.org/), [dat.GUI](https://workshop.chromeexperiments.com/examples/gui/#1--Basic-Usage), and [glMatrix](http://glmatrix.net/). Some other packages we'll be using make it significantly easier to develop your code and create modules for better code reuse and clarity. These tools make it _signficantly_ easier to write code in multiple `.js` files without globally defining everything.
The feather distribution one affects the bias of the position interpolation, so that the more it varies from 0.5, the more the featehrs shift to one side or the other.

2. Fork and clone your repository.
Feather size affects the scale of the feathers in a fairly straightforawrd way.

3. In the root directory of your project, run `npm install`. This will download all of those dependencies.
![](./progressShots/high-feather-size.PNG)

4. Do either of the following (but I highly recommend the first one for reasons I will explain later).
![](./progressShots/low-feather-size.PNG)

a. Run `npm start` and then go to `localhost:7000` in your web browser
Feather orientation affects the angle of the feathers, again in a pretty strightforward way.

b. Run `npm run build` and then go open `index.html` in your web browser
If the color slider is less than 1.5, the color palette is the default red, yellow, blue, whereas if its over 1.5, the color palette is a purple, blue, and turquoise palette. Below is the second color palette.

You should hopefully see the framework code with a 3D cube at the center of the screen!
![](./progressShots/second-color-palette.PNG)

Wind speed affects the amount of noise that can be placed on feathers during the wind animation, so higher speed creates a more violent wind displacement, and lower speed creates soft vibrations.

## Developing Your Code
All of the JavaScript code is living inside the `src` directory. The main file that gets executed when you load the page as you may have guessed is `main.js`. Here, you can make any changes you want, import functions from other files, etc. The reason that I highly suggest you build your project with `npm start` is that doing so will start a process that watches for any changes you make to your code. If it detects anything, it'll automagically rebuild your project and then refresh your browser window for you. Wow. That's cool. If you do it the other way, you'll need to run `npm build` and then refresh your page every time you want to test something.
Flap Speed affects the period of time during which one up and down motion of the wind occurs.

## Publishing Your Code
We highly suggest that you put your code on GitHub. One of the reasons we chose to make this course using JavaScript is that the Web is highly accessible and making your awesome work public and visible can be a huge benefit when you're looking to score a job or internship. To aid you in this process, running `npm run deploy` will automatically build your project and push it to `gh-pages` where it will be visible at `username.github.io/repo-name`.
Finally, wind direction affects the angle on which the wind is blowing, where 0 is coming straight down onto the wing, 90 is blowing from the right side of the screen, and -90 is blowing from the left side of the screen.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"scripts": {
"start": "webpack-dev-server --hot --inline",
"build": "webpack",
"deploy": "rm -rf npm-debug.log && git checkout master && git commit -am 'update' && gh-pages-deploy"
"deploy": "gh-pages-deploy"
},
"gh-pages-deploy": {
"prep": [
Expand Down
Binary file added progressShots/color-added.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added progressShots/curve.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added progressShots/high-curvature.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added progressShots/high-feather-size.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added progressShots/layers-added.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added progressShots/low-curvature.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added progressShots/low-feather-size.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added progressShots/original-feather.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added progressShots/placed-feathers-on-curve.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added progressShots/rotation-added.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added progressShots/second-color-palette.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added progressShots/wind-displacement.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added progressShots/with-scale-distribution.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added references/parrot01.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added references/parrot02.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added references/parrot03.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
250 changes: 250 additions & 0 deletions src/distribution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
const THREE = require('three');

var curvature = 1.0; //controls y positions of feathers on curve, ranges from 0 to 4, higher curvature means more variation in y positions
var featherDistribution = 0.5; //controls bias of feather positions, ranges 0 to 1
var featherSize = 1.0; //controls max scale values of feathers, ranges from 0 to 2
var featherOrientation = 0.0; //controls an extra rotation about y, ranges from -1 to 1
var windSpeed = 0.5; //speed of wind, ranges from 0 to 1, affects noise of feather displacement from sporadic to light
var flapSpeed = 1.0; //speed of wind, ranges from 0 to 5, affects speed of flap cycle
var windDirection = 45.0; //angle of wind parallel to y direction, goes from 0 to 360 degrees where 0 is straight down
var time = 0.0; //in order to animate with wind
var color = 1.0; //indicates the color palette, either 1 or 2

export function incTime()
{
time++;
}

export function changeColor()
{
if(color == 1){ color = 2; }
else { color = 1; }
}

export function updateCurve(newVal)
{
curvature = newVal;
}

export function updateDistrib(newVal)
{
featherDistribution = newVal;
}

export function updateSize(newVal)
{
featherSize = newVal;
}

export function updateOrient(newVal)
{
featherOrientation = newVal;
}

export function updateSpeed(newVal)
{
windSpeed = newVal;
}

export function updateFlapSpeed(newVal)
{
flapSpeed = newVal;
}

export function updateDir(newVal)
{
windDirection = newVal;
}

export function getPos(mesh, settings)
{
var b = featherDistribution;
var g = 0.4;

if(settings.num < settings.total1)
{
var t = gain(g, bias(b, settings.num/settings.total1));
t = Math.round(t*100);
var pos = settings.curve.getPoints(100)[t];
mesh.position.set(pos.x, pos.y*curvature, pos.z);
}
else if(settings.num < settings.total2)
{
var t = gain(g, bias(b, (settings.num-settings.total1)/(settings.total2-settings.total1)));
t = Math.round(t*70);
var pos = settings.curve.getPoints(100)[100-t];
mesh.position.set(pos.x, pos.y*curvature, pos.z+0.05);
}
else
{
var t = gain(g, bias(b, (settings.num-settings.total2)/(settings.total3-settings.total2)));
t = Math.round(t*55);
var pos = settings.curve.getPoints(100)[100-t];
mesh.position.set(pos.x, pos.y*curvature, pos.z+0.1);
}
}

export function getRot(mesh, settings)
{
var t;
var zmax;
var zmin;
var ymax;
var ymin;

if(settings.num < settings.total1)
{
t = settings.num/settings.total1;
zmin = -3*Math.PI/7.0;
zmax = Math.PI/8.0;
ymin = -Math.PI/4.0;
ymax = 0;
}
else if(settings.num < settings.total2)
{
t = 1 - (settings.num-settings.total1)/(settings.total2-settings.total1);
zmin = -3*Math.PI/8.0;
zmax = Math.PI/16.0;
ymin = -Math.PI/8.0;
ymax = 0;
}
else
{
t = 1 - (settings.num-settings.total2)/(settings.total3-settings.total2);
zmin = -Math.PI/3.0;
zmax = 0;
ymin = -Math.PI/16.0;
ymax = 0;
}

var zb = 0.2;
var zg = 0.3;
var yb = 0.1;
var yg = 0.3;

var zt = gain(zg, bias(zb, t));
var yt = gain(yg, bias(yb, t));

var extraZ = zmin*(1-zt) + zmax*(zt);
var extraY = ymin*(1-yt) + ymax*(yt);

mesh.rotation.z = -Math.PI/2.0 + extraZ;
mesh.rotation.y = 3*Math.PI/4.0 + extraY + featherOrientation*Math.PI/2.0;
mesh.rotation.x = 0;

var origyRot = mesh.rotation.y;
var origzRot = mesh.rotation.z;
var noise = findNoise(settings.num, time);
var jitterz = Math.PI/4.0;
var jittery = windSpeed*noise*Math.PI*windDirection/180;
if((flapSpeed*time)%120 < 60)
{
var timet = ((flapSpeed*time)%120)/60;
mesh.rotation.y = origyRot + jittery*timet;
mesh.rotation.z = origzRot + jitterz*timet;
}
else
{
var timet = ((flapSpeed*time)%120-60)/60;
mesh.rotation.y = origyRot + jittery*(1-timet);
mesh.rotation.z = origzRot + jitterz*(1-timet);
}
}

export function getScale(mesh, settings)
{
var t;
var yhighVal;
var ylowVal;
var zhighVal;
var zlowVal;

if(settings.num < settings.total1)
{
t = 1 - (settings.num/settings.total1);
yhighVal = 1.5*featherSize;
ylowVal = 1;
zhighVal = 1.5*featherSize;
zlowVal = 1;
}
else if(settings.num < settings.total2)
{
t = (settings.num-settings.total1)/(settings.total2-settings.total1);
yhighVal = 1*featherSize;
ylowVal = 0.5;
zhighVal = 1*featherSize;
zlowVal = 0.5;
}
else
{
t = (settings.num-settings.total2)/(settings.total3-settings.total2);
yhighVal = 0.5*featherSize;
ylowVal = 0.2;
zhighVal = 0.5*featherSize;
zlowVal = 0.25;
}

var zbias = 0.8;
var zgain = 0.8;
var ybias = 0.8;
var ygain = 0.8;

var zt = gain(zgain, bias(zbias, t));
var yt = gain(ygain, bias(ybias, t));

var scaleZ = zlowVal*(1-zt) + zhighVal*(zt);
var scaleY = ylowVal*(1-yt) + yhighVal*(yt);

mesh.scale.z = scaleZ;
mesh.scale.x = scaleY;
}

function bias(b, t)
{
return Math.pow(t, Math.log(b) / Math.log(0.5));
}

function gain(g, t)
{
if(t < 0.5)
{
return bias(1-g, 2*t) / 2;
}
else
{
return 1 - bias(1-g, 2 - 2*t) / 2;
}
}

function findNoise(x, y)
{
var bigNum = Math.sin(x*12.9898 + y*78.233)*43758.5453;
if(bigNum < 0.0){ return -1*(bigNum - Math.floor(bigNum)); }
else { return -1*(bigNum - Math.floor(bigNum)); }
}

var funcs = {
updateCurve: updateCurve,
updateDistrib: updateDistrib,
updateSize: updateSize,
updateOrient: updateOrient,
updateSpeed: updateSpeed,
updateFlapSpeed: updateFlapSpeed,
updateDir: updateDir,
incTime: incTime,
changeColor: changeColor,
color: color,
getPos: getPos,
getRot: getRot,
getScale: getScale,
curvature: curvature,
featherDistribution: featherDistribution,
featherSize: featherSize,
featherOrientation: featherOrientation,
windSpeed: windSpeed,
flapSpeed: flapSpeed,
windDirection: windDirection
}

export default funcs;

Loading