When using screen space global illumination, alpha blended surface become a problem because you can’t defer the ray evaluation. Doing the SS casts in a forward pass isn’t an option because of the high performance need and inability of temporal smoothing.
Therefore, we implemented a system that collects stochastic queries of all currently rendered objects. The queries are triggered at a random point inside the bounding volume of the object. We then merge those “points of interest”, to get a linearly decreasing density if you are far away from the viewer.
The cubemaps are then updated once the viewer does not move anymore. We do not update cubemaps outside the frustum and prefer the nearest ones.
We pick the nearest two cubemaps per alpha blended object and calculate the influence in the vertex shader as follows:
float DistNear = length(PositionWS-nearest_pos); float DistSecond = length(PositionWS-second_pos); blendVal=DistNear / (DistNear + DistSecond + epsilon);
For opaque objects, we render the cubemaps in a deferred pass into a RGB16F specular and a RGBA16F diffuse texture. We use blending to simple add the intensity values.
The cubemap radius is dependant of the mentioned density to get an average overlap of 1.5 cubemaps. We use a weighting of 1 from the center to half of the radius and smoothstep to zero from the half of the radius to the sphere’s boundary. The diffuse alpha channel stores the cubemap intensity. In the compositing process we do the following to normalize the overlapping cubemaps and add the skybox where we miss information:
vec4 CubeMapDiff = texture ( tIBL_cubeDiff, TexCoord); float normalizationFactor = 1.0; float skyBoxInfluence = 0.0; if(CubeMapDiff.w > 1.0) normalizationFactor /= CubeMapDiff.w; else skyBoxInfluence = pow(1.0 - CubeMapDiff.w, 3.0); vec3 cubemapInfluence = normalizationFactor*CubeMapDiff.xyz + skyBoxInfluence*diffuseCubeLookup(skybox, normalWS);
We then simply use Krzysztof Narkowicz’s Analytic DFG term for the final compositing step.
Unfortunately, we introduce some light bleeding for the blended cubemaps.
We clamp the cubemap influence based on the angle from the center to the surface, as done in a classic lambert term.
blendValue *= max(dot(ViewMatrixInv*normalize(deltaToCenter), -surfaceNormalWS), 0.0); ibl_diff = vec4(diffusePart * blendValue, blendValue); ibl_spec = specCubeLookup(cubeMap, reflect(ViewMatrixInv*PosSS, surfaceNormalWS), roughness) * blendValue;