使用GLSL着色器,在同一片段着色器中定义的多个子例程类型无法正常工作 [英] Multiple subroutine types defined in the same fragment shader does not work correctly using GLSL Shaders

查看:97
本文介绍了使用GLSL着色器,在同一片段着色器中定义的多个子例程类型无法正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用GLSL着色器开发程序.我用2种不同的方法编码了2种不同的方法来计算ADS(环境+漫射+镜面反射)阴影.为了正确完成这项工作,我使用了子例程来使用一种或另一种方法来计算ADS底纹.

I'm working on a program using GLSL shaders. I coded 2 different ways to compute ADS (Ambient + Diffuse + Specular) shading in 2 different methods. To do the job properly I used subroutines to use one or the other method to compute ADS shading.

这是片段着色器代码的一部分:

Here's a piece of the fragment shader code :

subroutine vec3 LightShadingEffectType(int idx, vec3 normal, vec3 lightDir);
subroutine uniform LightShadingEffectType LightShadingEffect;

subroutine (LightShadingEffectType)
vec3 Basic_ADS_Shading(int idx, vec3 normal, vec3 lightDir)
{
    vec3 reflectDir = reflect(-lightDir, normal);
    vec3 viewDir = normalize(-Position.xyz);

    vec3 Ambient = LightInfos[idx].La * MaterialInfos.Ka;
    vec3 Diffuse = LightInfos[idx].Ld * MaterialInfos.Kd * max(dot(lightDir, normal), 0.0f);
    vec3 Specular = LightInfos[idx].Ls * MaterialInfos.Ks * pow(max(dot(reflectDir, viewDir), 0.0f), MaterialInfos.Shininess);
    vec3 Emissive = MaterialInfos.Ke;

    return (Ambient + Diffuse + Specular + Emissive);
}

subroutine (LightShadingEffectType)
vec3 Phong_ADS_Shading(int idx, vec3 normal, vec3 lightDir)
{
    vec3 v = normalize(vec3(-Position));
    vec3 h = normalize(v + lightDir);

    vec3 Ambient = LightInfos[idx].La * MaterialInfos.Ka;
    vec3 Diffuse = LightInfos[idx].Ld * MaterialInfos.Kd * max(dot(lightDir, normal), 0.0f);
    vec3 Specular = LightInfos[idx].Ls * MaterialInfos.Ks * pow(max(dot(h, normal), 0.0f), MaterialInfos.Shininess);
    vec3 Emissive = MaterialInfos.Ke;

    return (Ambient + Diffuse + Specular + Emissive);
}

还有C ++代码:

type::uint32 basic_ads_idx = glGetSubroutineIndex(program->getHandle(), GL_FRAGMENT_SHADER, "Basic_ADS_Shading");
type::uint32 phong_ads_idx = glGetSubroutineIndex(program->getHandle(), GL_FRAGMENT_SHADER, "Phong_ADS_Shading");

glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &phong_ads_idx);

直到此处显示正确.在这种情况下,我选择执行第二个子例程定义(Phong_ADS_Shading调用).

Until here the display is correct. In this case I've choosen to execute the second subroutine definition (Phong_ADS_Shading call).

但是我想在程序中声明另一个子程序类型来管理纹理(签名不同).这是片段着色器代码的另一段(在同一着色器中):

But I want to declare another subroutine type in my program to manage textures (the signature is not the same). Here's an other piece of the fragment shader code (in the same shader) :

subroutine vec4 TexturedShadingType();
subroutine uniform TexturedShadingType TexturedShading;

subroutine (TexturedShadingType)
vec4 Textured_Shading(void)
{
    vec4 TexColor = texture(Tex1, TexCoords);
    return (vec4(getLightIntensity(), 1.0f) * TexColor);
}

subroutine (TexturedShadingType)
vec4 Untextured_Shading(void)
{
    return (vec4(getLightIntensity(), 1.0f));
}

因此,最后,我的C ++代码如下:

