阴影贴图生成不正确的结果

时间:2017-03-08 01:14:49

标签: c++ opengl glsl shadow-mapping

我正在尝试将阴影贴图实现到我的延迟渲染管道中,但是我遇到了一些实际生成阴影贴图的问题,然后阴影像素 - 我认为应该被遮蔽的像素不是。< / p>

我有一个方向灯,这是我发动机中的“太阳”。我已经推迟了为照明设置渲染,到目前为止它正常工作。我再次将场景渲染到阴影贴图的仅深度FBO中,使用以下代码生成视图矩阵:

glm::vec3 position = r->getCamera()->getCameraPosition(); // position of level camera
glm::vec3 lightDir = this->sun->getDirection(); // sun direction vector
glm::mat4 depthProjectionMatrix = glm::ortho<float>(-10,10,-10,10,-10,20); // ortho projection
glm::mat4 depthViewMatrix = glm::lookAt(position + (lightDir * 20.f / 2.f), -lightDir, glm::vec3(0,1,0));

glm::mat4 lightSpaceMatrix = depthProjectionMatrix * depthViewMatrix;

然后,在我的灯光着色器中,我使用以下代码来确定像素是否处于阴影中:

// lightSpaceMatrix is the same as above, FragWorldPos is world position of the texekl
vec4 FragPosLightSpace = lightSpaceMatrix * vec4(FragWorldPos, 1.0f);

// multiply non-ambient light values by ShadowCalculation(FragPosLightSpace)
// ... do more stuff ...

float ShadowCalculation(vec4 fragPosLightSpace) {
    // perform perspective divide
    vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
    // vec3 projCoords = fragPosLightSpace.xyz;

    // Transform to [0,1] range
    projCoords = projCoords * 0.5 + 0.5;

    // Get closest depth value from light's perspective (using [0,1] range fragPosLight as coords)
    float closestDepth = texture(gSunShadowMap, projCoords.xy).r;

    // Get depth of current fragment from light's perspective
    float currentDepth = projCoords.z;

    // Check whether current frag pos is in shadow
    float bias = 0.005;
    float shadow = (currentDepth - bias) > closestDepth  ? 1.0 : 0.0;

    // Ensure that Z value is no larger than 1
    if(projCoords.z > 1.0) {
        shadow = 0.0;
    }

    return shadow;
}

然而,这并没有真正让我得到我想要的东西。这是阴影后输出的屏幕截图,以及在Photoshop中半影转换为图像的阴影贴图:

Render Output 渲染输出

Shadow Map 阴影贴图

由于定向光是我的着色器中唯一的光线,因此阴影贴图似乎非常接近正确渲染,因为透视/方向大致匹配。然而,我不明白的是为什么没有一个茶壶最终会给其他茶壶留下阴影。

我很欣赏有关我可能做错的任何指示。我认为我的问题在于计算那个光空间矩阵(我不知道如何正确计算它,给定一个移动的摄像头,以便视图中的东西将被更新)或者我确定的方式延迟渲染器的着色纹理是否为阴影。 (FWIW,我从深度缓冲区确定世界位置,但我已经证明这个计算工作正常。)

感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

