Deferred shading

My stupid deferred shading algorithm seems to come together piece by piece, need to fix some non trivial bugs in the math lib to get it to work.

I really like the idea of deferred shading, it's an example of wholesale / data driven algorithm. Just love it.

1.png

1.png

More on Nana

There's no problem running nana (the Eutperea no TH port) on a simulator, but I just can't get it to run on a device.

It looks like a problem with IORef, but I had no problem with it before. It might be that the buffer is just too short, or that the remoteIO is running on a separate thread, I don't know yet.

Or is the IORef overhead be too large? Can it be solved by a faster CPU? I'm using a second generation iPod Touch, which is about twice as slow as the latest iPhone 3GS.

Anyway, I found a way to freeze an iPod touch though (restart required), just use remoteIO with IORef ...

Porting Eutperea

bamboo

a script I use to auto update remote repo after a push

# .git/hooks/post-receive


# create another branch
# then whenever a push is made to master,
# current branch is merged with master

cd ..
env -i git merge master

nana

OK, I need Eutperea, but ghc-iphone doesn't compile the ghci component, which is required by TH / Eutperea, so I had to port it, code name: nana.

The great CCA can't be used, so nana would be 20x slower then Eutperea, fuck.

I also did some research in SuperCollider and CSound. Porting CSound is practically impossible, since it uses python as build scripts. My cheap effort on building SuperCollder xcode project also failed miserably.

PureData is probably another option, but after a few hours with the CSound book, I really start to like it, and when I like something, it's hard to look at something else, like the way I found python, ruby and haskell.

I have yet to see any discussion on Eutperea, but imho it's one of the finest piece of haskell code ever written.

CNNIC

I sent another email to bugzilla on the topic of removing CNNIC CA from the default trusted root CA list. I have recommended Firefox to many non technical people, and wanted to do at least something for them.

Sleep mode

I'll be in no internet mode (a.k.a sleep mode) until I finish my next project. It's gonna take me some effort, but I've done enough research for it and it's time to get it out.

Project name: Hime.

My algorithm and design for the Google AI Challenge

In short: modified min-max + STM

I know multithreading is not allowed, but still I programed it in a way such that it has the potential to scale to arbitrary cores. There's one benefit even on the current system, that is the worker thread is detached from the main thread, so I can kill the worker thread when time is running out, I get an incorrect solution, but the program will not be rejected by the system.

I read the min-max tutorial on their website, and the article on wikipedia, from my understanding, it's just a function of:

1
2
3
4
eval :: State -> Int
eval = undefined

measure = eval my_state - eval your_state

Then this number is used as a measurement to pick the best move.

I had to tweak it in the following ways:

eval your move twice:

eval my_state and eval your_state have to be used independently, since two players move concurrently, how the other moves is unknown respectively. I cheated by calculating your state twice, where the latter takes into account this particular move of mine being considered.

dynamic depth of recursion

Depending on the size of the board, the number of moves I can look ahead various. This is a potential timeout. I simply hardcode a function ( Int -> Int ) that takes the number of free spot on the board and returns a recursion depth, that's been passed to the main worker function.

TVar board

Since the goal of this recursive thinking is to figure out if a free spot is worth to move to, i.e. comparing it's current weight to the weight we get from measure, there has to be a way to reference the original spot and set a value to it when the function thinks that this route actually worth more then it was previously considered. My solution is to put the board to a TVar and pass this reference around along with a pivot of the spot being considered.

(Bonus) The actual eval function

I first hacked up my own algorithm to determine if a spot leads to more space. After some tweaks, it started to perform pretty well, despite the fact I don't know why it works exactly, and there's no hope for me to reason the correctness of it. After some look around, I found out the proper name for the algorithm is call Flood Fill. Then I implemented a smaller, cuter version of flood fill, and it turned out that, despite being correct, it sucks.

Implicit or explicit -ly, I'm just too cheap to go with correctness.

That's pretty much it, btw, my data structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
data V2 = V2
{
x2 :: Int
, y2 :: Int
}
deriving (Show, Eq, Ord, Ix)

data Spot =
Me
| You
| Space Int
| Wall
deriving (Show, Eq)

type Grid = Map V2 Spot

data Board = Board
{
grid :: Grid
, bounds :: (V2, V2)
}
deriving (Show, Eq)

Pretty stupid, whatever.

Rika game engine preview 4

Added features

  • real time XMonad style dynamic source recompilation
  • real time scene reload
  • real time shader reload
  • more blender integration (camera, partial lighting support)

Such a great trouble to write a game engine from scratch, pretty fun though.

Rika game engine preview 3

Toon Shader

6.vert

varying vec3 normal;

void main()
{
  normal = gl_NormalMatrix * gl_Normal;

  gl_Position = ftransform();
}

6.frat

varying vec3 normal;

void main()
{
  vec4 color;
  float intensity;
  vec3 n = normalize(normal);

  intensity = dot(vec3(gl_LightSource[0].position),n);

  if( intensity > 0.75 )
    color = vec4( 1.0, 0.5, 0.5, 1.0 );
  else if (intensity > 0.5)
    color = vec4( 0.6, 0.3, 0.3, 1.0 );
  else if (intensity > 0.25)
    color = vec4( 0.4, 0.2, 0.2, 1.0 );
  else
    color = vec4( 0.2, 0.1, 0.1, 1.0 );

  gl_FragColor = color;
}
6.png

