Monday, September 1, 2014

vec3 type in Swift

I am back to writing a ray tracer in Swift for my own education.  I just did a vec3 class.  My current practice is directions, locations, and RGB colors are all vec3.  This is mainly because I like that in glsl.  I'm going with Double because I am not trying to be very fast here for now.  Swift has nice syntax for this.  All lines from the file are here.    Any suggestions appreciated.  Next on the list is a class for ray-intersectable surfaces.

// implicit type inference.  Swift when in doubt assumes Double
struct vec3 {
    var x = 0.0, y = 0.0, z = 0.0
}

func * (left: Double, right: vec3) -> vec3 {
    return vec3(x: left * right.x, y: left * right.y, z: left * right.z)
}

func / (left: Double, right: vec3) -> vec3 {
    return vec3(x: left / right.x, y: left / right.y, z: left / right.z)
}

func * (left: vec3, right: Double) -> vec3 {
    return vec3(x: left.x * right, y: left.y * right, z: left.z * right)
}

func / (left: vec3, right: Double) -> vec3 {
    return vec3(x: left.x / right, y: left.y / right, z: left.z / right)
}

func + (left: vec3, right: vec3) -> vec3 {
    return vec3(x: left.x + right.x, y: left.y + right.y, z: left.z + right.z)
}

func - (left: vec3, right: vec3) -> vec3 {
    return vec3(x: left.x - right.x, y: left.y - right.y, z: left.z - right.z)
}

func * (left: vec3, right: vec3) -> vec3 {
    return vec3(x: left.x * right.x, y: left.y * right.y, z: left.z * right.z)
}

func / (left: vec3, right: vec3) -> vec3 {
    return vec3(x: left.x / right.x, y: left.y / right.y, z: left.z / right.z)
}

func max(v: vec3) -> Double {
    if v.x > v.y && v.x > v.z {
        return v.x
    }
    else if v.y > v.x && v.y > v.z {
        return v.y
    }
    else {
        return v.z
    }
}

func min(v: vec3) -> Double {
    if v.x < v.y && v.x < v.z {
        return v.x
    }
    else if v.y < v.x && v.y < v.z {
        return v.y
    }
    else {
        return v.z
    }
}

func dot (left: vec3, right: vec3) -> Double {
    return left.x * right.x + left.y * right.y + left.z * right.z
}

func cross (left: vec3, right: vec3) -> vec3 {
    return vec3(x: left.y * right.z - left.z * right.y, y: left.z * right.x - left.z * right.x, z: left.x * right.y - left.y * right.x)
}

Saturday, August 30, 2014

A tale of three phones

In an effort to understand the phone ecosystem better I gave my daughter my iPhone 5 over a year ago and bought a Samsung Galaxy S 3 running Android and used it for about six months.  I then tried a blue iPhone 5c under the mistaken impression it would be the success in Asia Apple hoped it would be.  After a couple of months I got the Nokia Lumia Icon which I have had for about half a year now.

While I do regret that I couldn't get the a phablet Android (not available on Verizon at the time), and I regret not getting an iPhone 5s (preferably the gold one), and I wanted the long optical path of the Nokia Lumia 920 (again not available on Verizon at the time), I did accomplish the goal of my experiment to get the experience of being a "real" user of iOS, Android, and Windows Mobile.  The hardware of all three particular devices was very impressive, although the iPhone 5c was not much different than my previous 5, and the Galaxy was already mature technology so lagging a little.

Pros:
  • iOS  The app ecosystem was very rich, and the look and feel was very smooth.
  • Android   The app ecosystem was almost as rich as iOS, and the interoperation of apps was neat.  The support for various google services was of course good.
  • Windows   Very smooth UI that was much better to use than I imagined given this is such a new piece of software.  On par with iOS in my subjective opinion.
Cons:
  • iOS  No google maps.  I actually got lost in Boston due to this and ended up in a sketchy neighborhood.  This has been fixed but shows the dark side of the curated closed environment.   Also, the thing is too small for my old eyes.
  • Android  Too much software and Samsung preloaded apps made things confusing.  This shows the dark side of open environments where specific hardware manufacturers sometimes have a negative incentive to support common portable environments between Android devices.
  • Windows   Too few apps.