调试阴影问题可能很棘手。让我们从几点开始:

  1. 如果你仔细观察你的渲染,你会在左上角的一个花盆上看到一个阴影。

  2. 尝试旋转太阳,这通常有助于查看光变换矩阵是否有任何问题。根据您的输出,太阳看起来非常水平,并且可能不会在此设置上投射阴影。 (另一个角度可能会显示更多阴影)

  3. 看起来好像是在正确计算矩阵,但是尝试缩小glm :: ortho(-10,10,-10,10,-10,20)中的最大深度以紧密贴合你的场景。如果深度太大,您将失去精度,阴影将产生伪影。

  4. 要想象出问题来自哪里,请尝试从此处输出阴影贴图查找的结果:

  5. theory Scratch
    imports Main
    begin
    
    ML {*
    (* test_parser is just a definition of a silly example parser. It parses text of the form "123 * ‹x+y›"
       where 123 is an arbitrary natural, and x+y a term. test_parser is of type term context_parser.
       The parser returns a term that is a list 123 copies of x+y.
       If you have constructed a "term parser" instead, you can either convert it using Scan.lift, 
       or modify the definition of parse_cartouche below slightly. 
     *)
    fun sym_parser sym = Parse.sym_ident :-- (fn s => if s=sym then Scan.succeed () else Scan.fail) >> #1;
    val test_parser = Scan.lift Parse.nat --| Scan.lift (sym_parser "*" || Parse.reserved "x") -- Args.term
      >> (fn (n,t) => replicate n t |> HOLogic.mk_list dummyT)
    
    (* parse_cartouche: This function takes the cartouche that should be parsed (as a plain string
       without markup), together with its position. (All this information can be extracted using the 
       information available to a parse translation, see cartouch_tr below.) *)
    fun parse_cartouche ctx (cartouche:string) (pos:Position.T) : term = 
      let 
        (* This extracts the content of the cartouche as a "Symbol_Pos.T list".
           One posibility to continue from here would be to write a parser that works
           on "Symbol_Pos.T list". However, most of the predefined parsers expect 
           "Token.T list" (a single token may consist of several symbols, e.g., 123 is one token). *)
        val content = Symbol_Pos.cartouche_content (Symbol_Pos.explode (cartouche, pos))
        (* Translate content into a "Token.T list". *)
        val toks = content |> Source.of_list (* Create a "Source.source" containing the symbols *)
          |> Token.source' true Keyword.empty_keywords (* Translate into a "Source.source" containing tokens.
                 I don't know what the argument true does here. false also works, I think. *)
          |> Token.source_proper (* Remove things like whitespaces *)
          |> Source.exhaust (* Translate the source into a list of tokens *)
          |> (fn src => src @ [Token.eof]) (* Add an eof to the end of the token list, to enable Parse.eof below *)
        (* A conversion function that produces error messages. The ignored argument here
           contains the context and the list of remaining tokens, if needed for constructing
           the message. *)
        fun errmsg (_,SOME msg) = msg
          | errmsg (_,NONE) = fn _ => "Syntax error"
        (* Apply the parser "test_parser". We additionally combine it with Parse.eof to ensure that 
           the parser parses the whole text (till EOF). And we use Scan.!! to convert parsing failures 
           into parsing errors, and Scan.error to report parsing errors to the toplevel. *)
        val (term,_) = Scan.error (Scan.!! errmsg (test_parser --| Scan.lift Parse.eof)) (Context.Proof ctx,toks)
        (* If test_parser was of type "term parser" instead of "term context_parser", we would use instead:
        val (term,_) = Scan.error (Scan.!! errmsg (test_parser --| Parse.eof)) toks *)
      in term end
    
    (* A parse translation that translates cartouches using test_parser. The code is very close to 
       the examples from Cartouche_Examples.thy. It takes a given cartouche-subterm, gets its 
       position, and calls parse_cartouche to do the translation to a term. *)
    fun cartouche_tr (ctx:Proof.context) args =
        let fun err () = raise TERM ("cartouche_tr", args) in
          (case args of
            [(c as Const (@{syntax_const "_constrain"}, _)) $ Free (s, _) $ p] =>
              (case Term_Position.decode_position p of
                SOME (pos, _) => c $ (parse_cartouche ctx s pos) $ p
              | NONE => err ())
          | _ => err ())
        end;
    *}
    
    (* Define a syntax for calling our translation. In this case, the syntax is "MY ‹to-be-parsed›" *)
    syntax "_my_syntax" :: "cartouche_position ⇒ 'a" ("MY_")
    (* Binds our parse translation to that syntax. *)
    parse_translation ‹[(@{syntax_const "_my_syntax"}, cartouche_tr)]›
    
    term "(MY ‹3 * ‹b+c››, 2)" (* Should parse as ([b+c,b+c,b+c],2) *)
    term "(MY ‹10 x ‹q››, 2)" (* Should parse as ([q, q, q, q, q, q, q, q, q, q], 2) *)
    term "(MY ‹3 * ‹MY ‹3 * ‹b+c››››, 2)" (* Things can be nested! *)
    
    end
    

    如果正确投影阴影贴图,则表示您在深度比较中遇到问题。希望这有帮助!