Deferred shading implementation

Hi,

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
	drawToGBuffer(cam);

	//write to lightmap
	applyDirectionalLights(cam);
	applySpotlights(cam);
	applyPointLights(cam);

	deferredShading(cam);
}

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.xyz / 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;
	
	shader->bind();
	shader->updateUniforms();
	shader->setBlend(1);
	cam->_fbo->bindFbo();
	cam->getGBuffer().bindGBufferReadAt(0);
	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);
		}
		UniformBufferObjects::instance()->setPointLightsCount(j);
		UniformBufferObjects::instance()->updatePointLightUBO();

		_ndsQuad->draw();
		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;
	initData();

	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:

glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Assets::instance()->fetchMesh("NDS_QUAD")->draw();
glDisable(GL_BLEND);

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(difColor.xyz, 0);
out_g_unusedShadeless = vec4(0,0,0,1);
}

Have yet to implement transparency.
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