What really surprised me is that I liked my Android experience the least.  This is personal taste, and I am very impressed with the new HTC phone a friend showed me so this is a dynamic issue, but I am really liking my Lumia running Windows.  Until Windows solves the app problem I am still going to buy Apple once it joins the 5" club, but I think Microsoft really scored with this OS.  Android was nice, but it is already starting to have the Balkanization problem we saw with Linux.  I would love to see google have a "standard" set of apps and settings I could download and get rid of as much manufacturer "improvements" as possible; with that I would give Android another try.  After all, the wild west of Android hardware is why we have phablets, so it does spur innovation.

Color names of the corners of the RGB cube

I've been spending a lot of time with the RGB cube lately and my biggest lesson has been that while it is mathematically very symmetrical between the primaries, it is not symmetrical subjectively (no, this is not a surprise to most of you).   The corners of the cube are key points when messing with it, and in order of increasing luminance (which is a confusing order and I use to emphasize don't get fooled by the geometric symmetry):
  • black (0, 0, 0) 
  • blue (0, 0, 1)
  • red (1, 0, 0)
  • magenta (1, 0, 1)
  • green (0, 1, 0)
  • cyan (0, 1, 1)
  • yellow (1, 1, 0)
  • white (1, 1, 1)
If we believe advocates of opponent process color theory (I am one of them and am in good company given DaVinci stated the opponent primaries were the right ones like it was not a matter of debate), the "true" primaries  are red, green, blue, yellow, black, white.   Your own experience with the names of the "mixed" corners gives some meat to that; which color name is more part of the human vocabulary:
  • cyan
  • magenta
  • yellow
 In fact look at them and try to quickly name them:
The xkcd survey shows that the subject population is more comfortable with the color association with the word "cyan" than it is with "magenta", as is shown from this figure from the survey:

I am impressed people know what "cyan" is; I don't think I would if I weren't in graphics.  I think "pink" is not a bad choice subjectively, but (255,128,128) is more of a classic pink (and note how far it is from the magenta vertex!).  Anthopologists have studied what are the "order" color is added to languages as the develop and an image on the excellent Wired article about Crayola's influence on color naming in the current world has this figure summarizing the anthro work:

There too cyan and magenta appear to be second-class RGB citizens.  Interesting also that yellow trumps blue, and that is consistent with the theory that the RG part of the vison system came first in evolution but I don't know enough to comment on that.

Bottom-line is do take advantage of the wonderfully simple math on the unit cube when using RGB, but always keep in the back of your mind that color as an experience is more complicated and less symmetrical.

Thursday, August 28, 2014

Designing our logo

I am unconvinced logos for software companies (as opposed to programs) really matter as long as one doesn't go too far away from lower case letters in a box.  But it's fun to mess with them, and you have to look at your own logo every day.  We had some criteria for ours:
  1. Not too many hues (I think 2 max is good)
  2. Not too oblong for web layout
  3. Meaningful about the company
  4. Geometrically simple
We went with the key figure from our original meeting when we decided to go with mobile photography apps.

Excuse the "we're the best": it was for internal consumption.  There are a million versions of this diagram on the web and I believe they have their root in Good To Great (2001) where the center of the diagram is "the hedgehog".  The diagram itself has all the four properties above so let's use that.  We just have to work out the details. 

For geometry let's nerd out and look for too much order.  The size of the center region varies with spacing and we like the centers to be in cool spots: the center of each can be where the other two circles intersect.




We like the teal color, but it is low color purity, and we'd like at least the center to have 100% purity.  So we just need to decide how the color combines.  We could do a physical transparency model, but photo-processing is all about abstraction, and a physical model wouldn't do that, so we instead go to pure teal in the middle with lower purity teal in the surrounds.  We add white lines to make it less boring.  We pick the orientation by feel.

And now the key thing: declare it "good enough" and go on to things that definitely matter.   We added the name of the company over the logo on the website because we didn't like how it looked beside it:


But I do have to wonder whether we missed a chance for free publicity with accidental logo features.

Wednesday, August 27, 2014

RGB Luminance revisited

One of the first things you learn in graphics is that green is subjectively brighter than red than is brighter than blue.  This goes back to the old school TV formula for converting color (RGB) to BW TV.  The wikipedia article on this is pretty good, and makes the important practical distinction between non-linear luma and linear luminace that all that gamma business.   But is getting that luminance formula important?  And if so, what is it for modern LCD screens?  I have had to pick a formula for my apps (I use an HSV inspired color space so need a formula for V) and it in practice doesn't seem to matter, but I thought I should dig into this a little now that the betas are done. 

