1//////////////////////////////////////////////////////////////////////
2//
3// Ambient occlusion shader for sphere impostors
4//
5// This fragment shader is used for baking the ambient occlusion
6// maps.
7//
8//////////////////////////////////////////////////////////////////////
9
10//
11// Input
12//
13
14// the sphere center position: eye coords
15varying vec3 v_pos;
16// the sphere radius
17varying float v_radius;
18// streched corner: [-1.x, 1.x] (see below)
19varying vec2 v_corner;
20
21//
22// Uniforms
23//
24
25// the model-view matrix
26uniform mat4 u_modelView;
27// the orthographic projection matrix
28uniform mat4 u_projection;
29// depth texture sampler
30uniform sampler2D u_depthTex;
31// intensity = 1 / (number of light directions)
32uniform float u_intensity;
33
34/**
35 * Inverse gnomonic projection over octahedron unfloded into a square. This
36 * inverse  projection goes from texture coordinates to the surface of the unit
37 * sphere. Both the texture and unit sphere coordinates are in the range
38 * [-1, 1].
39 *
40 * In practice, this function returns the normal vector in model coordinate
41 * space. The z is inverted since going from clip coords to NDC inverts the
42 * z axis.
43 *
44 * reference: Tarini et al. page 3, eq. (5)
45 */
46vec3 textureToSphereSurfaceCoord(in vec2 coord)
47{
48  vec2 absCoord = abs(coord);
49  float h = 1.0 - absCoord.s - absCoord.t;
50  return (h >= 0.0) ? vec3(coord.st, -h) : vec3(sign(coord.st) * (1.0 - absCoord.ts), -h);
51}
52
53void main()
54{
55  // map texture coords to normal in model coords
56  vec3 N = textureToSphereSurfaceCoord(clamp(v_corner, -1.0, 1.0));
57
58  // model coords -> eye coords
59  N = normalize(vec3(u_modelView * vec4(N, 0.0)));
60
61  // add the normal xy components to the sphere eye coords
62  vec4 pos = vec4(v_pos, 1.0);
63  pos.xy += N.xy * v_radius;
64  // eye coord -> clip coords [-1, 1]
65  pos = u_projection * pos;
66  // clip coords -> [0, 1] for xy and [near, far] for z
67  pos.xy = (pos.xy + vec2(1.0, 1.0)) / 2.0;
68  pos.z = ((gl_DepthRange.diff * pos.z) + gl_DepthRange.near + gl_DepthRange.far) / 2.0;
69
70  // compute angle between sphere surface and light direction
71  float cos_alpha = dot(N, vec3(0, 0, 1));
72
73  // since we are using flat impostors in the depth texture, cos_alpha needs to be positive
74  if (cos_alpha > 0.0 && texture2D(u_depthTex, pos.xy).r > pos.z) {
75    // the texel is visible from the light source
76    gl_FragColor = vec4(vec3(1.0, 1.0, 1.0) * cos_alpha * u_intensity, 1.0);
77  } else {
78    // texel not visible
79    gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
80  }
81
82}
83