Eighteen painting with procedures. Review [bitmap-from-procedure procedure width height] Returns a...
-
date post
21-Dec-2015 -
Category
Documents
-
view
223 -
download
1
Transcript of Eighteen painting with procedures. Review [bitmap-from-procedure procedure width height] Returns a...
eighteen
painting with procedures
Review
[bitmap-from-procedure procedure width height] Returns a bitmap width pixels by height pixels Obtains colors for pixels by calling procedure
Passes procedure a point object with the coordinates Procedure returns a color object (color image) or an integer
(grayscale image)
[point x y] Returns a point object with the specified coordinates
[point-x p] or: p.X[point-y p] or: p.Y Returns x (or y) coordinate of p
Review
[color name] Returns color with the specified name
[color red green blue] Returns color with the specified amounts of red, green, and blue
light Arguments should be 0-255
[red c] or c.R[green c] or c.G[blue c] or c.B Returns the amount of red/green/blue light in the color c
New vector operations
[+ point point], [− point point] Returns the sum/difference of two points (vectors) Sum shifts the first point over by the amount of the second point (or vice-versa) Difference shifts the first one back
[× number point], [ ⁄ number point] Returns point stretched/shrunk by a factor of number
[rotate-vector point angle][rotate-vector-degrees point angle]
Rotates the vector about the origin [magnitude point]
Returns the length of the vector point [dot point point]
Returns the dot-product of one point onto the other aka the projection, aka the inner product, aka the scalar product
New color operations
[+ color color] Adds together the light of the two colors to form a new color
[× number color], [ ⁄ number color] Brightens/dims color by the factor number
[intensity color] Returns the total amount of light in the color from 0-255
[gray intensity] Returns a neutral gray color with the specified intensity (0-255)
Making a grayscale ramp
We can use dot products to make a grayscale ramp
The dot-product of two vectors is a single number
It increases as The first vector grows in the
direction of the second, or alternatively,
The second grows in the direction of the first
i.e. it’s symmetrical Each vector gets bigger
By manipulating the direction of the point in the dot-product, we can get different directions for the ramp
[bitmap-from-procedure[p → [dot p [point 0.5 0.5]]]256256]
Changing the vector direction
[bitmap-from-procedure[p → [dot p [point 1 0]]]256256]
[bitmap-from-procedure[p → [dot p [point 0 1]]]256256]
Clipping
Why is this one different? The display only has the
ability to produce amounts of light in the range 0-255
[bitmap-from-procedure[p → [dot p [point 1 1]]]256256]
Constructive laziness
It’s bad to keep typing the same arguments over and over again Slows you down Frustrates you Leads to errors
So make a new procedure that Just takes the parameter
we care about and Fills in the rest for us
[define show [procedure →
[bitmap-from-procedure procedure 256 256]]]
Now we can just say: [show [p → [dot p [point 1 1]]]]
Making it (slightly) more efficient
This version calls the point procedure for each pixel
Wasteful Arguments are always the
same Same point is always returned
(more or less) We only need to call it once
So we can call it once and save it in a local variable
[show [p → [dot p [point 1 1]]]]
[show [with vec = [point 1 1] [p → [dot p vec]]]
Getting cutesy (for advanced hackers)
[curry proc args …] Makes a new procedure that calls
proc with args followed by any arguments passed to the new procedure
[curry > 7] is the same as [n → [> 7 n]]
[curry-right proc args …] Same, but args go at the end
[curry-right > 7] is [n → [> n 7]]
Currying is a very useful technique Some languages (ML, Haskell)
automatically curry procedures Named after Haskell B. Curry So is the Haskell language
[show [curry dot [point 1 1]]]
Making another pattern
The magnitude procedure gives the length of the vector passed to it
So using it as the painting function for a bitmap gives us
A radial pattern That’s black at (0,0) And grows white farther away
Like a grayscale ramp, but circular
[show magnitude]
Shifting it over
How do we shift it so that the black part is in the center of the image?
We shift the vector we’re taking the magnitude of We use − to shift vectors
What vector do we subtract off? It’s a 256×256 image So the center is (128, 128)
[show [p → [magnitude [− p [point 128 128]]]]]
Making it brighter
Now it’s too dark How do we fix it?
Brighten by multiplying Multiplying by 2 Doubles the brightness Although now it clips
[show [p → [× 2 [magnitude [− p [point 128 128]]]]]
White on black
Okay, but what if we want it to be white in the center and black on the outside? How do we fix it?
Just subtract The brightness computed
brightness From 255 (the maximum
brightness)
[show [p → [− 255 [× 2 [magnitude [− p [point 128 128]]]]]
Thinking about it differently
We started with a simple pattern (magnitude)
We shifted it over We brightened it We inverted it
But the code doesn’t look like that Can we make it more
readable?
[show [p → [− 255 [× 2 [magnitude [− p [point 128 128]]]]]
Procedures that operate on whole patterns
Let’s call A pattern a procedure that
takes a point and returns a brightness
A pattern operator a procedure that takes a pattern and returns a new pattern
If we had pattern operators For shifting, brightening, and
inverting We could write the code much
more clearly
[shift point pattern] Makes a new pattern that’s
pattern shifted over by point [brighten level pattern]
Makes a new pattern that’s like pattern but with its brightness multiplied by level
[invert pattern] Makes a new pattern that’s
the same as pattern, but with black exchanged for white
Programming with pattern operators
Now we can take the magnitude pattern
[show magnitude]
Programming with pattern operators
Now we can take the magnitude pattern
Shift it
[show [shift [point 128 128] magnitude]]
Programming with pattern operators
Now we can take the magnitude pattern
Shift it Brighten it
[show [brighten 2 [shift [point 128 128] magnitude]]]
Programming with pattern operators
Now we can take the magnitude pattern
Shift it Brighten it And invert it
Now we don’t even need to know that patterns are really procedures
[show[invert [brighten 2 [shift [point 128 128] magnitude]]]]
Writing shift
Patterns are procedures
So shift is a higher order procedure
It takes a procedure as an argument
And returns a new procedure as its result
► [define shift ???]
Writing shift
Patterns are procedures
So shift is a higher order procedure
It takes a procedure as an argument
And returns a new procedure as its result
Shift must Take an offset and a pattern as
arguments
► [define shift [offset pattern → ???]]
Writing shift
Patterns are procedures
So shift is a higher order procedure
It takes a procedure as an argument
And returns a new procedure as its result
Shift must Take an offset and a pattern as
arguments Return a new procedure that
Takes a point as an argument
► [define shift [offset pattern → [p → ???]]]
Writing shift
Patterns are procedures
So shift is a higher order procedure
It takes a procedure as an argument
And returns a new procedure as its result
Shift must Take an offset and a pattern as
arguments Return a new procedure that
Takes a point as an argument And computes the correct
brightness
► [define shift [offset pattern → [p → [pattern [− p offset]]]]]
Writing brighten
Okay, now let’s write brighten ► [define brighten ???]
Writing brighten
Okay, now let’s write brighten
Brighten must Take a brightness level and a
pattern as arguments
► [define brighten [level pattern → ???]]
Writing brighten
Okay, now let’s write brighten
Brighten must Take a brightness level and a
pattern as arguments Return a new procedure that
Takes a point as an argument
► [define brighten [level pattern → [p → ???]]]
Writing brighten
Okay, now let’s write brighten
Brighten must Take a brightness level and a
pattern as arguments Return a new procedure that
Takes a point as an argument And computes the correct
brightness
► [define brighten [level pattern → [p → [× level [pattern p]]]]]
Writing invert
Okay, now let’s write invert ► [define invert ???]
Writing invert
Okay, now let’s write invert
Invert must Take a a pattern
► [define invert [pattern → ???]]
Writing invert
Okay, now let’s write invert
Invert must Take a a pattern Return a new procedure that
Takes a point as an argument
► [define invert [pattern → [p → ???]]]
Writing invert
Okay, now let’s write invert
Invert must Take a a pattern Return a new procedure that
Takes a point as an argument And computes the correct
brightness
► [define invert [pattern → [p → [− 255 [pattern p]]]]]
Repeated patterns
[mod a b] For positive numbers,
returns the remainder when dividing a by b
When a is negative, returns b minus the remainder
This turns out to be just the right thing to make a repeated texture
[define replicate[width height pattern → [p → [pattern [point [mod p.X width] [mod p.Y height]]]
[show [replicate 64 64 magnitude]]
Repeated patterns
[brighten 8 [replicate 64 64 [shift [point 32 32] magnitude]]]]
[replicate 64 64 [invert magnitude]]
And now, in color …
[define colorize[r-pattern g-pattern b-pattern → [p → [color [r-pattern p] [g-pattern p] [b-pattern p]]]]]
Takes three grayscale patterns and combines them into a single color pattern
Groovy, man
[colorize [brighten 8 [replicate 64 64 [shift [point 32 32] magnitude]]] [replicate 64 64 [invert magnitude]]
[p → 50]]]
Groovy, man
[colorize [brighten 8 [replicate 64 64 [shift [point 32 32] magnitude]]] [replicate 64 64 [curry dot [point 1 1]]]
[p → 50]]]
Spatial frequencies
Pictures can be thought of as having harmonic structure
Like sound Picture can be thought of as
many different frequencies combined
We won’t go into this in any detail, but …
Frequency corresponds roughly to size
Fine detail is high frequency Bigger structures are lower
frequeny
White noise
White noise is a random signal Every pixel (sample)
computed using a random number generator
Called “white” because it contains equal amounts of all frequencies
Not very interesting as a texture
► [show [p → [random-integer 0 255]]]
Bandpass noise
But now suppose we zoom in between the randomly chosen pixel values
And smoothly interpolate between them
The result is still a random texture, but it’s missing the very high and very low frequencies
[noise point] Interpolated noise Gaussian distribution Result is between -0.7 and 0.7
[show [p → [+ 128 [× 128 [noise [ ⁄ p 30]]]]]]
Bandpass noise at different frequences
[show [p → [+ 128 [× 128 [noise [ ⁄ p 10]]]]]]
[show [p → [+ 128 [× 128 [noise [ ⁄ p 80]]]]]]
Summing bandpass noise
You can get interesting effects by summing bandpass noise at different frequencies
[show [p → [+ 128 [× 128 [+ [noise [ ⁄ p 80]] [noise [ ⁄ p 40]] [noise [ ⁄ p 20]]]]]]]
1/f noise
Important kind of noise Amplitude of frequency is
inversely proportional to the frequency
Self-similar (like fractals) Zoom in on it and it still looks like
itself
Approximated using bandpass noise
Compute at different scales Sum with weights that vary
inversely with frequency
Also known as Brown noise (Brownian motion) Turbulence
[show [p → [+ 128 [× 128 [+ [noise [ ⁄ p 80]] [ ⁄ [noise [ ⁄ p 40]] 2] [ ⁄ [noise [ ⁄ p 20]] 4]]]]]]
Perlin noise
Ken Perlin (1985) Technique for
approximating 1/f noise using interpolated bandpass noise
Built into most graphics cards
[turbulence point] Computes a sum of many
calls to noise All you really need to
understand for this class
[show[p → [+ 128 [× 128 [turbulence [ ⁄ p 30]]]]]]
Caveat
You have to divide p by some number before calling noise or turbulence
I haven’t wrapped my head around Perlin’s old C code yet, but The noise function returns 0 for all points whose
coordinates are both integers So you have to divide by something to make sure the
coordinates are usually not integers Could well be my fault
The Art of Noise
[show[p → [× 255 [turbulence [ ⁄ p 30]]]]]
Noise clips on low end
[show[p → [abs [× 255 [turbulence [ ⁄ p 30]]]]]]
Abs produces abrupt change on low end
The Art of Noise
[show[with center = [point 128 128] [p → [× 255 [sin [+ [ ⁄ [magnitude [− p center]] 10]
[turbulence [ ⁄ p 30]]]]]]]
Noise used in input to another function (sin)
[show[p → [× 255 [sin [+ [turbulence [ ⁄ p 20]] [dot p [point 0.1 0.1]]]]]]]
Extra arguments to turbulence Turbulence works by calling noise at
different frequencies and summing
You can call turbulence with three extra arguments
Drop-off factor Amplitude of the new noise component
drops by this factor each iteration Frequency multiplier
Frequency get multiplied by this each iteration
Iteration count
Try playing with the extra arguments The default values are 2, 2, and 4
[show[p → [+ 127 [× 128 [turbulence [ ⁄ p 30] 1.5 2 8]]]]]
Extra arguments to turbulence Turbulence works by calling noise at
different frequencies and summing
You can call turbulence with three extra arguments
Drop-off factor Amplitude of the new noise component
drops by this factor each iteration Frequency multiplier
Frequency get multiplied by this each iteration
Iteration count
Try playing with the extra arguments The default values are 2, 2, and 4
[show[p → [+ 127 [× 128 [turbulence [ ⁄ p 30] 5 2 8]]]]]
The Art of Noise
[show[p → [× 256
[turbulence [× 0.001 p.X p.Y]]]]]
turbulence with a numeric argument
[show[p → [× 256 [cos [× 10 [turbulence [ ⁄ p 50]]]]]]]