2017-07-30 54 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

    @詹姆斯我曾經從開源項目中學習。嘗試一下 –

    相關問題