当前位置: 首页 > 知识库问答 >
问题:

对使用OpenGL/GLSL的纹理映射和着色器程序之间关系的简单好奇

丁经略
2023-03-14

我正在开发一个自制的小型3D引擎,更准确地说是渲染优化。在此之前,我开发了一种排序算法,其目标是将最多具有相同材质属性和相同着色器程序的几何体(网格)收集成批。通过这种方式,我将状态更改(glBindXXX)和draw调用(glDrawXXX)最小化。因此,如果我有一个由10个长方体组成的场景,所有长方体共享相同的纹理,需要使用相同的着色器程序进行渲染(例如,包括ADS照明),那么这些网格的所有顶点都将合并到一个唯一的VBO中,纹理将只绑定一次,只需要一个简单的绘制调用。

Scene description:

- 10 meshes (boxes) mapped with 'texture_1'

Pseudo-code (render):

shaderProgram_1->Bind()
{
     glActiveTexture(texture_1)
     DrawCall(render 10 meshes with 'texture_1')
}

但是现在我想确定一件事:让我们假设我们的场景总是由相同的10个框组成,但这次其中5个框将被映射为不同的纹理(不是多纹理,只是简单的纹理映射)。

Scene description:

- 5 boxes with 'texture_1'
- 5 boxes with 'texture_2'

Pseudo-code (render):

shaderProgram_1->Bind()
{
     glActiveTexture(texture_1)
     DrawCall(render 5 meshes with 'texture_1')
}
shaderProgram_2->Bind()
{
     glActiveTexture(texture_2)
     DrawCall(render 5 meshes with 'texture_2')
}

我的片段着色器有一个独特的sampler2D声明(我的着色器程序的目标是使用简单的纹理映射和ADS照明渲染几何体):

uniform sampler2D ColorSampler;

我想确定的是,不可能用唯一的绘制调用来绘制这个场景(就像我之前的示例(需要一批)一样)。这是可能的,因为我对整个几何体使用了相同的纹理。我想这次我需要两个批次,因此需要两个绘制调用,当然,对于每个批次的渲染,我会在每次绘制调用之前绑定“纹理_1”和“纹理_2”(一个用于前5个框,另一个用于其他5个框)。

总之,如果所有网格都使用简单纹理(简单纹理映射)进行映射:

  • 5带有红色纹理(纹理为红色)

是否可以通过简单的绘制调用渲染场景?我不这么认为,因为我的伪代码如下所示:

Pseudo-code:

shaderProgram->Bind()
{
     glActiveTexture(texture_blue)
     glActiveTexture(texture_red)
     DrawCall(render 10 meshes)
}

当我的片段着色器必须使用唯一的采样器2D均匀变量(简单的纹理映射)计算像素颜色时,我认为不可能区分2个纹理。

这是我的片段着色器:

#version 440

#define MAX_LIGHT_COUNT 1

/*
** Output color value.
*/
layout (location = 0) out vec4 FragColor;

/*
** Inputs.
*/
in vec3 Position;
in vec2 TexCoords;
in vec3 Normal;

/*
** Material uniforms.
*/
uniform MaterialBlock
{
    vec3 Ka, Kd, Ks;
    float Shininess;

}   MaterialInfos;

uniform sampler2D ColorSampler;

struct Light
{
    vec4 Position;
    vec3 La, Ld, Ls;
    float Kc, Kl, Kq;
};

uniform struct Light LightInfos[MAX_LIGHT_COUNT];

uniform unsigned int LightCount;

/*
** Light attenuation factor.
*/
float getLightAttenuationFactor(vec3 lightDir, Light 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);
}

/*
** Basic phong shading.
*/
vec3 Basic_Phong_Shading(vec3 normalDir, vec3 lightDir, vec3 viewDir, int idx)
{
    vec3 Specular = vec3(0.0f);

    float lambertTerm = max(dot(lightDir, normalDir), 0.0f);

    vec3 Ambient = LightInfos[idx].La * MaterialInfos.Ka;
    vec3 Diffuse = LightInfos[idx].Ld * MaterialInfos.Kd * lambertTerm;

    if (lambertTerm > 0.0f)
    {
        vec3 reflectDir = reflect(-lightDir, normalDir);
        Specular = LightInfos[idx].Ls * MaterialInfos.Ks * pow(max(dot(reflectDir, viewDir), 0.0f), MaterialInfos.Shininess);
    }
    return (Ambient + Diffuse + Specular);
}

