Last active
November 23, 2015 01:15
-
-
Save nandor/c5d453d415a7d6a539c9 to your computer and use it in GitHub Desktop.
Screen Space Ambient Occlusion
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
import glfw | |
import math | |
import numpy as np | |
import random | |
import sys | |
from OpenGL.GLUT import * | |
from OpenGL.GLU import * | |
from OpenGL.GL import * | |
def randVec(i, n): | |
"""Generates a random point in a hemisphere.""" | |
x = random.uniform(-1, 1) | |
y = random.uniform(-1, 1) | |
z = random.uniform( 0.5, 1) | |
l = random.uniform(0.1, 1) / math.sqrt(x * x + y * y + z * z) | |
scale = (i + 1.0) / n | |
l *= 0.1 + 0.9 * scale * scale | |
return (x * l, y * l, z * l) | |
def randomTexture(w, h): | |
mat = np.zeros((w, h, 3), dtype=np.float) | |
for i, j in np.ndindex((w, h)): | |
mat[i, j, 0] = random.uniform(-1, 1) | |
mat[i, j, 1] = random.uniform(-1, 1) | |
mat[i, j, 2] = 0.0 | |
return mat | |
VECS = [randVec(i, 8) for i in range(8)] | |
# Geometry shader. | |
GEOM_VS = ''' | |
#version 410 core | |
layout(location = 0) in vec3 i_vert; | |
layout(location = 1) in vec3 i_norm; | |
layout(location = 2) in vec2 i_uv; | |
uniform mat4 u_proj; | |
uniform mat4 u_view; | |
out vec3 v_norm; | |
out vec3 v_vert; | |
out vec2 v_uv; | |
void main() { | |
vec4 view_space_vert = u_view * vec4(i_vert, 1.0); | |
vec4 view_space_norm = u_view * vec4(i_norm, 0.0); | |
v_vert = view_space_vert.xyz; | |
v_norm = view_space_norm.xyz; | |
gl_Position = u_proj * view_space_vert; | |
} | |
''' | |
GEOM_FS = ''' | |
#version 410 core | |
uniform vec4 u_diffuse; | |
uniform mat4 u_proj; | |
layout(location = 0) out vec4 o_albedo; | |
layout(location = 1) out vec3 o_normal; | |
layout(location = 2) out vec4 o_vertex; | |
in vec3 v_norm; | |
in vec3 v_vert; | |
in vec3 v_uv; | |
float linear_depth(float depth) { | |
const float n = 0.1f; | |
const float f = 100.0f; | |
return (2 * n * f) / (f + n - (2.0 * depth - 1.0) * (f - n)); | |
} | |
void main() { | |
o_albedo = u_diffuse; | |
o_normal = normalize(v_norm); | |
o_vertex = vec4(v_vert, linear_depth(gl_FragCoord.z)); | |
} | |
''' | |
# SSAO shader. | |
SSAO_VS = ''' | |
#version 410 core | |
layout(location = 0) in vec3 i_vertex; | |
out vec2 v_uv; | |
void main() { | |
v_uv = (i_vertex.xy + 1.0) / 2.0; | |
gl_Position = vec4(i_vertex, 1.0); | |
} | |
''' | |
SSAO_FS = ''' | |
#version 410 core | |
const vec3 SAMPLES[] = vec3[](%(samples)s); | |
const float FOCUS = 0.15f; | |
const float POWER = 5.0; | |
uniform mat4 u_proj; | |
uniform sampler2D u_random; | |
uniform sampler2D u_normal; | |
uniform sampler2D u_vertex; | |
layout(location = 0) out float ao; | |
in vec2 v_uv; | |
void main() { | |
// Read the position, normal and the random vector. | |
vec2 scale = textureSize(u_normal, 0) / textureSize(u_random, 0); | |
vec4 vert = texture(u_vertex, v_uv); | |
vec3 norm = texture(u_normal, v_uv).rgb; | |
vec3 rvec = texture(u_random, v_uv * scale).rgb; | |
// Compute the btn matrix to rotate the hemisphere. | |
vec3 tang = normalize(rvec - norm * dot(rvec, norm)); | |
vec3 btan = cross(norm, tang); | |
mat3 tbn = mat3(tang, btan, norm); | |
float occlusion = 0.0; | |
for (int i = 0; i < %(count)d; ++i) { | |
// Compute an offset from the center point & project it. | |
vec3 smpl_view = tbn * SAMPLES[i] * FOCUS + vert.xyz; | |
vec4 smpl_proj = u_proj * vec4(smpl_view, 1); | |
smpl_proj.xyz /= smpl_proj.w; | |
smpl_proj.xy = smpl_proj.xy * 0.5f + 0.5f; | |
// Sample the depth. | |
float depth = -texture(u_vertex, smpl_proj.xy).w; | |
// Check for edges with a steep falloff. | |
float range = smoothstep(0, 1, FOCUS / abs(vert.w - depth)); | |
// If point occluded, add it to the accumulator. | |
occlusion += depth > smpl_view.z ? 1 : 0; | |
} | |
ao = pow(1.0 - occlusion / %(count)d, POWER); | |
} | |
''' % { | |
'samples': ','.join(['vec3(%1.4f,%1.4f,%1.4f)' % v for v in VECS]), | |
'count': len(VECS) | |
} | |
# Point light shader. | |
LIGHT_FS = ''' | |
#version 410 core | |
const vec3 LIGHT_DIR = vec3(0.5, 1, -1); | |
uniform sampler2D u_albedo; | |
uniform sampler2D u_normal; | |
uniform sampler2D u_vertex; | |
uniform sampler2D u_ao; | |
layout(location = 0) out vec4 o_colour; | |
in vec2 v_uv; | |
void main() { | |
vec3 albedo = texture(u_albedo, v_uv).rgb; | |
vec3 normal = normalize(texture(u_normal, v_uv).rgb); | |
vec3 vertex = texture(u_vertex, v_uv).rgb; | |
vec2 s = 1.0 / textureSize(u_normal, 0); | |
// 5x5 box blur. | |
float ao = 0.0; | |
ao += texture(u_ao, v_uv + vec2(-2, -2) * s).r; | |
ao += texture(u_ao, v_uv + vec2(-1, -2) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 0, -2) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 1, -2) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 2, -2) * s).r; | |
ao += texture(u_ao, v_uv + vec2(-2, -1) * s).r; | |
ao += texture(u_ao, v_uv + vec2(-1, -1) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 0, -1) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 1, -1) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 2, -1) * s).r; | |
ao += texture(u_ao, v_uv + vec2(-2, 0) * s).r; | |
ao += texture(u_ao, v_uv + vec2(-1, 0) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 0, 0) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 1, 0) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 2, 0) * s).r; | |
ao += texture(u_ao, v_uv + vec2(-2, 1) * s).r; | |
ao += texture(u_ao, v_uv + vec2(-1, 1) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 0, 1) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 1, 1) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 2, 1) * s).r; | |
ao += texture(u_ao, v_uv + vec2(-2, 2) * s).r; | |
ao += texture(u_ao, v_uv + vec2(-1, 2) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 0, 2) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 1, 2) * s).r; | |
ao += texture(u_ao, v_uv + vec2( 2, 2) * s).r; | |
ao /= 25.0; | |
float angle = max(dot(normalize(normal), normalize(LIGHT_DIR)), 0.0); | |
vec3 colour = vec3(0.0); | |
colour += albedo * ao * 0.3; | |
colour += albedo * angle; | |
o_colour = vec4(colour, 1.0); | |
} | |
''' | |
LIGHT_VS = ''' | |
#version 410 core | |
layout(location = 0) in vec3 i_vertex; | |
out vec2 v_uv; | |
void main() { | |
v_uv = (i_vertex.xy + 1.0) / 2.0; | |
gl_Position = vec4(i_vertex, 1.0); | |
} | |
''' | |
def frustum(left, right, bottom, top, znear, zfar): | |
"""Creates a view frustum.""" | |
assert(right != left and bottom != top and znear != zfar) | |
M = np.zeros((4,4), dtype=np.float32) | |
M[0,0] = +2.0 * znear / (right - left) | |
M[2,0] = +(right + left) / (right - left) | |
M[1,1] = +2.0 * znear / (top - bottom) | |
M[3,1] = +(top + bottom) / (top - bottom) | |
M[2,2] = -(zfar + znear) / (zfar - znear) | |
M[3,2] = -2.0 * znear * zfar / (zfar - znear) | |
M[2,3] = -1.0 | |
return M | |
def perspective(fovy, aspect, znear, zfar): | |
"""Creates a perspective projection matrix.""" | |
assert(znear != zfar) | |
h = np.tan(fovy / 360.0 * np.pi) * znear | |
w = h * aspect | |
return frustum( -w, w, -h, h, znear, zfar ) | |
def translate(M, x, y, z): | |
"""Translates a matrix along the axis.""" | |
T = [[ 1, 0, 0, x], [ 0, 1, 0, y], [ 0, 0, 1, z], [ 0, 0, 0, 1]] | |
M[...] = np.dot(M, np.array(T, dtype=np.float32).T) | |
return M | |
def scale(M, x, y, z): | |
"""Scales a matrix.""" | |
T = [[x, 0, 0, 0], [0, y, 0, 0], [0, 0, z, 0], [0, 0, 0, 1]] | |
M[...] = np.dot(M, np.array(T, dtype=np.float32).T) | |
return M | |
def rotate(M, angle, x, y, z): | |
"""Rotates a matrix around an axis.""" | |
angle = math.pi * angle / 180 | |
c = math.cos(angle) | |
s = math.sin(angle) | |
n = math.sqrt(x * x + y * y + z * z) | |
x /= n | |
y /= n | |
z /= n | |
cx, cy, cz = (1 - c) * x, (1 - c) * y, (1 - c) * z | |
M[...] = np.dot(M, np.array([ | |
[ cx * x + c, cy * x - z * s, cz * x + y * s, 0], | |
[ cx * y + z * s, cy * y + c, cz * y - x * s, 0], | |
[ cx * z - y * s, cy * z + x * s, cz * z + c, 0], | |
[ 0, 0, 0, 1] | |
]).T) | |
return M | |
class Mesh(object): | |
"""Wrapper around OpenGL VAO & VBO.""" | |
def __init__(self, mesh): | |
"""Creates a new mesh out of a numpy buffer.""" | |
self._vao = glGenVertexArrays(1) | |
self._vbo = glGenBuffers(1) | |
self._len = mesh.size / 8 | |
glBindVertexArray(self._vao) | |
glBindBuffer(GL_ARRAY_BUFFER, self._vbo) | |
glBufferData(GL_ARRAY_BUFFER, self._len * 32, mesh, GL_STATIC_DRAW) | |
glEnableVertexAttribArray(0) | |
glEnableVertexAttribArray(1) | |
glEnableVertexAttribArray(2) | |
glVertexAttribPointer(0, 3, GL_FLOAT, False, 32, ctypes.c_void_p(0)) | |
glVertexAttribPointer(1, 3, GL_FLOAT, False, 32, ctypes.c_void_p(12)) | |
glVertexAttribPointer(2, 2, GL_FLOAT, False, 32, ctypes.c_void_p(24)) | |
def render(self): | |
"""Renders the mesh.""" | |
glBindVertexArray(self._vao) | |
glDrawArrays(GL_TRIANGLES, 0, self._len) | |
class Cube(Mesh): | |
"""Simple cube mesh.""" | |
def __init__(self, x, y, z, w, h, d): | |
"""Createas a cube out of a numpy buffer.""" | |
super(Cube, self).__init__(np.array([ | |
x - w, y + h, z - d, +0.0, +1.0, +0.0, 0.0, 0.0, | |
x - w, y + h, z + d, +0.0, +1.0, +0.0, 0.0, 0.0, | |
x + w, y + h, z + d, +0.0, +1.0, +0.0, 0.0, 0.0, | |
x - w, y + h, z - d, +0.0, +1.0, +0.0, 0.0, 0.0, | |
x + w, y + h, z + d, +0.0, +1.0, +0.0, 0.0, 0.0, | |
x + w, y + h, z - d, +0.0, +1.0, +0.0, 0.0, 0.0, | |
x - w, y - h, z - d, -1.0, +0.0, +0.0, 0.0, 0.0, | |
x - w, y - h, z + d, -1.0, +0.0, +0.0, 0.0, 0.0, | |
x - w, y + h, z + d, -1.0, +0.0, +0.0, 0.0, 0.0, | |
x - w, y - h, z - d, -1.0, +0.0, +0.0, 0.0, 0.0, | |
x - w, y + h, z + d, -1.0, +0.0, +0.0, 0.0, 0.0, | |
x - w, y + h, z - d, -1.0, +0.0, +0.0, 0.0, 0.0, | |
x - w, y - h, z - d, +1.0, +0.0, -1.0, 0.0, 0.0, | |
x + w, y + h, z - d, +1.0, +0.0, -1.0, 0.0, 0.0, | |
x + w, y - h, z - d, +1.0, +0.0, -1.0, 0.0, 0.0, | |
x - w, y - h, z - d, +1.0, +0.0, -1.0, 0.0, 0.0, | |
x - w, y + h, z - d, +1.0, +0.0, -1.0, 0.0, 0.0, | |
x + w, y + h, z - d, +1.0, +0.0, -1.0, 0.0, 0.0, | |
x - w, y - h, z + d, +1.0, +0.0, +1.0, 0.0, 0.0, | |
x + w, y - h, z + d, +1.0, +0.0, +1.0, 0.0, 0.0, | |
x + w, y + h, z + d, +1.0, +0.0, +1.0, 0.0, 0.0, | |
x - w, y - h, z + d, +1.0, +0.0, +1.0, 0.0, 0.0, | |
x + w, y + h, z + d, +1.0, +0.0, +1.0, 0.0, 0.0, | |
x - w, y + h, z + d, +1.0, +0.0, +1.0, 0.0, 0.0, | |
], dtype=np.float32)) | |
class Object(Mesh): | |
"""Wrapper that loads object files.""" | |
def __init__(self, path): | |
with open(path) as f: | |
vert = [] | |
norm = [] | |
data = [] | |
def add(data, v, n): | |
data += v | |
data += n | |
data += [0, 0] | |
for line in f.readlines(): | |
tokens = line.strip().split(' ') | |
if tokens[0] == 'v': | |
[x, y, z] = map(float, tokens[1:]) | |
vert.append([x, y, z]) | |
continue | |
if tokens[0] == 'vn': | |
[nx, ny, nz] = map(float, tokens[1:]) | |
norm.append([nx, ny, nz]) | |
continue | |
if tokens[0] == 'f': | |
[f0, f1, f2] = map(lambda x: map(int, x.split('//')), tokens[1:]) | |
add(data, vert[f0[0] - 1], norm[f0[1] - 1]) | |
add(data, vert[f1[0] - 1], norm[f1[1] - 1]) | |
add(data, vert[f2[0] - 1], norm[f2[1] - 1]) | |
continue | |
super(Object, self).__init__(np.array(data, dtype=np.float32)) | |
class Quad(Mesh): | |
"""Wrapper around an OpenGL quad VAO.""" | |
def __init__(self): | |
"""Initializes the mesh.""" | |
super(Quad, self).__init__(np.array([ | |
-1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, | |
-1.0, +1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, | |
+1.0, +1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, | |
-1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, | |
+1.0, +1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, | |
+1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, | |
], dtype=np.float32)) | |
class Plane(Mesh): | |
"""Draws a plane parallel to xz.""" | |
def __init__(self): | |
"""Initializes the mesh.""" | |
super(Plane, self).__init__(np.array([ | |
-10.0, -1.0, -10.0, 0.0, 1.0, 0.0, 0.0, 0.0, | |
-10.0, -1.0, +10.0, 0.0, 1.0, 0.0, 0.0, 0.0, | |
+10.0, -1.0, +10.0, 0.0, 1.0, 0.0, 0.0, 0.0, | |
-10.0, -1.0, -10.0, 0.0, 1.0, 0.0, 0.0, 0.0, | |
+10.0, -1.0, +10.0, 0.0, 1.0, 0.0, 0.0, 0.0, | |
+10.0, -1.0, -10.0, 0.0, 1.0, 0.0, 0.0, 0.0, | |
], dtype=np.float32)) | |
class Program(object): | |
"""Wrapper around OpenGL programs.""" | |
def __init__(self, vert_source, frag_source): | |
"""Creates a new program.""" | |
self._vert = self._compile_shader(GL_VERTEX_SHADER, vert_source) | |
self._frag = self._compile_shader(GL_FRAGMENT_SHADER, frag_source) | |
self._prog = self._link_shaders([self._vert, self._frag]) | |
def bind(self): | |
"""Binds the program to the context.""" | |
glUseProgram(self._prog) | |
def __setitem__(self, key, value): | |
"""Sets the value of a uniform parameter.""" | |
if not key in self._unifs: | |
return | |
unif = self._unifs[key] | |
if value.__class__ == np.ndarray: | |
glUniformMatrix4fv(unif, 1, False, value) | |
return | |
if value.__class__ == tuple: | |
if len(value) == 2 and value[0].__class__ == Texture: | |
tex, loc = value | |
tex.bind(loc) | |
glUniform1i(unif, loc) | |
return | |
if len(value) == 4: | |
glUniform4f(unif, value[0], value[1], value[2], value[3]) | |
return | |
raise TypeError('Invalid uniform type for %s: %s', key, value.__class__) | |
def _compile_shader(self, type, source): | |
"""Compiles a single shader of a give type from source.""" | |
shader = glCreateShader(type) | |
if shader < 0: | |
raise RuntimeError('Cannot create shader object.') | |
glShaderSource(shader, source) | |
glCompileShader(shader) | |
if not glGetShaderiv(shader, GL_COMPILE_STATUS): | |
raise ValueError(glGetShaderInfoLog(shader)) | |
return shader | |
def _link_shaders(self, shaders): | |
"""Links all shader objects.""" | |
prog = glCreateProgram() | |
for shader in shaders: | |
glAttachShader(prog, shader) | |
# Link the program. | |
glLinkProgram(prog) | |
if not glGetProgramiv(prog, GL_LINK_STATUS): | |
raise ValueError(glGetProgramInfoLog(prog)) | |
# Fetch the location of all uniforms. | |
self._unifs = {} | |
for i in range(glGetProgramiv(prog, GL_ACTIVE_UNIFORMS)): | |
name, size, type = glGetActiveUniform(prog, i) | |
self._unifs[name] = glGetUniformLocation(prog, name) | |
return prog | |
class Texture(object): | |
"""Wrapper around OpenGL textures.""" | |
def __init__(self, w, h, d, type, data=None): | |
"""Initializes an empty texture.""" | |
self.w = w | |
self.h = h | |
self.d = d | |
self.type = type | |
self.tex = glGenTextures(1) | |
glBindTexture(GL_TEXTURE_2D, self.tex) | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) | |
fmt, ct, src = { | |
(1, GL_UNSIGNED_BYTE,): ( GL_LUMINANCE, GLubyte, GL_LUMINANCE), | |
(2, GL_UNSIGNED_BYTE,): ( GL_LUMINANCE_ALPHA, GLubyte, GL_LUMINANCE_ALPHA), | |
(3, GL_UNSIGNED_BYTE,): ( GL_RGB, GLubyte, GL_RGB), | |
(4, GL_UNSIGNED_BYTE,): ( GL_RGBA, GLubyte, GL_RGBA), | |
(1, GL_FLOAT): ( GL_R32F, GLfloat, GL_LUMINANCE), | |
(2, GL_FLOAT): ( GL_RG32F, GLfloat, GL_LUMINANCE_ALPHA), | |
(3, GL_FLOAT): ( GL_RGB32F, GLfloat, GL_RGB), | |
(4, GL_FLOAT): ( GL_RGBA32F, GLfloat, GL_RGBA), | |
}[d, type] | |
if data is None: | |
glTexImage2D( | |
GL_TEXTURE_2D, | |
0, | |
fmt, | |
w, | |
h, | |
0, | |
GL_RGBA, | |
GL_UNSIGNED_BYTE, | |
ctypes.c_void_p(None) | |
) | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) | |
else: | |
glTexImage2D( | |
GL_TEXTURE_2D, | |
0, | |
fmt, | |
w, | |
h, | |
0, | |
src, | |
type, | |
data | |
) | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) | |
def bind(self, loc): | |
"""Binds a texture to a location.""" | |
glActiveTexture(GL_TEXTURE0 + loc) | |
glBindTexture(GL_TEXTURE_2D, self.tex) | |
class FBO(object): | |
"""Wrapper around OpenGL frame buffer objects.""" | |
def __init__(self, textures): | |
"""Initializes the FBO.""" | |
self._buffers = [GL_COLOR_ATTACHMENT0 + i for i in range(len(textures))] | |
if not textures: | |
self._fbo = 0 | |
return | |
self._fbo = glGenFramebuffers(1) | |
glBindFramebuffer(GL_FRAMEBUFFER, self._fbo) | |
# Create a renderbuffer for depth. | |
self._depth = glGenRenderbuffers(1) | |
glBindRenderbuffer(GL_RENDERBUFFER, self._depth) | |
glRenderbufferStorage( | |
GL_RENDERBUFFER, | |
GL_DEPTH_COMPONENT24, | |
textures[0].w, | |
textures[0].h | |
) | |
glFramebufferRenderbuffer( | |
GL_FRAMEBUFFER, | |
GL_DEPTH_ATTACHMENT, | |
GL_RENDERBUFFER, | |
self._depth | |
) | |
# Attach all textures to color attachments. | |
for idx, texture in enumerate(textures): | |
glFramebufferTexture2D( | |
GL_FRAMEBUFFER, | |
GL_COLOR_ATTACHMENT0 + idx, | |
GL_TEXTURE_2D, | |
texture.tex, | |
0 | |
) | |
if glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE: | |
raise ValueError('Framebuffer not complete.') | |
def bind(self): | |
"""Activates the frame buffer object.""" | |
glBindFramebuffer(GL_FRAMEBUFFER, self._fbo) | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) | |
if self._buffers: | |
glDrawBuffers(self._buffers) | |
def main(): | |
"""Entry point of the application.""" | |
if not glfw.init(): | |
return | |
glfw.window_hint(glfw.RESIZABLE, False) | |
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3) | |
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3) | |
glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, GL_TRUE) | |
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE) | |
window = glfw.create_window(1280, 720, 'SSAO', None, None) | |
if not window: | |
glfw.terminate() | |
return | |
width, height = glfw.get_framebuffer_size(window) | |
glfw.make_context_current(window) | |
glViewport(0, 0, width, height) | |
glFrontFace(GL_CCW) | |
glCullFace(GL_BACK) | |
# Load and create OpenGL resources. | |
quad = Quad() | |
plane = Plane() | |
bunny = Object('bunny.obj') | |
cubes = [ | |
Cube(1, -1.2, 0.5, 0.5, 0.5, 0.5), | |
Cube(1.2, -1.4, 0.7, 0.5, 0.5, 0.5) | |
] | |
geom = Program(GEOM_VS, GEOM_FS) | |
ssao = Program(SSAO_VS, SSAO_FS) | |
light = Program(LIGHT_VS, LIGHT_FS) | |
albedo = Texture(width, height, 4, GL_UNSIGNED_BYTE) | |
normal = Texture(width, height, 2, GL_FLOAT) | |
vertex = Texture(width, height, 4, GL_FLOAT) | |
ao = Texture(width, height, 1, GL_FLOAT) | |
gao = Texture(width, height, 1, GL_FLOAT) | |
rand = Texture(5, 5, 3, GL_FLOAT, randomTexture(5, 5)) | |
geom_fbo = FBO([albedo, normal, vertex]) | |
ao_fbo = FBO([ao]) | |
no_fbo = FBO([]) | |
# Set up the camera. | |
proj = perspective(45.0, 1.0 * width / height, 0.1, 100.0) | |
view = np.eye(4, dtype=np.float32) | |
view = rotate(view, 20, 1, 0, 0) | |
view = translate(view, 0, 0, -7) | |
frames, lastTime = 0, glfw.get_time() | |
while not glfw.window_should_close(window): | |
# Render the model. | |
glEnable(GL_DEPTH_TEST) | |
glEnable(GL_CULL_FACE) | |
geom_fbo.bind() | |
geom.bind() | |
geom['u_proj'] = proj | |
geom['u_view'] = view | |
geom['u_diffuse'] = (0.9, 0.9, 0.9, 1) | |
plane.render() | |
geom['u_diffuse'] = (1, 1, 1, 1) | |
bunny.render() | |
for cube in cubes: | |
cube.render() | |
glDisable(GL_CULL_FACE) | |
glDisable(GL_DEPTH_TEST) | |
# Compute ambient occlusion. | |
ao_fbo.bind() | |
ssao.bind() | |
ssao['u_proj'] = proj | |
ssao['u_random'] = (rand, 0) | |
ssao['u_normal'] = (normal, 1) | |
ssao['u_vertex'] = (vertex, 2) | |
quad.render() | |
# Render the model with lighting. | |
no_fbo.bind() | |
light.bind() | |
light['u_albedo'] = (albedo, 0) | |
light['u_normal'] = (normal, 1) | |
light['u_ao'] = (ao, 3) | |
quad.render() | |
glfw.swap_buffers(window) | |
glfw.poll_events() | |
# FPS counter. | |
time = glfw.get_time() | |
frames += 1 | |
if time - lastTime > 1.0: | |
print 'FPS %2f' % frames | |
frames = 0 | |
lastTime = time | |
glfw.terminate() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment