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