Because it's Xmas!
Pre-requiries: http://pyopengl.sourceforge.netSource code: https://github.com/cudaopencl/pyRayCaster
Vertex shader
#version 120
varying vec3 normal;
attribute vec2 a_position;
varying vec2 v_position;
void main()
{
normal = gl_NormalMatrix * gl_Normal;
gl_Position = vec4(a_position, 0.0, 1.0);
v_position = a_position;
}
Fragment shader:
#version 120
varying vec2 v_position;
uniform float u_fov = 0.0;
uniform float u_timer = 0.0;
uniform vec3 u_origin = vec3( 0.f, 0.f, -4.f );
uniform float u_rotation_x = 0.0;
uniform float u_rotation_y = 0.0;
uniform float u_rotation_z = 0.0;
vec3 rotate( in vec3 source, in vec3 angles )
{
vec3 cosAngles = vec3( cos(angles.x), cos(angles.y), cos(angles.z));
vec3 sinAngles = vec3( sin(angles.x), sin(angles.y), sin(angles.z));
float x,y,z;
vec3 returnValue = source;
// Y axis
z = source.z*cosAngles.y - source.x*sinAngles.y;
x = source.z*sinAngles.y + source.x*cosAngles.y;
returnValue.z = z;
returnValue.x = x;
// X axis
z = returnValue.z*cosAngles.x + returnValue.y*sinAngles.x;
y = returnValue.z*sinAngles.x - returnValue.y*cosAngles.x;
returnValue.z = z;
returnValue.y = y;
return returnValue;
}
vec4 calc_mandel(vec3 o, vec3 d, float t)
{
float X = o.x + d.x * t;
float Y = o.y + d.y * t;
float Z = o.z + d.z * t;
vec2 uv = vec2(X*0.2f+0.5f,Y*0.2f+0.5f);
float scale = 1.f; //512 / 512;
uv=((uv-0.5)*5.5);
uv.y*=scale;
uv.y+=0.0;
uv.x-=0.5;
vec2 z = vec2(0.0, 0.0);
vec3 c = vec3(0.0, 0.0, 0.0);
float v;
float I=0.f;
for(int i=0;(i<100);i++)
{
if(((z.x*z.x+z.y*z.y) >= 4.0+cos(u_timer*3.f))) break;
z = vec2(z.x*z.x - z.y*z.y, 2.0*sin(u_timer*2.6f)*z.y*z.x) + uv;
if((z.x*z.x+z.y*z.y) >= 2.0)
{
c.b=float(i)/20.0;
c.r=sin((float(i)/5.0));
}
I += 1.f+cos(u_timer);
}
if(I<Z)
return vec4(c.b,c.r,0.f,1.0);
else
return vec4(0.f,0.f,c.r,0.f);
}
vec4 wave( vec3 o, vec3 d, float t )
{
float x = o.x + d.x * t;
float y = o.y + d.y * t;
float z = o.z + d.z * t;
float h = cos(x+u_timer)*sin(z);
if(y<h) {
float a = 0.2f*(h/(1.f+t*2));
return vec4(a,a,a,0);
}
return vec4(0.f,0.f,0.f,0.f);
}
void main()
{
vec3 angles = vec3(u_rotation_x,u_rotation_y,u_rotation_z);
vec2 pos = v_position;
vec3 origin = u_origin;
origin.z = u_fov;
origin = rotate( origin, angles );
vec3 dir = (vec3(pos.x, pos.y, u_fov+2.f)-origin);
dir = rotate( dir, angles );
bool found=false;
vec4 color = vec4(0.f,0.f,0.f,0.f);
for( float t=1.f; t<10.f; t+=.5f)
{
//color += 5.f*wave(origin, dir, t);
color += 0.3f*calc_mandel(origin, dir, t);
}
gl_FragColor = color;
}
Python code:
from OpenGL.GLUT import *
from OpenGL.GLU import *
from OpenGL.GL import shaders
import sys
SHADER_NAME = 'raycaster'WINDOW_NAME = 'RayCaster'
class Application:
def __init__(self):
self.geometryRotation = [0.0, 0.0, 0.0]
self.timer = 0 self.fov = -2.0 self.old = [0.0, 0.0, 0.0]
self.shader = None self.origin = [0.0, 0.0, -1.0]
@staticmethod def read_shader_from_file(filename):
file_handle = open(filename, 'r')
file_content = file_handle.read()
file_handle.close()
return file_content
def mouse(self, button, status, x, y):
self.old[0] = x
self.old[1] = y
def motion(self, x, y):
#self.fov += (self.old[1] - y) / 500.0 self.geometryRotation[1] -= (self.old[0] - x) / 100.0 self.geometryRotation[0] -= (self.old[1] - y) / 100.0 self.old[0] = x
self.old[1] = y
def keyboard(self, key, x, y):
print key + " pressed" if key == 'q':
sys.exit()
if key == 'r':
self.compile_shaders()
def compile_shaders(self):
print "Initializing shaders" vertex_shader = shaders.compileShader(
self.read_shader_from_file(
'shaders/' + SHADER_NAME + '.vert'), GL_VERTEX_SHADER)
fragment_shader = shaders.compileShader(
self.read_shader_from_file(
'shaders/' + SHADER_NAME + '.frag'), GL_FRAGMENT_SHADER)
self.shader = shaders.compileProgram(vertex_shader, fragment_shader)
glUseProgram(self.shader)
def run(self):
# self.shader = shaders.compileProgram(VERTEX_SHADER,FRAGMENT_SHADER) width = 512 height = 512 glutInit(sys.argv)
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH)
glutInitWindowSize(width, height)
glutCreateWindow(WINDOW_NAME)
glClearColor(0.2, 0.2, 0.2, 1.0)
glShadeModel(GL_SMOOTH)
glEnable(GL_CULL_FACE)
glEnable(GL_DEPTH_TEST)
glutIdleFunc(self.display)
glutDisplayFunc(self.display)
glutMouseFunc(self.mouse)
glutMotionFunc(self.motion)
glutKeyboardFunc(self.keyboard)
glMatrixMode(GL_PROJECTION)
gluPerspective(45.0, float(width) / float(height), 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)
gluLookAt(0, 0, 10, 0, 0, 0, 0, 1, 0)
glPushMatrix()
self.compile_shaders()
print "Entering main loop..." glutMainLoop()
def display(self):
self.timer += 0.01 loc = glGetUniformLocation(self.shader, 'u_timer')
glUniform1f(loc, self.timer)
loc = glGetUniformLocation(self.shader, 'u_rotation_x')
glUniform1f(loc, self.geometryRotation[0])
loc = glGetUniformLocation(self.shader, 'u_rotation_y')
glUniform1f(loc, self.geometryRotation[1])
loc = glGetUniformLocation(self.shader, 'u_rotation_z')
glUniform1f(loc, self.geometryRotation[2])
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
color = [1.0, 1.0, 1.0, 1.0]
glMaterialfv(GL_FRONT, GL_DIFFUSE, color)
# Draw scene glPushMatrix()
size = 1 depth = 0.0 glBegin(GL_QUADS)
glVertex3f(-size, -size, depth)
glNormal3f(0.0, 0.0, -1.0)
glVertex3f(size, -size, depth)
glNormal3f(0.0, 0.0, -1.0)
glVertex3f(size, size, depth)
glNormal3f(0.0, 0.0, -1.0)
glVertex3f(-size, size, depth)
glNormal3f(0.0, 0.0, -1.0)
glEnd()
glPopMatrix()
glutSwapBuffers()
if __name__ == '__main__':
app = Application()
app.run()