2017-07-30 41 views
0

我正在为物理引擎(类似于this)做出相对简单的渲染。我刚刚学习OpenGL并一直关注这个tutorial。我希望渲染器能够处理从方向,点,聚光灯和区域灯中选择的少量灯光。另外我想要使用阴影贴图的简单阴影。例如,一个场景可能包含两个聚光灯或一个方向灯或一个点光源和一个聚光灯等。目前,我有一个较大的着色器可以处理所有的灯光,但现在我正在试验阴影贴图,看起来很光亮(从模块化设计的角度来看)更好地为每个灯或至少每种灯类型设置不同的着色器。我想知道从效率的角度来看这是否合理。为了使这个更具体的我当前顶点的样子:我应该为OpenGL渲染器中的每种灯光类型使用不同的着色器

#version 130 

in vec3 position; 
in vec3 normal; 
in vec2 atexture; 

out vec3 FragPos; 
out vec3 Normal; 
out vec2 TexCoord; 
out vec4 FragPosLightSpace; 

uniform mat4 model; 
uniform mat4 view; 
uniform mat4 projection; 
uniform mat4 lightView; 
uniform mat4 lightProjection; 

void main() 
{ 
    gl_Position = projection * view * model * vec4(position.x, position.y, position.z, 1.0); 
    FragPos = vec3(model * vec4(position, 1.0)); 
    Normal = normalize(normal); 
    TexCoord = atexture; 

    FragPosLightSpace = lightProjection * lightView * vec4(FragPos, 1.0f); 
} 

和片段着色器:

#version 130 

struct Material 
{ 
    float shininess; 
    vec3 ambient; 
    vec3 diffuse; 
    vec3 specular; 
}; 

struct DirLight 
{ 
    vec3 direction; 

    vec3 ambient; 
    vec3 diffuse; 
    vec3 specular; 
}; 

struct PointLight 
{ 
    vec3 position; 

    float constant; 
    float linear; 
    float quadratic; 

    vec3 ambient; 
    vec3 diffuse; 
    vec3 specular; 
}; 

struct SpotLight { 
    vec3 position; 
    vec3 direction; 
    float cutOff; 
    float outerCutOff; 

    float constant; 
    float linear; 
    float quadratic; 

    vec3 ambient; 
    vec3 diffuse; 
    vec3 specular;  
}; 

struct AreaLight 
{ 
    vec3 position; 
    vec3 ambient; 
    vec3 diffuse; 
    vec3 specular; 
}; 

out vec4 FragColor; 

in vec3 FragPos; 
in vec3 Normal; 
in vec2 TexCoord; 
in vec4 FragPosLightSpace; 

uniform Material material; 
uniform DirLight dirLight; 
uniform PointLight pointLight; 
uniform SpotLight spotLight; 
uniform AreaLight areaLight; 

uniform vec3 cameraPos; 

uniform sampler2D texture1; 
uniform sampler2D shadowMap; 

float CalcShadow(vec4 FragPosLightSpace); 
vec3 CalcDirLight(Material material, DirLight light, vec3 normal, vec3 viewDir); 
vec3 CalcPointLight(Material material, PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir); 
vec3 CalcSpotLight(Material material, SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir); 
vec3 CalcAreaLight(Material material, AreaLight light); 

void main(void) 
{ 
    vec3 viewDir = normalize(cameraPos - FragPos); 

    vec3 finalLight = vec3(0.0f, 0.0f, 0.0f); 

    finalLight += CalcDirLight(material, dirLight, Normal, viewDir); 

    finalLight += CalcPointLight(material, pointLight, Normal, FragPos, viewDir); 

    finalLight += CalcSpotLight(material, spotLight, Normal, FragPos, viewDir); 

    finalLight += CalcAreaLight(material, areaLight); 

    FragColor = texture2D(texture1, TexCoord) * vec4(finalLight, 1.0f); 
} 


float CalcShadow(vec4 fragPosLightSpace) 
{ 
    // only actually needed when using perspective projection for the light 
    vec3 projCoords = fragPosLightSpace.xyz/fragPosLightSpace.w; 

    // projCoord is in [-1,1] range. Convert it ot [0,1] range. 
    projCoords = projCoords * 0.5 + 0.5; 

    float closestDepth = texture(shadowMap, projCoords.xy).r; 

    float currentDepth = projCoords.z; 

    float bias = 0.005f; 
    float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; 

    return shadow; 

} 


vec3 CalcDirLight(Material material, DirLight light, vec3 normal, vec3 viewDir) 

{ 
    vec3 lightDir = normalize(-light.direction); 

    vec3 reflectDir = reflect(-lightDir, normal); 

    float ambientStrength = 1.0f; 
    float diffuseStrength = max(dot(normal, lightDir), 0.0); 
    float specularStrength = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); 

    float shadow = CalcShadow(FragPosLightSpace); 

    vec3 ambient = light.ambient * material.ambient * ambientStrength; 
    vec3 diffuse = (1.0f - shadow) * light.diffuse * material.diffuse * diffuseStrength; 
    vec3 specular = (1.0f - shadow) * light.specular * material.specular * specularStrength; 

    return (ambient + diffuse + specular); 
} 


vec3 CalcPointLight(Material material, PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir) 
{ 
    vec3 lightDir = normalize(light.position - fragPos); 

    vec3 reflectDir = reflect(-lightDir, normal); 

    float ambientStrength = 1.0f; 
    float diffuseStrength = max(dot(normal, lightDir), 0.0); 
    float specularStrength = pow(max(dot(viewDir, reflectDir), 0.0f), material.shininess); 

    float attenuation = 1.0f/(1.0f + 0.01f*pow(length(light.position - fragPos), 2)); 

    vec3 ambient = light.ambient * material.ambient * ambientStrength; 
    vec3 diffuse = light.diffuse * material.diffuse * diffuseStrength; 
    vec3 specular = light.specular * material.specular * specularStrength; 

    ambient *= attenuation; 
    diffuse *= attenuation; 
    specular *= attenuation; 

    return vec3(ambient + diffuse + specular); 
} 


