2017-07-30 5 views
0

物理エンジン用のレンダリングは比較的簡単です(thisと似ています)。私はOpenGLを学んでいて、これに続いていますtutorial。レンダラーは、方向、点、スポットライト、エリアライトの中から選択された少数のライトを処理できるようにします。また、シャドウマップを使用して簡単なシャドウが欲しいです。たとえば、シーンには2つのスポットライトや1つの指向性ライト、1つのポイントライト、1つのスポットライトなどが含まれている可能性があります。現在、私はすべてのライトを一緒に扱う1つの大きなシェーダを持っています。モジュール式の設計の観点からは、各ライトまたは少なくともライトの種類ごとに異なるシェーダを持つ方が良いでしょう。これが効率の観点から合理的な考えであるかどうか疑問に思います。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); 
} 

私は何をしたいのは、それぞれの光がとても代わりに、1つを有するの異なるシェーダに出タイプ別であります"ubershader"私はdirectionalLightシェーダとスポットライトシェーダなどがあります。これは良いアイデアですか?特に、レンダーコールごとにシェーダを何度も切り替えるのは高価かもしれないと私は心配していますか?

+0

この質問に対する回答は非常に幅広く、ビデオ(あなたの質問にリンクされているビデオ)には何も「*比較的シンプル」ではありません。 – Rabbid76

+0

ビデオの物理学は複雑ですが、レンダラは私がそうではないと思いますか?おそらくあなたは正しい答えが広がるだろう(私はそれを知らなかったが)。各レンダリングパスで複数のシェーダを使用するのは良い考えですか?それとも他の要因にも依存していますか? – James

+0

申し訳ありませんあなたはおそらく水を複雑にすることを指していました。私が剛体だけを使用すると言って問題を簡略化しましょう。だから私はたくさんのオブジェクトをレンダリングしています。私は、マーチング・キューブとSPHを使って私のエンジンに水を入れていきますが、私たちはこの質問のためにちょうど剛体を仮定することができます。 – James

答えて

2

あなたの質問は広すぎるため、SOフォーマットには適合しません。しかし、私は頻繁に初心者からエンジンプログラミングに尋ねられるので、私はそれに答えようとします。 は照明に異なるシェーダセットアップを操作するには、あなたは2つの標準慣行を持っているシャドウイング:

  1. 「ユーバー・シェーダ」

この背後にある考え方は、あなたがこのに埋め込まれたすべての可能なケースがあるということですシェイダー。たとえば、最大4つの光源をレンダリングできるようにするには(forward-renderingという)、ループの最大数をループにしてforループを挿入し、均一な(シーン内のライトの数)ループにリアルタイムで何回反復するかを指示します。次に、シャドウパスを有効にすると、シャドウマップサンプリングの "if"条件をアクティブにするためにユニバーサルをuberシェーダに渡します。すでに見てきたように、この方法は非常に非効率的です。シェーダの全体を複雑に分岐させ、ランタイム中にシェーダの状態を変更するために複数のユニフォームを提出する必要があります。このすべてがパフォーマンスとユーザビリティに影響します。 OpenGL 4.0 subroutinesを使用することで、これを少し簡略化できます。しかし、一般的に言えば - それをしないでください。

  • シェーダが
  • これはかなり業界の一般的な方法であり、それは、そのようなシステムを設計し、セットアップがより複雑であるが、それは長い間に報わを順列

      走るアイデアは、実行時のユースケースのシナリオに基づいてシェーダコードを設定することです(または、オフラインシェーダコンパイラを使用できる場合はコンパイル時にもこれを行うことができます)ので、最後にシェーダ文字列を取得します特定のレンダリング設定のコードが含まれています。たとえば、シーンに2つのライト+シャドウがあり、レンダリング可能オブジェクトが拡散およびノー​​マルマップを使用するマテリアルである場合、そのマテリアルのシェーダを設定して2つのライト、シャドウマッピング、拡散およびノー​​マルマップサンプリングを処理するコードを生成します。このようなシステムを設計してコード化する方法を詳しく書くには、ここで時間と空間があまりにも多くなります。しかし、一般的に言えば、さまざまな順列のプリプロセッサフ​​ラグでいっぱいのシェーダテンプレートを作成します。特定の順列型のプリプロセッサフ​​ラグを挿入し、シェーダとシェーダプログラムをコンパイルします。Unity3DやUnrealのようなトップクラスのゲームエンジンでは、オーサリング時にすべての可能なシェーダの順列がエディタですでに生成されています。独自のエンジンを動かす場合は、実行時に必要な順列を作成し、それをシェーダコンパイラにスローします。長いシェーダ文字列では、オンライン編集中にわずかなフリーズが発生しますが、シェーダプログラムのコンパイル済みの順列をキャッシュして再利用すると、うまくいくはずです。

      ボーナスパーツ

      あなたはまた、あなたが提案したようにそれを行うことができます - 効果的に私の番号2のアプローチであるシェーダの異なるバリエーションを、事前に作成。しかし、1つのライトレンダリングロジックを別のプログラムにラップすると、2つの光源を持つシーンの場合に意味します:

      1 - 最初の光源でオブジェクトをレンダリングします。

      2 - オブジェクトを第2の光源でレンダリングします。

      最終結果として2つのフレームを構成します。これはすでに3回のレンダーパスが必要で、かなり高度なテクニックであり、膨大な量のジオメトリや光源を扱うエンジンを開発する計画でない限り、いつも必要なものではありません。deferred shading

    +0

    お返事ありがとうございます。シェーダとレンダラのデザイン、特にシェーダのパーミュテーションの方法について詳しく説明している優れたリソースを知っていますか?私は前に遅延シェーディングメソッドについて聞いたことがありますが、私はそれが価値があるよりも面倒であるように私のプロジェクトには適切ではないと私は思っています。私はいくつかのライトしか持たず、一般的に私のカメラは、時間。 – James

    +0

    @James私はオープンソースプロジェクトから学びました。それを試してみてください –

    関連する問題