volumeLight.png
defferedRendering.png

My favorite parts of game development are writing graphics and writing gameplay. Below i have some examples of graphics programming. Gameplay is much more suited for viewing then reading, so please check out my homepage for a video on Illusion, a Unity game that showcases my skills as a gameplay programmer. But for now...GRAPHICS.

All code on this page is C++ or GLSL using OpenGL version 3.3.

Volume Light

Volume light is a post process where you simulate the effect that is created when particles pass through light. This basic sample shows the shader code i used to implemented the technique

#define MAX_STEPS 2000

vec3 GetPos()
{
vec2 coords;
coords.x = texCoord.s * WIDTH;
coords.y = texCoord.t * HEIGHT;
ivec2 iCoords = ivec2(coords.x,coords.y);
// index into the gBufferPos texture correctly to grab the appropiate texel for final rendering
vec3 pos = texelFetch(gBufferPos,iCoords,0).xyz;
return pos;
}

float VolumeLight()
{
vec3 vertexPosWS = GetPos();

// gets initial vector for screen trace
vec3 forwardVec = normalize(vertexPosWS - cameraPos);
// scale vector to nearPlane(to start trace)
float traceDistance = dot(vertexPosWS - (cameraPos + forwardVec * camNear),forwardVec);
traceDistance = clamp(traceDistance, 0.0,2500.0); // clamp at max traceDistance

// get first trace position.
vertexPosWS = cameraPos + forwardVec * camNear;
float samplingRate = 1.0f;
// NOTE: make sampling rate a modifable variable, pass it in from cpu, maybe make a gui
forwardVec *= samplingRate * 2.0f;
float numberOfSteps = min(traceDistance / length(forwardVec),MAX_STEPS);

//add jitter, this wil benefit from having a noise texture, but a rand might do
float seed = 1234.0f;
float jitter = noise1(seed) * 256.0f;
//float jitter = 1.0f;

float step = length(forwardVec);
float baseBrightness = 0.0009 ; // minimum brightness for samples???
float scale = step * baseBrightness;

vec3 curPos = vertexPosWS + forwardVec * jitter;
vec4 coordinates = ShadowProjMatrix*ShadowViewMatrix*vec4(curPos.x,curPos.y,curPos.z,1.0f);
coordinates.z = curPos.z;

curPos = vertexPosWS + forwardVec * (1.0f + jitter);
vec4 coordinatesEnd = ShadowProjMatrix*ShadowViewMatrix*vec4(curPos.x,curPos.y,curPos.z,1.0f);
coordinatesEnd.z = curPos.z;


vec4 coordinateDelta = coordinatesEnd - coordinates;

float sample;
float light = 0;

for(float i = 0; i < numberOfSteps; ++i)
{

vec2 tc = coordinates.st / coordinates.q;
sample = float(texture2D(ldepth, tc).x < coordinates.z); // float() is a cast from bool to float
if(sample > 0)
light += scale * sample;
coordinates += coordinateDelta;
}


return light;
}

void main()
{
vec3 worldVertex = GetPos();

float light = VolumeLight(); // calculates volume light value of this pixel by tracing the screen

fragColor.xyz = vec3(light,light,light);
fragColor.w = 1.0;
}

Although the above produces a nice effect, it is very basic and there is a lot of optimization that can be done like using a shadow map hierarchy to smooth out the caustic rays and do some fancy prediction to end the traces sooner. 

Deferred Rendering

Here we have a piece of a basic deferred renderer. I will show how i set FBO's to draw upon them and some shader code i use to make my G-Buffer.


///////////////////////////////////////////////////////////////////
// G-BUFFER PASS
///////////////////////////////////////////////////////////////////


program = scene.shaderGBUFFER.program;

// Use GBuffer shader
scene.shaderGBUFFER.Use();
CHECKERROR;
GraphicsSystemNS::g_GraphicsGL->fbo_gBuffer.Bind();
CHECKERROR;

// Set the viewport, and clear the screen
glViewport(0,0,scene.width, scene.height);
glClearColor(0.0,0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);

// load matrices
loc = glGetUniformLocation(program, "ProjectionMatrix");
glUniformMatrix4fv(loc, 1, GL_FALSE, value_ptr(WorldProj));
loc = glGetUniformLocation(program, "ViewMatrix");
glUniformMatrix4fv(loc, 1, GL_FALSE, value_ptr(WorldView));
loc = glGetUniformLocation(program, "ViewInverse");
glUniformMatrix4fv(loc, 1, GL_FALSE, value_ptr(WorldInv));
CHECKERROR;

loc = glGetUniformLocation(program, "ModelMatrix");
glUniformMatrix4fv(loc, 1, GL_FALSE, value_ptr(Identity));
loc = glGetUniformLocation(program, "NormalMatrix");
glUniformMatrix4fv(loc, 1, GL_FALSE, value_ptr(Identity));
CHECKERROR;

// set color attachment( determines which textures in the current fbo we draw to )
GLuint glArray[] = {GL_COLOR_ATTACHMENT0_EXT,
GL_COLOR_ATTACHMENT1_EXT,
GL_COLOR_ATTACHMENT2_EXT};

glDrawBuffers(3,glArray);
// Draw the scene objects.
DrawSun(scene, program, SunModelTr);
GraphicsSystemNS::g_GraphicsGL->DrawLightModels(program);
DrawSpheres(scene, program, SphereModelTr);
DrawGround(scene, program, Identity);
DrawCentral(scene, program, scene.centralTr);
GraphicsSystemNS::g_GraphicsGL->DrawLightModels(program);
DrawGround(scene, program, field1);
DrawGround(scene, program, field2);
DrawGround(scene, program, field3);
DrawGround(scene, program, field4);
DrawGround(scene, program, daBasementTransform);
//DrawGround(scene, program, shadowTestGeo);
CHECKERROR;

// Done with shader program
GraphicsSystemNS::g_GraphicsGL->fbo_gBuffer.Unbind();
scene.shaderGBUFFER.Unuse();
CHECKERROR;

Shader Code use to render to GBuffer

Fragment Shader:

uniform vec3 phongDiffuse;
uniform bool useTexture;
uniform sampler2D groundColor;

in vec3 normalVec;
in vec4 pos;

out vec4 f_vertexPos;
out vec4 f_vertexNormal;
out vec4 f_vertexDiffuse;

in vec2 texCoord;

void main()
{
vec3 Kd = phongDiffuse;
if (useTexture)
{
Kd = texture(groundColor,2.0*texCoord.st).xyz;
}

f_vertexPos.xyz = pos.xyz;
f_vertexPos.w = 1.0f;

f_vertexNormal.xyz = normalVec;
f_vertexNormal.w = 1.0f;

f_vertexDiffuse.xyz = Kd;
}

 

If you want to find out more on how i implemented these techniques, download these cpp's and shader files:
VolumeLight.frag

GBuffer.frag

GBuffer.vert

Render scene function