Deferred shading implementation


I have implemented simple deferred rendering.
Here is the render loop:

for (auto cam : EntitySystem::instance()->_cameras) {
	perCameraUpdate(cam);		//shadowMaps, frustum update, terrain lod, camera ubo

	//write to lightmap


This is the gBuffer:

_depthComponentTexture = new Texture(_width, _height, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_NEAREST);		
_worldNormalSpecPower = new Texture(_width, _height, GL_RGBA16F, GL_RGBA, GL_NEAREST);
_albedoSpecularIntensity = new Texture(_width, _height, GL_RGBA8, GL_RGBA, GL_NEAREST);
_3unusedShaderless = new Texture(_width, _height, GL_RGBA8, GL_RGBA, GL_NEAREST);

I don’t know if gBuffer internal format is correct. Should bytes of internal format add up? GL_RGBA16F = 8 bytes and GL_RGBA8 is 4 bytes.
Position is reconstructed from depth like this:

vec3 worldSpaceFromDepth(in float depth) {
    float z = depth * 2.0 - 1.0;

    vec4 clipSpacePosition = vec4(uv0 * 2.0 - 1.0, z, 1.0);
    vec4 direct = pc.projectionCameraMatInverse * clipSpacePosition;
    return / direct.w;

If _3unusedShaderless.a is 1 lighting is discarded and its used for debug lines, skybox etc.
The lighting is done in world space. If i do lighting in view space then normal can be encoded with spheremap transform to vec2, it might be worth it.

Point light pass, same for other lights:

void DeferredRenderer::applyPointLights(Camera* cam) {
	auto shader = _pointLightShader;
	auto pointLightsArray = Scene::instance()->_inFrustum_pointsLights;
	cam->_fbo2->getTexture().bind(4);  //read previous lights and add to current

	unsigned i = 0;
	while (true) {
		if (i == pointLightsArray.size()) break;
		//glClear(GL_COLOR_BUFFER_BIT);  //it works without clearing fbo1 for some reason

		unsigned j = 0;
		for (; i < pointLightsArray.size() && j < 50; ++i, ++j) {//batch of 50 shadowless lights
			auto pl = pointLightsArray[i];
			UniformBufferObjects::instance()->pushPointLightToUBO(pl, j);

		Graphics::instance()->fboToFbo(cam->_fbo->getFramebufferID(), cam->_fbo2->getFramebufferID()); //fbo blit, copy cur lightmap to fbo2

Light shader:

#version 330
out vec4 outColor;
void main(){
	if(int(texture2D(g_unusedShadeless, uv0).a) == 1) return;

	vec3 result = CalcDirLight();
	outColor = vec4(result + texture2D(previousDraw, uv0).xyz * blend, 1.0);

Position is reconstructed and if fragment is not in lights range its discarded.
Point and spot lights have no shadow maps for now. Lights are rendered in batches, 4 textures from gbuffer + shadowMap + lightMap = 6 textures -> 5 lights with shadows per batch.
Batching lights is way faster then rendering one by one. Blitting fbos to accumulate light seems like a hack, can this be done any other way?

I cant get transparency for sun to work. It worked it forward rendering, does it have something to do with MRTs?
Skybox is just a cube and sun a plane.
Draw sun:


Skybox shader:

#version 330
int main(){
vec4 color = texture(diffuse, uv0);
out_g_worldNormalSpecPower = vec4(0.0, 0.0, 0.0, 0.0);
out_g_albedoSpecIntesity = vec4(color);
out_g_unusedShadeless = vec4(0,0,0,1);

Sun shader:

#version 330
int main(){
vec4 difColor = texture2D(diffuseTex, uv0) * sunColor;		//sun color = glm::vec4(1, 1, 0.6, 1), diffuseTex = white halo
out_g_worldNormalSpecPower = vec4(0);
out_g_albedoSpecIntesity = vec4(, 0);
out_g_unusedShadeless = vec4(0,0,0,1);

Have yet to implement transparency.

  • sort transparent objects by z axis
  • draw them in forward rendering
  • discard every pixels by comparing them with depth from gBuffer
  • calculate all lights in frustum

Is this a good idea?

Thanks for reading!

deferred sun sun