Showing posts with label glsl. Show all posts
Showing posts with label glsl. Show all posts

Tuesday, March 1, 2011

PyOpenGL Geometry Shaders - Python and OpenGL Geometry Shader

Geometry Shaders using PyOpenGL - Implementation



Extending our shader from a previous post, Using GLSL in Python PyOpenGL we can simply add geometry shaders if your video card supports such a beast.



Geometry Shader Class in Python



The following is a shader class written in Python. Its based on our previous shader class except weve made some additions to support Geometry Shaders. We will start with the includes




from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GL.ARB.framebuffer_object import *
from OpenGL.GL.EXT.framebuffer_object import *
from OpenGL.GL.ARB.vertex_buffer_object import *
from OpenGL.GL.ARB.geometry_shader4 import *
from OpenGL.GL.EXT.geometry_shader4 import *


Not sure how many of these are actually used in this class, but they are there in case. The file contains other opengl related classes so they are there. Next I will show the class itself




class shader(node) :
def __init__(self, filename):
node.__init__(self)
self.filename = filename
self.load()

def load(self, debug=False):
fh = open(self.filename)
self.source = {'vertex': '', 'fragment':'', 'geometry':''}
write = None
for line in fh :
if line == '[[vertex-program]]\n' :
write = 'vertex'
elif line == '[[fragment-program]]\n' :
write = 'fragment'
elif line == '[[geometry-program]]\n' :
write = 'geometry'
else :
self.source[write] += line

self.draw = self.init
if debug :
print self.source['vertex']
print self.source['fragment']
print self.source['geometry']



def init(self):
##compile and link shader
self.vs = self.fs = self.gs = 0

self.vs = glCreateShader(GL_VERTEX_SHADER)
self.fs = glCreateShader(GL_FRAGMENT_SHADER)
self.gs = glCreateShader(GL_GEOMETRY_SHADER_EXT)

glShaderSource(self.vs, self.source['vertex'])
glShaderSource(self.fs, self.source['fragment'])
glShaderSource(self.gs, self.source['geometry'])

glCompileShader(self.vs)
log = glGetShaderInfoLog(self.vs)
if log: print 'Vertex Shader: ', log

glCompileShader(self.gs)
log = glGetShaderInfoLog(self.gs)
if log: print 'Geometry Shader: ', log

glCompileShader(self.fs)
log = glGetShaderInfoLog(self.fs)
if log: print 'Fragment Shader: ', log

self.prog = glCreateProgram()

glAttachShader(self.prog, self.vs)
glAttachShader(self.prog, self.fs)
glAttachShader(self.prog, self.gs)

glLinkProgram(self.prog)

glUseProgram(self.prog)

self.draw = self.use

def use(self):
glUseProgram(self.prog)
uniform_location = glGetUniformLocation(self.prog, "time")
glUniform1i(uniform_location, pygame.time.get_ticks())

def end(self):
glUseProgram(0)


Explaination of OpenGL Geometry Shader GL_GEOMETRY_SHADER_EXT



First difference is in the shader.load function. We have added code to load the actual source code for the shader. Secondly we create the shader with:



self.gs = glCreateShader(GL_GEOMETRY_SHADER_EXT)


GL_GEOMETRY_SHADER_EXT seems to be the only one defined for me. We contine normally now. The main additions are to the shader source code.



OpenGL Geometry Shader source code



Each section of the code is marked by delimters [[vertex-program]], [[fragment-program]], and [[geometry-program]]. Below is the source code




[[vertex-program]]
uniform int time;