6.png

Per vertex spot light

9.vert

void main()
{
    vec3 normal, lightDir;
    vec4 diffuse, ambient, globalAmbient, specular;
    float NdotL, NdotHV;

    normal = normalize(gl_NormalMatrix * gl_Normal);
    lightDir = normalize(vec3(gl_LightSource[0].position));
    NdotL = max(dot(normal, lightDir), 0.0);
    diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;

    if (NdotL > 0.0) {

        // normalize the half-vector, and then compute the 
        // cosine (dot product) with the normal
        NdotHV = max(dot(normal, gl_LightSource[0].halfVector.xyz),0.0);
        specular = gl_FrontMaterial.specular * gl_LightSource[0].specular * 
            pow(NdotHV,gl_FrontMaterial.shininess);
      }
    else {
      specular = vec4( 0.0, 0.0, 0.0, 0.0);
    }
    /* Compute the ambient and globalAmbient terms */
    ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
    globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient;

    gl_FrontColor =  NdotL * diffuse + globalAmbient + ambient + specular;

    gl_Position = ftransform();
}

9.frag

void main()
{
	gl_FragColor = gl_Color;
}
9.png

9.png

Per pixel spot light

10.vert

varying vec4 diffuse,ambient;
varying vec3 normal,lightDir,halfVector;

void main()
{ 
  /* first transform the normal into eye space and 
  normalize the result */
  normal = normalize(gl_NormalMatrix * gl_Normal);

  /* now normalize the light's direction. Note that 
  according to the OpenGL specification, the light 
  is stored in eye space. Also since we're talking about 
  a directional light, the position field is actually direction */
  lightDir = normalize(vec3(gl_LightSource[0].position));

  /* Normalize the halfVector to pass it to the fragment shader */
  halfVector = normalize(gl_LightSource[0].halfVector.xyz);

  /* Compute the diffuse, ambient and globalAmbient terms */
  diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
  ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
  ambient += gl_LightModel.ambient * gl_FrontMaterial.ambient;

  gl_Position = ftransform();
}

10.frag

varying vec4 diffuse,ambient;
varying vec3 normal,lightDir,halfVector;


void main()
{
  vec3 n,halfV;
  float NdotL,NdotHV;

  /* The ambient term will always be present */
  vec4 color = ambient;

  /* a fragment shader can't write a varying variable, hence we need
  a new variable to store the normalized interpolated normal */
  n = normalize(normal);

  /* compute the dot product between normal and ldir */
  NdotL = max(dot(n,lightDir),0.0);


  if (NdotL > 0.0) {
    color += diffuse * NdotL;
    halfV = normalize(halfVector);
    NdotHV = max(dot(n,halfV),0.0);
    color += gl_FrontMaterial.specular * 
        gl_LightSource[0].specular * 
        pow(NdotHV, gl_FrontMaterial.shininess);
  }

  gl_FragColor = color;
}
10.png

10.png

Per pixel point light

11.vert

varying vec4 diffuse,ambientGlobal,ambient;
varying vec3 normal,lightDir,halfVector;
varying float dist;

void main()
{ 
  vec4 ecPos;
  vec3 aux;

  normal = normalize(gl_NormalMatrix * gl_Normal);

  /* these are the new lines of code to compute the light's direction */
  ecPos = gl_ModelViewMatrix * gl_Vertex;
  aux = vec3(gl_LightSource[0].position-ecPos);
  lightDir = normalize(aux);
  dist = length(aux);

  halfVector = normalize(gl_LightSource[0].halfVector.xyz);

  /* Compute the diffuse, ambient and globalAmbient terms */
  diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;

  /* The ambient terms have been separated since one of them */
  /* suffers attenuation */
  ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
  ambientGlobal = gl_LightModel.ambient * gl_FrontMaterial.ambient;

  gl_Position = ftransform();
}

11.frag

varying vec4 diffuse,ambientGlobal, ambient;
varying vec3 normal,lightDir,halfVector;
varying float dist;


void main()
{
  vec3 n,halfV,viewV,ldir;
  float NdotL,NdotHV;
  vec4 color = ambientGlobal;
  float att;

  /* a fragment shader can't write a varying variable, hence we need
  a new variable to store the normalized interpolated normal */
  n = normalize(normal);

  /* compute the dot product between normal and normalized lightdir */
  NdotL = max(dot(n,normalize(lightDir)),0.0);

  if (NdotL > 0.0) {

    att = 1.0 / (gl_LightSource[0].constantAttenuation +
        gl_LightSource[0].linearAttenuation * dist +
        gl_LightSource[0].quadraticAttenuation * dist * dist);
    color += att * (diffuse * NdotL + ambient);


    halfV = normalize(halfVector);
    NdotHV = max(dot(n,halfV),0.0);
    color += att * gl_FrontMaterial.specular * gl_LightSource[0].specular * 
            pow(NdotHV,gl_FrontMaterial.shininess);
  }

  gl_FragColor = color;
}
11.png

11.png