//Jinhee Lee //Section C //jinheel1@andrew.cmu.edu //Final-Project //dimensions for keys var keyY = 100; var whiteWidth = 40; var whiteHeight = 250; var whiteOffset = 120; var blackWidth = 20; var blackHeight = 160; var blackOffset = 30; var whiteKeyArray = []; var blackKeyArray = []; //password specification and intializations var password = "determination"; var passwordArray = []; var numPresses = 0; var display = ""; var messageSans = "all you gotta do is stay determined."; var messagePapyrus1 = "JUST DO WHAT"; var messagePapyrus2 = "I WOULD DO!"; var messagePapyrus3 = "BELIEVE IN YOU!"; var brothersLink = "http://i.imgur.com/n33OgDM.png"; var brothers; var blueSoulLink = "http://i.imgur.com/SVAUjD8.jpg"; var blueSoul; //for the easter egg var easterEgg = "legs"; var easterEggArray = []; var numLegs = 0; var displayLegs = ""; var messageMettaton1 = "That's right, legs was the correct answer!"; var messageMettaton2 = "Lights!"; var messageMettaton3 = "Camera!"; var messageMettaton4 = "Action!"; var mettatonLink = "http://i.imgur.com/T4XxnZr.png"; var mettaton; var yellowSoulLink = "http://i.imgur.com/Xo6GCyb.jpg"; var yellowSoul; function setup() { createCanvas(800,600); //making white key objects for (var i = 0; i < 14; i++) { wKey = new makeWhiteKey(whiteWidth*i + whiteOffset, keyY, whiteWidth, whiteHeight); whiteKeyArray.push(wKey); } //making black key objects for (var i = 0; i < 14; i++) { if ((i % 7) !== 2 && (i % 7) !== 6) { bKey = new makeBlackKey(whiteWidth*i + whiteOffset + blackOffset, keyY, blackWidth, blackHeight); blackKeyArray.push(bKey); } } //loading pixels of preloaded images brothers.loadPixels(); blueSoul.loadPixels(); mettaton.loadPixels(); yellowSoul.loadPixels(); } function draw() { background(0); //building white keys for (var i = 0; i < whiteKeyArray.length; i++) { whiteKeyArray[i].drawWhite(); } //building black keys for (var i = 0; i < blackKeyArray.length; i++) { blackKeyArray[i].drawBlack(); } //font details textFont("Comic Sans MS"); textSize(30); textAlign(CENTER); fill(255); //what to display depending on the array length if (passwordArray.length == password.length) { text(messageSans, width/2, 65); textFont("Papyrus"); textAlign(LEFT); text(messagePapyrus1,20,400); text(messagePapyrus2,20,440); text(messagePapyrus3,20,480); image(brothers, 280, 370); scale(0.5); image(blueSoul, 1100, 700); } else if (passwordArray.length < password.length) { text(display, width/2, 65); } //for the easter egg if (easterEggArray.length == easterEgg.length) { textFont("Lucida Handwriting"); text(messageMettaton1, width/2, 65); text(messageMettaton2,140,400); text(messageMettaton3,140,440); text(messageMettaton4,140,480); scale(0.4); image(mettaton, 670, 900); scale(1.25) image(yellowSoul,1090,700); } else if (easterEggArray.length < easterEgg.length) { textFont("Lucida Handwriting"); text(displayLegs, width/2, 65); } } // Helper functions to make piano key objects //////////////////////// //white keys function makeWhiteKey(whiteX,whiteY,whiteW,whiteH) { return {wx: whiteX, wy: whiteY, ww: whiteW, wh: whiteH, drawWhite: drawWhiteKey}; } function drawWhiteKey() { fill(255); rect(this.wx, this.wy, this.ww, this.wh); } //black keys function makeBlackKey(blackX,blackY,blackW,blackH) { return {bx: blackX, by: blackY, bw: blackW, bh: blackH, drawBlack: drawBlackKey}; } function drawBlackKey() { fill(0); rect(this.bx, this.by, this.bw, this.bh); } // Keeping track of the typed password //////////////////////// function keyTyped() { //affects the array if (key == password.charAt(numPresses)) { sansTalk.play(); display += password[numPresses]; numPresses += 1; passwordArray.push(key); numLegs = 0; displayLegs = ""; } else if (key !== password.charAt(numPresses)) { display = ""; numPresses = 0; passwordArray = []; } //for the easter egg if (key == easterEgg.charAt(numLegs)) { displayLegs += easterEgg[numLegs]; numLegs += 1; easterEggArray.push(key); numPresses = 0; display = ""; } else if (key !== easterEgg.charAt(numLegs)) { displayLegs = ""; numLegs = 0; easterEggArray = []; } //determines what melody to play and stops previous melody if (numPresses == password.length) { //plays determination glamour.stop(); determination.stop(); papyrusTalk.play(); determination.play(); } if (numLegs == easterEgg.length) { //plays glamour determination.stop(); glamour.stop(); glamour.play(); } } // Playing individual piano keys function mousePressed() { //play notes for black keys if (mouseY > keyY && mouseY < keyY + blackHeight) { if (mouseX > whiteOffset + 12*whiteWidth + blackOffset && mouseX < whiteOffset + 12*whiteWidth + blackOffset + blackWidth) { //high Asharp highAsharp.play(); } else if (mouseX > whiteOffset + 11*whiteWidth + blackOffset && mouseX < whiteOffset + 11*whiteWidth + blackOffset + blackWidth) { //high Gsharp highGsharp.play(); } else if (mouseX > whiteOffset + 10*whiteWidth + blackOffset && mouseX < whiteOffset + 10*whiteWidth + blackOffset + blackWidth) { //high Fsharp highFsharp.play(); } else if (mouseX > whiteOffset + 8*whiteWidth + blackOffset && mouseX < whiteOffset + 8*whiteWidth + blackOffset + blackWidth) { //high Dsharp highDsharp.play(); } else if (mouseX > whiteOffset + 7*whiteWidth + blackOffset && mouseX < whiteOffset + 7*whiteWidth + blackOffset + blackWidth) { //high Csharp highCsharp.play(); } else if (mouseX > whiteOffset + 5*whiteWidth + blackOffset && mouseX < whiteOffset + 5*whiteWidth + blackOffset + blackWidth) { //low Asharp lowAsharp.play(); } else if (mouseX > whiteOffset + 4*whiteWidth + blackOffset && mouseX < whiteOffset + 4*whiteWidth + blackOffset + blackWidth) { //low Gsharp lowGsharp.play(); } else if (mouseX > whiteOffset + 3*whiteWidth + blackOffset && mouseX < whiteOffset + 3*whiteWidth + blackOffset + blackWidth) { //low Fsharp lowFsharp.play(); } else if (mouseX > whiteOffset + 1*whiteWidth + blackOffset && mouseX < whiteOffset + 1*whiteWidth + blackOffset + blackWidth) { //low Dsharp lowDsharp.play(); } else if (mouseX > whiteOffset + blackOffset && mouseX < whiteOffset + blackOffset + blackWidth) { //low Csharp lowCsharp.play(); } } //play notes for white keys else if (mouseY > keyY && mouseY < keyY + whiteHeight) { if (mouseX > whiteOffset + 13*whiteWidth) { //high B highB.play(); } else if (mouseX > whiteOffset + 12*whiteWidth) { //high A highA.play(); } else if (mouseX > whiteOffset + 11*whiteWidth) { //high G highG.play(); } else if (mouseX > whiteOffset + 10*whiteWidth) { //high F highF.play(); } else if (mouseX > whiteOffset + 9*whiteWidth) { //high E highE.play(); } else if (mouseX > whiteOffset + 8*whiteWidth) { //high D highD.play(); } else if (mouseX > whiteOffset + 7*whiteWidth) { //high C highC.play(); } else if (mouseX > whiteOffset + 6*whiteWidth) { //low B lowB.play(); } else if (mouseX > whiteOffset + 5*whiteWidth) { //low A lowA.play(); } else if (mouseX > whiteOffset + 4*whiteWidth) { //low G lowG.play(); } else if (mouseX > whiteOffset + 3*whiteWidth) { //low F lowF.play(); } else if (mouseX > whiteOffset + 2*whiteWidth) { //low E lowE.play(); } else if (mouseX > whiteOffset + whiteWidth) { //low D lowD.play(); } else if (mouseX > whiteOffset) { //low C lowC.play(); } } } // Preloading the music files and images //////////////////////// function preload() { //meloadies and sound effects determination = loadSound("determination.wav"); sansTalk = loadSound("sansTalk.wav"); papyrusTalk = loadSound("papyrusTalk.wav"); glamour = loadSound("glamour.wav"); //white key sounds highB = loadSound("highB.wav"); highA = loadSound("highA.wav"); highG = loadSound("highG.wav"); highF = loadSound("highF.wav"); highE = loadSound("highE.wav"); highD = loadSound("highD.wav"); highC = loadSound("highC.wav"); lowB = loadSound("lowB.wav"); lowA = loadSound("lowA.wav"); lowG = loadSound("lowG.wav"); lowF = loadSound("lowF.wav"); lowE = loadSound("lowE.wav"); lowD = loadSound("lowD.wav"); lowC = loadSound("lowC.wav"); //black key sounds highAsharp = loadSound("highAsharp.wav"); highGsharp = loadSound("highGsharp.wav"); highFsharp = loadSound("highFsharp.wav"); highDsharp = loadSound("highDsharp.wav"); highCsharp = loadSound("highCsharp.wav"); lowAsharp = loadSound("lowAsharp.wav"); lowGsharp = loadSound("lowGsharp.wav"); lowFsharp = loadSound("lowFsharp.wav"); lowDsharp = loadSound("lowDsharp.wav"); lowCsharp = loadSound("lowCsharp.wav"); //images brothers = loadImage(brothersLink); mettaton = loadImage(mettatonLink); blueSoul = loadImage(blueSoulLink); yellowSoul = loadImage(yellowSoulLink); }
determination
glamour
highA
highAsharp
highB
highC
highCsharp
highD
highDsharp
highE
highF
highFsharp
highG
highGsharp
lowA
lowAsharp
lowB
lowC
lowCsharp
lowD
lowDsharp
lowE
lowF
lowFsharp
lowG
lowGsharp
papyrusTalk
sansTalk
My project is an interactive piano keyboard that will play when the user presses the mouse over the individual keys. Keep in mind that because of the way the code is written, the white keys are only playable in the area below the black keys.
For a simple melody from the indie game Undertale to play, the user needs to type in a certain password that will display as the user types. That certain is… “determination.”
Screenshots to prove functionality of project:
Running a local server is required for the code template. I didn’t know how to display the project for this reason, and so I have attached my code and embedded all 28 of the .wav sound files used in the code. (If the .wav files are inaccessible somehow even when looking at the text, please let me know!) All the image URLs are direct links to the images themselves. Directions for running a local server are here.
Credits:
All copyright and materials, including the depicted characters, sound effects, quotes, and songs, of Undertale belong to its creator, Toby Fox.
The performance of the “determination” melody belongs to Kyle Landry, whose video can be found here for a direct link or embedded below.
All manual piano key notes were recorded by myself using the sound editor Audacity and the piano in Mudge residence hall in Carnegie Mellon University.
P.S., if you’ve played Undertale, try typing in the word that would please a certain fabulous robot the most when he presents his essay question… or you could just look at the code for the little treat I put in. 🙂
]]>Undertale Piano
I propose making an interactive piano project, playing to a selected keyword. The respective chords would play a melody in tandem with the appropriate key presses and indicate which keys on the piano are being pressed. Whether the keys can be played individually by choice remains to be seen. The white keys I could simply map to the number keys, but I would have to put more thought into how to map the black keys, as the keyword “determination” already occupies the three rows of letters on the keyboard.
The melody is inspired from Undertale, a popular indie game released in September 2015. Various characters and visuals will pop up with each key press, and since they’re sprite-animated, I could possibly make helper functions for the sole purpose of drawing the characters on each key press. I may even use sprite sheets to animate the characters during the key presses.
All material and rights of Undertale are reserved to its creator, Toby Fox.
]]>//Jinhee Lee
//Section C
//jinheel1@andrew.cmu.edu
//Project-10
var buildings = [];
var ourLordandSaviour;
function preload() {
ourLordandSaviour = loadImage("http://i.imgur.com/b4dYgGz.jpg"); //I don't know
}
function setup() {
createCanvas(600, 400);
ourLordandSaviour.loadPixels();
// create an initial collection of buildings
for (var i = 0; i < 10; i++){
var rx = random(width);
buildings[i] = makeBuilding(rx);
}
frameRate(30);
}
function draw() {
var skyCol = color(135,206,235);
background(skyCol); //sky color backdrop
image(ourLordandSaviour, 25, 0);
displayGround(); //displaying grass
updateAndDisplayBuildings();
removeBuildingsThatHaveSlippedOutOfView();
addNewBuildingsWithSomeRandomProbability();
}
function updateAndDisplayBuildings(){
// Update the building's positions, and display them.
for (var i = 0; i < buildings.length; i++){
buildings[i].move();
buildings[i].display();
}
}
function removeBuildingsThatHaveSlippedOutOfView(){
// If a building has dropped off the left edge,
// remove it from the array. This is quite tricky, but
// we've seen something like this before with particles.
// The easy part is scanning the array to find buildings
// to remove. The tricky part is if we remove them
// immediately, we'll alter the array, and our plan to
// step through each item in the array might not work.
// Our solution is to just copy all the buildings
// we want to keep into a new array.
var buildingsToKeep = [];
for (var i = 0; i < buildings.length; i++){
if (buildings[i].x + buildings[i].breadth > 0) {
buildingsToKeep.push(buildings[i]);
}
}
buildings = buildingsToKeep; // remember the surviving buildings
}
function addNewBuildingsWithSomeRandomProbability() {
// With a very tiny probability, add a new building to the end.
var newBuildingLikelihood = 0.007;
if (random(0,1) < newBuildingLikelihood) {
buildings.push(makeBuilding(width));
}
}
// method to update position of building every frame
function buildingMove() {
this.x += this.speed;
}
// draw the building and some windows
function buildingDisplay() {
var floorHeight = 20;
var bHeight = this.nFloors * floorHeight;
fill(255);
stroke(0);
push();
translate(this.x, height - 40);
rect(0, -bHeight, this.breadth, bHeight);
fill("red"); //red windows and roofs
triangle(0, -bHeight, //roofs
this.breadth, -bHeight,
this.breadth/2, 3 / 2 * -bHeight);
stroke(200);
for (var i = 0; i < this.nFloors; i++) {
rect(5, -15 - (i * floorHeight), this.breadth - 10, 10);
}
pop();
}
function makeBuilding(birthLocationX) {
var bldg = {x: birthLocationX,
breadth: 50,
speed: -1.0,
nFloors: round(random(1,4)),
move: buildingMove,
display: buildingDisplay}
return bldg;
}
function displayGround(){
var groundCol = color(66, 244, 86);
fill(groundCol);
rect(0,height/2, width, height);
}
I used the template and manipulated and added various elements to simulate a suburb.
P.S., This has been a very long week and weekend for me… I apologize for the lackluster submission. That said, I added in something that I hope will at least make someone smile, if not be impressed. Any partial credit received is greatly appreciated.
]]>The person whose work I am writing about is Chloe Varelidi. She has a Master’s in Fine Arts at Parsons’ Design and Technology Program. Currently she works at littleBits as a Sr. Product Strategist, and is also a resident artist at Eyebeam, making her own games.
The littleBits Analog Arcade Machine, for the 2015 Bay Area MakerFaire, presented by Kristin Salomon, Paul Rothman, and their littleBits team, of which Varelidi was a member.
The team’s projects include an arcade game that dispenses candy, an electronic drum module creating synth beats, and even an animatronic hand project that allows for games of rock-paper-scissors with a computer using one’s own hand in a glove with a wireless receiver. According to the presenters, the modules are assembled with bits (hence the name) which challenge the creators’ creative electrical engineering skills without being overly complicated, at least in terms of assembly.
The rock-paper-scissors game I find particularly interesting because of the many touches to help simulate a real game, such as using one’s own hand, having the opposing animatronic shaped like a hand, and simulating prediction of your move by detecting the subtle movements of your hand as you play.
P.S., the Donkey Kong theme is a nice touch. 🙂
]]>//Jinhee Lee
//Section C
//jinheel1@andrew.cmu.edu
//Project-09
var underlyingImage;
function preload() {
var myImageURL = "http://i.imgur.com/EH0p0KK.png";
underlyingImage = loadImage(myImageURL);
}
function setup() {
createCanvas(600, 600);
background(70); //dark grey background to contrast with black
underlyingImage.loadPixels();
frameRate(25); //fast frame rate to fill up canvas
}
function draw() {
var px = random(width);
var py = random(height);
var ix = floor(px);
var iy = floor(py);
var theColorAtLocationXY = underlyingImage.get(ix, iy);
var pixelSize = 10; //size of pixels spawning
var bright = brightness(theColorAtLocationXY); //extract brightness of pixel for greyscale effect
var amt = map(bright, 0, 100, 0, 255); //map from 0 to 255 so overall image isn't too dark
noStroke();
fill(amt); //fills pixel with appropriate brightness value
ellipse(px, py, pixelSize, pixelSize);
}
function mouseDragged() { //draws a line with mouse
var theColorAtTheMouse = underlyingImage.get(mouseX, mouseY);
stroke(theColorAtTheMouse); //while pixels are greyscaled, the line is colored
strokeWeight(10); //thicker stroke so it can be seen
line(pmouseX, pmouseY, mouseX, mouseY);
}
I made the generated pixels with a greyscale effect, along with a mouseDragged() function that adds color depending on the stroke of the mouse. Like breathing life into the photo, though if left alone too long, it would revert back to grey with the refreshing pixels.
]]>//Jinhee Lee
//Section C
//jinheel1@andrew.cmu.edu
//Project-07
var nPoints = 100;
var angle = 0; //start angle for rotating (out of canvas)
function setup() {
createCanvas(500, 500);
}
function draw() {
background(250);
translate(width / 2, height / 2);
if (mouseX > width) { //this one's for you, Anirudh
rotate(angle); //shape rotates in place
}
drawDeltoidCurve(); //calling helper function
angle += 0.05; //increment for rotation
}
function drawDeltoidCurve() {
// Deltoid
// http://mathworld.wolfram.com/Deltoid.html
var x; //curve in parametric form
var y;
var minSize = 80; //min size of shape
var maxSize = 160; //max size of shape
var mapA = map(mouseY, 0, width, minSize, maxSize); //maps size between minSize and maxSize
var a = constrain(mapA, minSize, maxSize); //so that shape doesn't grow when mouseX beyond canvas
var b = a / 3; //variable for curve equations
var rot = constrain(mouseX / 30, 0, width / 30); //rotate according to mouseX
fill("red"); //larger red circle
ellipse(0, 0, 2 * a, 2 * a);
fill(0);
beginShape();
for (var i = 0; i < nPoints; i++) {
var theta = map(i, 0, nPoints, 0, TWO_PI);
x = 2 * b * cos(theta) + b * cos(2 * theta - rot); //parametric equations of curve
y = 2 * b * sin(theta) - b * sin(2 * theta - rot);
vertex(x, y);
}
endShape(CLOSE);
fill("red"); //smaller red circle
ellipse(0, 0, b, b);
}
Having to implement parametric equations felt daunting at first, but in a broader look, it was mostly plugging in the deltoid curve’s equation with the templates given in the prompt. The two red circles I made separately fairly easily, but I made them share variables with the deltoid curve that governed its size, so they would all grow proportionally.
P.S.,
Fun fact, if you spin the deltoid to the right past the canvas, then you get-
YOU ARE NOW UNDER MY GENJUTSU
This is a very small example that very few people have commented on, but small details like this are what immerse me into games. In the Devil May Cry series, the game encourages the player to fight expressively and stylishly by grading their performance at the end of every mission.
An example of this can be seen here, in which the player receives an “S” rank and the screen is sliced many, many times and eventually breaks at the seams.
The number of slices/gunshots (depending on the played character) that appear on the screen before the displayed rank is random, specifically “pseudo-random”, in that it can actually be determined and reproducible depending on the player’s rank. The higher the rank, the more slices/gunshots.
I believe that the cutting angles are all predetermined, and that the game picks a fairly consistent amount to display depending on the rank, but randomizes the order in which each cut is executed. Additionally, when the screen breaks apart like glass, randomness seems to be utilized to a greater extent as the shards drop. Particularly, applying the physics engine to each shard and treating it as an object in free-fall. This is all speculation though, seeing as how neither player nor developer has really elaborated on such a minor feature.
What this produces is a nice visual that reflects the free-flowing rather than choreographed nature of the combat and feels empowering to players that take the time to refine their skill.
]]>//Jinhee Lee
//Section C
//jinheel1@andrew.cmu.edu
//Project-06
var theSunX = 0; //starting coordinates for the Sun/Moon at midnight
var theSunY = 200;
var theMoonX = 0;
var theMoonY = -200;
function setup() {
createCanvas(600, 600);
}
function draw() {
var h = hour();
var m = minute();
var s = second();
var daySecs = 86400; //# of seconds in a day
var timeSecs = (h * 60 * 60 + m * 60 + s); //current time in seconds
var theSun = 100; //size of theSun
var theMoon = 100; //size of theMoon
var skyDayCol = color(135,206,235); //sky color at day
var skyNightCol = color(0); //sky color at night
var gDayCol = color(222,184,135); //ground color at day
var gNightCol = color(139,69,19); //ground color at night
//various times of day in seconds
var dawn = daySecs/5;
var dusk = 4 * daySecs/5;
var sunrise = 3 * daySecs/10;
var sunset = 7 * daySecs/10;
//for future lerpColor() functions
var twilight1 = map(timeSecs,dawn,sunrise,0,1); //"amt" arguments for lerpColor
var twilight2 = map(timeSecs,sunset,dusk,0,1);
var cG1 = lerpColor(gNightCol,gDayCol,twilight1); //color changes during twilight
var cG2 = lerpColor(gDayCol,gNightCol,twilight2);
var cSky1 = lerpColor(skyNightCol,skyDayCol,twilight1);
var cSky2 = lerpColor(skyDayCol,skyNightCol,twilight2);
//sky
if (timeSecs < dawn || timeSecs > dusk) { //between dawn and dusk
background(skyNightCol);
} else if (timeSecs > sunrise & timeSecs < sunset) { //between sunrise and sunset
background(skyDayCol);
} else if (timeSecs >= dawn & timeSecs <= sunrise) { //during 1st twilight
background(cSky1);
} else if (timeSecs >= sunset & timeSecs <= dusk) { //during 2nd twilight
background(cSky2);
}
//rising/setting sun and moon
push();
translate(width/2,height/2);
rotate(TWO_PI * timeSecs / daySecs);
fill("yellow");
ellipse(theSunX,theSunY,theSun,theSun);
fill("white");
ellipse(theMoonX,theMoonY,theMoon,theMoon);
pop();
//ground
if (timeSecs < dawn || timeSecs > dusk) { //between dawn and dusk
fill(gNightCol);
rect(0,height/2,width,height/2);
} else if (timeSecs > sunrise & timeSecs < sunset) { //between sunrise and sunset
fill(gDayCol);
rect(0,height/2,width,height/2);
} else if (timeSecs >= dawn & timeSecs <= sunrise) { //during 1st twilight
fill(cG1);
rect(0,height/2,width,height/2);
} else if (timeSecs >= sunset & timeSecs <= dusk) { //during 2nd twilight
fill(cG2);
rect(0,height/2,width,height/2);
}
}
Coming up with the individual parts, such as defining certain times of day like dusk and dawn and determining arguments for lerpColor() functions, wasn’t difficult. However, implementing them to work in combination was a tougher task. In an attempt to piecemeal the process, I came up with various “if” statements detailing different periods of time during the day, and duplicating said “if” statements so I could use them for changing the ground and sky separately (otherwise there would have been a layering error where the Sun was “on top” of the ground instead of in the horizon).
I initially had an explicit time display, but after reading that conventional numerals were disallowed, I had to take it out. I didn’t want there to just be a black sky at night, so I also added a moon to take the place of the Sun at night. Though you’ll see both of them during the twilight times.
]]>Gollum (pictured right), played by Andy Serkis (pictured left) in Lord of the Rings (2001-2003), and The Hobbit: An Unexpected Journey (2012), both directed by Peter Jackson.
Being that this blog post is about images rather than animation, I will talk about how Gollum’s appearance was rendered, though I will reference the latter in regards to The Hobbit.
Gollum’s appearance I believe is a rare achievement amongst CGI characters (even in modern films), as it incorporates several elements to create the character other than CGI, combining, to name a few, the 3D model with sculpted models as reference, motion capture with Serkis’ facial expressions, and Serkis’ physical presence on the set to help his fellow actors react believably.
According to this article, Bay Raitt and his team at Weta Digital created a system “which implemented 964 control points on Gollum’s face,” allowing detailed control of his facial expressions. To summarize, this system was used to animate Gollum’s face and a 3D model was drawn over the rest of Serkis’ body during recording.
The additional technique used in The Hobbit is particularly interesting in that it essentially gives Gollum a “living” skeleton, muscles, fascia, etc. to further enhance his lifelike appearance. As for the algorithms, I believe that the individual body elements are calculated to react to Serkis’ nuances in facial expression, in order to save the trouble of having to animate every single frame by hand.
]]>//Jinhee Lee
//Section C
//jinheel1@andrew.cmu.edu
//Project-05
function setup() {
createCanvas(600, 600);
}
function draw() {
background(20);
var scale = Math.sqrt(3)/2; //helps make equilateral triangle
var triDistX = 60; //increments triangle on x-axis
var triDistY = 60 * scale; //increments triangle on y-axis
var triOffset = 30; //for making bottom vertices of triangles
var fro = color(100,20,100); //color for stripes
var to = color(150,20,150);
var fro2 = color(20,20,100); //color for triangles
var to2 = color(20,20,255);
for (var y = 0; y < height; y += 2 * triDistY) { //for each row
var amt = map(y,0,height,0,1);
var col = lerpColor(fro,to,amt);
fill(col); //fills the stripes
stroke(col);
rect(0,y + triDistY,width,triDistY); //draws the stripes
for (var x = 0; x < width; x += triDistX) { //for each "column"
var amt2 = map(x,0,width,0,1);
var col2 = lerpColor(fro2,to2,amt2);
fill(col2); //fills the triangles
stroke(col2);
triangle(x + triOffset, y, //each line is a coordinate pair
x, y + triDistY,
x + 2 * triOffset, y + triDistY);
}
}
noLoop(); //as per instructions
}
I wanted to go for cooler colors on the color spectrum, to give more of a subdued tone to the wallpaper. Drawing nested for() loops is, in itself, not difficult, but it did present a bunch of restrictions I wasn’t aware of. I previously tried to use variables defined locally, before the for() loop, for incrementation. However, with such a configuration I ended up with only a single row of triangles that I had intended to repeat down the canvas. I had to change it later so that the variables for incrementation were defined inside the for() loop arguments.
P.S., if this were a shirt, I would NOT wear this in public.
]]>