# Specular convolution problem.

This topic is 639 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hello guys! Another IBL / convolution topic here.

I was doing specular-ibl-ggx-convolution and had encountered following artefact. It happens on +Z and -Z faces.

This is -Z cubemap face viewed in Renderdoc (resolutions are 256x256, 128x128, 64x64, cubemap is little bit washed out because it is in float16 hdr format, so I had changed range to better visualize artefact):

[attachment=35679:bug.png]

In final rendered image looks like that:

[attachment=35680:bug1.PNG]

Looks like it "swirls" at poles along Z axis. Diameter of the "dot" in the middle depends on 0.999 number from:

float3 tangentY = ((abs(N.z) < 0.999) ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0));


Code for convolution is pretty standard:

uint ReverseBits(in uint bits)
{
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);

return bits;
}

float2 Hammersley(in uint i, in uint N)
{
return float2(float(i) / float(N), float(ReverseBits(i)) * 2.3283064365386963e-10);
}

float3 ImportanceSampleGGX(in float roughness, in float2 eta)
{
float phi = 2.0 * PI * eta.x;
float cosTheta = sqrt((1.0 - eta.y) / ((roughness * roughness - 1.0) * eta.y + 1.0));
float sinTheta = sqrt(1.0 - cosTheta * cosTheta);

float3 H = float3(sinTheta * cos(phi),  sinTheta * sin(phi), cosTheta);

return H;
}

float TrowbridgeReitzGGX(in float roughness, in float NdotH)
{
float a2 = roughness * roughness;
float denominator = (a2 * NdotH - NdotH) * NdotH + 1.0;

return a2 / (PI * denominator * denominator);
}

float3 ConvolveEnvironmentMap(in float linearRoughness,
in float3 V,
in SamplerState trilinearSampler,
in TextureCube<float4> environmentMap,
in float currentResolution,
in float activeMipsCountZeroBased,
in uint currentMip)
{
float3 N = V;

float3 tangentY = ((abs(N.z) < 0.999) ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0));
float3 tangentX = normalize(cross(tangentY, N));
tangentY = cross(N, tangentX);

float3x3 W = float3x3(tangentX, tangentY, N);

float roughness = linearRoughness * linearRoughness;

float3 Integral = float3(0.0, 0.0, 0.0);
float Weight = 0.0;

const uint samplesCount = 32;
for (uint i = 0; i < samplesCount; i++)
{
float2 eta = Hammersley(i, samplesCount);
float3 H = mul(ImportanceSampleGGX(roughness, eta), W);
float3 L = 2.0 * dot(V, H) * H - V;
float NdotL = saturate(dot(N, L));
float NdotH = saturate(dot(N, H));
float LdotH = saturate(dot(L, H));
float pdf = DTrowbridgeReitzGGX(roughness, NdotH) * 0.25;
float sampleSolidAngle = 1.0 / (float(samplesCount0) * pdf);
float texelSolidAngle = (4.0 * PI) / (6.0 * currentResolution * currentResolution);
float environmentMapMipLevel = clamp(0.5 * log2(sampleSolidAngle / texelSolidAngle), 0.0, activeMipsCountZeroBased);

float3 Li = environmentMap.SampleLevel(trilinearSampler, L, environmentMapMipLevel).rgb;

Integral += Li * NdotL;
Weight += NdotL;
}

return Integral / Weight;
}


cbuffer ConvolutionData : register(b0)
{
...
}

SamplerState LinearClampSampler : register(s0);

TextureCube Input : register(t0);

RWTexture2DArray<float4> CurrentMipSlice : register(u0);

void cs_main(
uint3 groupID : SV_GroupID,
uint groupIndex : SV_GroupIndex)
{
const float2 uv = ((float2(dispatchThreadID.xy) + 0.5) * InvCubemapResolution) * 2.0 - 1.0;

const float3 V0 = normalize(float3(1.0, -uv.y, -uv.x));
const float3 V1 = normalize(float3(-1.0, -uv.y, uv.x));
const float3 V2 = normalize(float3(uv.x, 1.0, uv.y));
const float3 V3 = normalize(float3(uv.x, -1.0, -uv.y));
const float3 V4 = normalize(float3(uv.x, -uv.y, 1.0));
const float3 V5 = normalize(float3(-uv.x, -uv.y, -1.0));

CurrentMipSlice[uint3(dispatchThreadID.xy, 0)] = float4(ConvolveEnvironmentMap(V0, ...), 1.0);
CurrentMipSlice[uint3(dispatchThreadID.xy, 1)] = float4(ConvolveEnvironmentMap(V1, ...), 1.0);
CurrentMipSlice[uint3(dispatchThreadID.xy, 2)] = float4(ConvolveEnvironmentMap(V2, ...), 1.0);
CurrentMipSlice[uint3(dispatchThreadID.xy, 3)] = float4(ConvolveEnvironmentMap(V3, ...), 1.0);
CurrentMipSlice[uint3(dispatchThreadID.xy, 4)] = float4(ConvolveEnvironmentMap(V4, ...), 1.0);
CurrentMipSlice[uint3(dispatchThreadID.xy, 5)] = float4(ConvolveEnvironmentMap(V5, ...), 1.0);
}


Sample count is pretty low (32), but changing it to 64 doesn't really help. Also because I'm using FIS approach, it should look better than that, I think.

[attachment=35681:bug2.PNG]

Appreciate any help, thanks!

Edited by xxxkxxx

##### Share on other sites
locking thread at request of OP (solved)

##### Share on other sites

(Unlocking so he can post his solution to the problem)

##### Share on other sites

Problem was solved by changing

float texelSolidAngle = (4.0 * PI) / (6.0 * currentResolution * currentResolution);
float environmentMapMipLevel = clamp(0.5 * log2(sampleSolidAngle / texelSolidAngle), 0.0, activeMipsCountZeroBased);


to

float texelSolidAngle = (4.0 * PI) / (6.0 * Mip0Resolution * Mip0Resolution);
float environmentMapMipLevel = max(0.5 * log2(sampleSolidAngle / texelSolidAngle) + 1.0, 0.0);


• ### What is your GameDev Story?

In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

• 28
• 16
• 10
• 10
• 11
• ### Forum Statistics

• Total Topics
634111
• Total Posts
3015575
×