So, finally, my C++ code is the following :

type::uint32 basic_ads_idx = glGetSubroutineIndex(program->getHandle(), GL_FRAGMENT_SHADER, "Basic_ADS_Shading");
type::uint32 phong_ads_idx = glGetSubroutineIndex(program->getHandle(), GL_FRAGMENT_SHADER, "Phong_ADS_Shading");

glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &phong_ads_idx);

type::uint32 textured_idx = glGetSubroutineIndex(program->getHandle(), GL_FRAGMENT_SHADER, "Textured_Shading");
type::uint32 untextured_idx = glGetSubroutineIndex(program->getHandle(), GL_FRAGMENT_SHADER, "Untextured_Shading");

glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &untextured_idx);

这是每个子例程索引的值:

Here's the value for each subroutine index :

std::cout << phong_idx << ", " << blinn_phong_idx << ", " << textured_idx << ", " << untextured_idx << std::endl;

-> 1, 0, 4294967295, 4294967295

前两个值似乎是正确的,但另外两个是正确的.

The two first values seems to be correct but the two others.

现在是整个片段着色器代码,可以更好地理解我的问题:

Here's now the whole fragment shader code to have a better understanding of my problem:

#version 400

in vec3 Position;
in vec3 Normal;
in vec2 TexCoords;

layout (location = 0) out vec4 FragColor;

uniform sampler2D Tex1;
uniform int lightCount;

struct PointLight
{
    vec4 Position;
    vec3 La, Ld, Ls;
    float Kc, Kl, Kq;
    vec3 direction;
    float exponent;
    float cutoff;
    int type;
};

struct Material
{
    vec3 Ka, Kd, Ks, Ke;
    float Shininess;
};

uniform PointLight LightInfos[10];
uniform Material MaterialInfos;

subroutine vec3 LightShadingEffectType(int idx, vec3 normal, vec3 lightDir);
subroutine uniform LightShadingEffectType LightShadingEffect;

subroutine (LightShadingEffectType)
vec3 Phong_Shading(int idx, vec3 normal, vec3 lightDir)
{
    vec3 reflectDir = reflect(-lightDir, normal);
    vec3 viewDir = normalize(-Position.xyz);

    vec3 Ambient = LightInfos[idx].La * MaterialInfos.Ka;
    vec3 Diffuse = LightInfos[idx].Ld * MaterialInfos.Kd * max(dot(lightDir, normal), 0.0f);
    vec3 Specular = LightInfos[idx].Ls * MaterialInfos.Ks * pow(max(dot(reflectDir, viewDir), 0.0f), MaterialInfos.Shininess);
    vec3 Emissive = MaterialInfos.Ke;

    return (Ambient + Diffuse + Specular + Emissive);
}

subroutine (LightShadingEffectType)
vec3 Blinn_Phong_Shading(int idx, vec3 normal, vec3 lightDir)
{
    vec3 v = normalize(vec3(-Position));
    vec3 h = normalize(v + lightDir);

    vec3 Ambient = LightInfos[idx].La * MaterialInfos.Ka;
    vec3 Diffuse = LightInfos[idx].Ld * MaterialInfos.Kd * max(dot(lightDir, normal), 0.0f);
    vec3 Specular = LightInfos[idx].Ls * MaterialInfos.Ks * pow(max(dot(h, normal), 0.0f), MaterialInfos.Shininess);
    vec3 Emissive = MaterialInfos.Ke;

    return (Ambient + Diffuse + Specular + Emissive);
}

float getLightAttenuation(vec3 lightDir, PointLight light)
{
    float lightAtt = 0.0f;
    float dist = 0.0f;

    dist = length(lightDir);
    lightAtt = 1.0f / (light.Kc + (light.Kl * dist) + (light.Kq * pow(dist, 2)));
    return (lightAtt);
}

float getSpotFactor(vec3 lightDir, vec3 spotDir, PointLight light)
{
    return (pow(dot(-lightDir, spotDir), light.exponent));
}