void main(void) {
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

[[geometry-program]]
#version 150
#extension GL_EXT_geometry_shader4 : enable

layout(triangles) in;
layout(line_strip, max_vertices = 20) out;

void emit(vec4 vertex) {
gl_Position = vertex;
EmitVertex();
}

void main(void) {
vec4 avg = vec4(0., 0., 0., 0.);
int i;
for(i=0; i< gl_VerticesIn; i++){
avg += gl_PositionIn[i];
}
avg /= (gl_VerticesIn * 1.0);

vec4 mid1 = (gl_PositionIn[0] + gl_PositionIn[1]) / 2.0;
vec4 mid2 = (gl_PositionIn[1] + gl_PositionIn[2]) / 2.0;
vec4 mid3 = (gl_PositionIn[2] + gl_PositionIn[0]) / 2.0;

emit(gl_PositionIn[0]);
emit(mid1);
emit(mid3);
emit(mid2);
emit(mid1);
emit(gl_PositionIn[1]);
emit(mid2);
emit(gl_PositionIn[2]);
emit(mid3);
emit(gl_PositionIn[0]);

EndPrimitive();
}


[[fragment-program]]
uniform sampler2D texture_0;

void main (void) {
vec4 cvec = texture2D(texture_0, gl_TexCoord[0].xy);
gl_FragColor = cvec;
gl_FragColor.a = 1.0;
}


Caveats with OpenGL Geometry Program, it always uses triangles



Something that took me awhile to figure out is that the geometry shader always works in triangles. I couldn't figure out why quads was not defined in GLSL. I was using GL_QUADS so I naturally figured it would exist. It does not. Internal OpenGL pipeline converts to triangles here. Maybe this is common knowledge, maybe it is not. Someone can feel free to correct me.



Other interesting things are the extra lines there




#version 150
#extension GL_EXT_geometry_shader4 : enable

layout(triangles) in;
layout(line_strip, max_vertices = 20) out;


The top comments are required for me. The first function layout() show the input, triangles as mentioned earlier. The next specifies output, which is line strip. I did this to show the effect of the shader, it will draw line strips instead of triangle_strip which would make more sense in the real world perhaps.

Wednesday, April 14, 2010

Depth texturing in OpenGL GLSL Shader Program

Depth Texturing in GLSL



This GLSL shader program is used to enable depth texturing. The depth texture is located under texture unit 5 as shown:


uniform sampler2DShadow texture_5;


Note we had to pass the light's translation matrix in gl_TextureMatrix element 0 as shown:

ShadowCoord = gl_TextureMatrix[0] * gl_Vertex;


Here are the shaders

Vertex Program



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

void main()
{
ShadowCoord = gl_TextureMatrix[0] * gl_Vertex;

/* 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_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}


Fragment Program



varying vec4 diffuse,ambient;
varying vec3 normal,lightDir,halfVector;
varying vec4 ShadowCoord;
uniform sampler2D texture_0;
uniform sampler2DShadow texture_5;

void main()
{
vec3 n,halfV;
float NdotL,NdotHV;
diffuse = texture2D(texture_0, gl_TexCoord[0].xy);
ambient = vec4(0.0, 0.0, 0.0, 0.0);


vec4 shadowCoordinateWdivide = ShadowCoord / ShadowCoord.w;
shadowCoordinateWdivide.z += 0.0001;
float distanceFromLight = shadow2DProj(texture_5, shadowCoordinateWdivide).a;

vec4 tc = shadowCoordinateWdivide;
/*
MultiSample depth texture
float s = 0.00005;
//float s = 1.0 / 10.0;
//float d = distanceFromLight;
float d1 = shadow2DProj(texture_5, vec4(tc.r+s, tc.g+s, tc.b, tc.a)).a;
float d2 = shadow2DProj(texture_5, vec4(tc.r+s, tc.g-s, tc.b, tc.a)).a;
float d3 = shadow2DProj(texture_5, vec4(tc.r-s, tc.g+s, tc.b, tc.a)).a;
float d4 = shadow2DProj(texture_5, vec4(tc.r-s, tc.g-s, tc.b, tc.a)).a;
float d5 = shadow2DProj(texture_5, vec4(tc.r+s, tc.g, tc.b, tc.a)).a;
float d6 = shadow2DProj(texture_5, vec4(tc.r+s, tc.g, tc.b, tc.a)).a;
float d7 = shadow2DProj(texture_5, vec4(tc.r-s, tc.g, tc.b, tc.a)).a;
float d8 = shadow2DProj(texture_5, vec4(tc.r-s, tc.g, tc.b, tc.a)).a;*/

/* 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;
gl_FragColor.a = diffuse.a;

float shadow = 1.0;
if(ShadowCoord.w > 0.0) {
shadow = distanceFromLight < shadowCoordinateWdivide.z ? 0.2 : 1.0 ;
}

gl_FragColor.rgb = shadow * gl_FragColor.rgb;

}

Wednesday, March 3, 2010

Using GLSL programs in python pyopengl

Here is an easy straightforward example of using GLSL shader programs with pyopengl.

To use this you will need to import the following:


from OpenGL.GL import *
from OpenGL.GLU import *


Thus we will need pyopengl package installed.



##compile and link shader
vs = fs = 0

vs = glCreateShader(GL_VERTEX_SHADER)
fs = glCreateShader(GL_FRAGMENT_SHADER)

vs_source = """
void main(void) {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
"""
fs_source = """
void main (void) {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
"""
glShaderSource(vs, vs_source)
glShaderSource(fs, fs_source)

glCompileShader(vs)
log = glGetShaderInfoLog(vs)
if log: print 'Vertex Shader: ', log

glCompileShader(fs)
log = glGetShaderInfoLog(fs)
if log: print 'Fragment Shader: ', log

prog = glCreateProgram()
glAttachShader(prog, vs)
glAttachShader(prog, fs)

glLinkProgram(prog)
glUseProgram(prog)


glShaderSource(shader_id, text) is a handy python function that will load our shader for us into opengl.


I also have a handy shader class I use in my engine program. Here it is:


class shader(node) :
def __init__(self, filename):
node.__init__(self)
fh = open(filename)
self.source = {'vertex': '', 'fragment':''}
write = None
for line in fh :
if line == '[[vertex-program]]\n' :
write = 'vertex'
elif line == '[[fragment-program]]\n' :
write = 'fragment'
else :
self.source[write] += line

self.draw = self.init

def init(self):
##compile and link shader
self.vs = self.fs = 0

self.vs = glCreateShader(GL_VERTEX_SHADER)
self.fs = glCreateShader(GL_FRAGMENT_SHADER)

glShaderSource(self.vs, self.source['vertex'])
glShaderSource(self.fs, self.source['fragment'])

glCompileShader(self.vs)
log = glGetShaderInfoLog(self.vs)
if log: print 'Vertex Shader: ', log

glCompileShader(self.fs)
log = glGetShaderInfoLog(self.fs)
if log: print 'Fragment Shader: ', log

self.prog = glCreateProgram()
glAttachShader(self.prog, self.vs)
glAttachShader(self.prog, self.fs)

glLinkProgram(self.prog)
self.use()
self.draw = self.use

def use(self):
glUseProgram(self.prog)

def end(self):
glUseProgram(0)

Wednesday, February 17, 2010

GLSL Shader Examples Vertex and Fragment

Hi Everyone,

Here is a quick example of a GLSL shader I use in my c++ 3d engine. Just thought I'd share some of the GLSL syntax with the internet community. I will fill in more information soon!

Vertex Shader


varying vec3 normal;

void main() {

normal = normalize(gl_NormalMatrix * gl_Normal);


//gl_Position = gl_ModelViewMatrix * gl_Vertex;

/*vec2 test = vec2((gl_MultiTexCoord0.xy - 0.5) * 2.0);
test.x *= 1.01;
test.y *= 1.01;
gl_Position.xy = test.xy;
gl_Position.z = 0.0;
gl_Position.w = 1.0;*/

gl_Position = ftransform();
gl_TexCoord[0] = gl_MultiTexCoord0;
}


Fragment Shader

uniform sampler2D texture_0;
uniform sampler2D texture_1;
varying vec3 normal;

void main() {
vec4 color = vec4(texture2D(texture_0, gl_TexCoord[0].xy));
float NdotL,NdotHV;
vec3 lDir = vec3(0.0, 0.0, 1.0);

if(color.r + color.g + color.b == 0.0)
color = vec4(1.0, 1.0, 1.0, 1.0);

if(color.r < 0.85)
color.r = 0.0;

color.g = color.r;
color.b = color.r;
//color.a = color.r;

vec3 n = normalize(normal);
NdotL = max(dot(n,lDir),0.0);
color.rgb *= NdotL;
gl_FragColor = color;
}