IBL探针BRDF预集成工件

我正在使用OpenGL中的“渲染引擎”来执行“ 移动Frostbite到PBR课程笔记 ”来实现IBL,但是我在预处理方程的镜面reflection部分时遇到了一些麻烦。

正如您从下一张图片中看到的那样,问题在预过滤的重要性采样的立方体贴图结果的mipmap中可见。

这是正面的X面MIP链: 在这里输入图像说明

这是负面的X面MIP链: 在这里输入图像说明

这是我使用的环境地图(它是以HDR格式dynamic创建的基于物理的天空纹理): 在这里输入图像说明

这是环境地图的正面X面的mip链: 在这里输入图像说明

正如你所看到的,在正面X面的mips的右侧看起来好像采样方向类似于mip左侧的采样方向。

另外,正X面的第二个mip上可以看到那些“点状”形状,我认为这是由于样本数量太less造成的?

这是我用来预集成镜像IBL的代码:

float radicalInverse_VdC(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 float(bits) * 2.3283064365386963e-10; // / 0x100000000 } // // Attributed to: // http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html // Holger Dammertz. // vec2 Hammersley(uint i, uint N) { return vec2(float(i)/float(N), radicalInverse_VdC(i)); } // Based on GGX example in: // http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf vec3 importanceSampleGGX(vec2 u, float roughness, vec3 N, vec3 upVector, vec3 tangentX, vec3 tangentY) { float a = roughness * roughness; float phiH = ux * PI * 2.0f; float cosThetaH = sqrt((1.0f - uy) / (1.0f + (a * a - 1.0f) * uy)); float sinThetaH = sqrt(1.0f - min(1.0f, cosThetaH * cosThetaH)); vec3 H = vec3(sinThetaH * cos(phiH), sinThetaH * sin(phiH), cosThetaH); H = normalize(tangentX * Hx + tangentY * Hy + N * Hz); return H; } // D(h) for GGX. // http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html float D_GGX(float roughness, float NdotH) { float a = roughness * roughness; float a2 = a * a; float NdotH2 = NdotH * NdotH; float f = 1.0f + (NdotH2 * (a2 - 1.0f)); return a2 / (f * f); } float D_GGX_Divide_Pi(float roughness, float NdotH) { return D_GGX(roughness, NdotH) / PI; } vec3 ImportanceSample (vec3 N) { vec3 V = N; float size2 = ConvolutionSrcSize * ConvolutionSrcSize; vec3 upVector = abs(Nz) < 0.999 ? vec3(0.0f, 0.0f, 1.0f) : vec3(1.0f, 0.0f, 0.0f); vec3 tangentX = normalize(cross(upVector, N)); vec3 tangentY = cross(N, tangentX); vec3 accBrdf = vec3(0.0f); float accBrdfWeight = 0.0f; float roughness = ConvolutionRoughness; uint samplesCount = uint(ConvolutionSampleCount); for(uint i = uint(0); i < samplesCount; i++) { vec2 eta = Hammersley(i, samplesCount); vec3 H = importanceSampleGGX(eta, roughness, N, upVector, tangentX, tangentY); vec3 L = 2.0f * dot(V, H) * H - V; float NdotL = dot(N, L); if(NdotL > 0.0f) { float NdotH = saturate(dot(N, H)); float LdotH = saturate(dot(L, H)); float pdf = D_GGX_Divide_Pi(roughness, NdotH) * NdotH / (4.0f * LdotH); float omegaS = 1.0f / (samplesCount * pdf); float omegaP = 4.0f * PI / (6.0f * size2); float mipLevel = roughness == 0.0f ? 0.0f : clamp(0.5f * log2(omegaS / omegaP), 0.0f, ConvolutionMipCount); vec4 Li = textureLod(ConvolutionSrc, L, mipLevel); accBrdf += Li.rgb * NdotL; accBrdfWeight += NdotL; } } if(accBrdfWeight > 0.0f) return accBrdf * (1.0f / accBrdfWeight); else return accBrdf; } void main() { // VertexIn.textureCoord is the normal of a sphere I use as mesh to draw to the IBL cubemap FragColor = vec4(ImportanceSample(VertexIn.textureCoord), 1.0f); }