vec3 Spot_ADS_Shading(float lightAtt, vec3 tnorm, vec3 lightDirNorm, int idx)
{
    vec3 LightIntensity = vec3(0.0f);
    vec3 spotDirNorm = normalize(LightInfos[idx].direction);
    float angle = acos(dot(-lightDirNorm, spotDirNorm));
    float cutoff = radians(clamp(LightInfos[idx].cutoff, 0.0f, 90.0f));

    if (angle < cutoff)
    {
        float spotFactor = getSpotFactor(lightDirNorm, spotDirNorm, LightInfos[idx]);
        LightIntensity = lightAtt * spotFactor * LightShadingEffect(idx, -tnorm, lightDirNorm);
    }
    else
    {
        LightIntensity = LightShadingEffect(idx, -tnorm, lightDirNorm) * MaterialInfos.Ka;
    }
    return (LightIntensity);
}

vec3 Point_ADS_Shading(float lightAtt, vec3 tnorm, vec3 lightDirNorm, int idx)
{
    return (lightAtt * LightShadingEffect(idx, tnorm, lightDirNorm));
}

vec3 getLightIntensity(void)
{
    vec3 LightIntensity = vec3(0.0f);

    for (int idx = 0; idx < lightCount; idx++)
    {
        vec3 tnorm = (gl_FrontFacing ? -normalize(Normal) : normalize(Normal));
        vec3 lightDir = vec3(LightInfos[idx].Position) - Position;
        vec3 lightDirNorm = normalize(lightDir);
        float lightAtt = getLightAttenuation(lightDir, LightInfos[idx]);

        if (LightInfos[idx].type == 1)
        {
            LightIntensity += Spot_ADS_Shading(lightAtt, tnorm, lightDirNorm, idx);
        }
        else
        {
            LightIntensity += Point_ADS_Shading(lightAtt, -tnorm, lightDirNorm, idx);
        }
    }
    return (LightIntensity);
}

subroutine vec4 TexturedShadingType();
subroutine uniform TexturedShadingType TexturedShading;

subroutine (TexturedShadingType)
vec4 Textured_Shading(void)
{
    vec4 TexColor = texture(Tex1, TexCoords);
    return (vec4(getLightIntensity(), 1.0f) * TexColor);
}

subroutine (TexturedShadingType)
vec4 Untextured_Shading(void)
{
    return (vec4(getLightIntensity(), 1.0f));
}

void main(void)
{
    FragColor = TexturedShading();
}

问题是我的几何图形以黑色渲染.我认为这两个统一子例程之间存在冲突.我迷路了.有人可以帮助我吗?

The problem is my geometry is render in black. I think there is a conflict between the two uniform subroutines. I'm lost. Does anyone can help me ?

推荐答案

glUniformSubroutines 为着色器阶段设置子程序的 all ,而不仅仅是其中之一.

glUniformSubroutines sets all of the subroutines for a shader stage, not just one of them.

