为什么 OpenSceneGraph 将所有 Sampler2D 映射到第一个纹理

Why does OpenSceneGraph map all Sampler2D to the first texture

我目前正在用 OpenSceneGraph (3.4.0) 和我自己的 glsl (330) 着色器编写程序。 它使用多个纹理作为输入,然后使用预渲染相机进行多个渲染目标渲染,并使用第二个相机读取这些多个渲染目标纹理以进行延迟着色。因此,两个相机都有自己的着色器(此处称为 geometry_pass 和 lighting_pass)。 我的问题:两个着色器在阅读时在所有 sampler2D 制服中使用相同的纹理。

//in geometry_pass.frag
uniform sampler2D uAlbedoMap;
uniform sampler2D uHeightMap;
uniform sampler2D uNormalMap;
uniform sampler2D uRoughnessMap;
uniform sampler2D uSpecularMap;
[...]
layout (location = 0) out vec4 albedo;
layout (location = 1) out vec4 height;
layout (location = 2) out vec4 normal;
layout (location = 3) out vec4 position;
layout (location = 4) out vec4 roughness;
layout (location = 5) out vec4 specular;
[...]
albedo = vec4(texture(uAlbedoMap, vTexCoords).rgb, 1.0);
height = vec4(texture(uHeightMap, vTexCoords).rgb, 1.0);
normal = vec4(texture(uNormalMap, vTexCoords).rgb, 1.0);
position = vec4(vPosition_WorldSpace, 1.0);
roughness = vec4(texture(uRoughnessMap, vTexCoords).rgb, 1.0);
specular = vec4(texture(uSpecularMap, vTexCoords).rgb, 1.0);    

此处输出始终是 uAlbedoMap 除了位置之外的颜色,它可以正确导出。

在光照通道中,当我读入几何通道的纹理时,再次所有输入纹理都是相同的

//in lighting_pass.frag
uniform sampler2D uAlbedoMap;
uniform sampler2D uHeightMap;
uniform sampler2D uNormalMap;
uniform sampler2D uPositionMap;
uniform sampler2D uRoughnessMap;
uniform sampler2D uSpecularMap;
[...]
vec3 albedo = texture(uAlbedoMap, vTexCoord).rgb;
vec3 height = texture(uHeightMap, vTexCoord).rgb;
vec3 normal_TangentSpace = texture(uNormalMap, vTexCoord).rgb;
vec3 position_WorldSpace = texture(uPositionMap, vTexCoord).rgb;
vec3 roughness = texture(uRoughnessMap, vTexCoord).rgb;
vec3 specular = texture(uSpecularMap, vTexCoord).rgb;

即正确导出的位置图也具有照明通道中反照率的颜色。

因此,似乎工作正常的是纹理输出,但显然工作不正常的是输入。 我尝试使用 CodeXL 对此进行调试,我可以看到 geometry_pass 的所有图像(至少在某些时候)都已正确绑定,它们都是可见的。帧缓冲区 object 的输出纹理确认 geometry_pass 的位置纹理是正确的。 据我逐步了解时所见,纹理已正确绑定(即统一位置正确)。

现在是一个明显的问题:我怎样才能让这些纹理在着色器中正确使用?


构建程序

查看者是osgViewer::Viewer,所以只有一个视图。 场景图如下: displayCamera 是来自查看器的相机。由于我使用的是 Qt (5.9.1),所以我在对场景图执行任何其他操作之前重置了 GraphicsContext。

osg::ref_ptr<osg::Camera> camera = viewer.getCamera();

osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
traits->windowDecoration = false;
traits->x = 0;
traits->y = 0;
traits->width = 640;
traits->height = 480;
traits->doubleBuffer = true;

camera->setGraphicsContext(new osgQt::GraphicsWindowQt(traits.get()));
camera->getGraphicsContext()->getState()->setUseModelViewAndProjectionUniforms(true);
camera->getGraphicsContext()->getState()->setUseVertexAttributeAliasing(true);
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
camera->setClearColor(osg::Vec4(0.2f, 0.2f, 0.6f, 1.0f));
camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
camera->setViewMatrix(osg::Matrix::identity());

然后我将 displayCamera 设置为此查看器相机,创建第二个相机用于渲染到纹理(因此称为 rttCamera)并将其作为 child 添加到 displayCamera.我将场景(由包含硬编码几何体的 geode 的组节点组成)添加到 rttCamera,最后创建一个屏幕四边形几何体(在 geode 下方,它又是 child 矩阵变换;这个矩阵transform 是作为 child 添加到 displayCamera).

因此displayCamera有两个children rttCamera和matrixtransform->screenQuad。 rttCamera 有 child scene->geode。 两个摄像头都有自己的渲染掩码,屏幕四边形使用 displayCameras 渲染掩码,场景使用 rttCameras 渲染掩码。

使用场景节点,我从文件(所有位图)中读取了 5 个纹理,然后使用多个渲染目标(用于延迟着色)将 rttCamera 渲染到帧缓冲区 Object 中。

//model is the geode in the scene group node
osg::ref_ptr<osg::StateSet> ss = model->getOrCreateStateSet();
ss->addUniform(new osg::Uniform(name.toStdString().c_str(), counter));
ss->setTextureAttributeAndModes(counter, pairNameTexture.second, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED);

.

//camera is the rttCamera
//bufferComponent is constructed by osg::Camera::COLOR_BUFFER0+counter
//(where counter is just an integer that gets incremented)
//texture is an osg::Texture2D that is newly created
camera->attach(bufferComponent, texture);
//the textures get stored to assign them later on
gBufferTextures[name] = texture;

这些 mrt 纹理作为纹理绑定到 screenquad

//ssQuad is the stateset of the screen quad geode
QString uniformName = "u" + name + "Map";
uniformName[1] = uniformName[1].toUpper();

ssQuad->addUniform(new osg::Uniform(uniformName.toStdString().c_str(), counter));
osg::ref_ptr<osg::Texture2D> tex = gBufferTextures[name];
ssQuad->setTextureAttributeAndModes(counter, gBufferTextures[name], osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);

其他设置是渲染目标(rttCamera 的 FBO,displayCamera 的 Framebuffer),光照(两个摄像头都关闭)。 rttCamera 获得与它为 displaycamera 创建的相同的图形上下文(即图形上下文 object 被传递给 rttCamera 并设置为它自己的图形上下文)。

纹理附件创建如下(使用宽度和高度或大小的 power-of-2 值没有区别)

osg::ref_ptr<osg::Texture2D> Utils::createTextureAttachment(int width, int height)
{
    osg::Texture2D* texture = new osg::Texture2D();
    //texture->setTextureSize(width, height);
    texture->setTextureSize(512, 512);
    texture->setInternalFormat(GL_RGBA);
    texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
    texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);

    return texture;
}

如果缺少更多 crucial-for-solving 代码或信息,请告诉我。

所以我终于找到了错误。我的计数器是 unsigned int,这显然是不允许的。由于 osg 对我隐藏了很多错误,我没有发现这是一个问题...

将其更改为普通 int 后,我现在将不同的纹理添加到我的着色器中。