我正在尝试实现 Epic 的游戏分割总和近似,但我陷入了 BRDF 查找纹理创建。
我已经实现了这个,但代码非常混乱,因此我决定创建某种类结构。
一切都很好。辐照度图、预滤图等...按其应有的方式生成
但是,当我尝试渲染 BRDF 纹理时,我得到以下伪像。
当我试图解决这个问题时,我偶然发现了以下文章,它描述了我的问题并建议我应该使用
glPixelStorei(GL_UNPACK_ALIGNMENT, alignment)
函数。
所以我在像这样初始化绘图调用之前已经尝试过了
void BRDF::execute() {
auto colorAttachemnt = std::make_unique<Texture2D>(512,512,GL_RG16F);
colorAttachemnt->setUnpackAlignment(2);
this->frameBuffer = std::make_unique<FrameBuffer>(colorAttachemnt->texWidth,colorAttachemnt->texHeight, this->shader,std::move(colorAttachemnt));
this->frameBuffer->drawInsideSelf();
}
其中
setUnpcackAlignment(2)
方法是在纹理的基类中声明的,如下所示
void TextureBase::setUnpackAlignment(int alignment) {
this->bind();
glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
this->unbind();
}
它不起作用,所以我开始深入研究上述 GL 函数的deocumentation,但是我不太明白该使用什么或什么对我有益,因此我在这里寻求帮助。
为了提供更多洞察,Texture2D 的创建方式如下
Texture2D::Texture2D(int width, int height, GLenum foramt): TextureBase() {
this->type = GL_TEXTURE_2D;
this->isPBRMaterial = false;
//more class properties...
// ------------------
// TEXTURE GENERATION
// -------------------
glCreateTextures(GL_TEXTURE_2D,1, &this->ID);
glBindTexture(GL_TEXTURE_2D, this->ID);
glTexStorage2D(GL_TEXTURE_2D, 1, foramt, width, height);
// ------------------
// TEXTURE PARAMETERS
// -------------------
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D,0);
}
负责渲染 BRDF 纹理本身的绘制调用位于 FrameBuffer 类中,其构造函数如下所示
FrameBuffer::FrameBuffer(int SCR_WIDTH, int SCR_HEIGHT,std::shared_ptr<Shader> customShader, std::unique_ptr<Texture2D> customColorAttachment)
this->shader = customShader;
//FRAME BUFFER CONFIG
glCreateFramebuffers(1, &this->ID);
glBindFramebuffer(GL_FRAMEBUFFER, this->ID);
// RENDER BUFFER CONFIG
this->renderBuffer = std::make_unique<RenderBuffer>(SCR_WIDTH, SCR_HEIGHT);
this->renderBuffer->bind();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, this->renderBuffer->ID);
this->colorAttachment = std::move(customColorAttachement);
this->colorAttachment->bind();
this->colorAttachment->setSamplerID(0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->colorAttachment->ID, 0);
//--------------------
// MORE CONFIGURATION
// .....
//--------------------
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
注意:渲染缓冲区存储是
GL_DEPTH24_STENCIL8
最后我在以下 FrameBuffer 方法中执行绘制调用
void FrameBuffer::drawInsideSelf() {
glViewport(0, 0, width, height);
this->bind();
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
this->objectMaterial->configureShader();
this->objectGeometry->render();
}
我没有包含 BRDF 着色器代码,因为它似乎是正确的,但如果需要,我可以稍后附加它。
如果有人感兴趣的话,这是上传到我的 iCloud 的 RenderDoc 捕获
很抱歉问了这么长的问题,非常感谢您的每一个帮助。为了获得更多说明,我附加了
BRDFTexture
#version 440 core
out vec2 FragColor;
const float PI = 3.14159265359;
in vec2 TexCoords;
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
}
vec2 Hammersley(uint i, uint N)
{
return vec2(float(i)/float(N), radicalInverse_VdC(i));
}
vec3 importanceSampleGGX(vec2 Xi, vec3 N, float rougness)
{
float a = rougness * rougness;
float phi = 2.0 * PI * Xi.x;
float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
vec3 H;
H.x = cos(phi) * sinTheta;
H.y = sin(phi) * sinTheta;
H.z = cosTheta;
vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
vec3 tangent = normalize(cross(up, N));
vec3 bitangent = cross(N, tangent);
//final sample vector that is withing the specular lobe
vec3 sampleVector = tangent * H.x + bitangent * H.y + N * H.z;
return normalize(sampleVector);
}
float GeometrySchlickGGX(float NdotV, float roughness)
{
float a = roughness;
float k = (a * a) / 2.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
//for view direction
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
//for light direction
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
vec2 IntagrateBRDF(float NdotV, float roughness)
{
vec3 V;
V.x = sqrt(1.0 - NdotV * NdotV);
V.y = 0.0;
V.z = NdotV;
float A = 0.0;
float B = 0.0;
vec3 N = vec3(0.0, 0.0, 1.0);
const uint SAMPLE_COUNT = 1024u;
for(uint i = 0u; i<SAMPLE_COUNT ; ++i)
{
//select sample semi-random sample
vec2 Xi = Hammersley(i, SAMPLE_COUNT);
//place it within the specular lobe
vec3 H = importanceSampleGGX(Xi, N, roughness);
//calculate the light direction
vec3 L = normalize(2.0* dot(V, H) * H-V);
float NdotL = max(L.z, 0.0);
float NdotH = max(H.z, 0.0);
float VdotH = max(dot(V,H), 0.0);
if(NdotL > 0.0)
{
float G = GeometrySmith(N, V, L, roughness);
float G_vis = (G* VdotH)/(NdotH * NdotV);
float Fc = pow(1.0 - VdotH, 5.0);
A += (1.0 - Fc) * G_vis;
B += Fc * G_vis;
}
}
A /= float(SAMPLE_COUNT);
B /= float(SAMPLE_COUNT);
return vec2(A,B);
}
void main()
{
vec2 integratedBRDF = IntagrateBRDF(TexCoords.x, TexCoords.y);
FragColor = vec4(integratedBRDF, 0.0, 1.0);
}
最后注意:我有 glError 验证,并且我没有收到任何错误,可以 100% 确定我已经通过 RenderDoc 运行了这个,并且一切看起来都是正确的,除了......,好吧,BRDF 纹理
需要注意的重要一点是,在
Fragement Shader
中,我的输出变量是
vec2
类型,出于某种原因,这适用于我第一次编写此内容的
OpenGL 3.3
。但是,当我将
OpenGL
版本更新为4.6 时,它停止工作,直到我指定在本例中
Fragment Shader
的输出变量为
vec2 FragColor
类型,因此它将变为
vec4
随后修正后的
vec4 FragColor
应该看起来像这样
Fragment Shader