请参见,当OpenGL链接您的程序时,它将获取所有子例程统一并从中构建一个数组.每个制服在此数组中都有一个索引.如果要弄清楚数组中特定子例程统一的索引是什么,则需要调用 glGetSubroutineIndex .另外,假设您有4.3/ARB_explicit_uniform_locations(当然,AMD的运行速度很慢),则可以直接使用 layout(location = #)布局限定符.这样,您不必查询它.

See, when OpenGL links your program, it takes all of the subroutine uniforms and builds an array out of them. Each uniform has an index into this array. If you want to figure out what the index for a particular subroutine uniform in the array is, you need to call glGetSubroutineIndex. Alternatively, assuming you have 4.3/ARB_explicit_uniform_locations (which admittedly AMD is rather slow on), you can just set this directly with the layout(location = #) layout qualifier. That way, you don't have to query it.

一旦知道了每个子例程制服指的是什么索引,就可以通过单个调用将一个阶段的所有子例程制服设置为glUniformSubroutines.您将构建一个简短的数组,该数组中的每个索引都包含您要使用的子例程函数的索引.

Once you know what index each subroutine uniform refers to, you can then set all of the subroutine uniforms for a stage with a single call to glUniformSubroutines. You build up a short array, where each index in the array contains the index of the subroutine function you want to use.

但是我想在这四个子程序中每次只选择2个子程序.

But I want to choose just 2 subroutines each time among the four subroutines.

您可能有4个子例程,但是您只有两个子例程 uniform 变量.这些制服代表用户在要着色器中设置要调用的特定功能.

You may have 4 subroutines, but you only have two subroutine uniform variables. These uniforms represent the user setting the particular function in the shader to be called.

此外,两种制服都使用不同的类型,因此它们不能从4个子例程中进行选择.每个子例程统一只能从该特定类型使用的 specific 子例程中进行选择.当您使用subroutine(SubroutineType)声明子例程函数时定义此函数.每种类型都有其自己的功能集,可以与之一起使用.因此,每个制服只能从为该制服声明的子例程类型中设置的那些特定功能中进行选择.

Furthermore, both of the uniforms use different types, so they cannot select from among the 4 subroutines. Each subroutine uniform can only select from among the specific subroutines used by that particular type. This is defined when you declare the subroutine function with subroutine(SubroutineType). Each type has its own set of functions which it could be used with. So each uniform can only select from among those specific functions set in the subroutine type declared for that uniform.

因此,您无法在4个子例程中进行选择;每个制服只能在为每种子例程类型设置的功能中进行选择.每个制服只能在您使用子程序类型声明的两个函数之间进行选择.

So you can't choose among 4 subroutines; each uniform can only choose among the functions you set for each subroutine type. Each uniform can only pick between the two functions you declared the subroutine type with.

如果仅调用glUniformSubroutinesuiv,如何选择它们?

If I have just a single call of glUniformSubroutinesuiv how can I select them ?

通过将其传递给 array .有一个原因,为什么第三个参数是 pointer ,为什么第二个参数是数组中的条目数.

By passing it an array. There's a reason why the third parameter is a pointer, and why the second parameter is the number of entries in the array.

您的片段着色器具有两个子例程统一值.因此,数组中有2个元素,每个元素代表一个特定子例程统一的子例程索引.您可以通过创建一个数组,在其中设置两个子例程索引并将该数组传递给函数来选择它们.

Your fragment shader has two subroutine uniform values. Therefore, there are 2 elements in the array, each one of which represents the subroutine index for a particular subroutine uniform. You select them both by creating an array, setting both subroutine indexes into it, and passing that array to the function.

我认为两个统一子例程之间存在冲突.

I think there is a conflict between the two uniform subroutines.

不,问题(除了前面提到的数组问题)是您不使用其他子例程.虽然子例程制服在大多数方面与常规制服不同,但在这种情况下,它们类似于常规制服.如果您不使用它们,驱动程序可以优化它们(以及它们所依赖的任何东西).

No, the problem (besides the array issue noted earlier) is that you're not using the other subroutine. While subroutine uniforms are different from regular uniforms in most respects, they are like regular uniforms in this way. If you don't use them, the driver can optimize them (and anything they rely on) away.

您的片段着色器可能在任何地方都不使用TexturedShading.并且由于没有其他子例程统一声明TexturedShadingType统一,因此编译器意识到这两个函数从未被用作子例程.因此,它可以优化它们.因此,您获得的索引为GL_INVALID_INDEX.

Your fragment shader probably doesn't use TexturedShading anywhere. And since there's no other subroutine uniform that declares a TexturedShadingType uniform, the compiler realizes that those two functions are never used as subroutines. So it optimizes them out. Therefore, the indices you get for them are GL_INVALID_INDEX.

这篇关于使用GLSL着色器,在同一片段着色器中定义的多个子例程类型无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