/*
** Fragment shader entry point.
*/
void main(void)
{
    vec3 LightIntensity = vec3(0.0f);

    vec4 texDiffuseColor = texture2D(ColorSampler, TexCoords);
    vec3 normalDir = (gl_FrontFacing ? -Normal : Normal);

    for (int idx = 0; idx < LightCount; idx++)
    {
        vec3 lightDir = vec3(LightInfos[idx].Position) - Position.xyz;
        vec3 viewDir = -Position.xyz;

        float lightAttenuationFactor = getLightAttenuationFactor(lightDir, LightInfos[idx]);

        LightIntensity += Basic_Phong_Shading(
            -normalize(normalDir), normalize(lightDir), normalize(viewDir), idx
        ) * lightAttenuationFactor;
    }
    FragColor = vec4(LightIntensity, 1.0f) * texDiffuseColor;
}

你同意我的观点吗?

共有1个答案

陆承宣
2023-03-14

如果你:(i)认为这是一个多文本问题,每个片段的函数只在两个传入片段之间进行选择(理想情况下使用系数为0.01.0mix,而不是真正的分支);或者(ii)将两个纹理合成为一个纹理(取决于你是否能够有效地包装和夹紧纹理坐标——注意那些相关的读取——以及最大纹理大小限制)。

这是一个悬而未决的问题,是否这些东西会提高性能。如果可以的话,一定要选择(ii)。

 类似资料:
  • 我想在FBO中加载两个纹理,其中一个纹理包含HDR图像,我的第一个目标是将图像从第一个纹理“复制”到第二个纹理(该纹理为空),并称为“下采样纹理”。 所以我创建FBO,加载我想用颜色_附件_0书写的纹理,并绑定它;然后初始化我的着色器程序并渲染一个四边形,其中包含我要在GL\u texture\u 0中读取的纹理。 然后我解开FBO并绑定“DownSamplingTex”,然后画一个四元组。 我不

  • 如何在平面上滚动纹理?所以我有一个平面有一个纹理,我可以使用一个着色器滚动左从右(无限)的纹理上?

  • 我是OpenGL的新手,在整理如何将纹理和着色器绑定到VBOs时遇到了困难。 我正在使用Cinder的纹理和着色器类。以下是我绘制方法的一部分: 在上面的代码中,如果我注释掉对mShader的调用。bind(),我的球体VBO将显示纹理(myImage)。我的着色器适用于普通(无纹理)形状,但当我在绘制任何带有包裹纹理的形状之前绑定着色器时,它会阻止纹理显示。 这是我使用的着色器的问题,还是我不理

  • 我试图在OpenGL中制作一个场景,从太空模拟地球。我现在有两个球体,一个用于地球,另一个稍大一些用于云层。地球和云球体对象都有自己的着色器程序来保持简单。地球着色器程序采用4种纹理(白天、夜晚、specmap和normalmap),而云着色器程序采用2种纹理(cloudmap和normalmap)。我有一个对象类,它有一个渲染函数,在该函数中我使用以下逻辑: 它从第0个纹理单元开始,从GL_TE

  • 我有一个非常简单的OpenGL应用程序,只渲染一个带纹理的四边形。这是我的代码,效果很好(带纹理的四边形看起来很好): 然后我想介绍一个简单的着色器。所以我稍微修改了我的代码: 顶点着色器: 片段着色器: 现在我得到的只是一个黑色的四边形:-( 我已经尝试并测试了很多东西: 着色器编译良好(无错误) 有人知道为什么我在使用着色器时看不到我的纹理吗?

  • 我正在努力发送多个纹理到一个单一的着色器,并有一个奇怪的问题,在着色器中的两个采样器似乎得到相同的纹理数据。我知道还有很多其他的多纹理问题和答案(这里有一些我已经读过很多次了1,2,3),但一些bug正在逃避我,我开始失去我的弹珠。我很有信心我已经把一切都安排好了,但显然还是有一些问题。 所以,目前我有形状,材料,纹理,和着色器类。我的shape类是执行实际绘制的父类。它有一个具有着色器和纹理数组