vec3 CalcSpotLight(Material material, SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir) 
{ 
    vec3 lightDir = normalize(light.position - fragPos); 

    vec3 reflectDir = reflect(-lightDir, normal); 

    float ambientStrength = 0.05f; 
    float diffuseStrength = max(dot(normal, lightDir), 0.0); 
    float specularStrength = pow(max(dot(viewDir, reflectDir), 0.0f), material.shininess); 

    float attenuation = 1.0f/(1.0f + 0.01f*pow(length(light.position - fragPos), 2)); 

    float theta = dot(lightDir, normalize(-light.direction)); 
    float epsilon = light.cutOff - light.outerCutOff; 
    float intensity = clamp((theta - light.outerCutOff)/epsilon, 0.0f, 1.0f); 

    vec3 ambient = light.ambient * material.ambient * ambientStrength; 
    vec3 diffuse = light.diffuse * material.diffuse * diffuseStrength; 
    vec3 specular = light.specular * material.specular * specularStrength; 

    ambient *= attenuation * intensity; 
    diffuse *= attenuation * intensity; 
    specular *= attenuation * intensity; 

    return vec3(ambient + diffuse + specular); 
} 


vec3 CalcAreaLight(Material material, AreaLight light) 
{ 
    // return vec3(0.0f, 0.0f, 0.0f); 
    return vec3(2*material.ambient); 
} 

我想要做的是独立的每个光打出来的不同的着色器等等,而不必一“ubershader”我会有一个方向性光着色器和聚光灯着色器等。这是一个好主意吗?特别是我担心每次渲染调用多次切换着色器可能会很昂贵?

+0

对这个问题的回答将非常广泛,视频中没有任何内容(您的问题中链接的视频)是“*相对简单*”。 – Rabbid76

+0

视频中的物理是复杂的,我不认为是渲染器?也许你是对的,答案是广泛的(尽管我不知道)。在每个渲染过程中使用多个着色器被认为是一个好主意?还是那太依赖于其他因素? – James

+0

很抱歉,您可能是指将水渲染为复杂的。让我简化问题,说我只会使用刚体,所以我只渲染一堆物体。我将使用行军立方体和SPH在我的发动机中加入水,但是我们可以假设它只是刚体而已。 – James

回答

2

您的问题太广泛,不适合SO格式。不过,我会尽力回答它,主要是因为初学者经常会问到引擎编程。 为了操纵不同的着色器设置灯光和阴影你有2分标准的做法:

  1. “尤伯杯着色器”

这背后的想法是,你必须嵌入到这种每一种可能的情况下,着色器。因此,例如,您希望能够渲染多达4个光源(我在这里谈论的是forward-rendering),因此您可以插入带有最大光照数量的for循环,然后传递一个统一(场景中的灯光数量)以实时告诉循环多少次迭代。然后,如果启用了阴影通过,则还会将制服传递到超级着色器中以激活阴影贴图采样的“if”条件。正如你已经看到的那样,这种方式效率很低。您将在整个着色器中形成复杂的分支,并且在运行时必须提交多个制服来更改着色器状态。所有这些都会影响性能和可用性。那么,你可以通过使用OpenGL 4.0 subroutines来简化这一点。但一般来说 - 不要这样做。

  • 着色排列
  • 这是一个相当行业常用的方法,虽然它是比较复杂的设计和安装这样的系统,它在长回报跑。这个想法是基于运行时的用例场景来配置着色器代码(或者如果您有脱机着色器编译器可用,那么您甚至可以在编译时执行此操作),因此最终会得到着色器字符串包含特定渲染设置的代码。例如,如果场景有2个灯光+阴影,并且一个可渲染对象使用漫反射和法线贴图的材质,则可以配置该材质的着色器以生成处理2个灯光,阴影贴图,漫射和法线贴图采样的代码。这里需要太多的时间和空间来详细写出如何设计和编写这样的系统。但总体而言,您可以编写一种着色器模板,其中充满了用于不同排列的预处理器标志。您为特定的排列类型注入预处理器标志,然后编译着色器和着色器程序。在Unity3D和Unreal等顶级游戏引擎中,创作期间编辑器中已经生成了所有可能的着色器排列。如果您推出自己的引擎,只需在运行时组合所需的排列并将其投影到着色器编译器中。使用长着色器字符串时,在线编译期间您会注意到轻微冻结,但是如果您缓存并重新使用着色器程序的已编译排列,则会很好。

    奖金部分

    还可以,做你建议 - 预生成着色器的不同变化,这实际上是我的号码2的方法。但是你的提议是有问题的,因为如果你将单个光线渲染逻辑包装到单独的程序中,这意味着在具有2个光源的场景的情况下:

    1 - 用第一光源渲染对象。

    2 - 用第二个光源渲染物体。

    将两帧组合成最终结果。这已经需要3次渲染过程,并且更多地朝着deferred shading的方向发展,这是一项相当先进的技术,并非总是您所需要的,除非您的计划是开发引擎来处理大量的几何图形和光源。

    +0

    感谢您的回复。你知道有哪些好的资源可以提供关于着色器和渲染器设计的更多细节,特别是你描述的着色器置换方法吗?我之前听说过延期着色方法,但我认为这不适合我的项目,因为它比它的价值更加麻烦,因为我只有几盏灯,而且我的相机通常会指向大部分时间。 – James

    +0

    @詹姆斯我曾经从开源项目中学习。尝试一下 –