Sign in to follow this  
openvisiongames

Global Illumination (Voxel Cone Tracing) in OpenGL

Recommended Posts

Hi all, 

 

just wanted to show you guys some screenshots and some of my code for global illumination in OpenGL. I am using LWJGL, so my code is in Java.

 

I was inspired to implement this into my game engine after watching Unreal Engine footage.

After finding some sample code for OpenGL in C++, I was more intrigued and decided to implement my own version. 

 

6f5RqIi.png?1

 

xmWejYP.jpg?1

 

 

 

Here is some of my code for building the textures:

for (int i = 0; i < 2; i++) {
			textures[i] = GL11.glGenTextures();
			glBindTexture(GL_TEXTURE_3D, textures[i]);
			glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, TextureFilter.LinearMipMapLinear);
			glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, TextureFilter.LinearMipMapLinear);
			glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
			glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
			glTexStorage3D(GL_TEXTURE_3D, 1, GL_RGBA32F, 128, 128, 128);
			GL30.glGenerateMipmap(GL_TEXTURE_3D);
			glBindTexture(GL_TEXTURE_3D, 0);
			gilight[i] = new GIRenderer(rm, textures, voxelShader, i, viewCam, camDir[i]);
			gilight[i].init();
		}

This is where I set up the two (for now) 3D textures. I have a camera facing one side of the scene, and another facing the opposite side of the scene.

When the scene is rendered for each camera, the pixels that are rendered are stored into it's respective 3D texture.

 

I then bind the 3D textures to the forward lighting shader, here is what this looks like.

uniform sampler3D tex[2];

I then trace a cone into the 3D texture to retrieve reflections. (Credit to Alex Nankervis, found this code on geeks3D)

vec4 voxelTraceCone(float minVoxelDiameter, vec3 origin, vec3 dir, float coneRatio, float maxDist)
{
	float minVoxelDiameterInv = 1.0/minVoxelDiameter;
	vec3 samplePos = origin;
	vec4 accum = vec4(0.0);
	float minDiameter = minVoxelDiameter;
	float startDist = minDiameter;
	float dist = startDist;
	vec4 fadeCol = ambientLight.color*0.2;
	while (dist <= maxDist && accum.w < 1.0)
	{
		float sampleDiameter = max(minDiameter, coneRatio * dist);
		float sampleLOD = log2(sampleDiameter * minVoxelDiameterInv);
		vec3 samplePos = origin + dir * dist;
		vec4 sampleValue = voxelFetch(samplePos, -dir, sampleLOD);
		sampleValue = mix(sampleValue,fadeCol, clamp(dist/maxDist-0.25, 0.0, 1.0));
		float sampleWeight = (1.0 - accum.w);
		accum += sampleValue * sampleWeight;
		dist += sampleDiameter;
	}
	return accum;
}

I hope this helped some people understand how voxel cone tracing works in some more simpler terms. It was a real task to implement this and I still have some work to do, but as you can see from my screenshots, it works well so far!

Share this post


Link to post
Share on other sites
ongamex92    3256

BTW I think the method should be called voxel cone marching.

I've never implemented? this effect, the only part I personally don't know how to do is the voxelization, could you explain how it is done?

 

EDIT :

And Welcome to GDNet!

Edited by imoogiBG

Share this post


Link to post
Share on other sites

BTW I think the method should be called voxel cone marching.

I've never implemented? this effect, the only part I personally don't know how to do is the voxelization, could you explain how it is done?

 

EDIT :

And Welcome to GDNet!

 

Hey, thanks for the welcome! smile.png

 

As it turns out, you don't actually have to voxelize anything

 

However, you will have to render your scene twice, with two different shaders - Let's call the first one the voxel shader, and the second one be the gi shader

The first time we render, we use the voxel shader, to store the "voxels" into the 3D texture. (almost like rendering to a framebuffer, in a way)

And the second time, we use the gi shader to render your scene like normal, and perform the "cone tracing" part.

 

You will have to use the imageStore function in your voxel shader to store the "voxels" in the 3D texture.This is what my code essentially looks like:

layout(binding = 0, rgba32f) uniform image3D currentVoxelTex;
uniform vec3 u_probePos;
varying vec3 worldPos;
void main() 
{
     int voxelImageSize = 128;
     int halfVoxelImageSize = voxelImageSize/2;

     // .. all your lighting, texturing code etc... 

     imageStore(currentVoxelTex, ivec3(worldPos.x, worldPos.y, worldPos.z)+ivec3(halfVoxelImageSize), color);
	

} 

This is what my code looks like for binding that "image3D" to the shader.

glBindImageTexture(0, textures[index], 0, true, 0, GL_READ_WRITE, GL_RGBA32F);

Also, I use a compute shader to clear the 3D texture every frame - However this is not necessary if all the geometry is stationary.

 

 

Hope that helps - let me know if you want to hear more, I would love to explain more, either way! biggrin.png  

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this