First, always question all assumptions if easy to do so!  Are the primaries really different?  I find this question confusing: which of the colors below is brighter?

So we shouldn't expect an easy or clean answer here, and thinking there is an exact one is probably naive.  But that doesn't mean we shouldn't pick a standard if they are different.  To get at this I use a really cool indirect measure Gordon Kindlmann and collaborators used over a decade ago for designing pseudocolor maps.  See the paper for the roots of this idea.  I use the image from that paper with a few mods for easy photoshopping.  The basic idea is we see something as a face if light and dark are distributed "well".   It's faces and saturated colors and who knows what monitor you are looking at this on, so if you think this is science, you didn't read the disclaimer that is implicit here.

Here are images with all-on RGB (255, 0, 0), (0, 255, 0), (0, 0, 255), and black, white, and neutral grey (128, 128, 128).


Note some clearly look like faces, and some are more confusing.   Where the grey level crosses over so they are both confusing, the saturated color and the grey are about the same "brightness" under this measure.  Note with black, the right part of the pair always looks more natural, and the opposite with white.  If you look in the middle pairs, it does appear true that green is brighter than neutral grey, blue is darker, and red is more uncertain.  So the green > red > blue we always hear is still true.

Using my computer and my eye, here are the cross-over greys:
Cross-over greys are about 76/255, 128/255,  213/255.
This implies the primaries are very different.  For luma (not linear intensities)  and these 3 data points this implies

L = 0.29B + 0.5R +  0.83G

Those don't add up to one, so the non-linearities are in play for sure.   What if we assume gamma = 2.2, and then redo this.  Now the cross over points are: (0.07, 0.21, 0.66).   That's a lot closer to adding up to one, so more what we are used no in messing with linear gamma.  But is that a coincidence or is it useful?  Let's see if it is predictive.  What if we use pure magenta (255, 0, 255) and predict the cross-over.  If we use the luminance formula were we normalize the constants above to get to sum to 1.0:

L = 0.07B + 0.22R + 0.71G

We get for magenta 0.7+0.22 = 0.29.  If we gamma correct that at 2.2 and convert to 0..255 we get (147/255).  Trying it, before I knew this number, I got about 150 as my subjective crossover:
Greys 0, 128, 150, 255

So it looks like for this half-baked test, the luminance formula for sRGB is not far off:

L = 0.0722B + 0.2126R + 0.722G

Actually it's suspiciously close.  Either the sRGB standard is really making monitor calibration/standardization finally kind of happen, or I got lucky. 

Note that while this post was accidently a post endorsing doing gamma right, I actually believe that what matters in practice is not doing gamma wrong.  But doing it "right" is one way to make sure you don't do it wrong (thinking 128/255 is half as much physical intensity as 255/255).

Monday, August 25, 2014

Requesting your help for new app: Pando

We've had a social photo app in the works for some time that we will soon be doing a limited beta launch of: pando.  Pando is a user-to-user ephemeral photo sharing app, but that function is not what I want your help with.  Pando also can anonymously share to the web at pandopix.com.   Please download the app (iOS, Windows Mobile, or Android) by clicking on the appropriate link icon at the top right of the site:

The "doorknop" (I meant to do that!  Yeah that's the ticket) was uploaded from the app using the steps here:
Once you do that with a photo, try adding a hashtag (currently limited to our set listed in "links" at the top of the site) anywhere in the message you add.  For example if you say "I have crazy #cats" it will go to pandopix.com/cats

Your mission, should you choose to accept it, is to help me fill all the subdirectories (we call them "subpandos" inspired by reddit terminology) with nicer pictures than I would make.

And for extra credit, add "shirley" as a friend and send me a private message.  Use the stickers for that for my amusement :)

Sunday, August 24, 2014

Color bleeding looks like a bug!

This red effect is color bleeding in my living room this morning, and it is about what it really looks like (no photoshop or camera curve amplification).  I looked through the window in the image and no red out there!


The source for the image was obvious once I shadowed it.  It is all a red car across the screet that has caught the sun before most of the other objects.  It is mostly burned out here which is because it is bright.  I should have HDR'd it!  Next time.   On the right a figure blocking that car (I am standing by the wall where the red pattern appears, taking this picture) completely eliminated the effect.  Now THAT is a secondary light source like we idealize in discussions...


Also for those that enjoy these, Morgan McGuire collects images that look like bugs.  My favorite is the obvious shadow aliasing.