I'm implementing Real-Time Polygonal-Light Shading with Linearly Transformed Cosines from Siggraph 2016.

I'm stuck on the texture prefiltering (section 5.3).

As I understand they use Gaussian Blur on the texture, but clamp and normalize kernel to fragments that intersect the original texture.

But how do they create the margin? I have tried proper Gaussian with 2D kernel and 2 pass Gaussian but neither seems similar to what is in the paper.

Also how should weight of Gaussian be chosen?

Or maybe I'm totally wrong and it is something more than simple Gaussian?

I have ported their WebGL demo in DX shader model 4, but I have not implemented such texture preprocessing. Instead, I use some abstract pattern (like a comuflage) that is twice wider and higher. So, in a reflected area only internal part is rendered if roughness is low, and with a greater roughness, mip-map with lower resolution is taken. That spreads pattern in zone of reflection realistically. It could be some starting point for your implementation.

It is possible to make some "texture expansion" like in article, but some special code to do this is needed. This is "special", it modifies Gaussian.

But in many cases, reflected pattern could be very simple, and such expansion can be eailly created by artist and mip chain generated. Again -good for start point.

I achieved similar result to theirs. First, the target texture for mip generation is base texture size + two margins, each equals 0.125 of base size.

Then during generation of base mip level, there are two cases:

  • inside the square, then just sample original texture,
  • beyond the central square, then blur the original texture with varying sigma, which depends on distance of current fragCoord from central square borders. While blurring texcoord is clamped to central square borders.

How to compute distance: https://gamedev.stackexchange.com/a/44496/91268

Gaussian one pass blur I used: https://www.shadertoy.com/view/XdfGDH

Here are code fragments:

vec2 coord = gl_FragCoord.xy;
vec2 minCorner = margin;
vec2 maxCorner = filteredMipRes - margin;
if(insideBox(coord, minCorner, maxCorner) > 0.5)
    fragColor = textureLod(sourceTexture, tc, 0.0).rgb;
    vec2 center = filteredMipRes / 2.0;
    vec2 d = max(abs(coord - center) - baseTextureRes / 2.0, vec2(0.0));
    float dist = sqrt(d.x * d.x + d.y * d.y);
    sigma = max(dist*2.0, 0.5);
    fragColor = blur(sigma);

Rest of mipmaps are just equally gaussian blurred, each based on one level higher mip.

Your Answer


By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.