2017-02-15 36 views
1

Как подразумевается, я теряю точность или недостаточно, когда я использую буфер глубины для других целей рендеринга.Точность потери буфера глубины HLSL

В настоящее время, мой рендеринг выглядит следующим образом:

  • рисовать все
  • рисовать снова буфер глубины. (это rendertarget, который использует индекс z как красный канал)
  • рисовать огни (здесь я передаю буфер глубины, созданный выше, так что огни рисуются спереди или сзади объектов, и здесь я не получаю достаточной точности)
  • объединить обе цели рендеринга.

Я не уверен, вернее, я знаю, что я делаю что-то неправильно, но не знаю, где и как.

Я хочу, чтобы иметь возможность, в затенении огни, чтобы сделать это:

  • если (глубина пикселей> глубина света) отбрасывать (не наносите на него свет)

, но с потерей точности или некоторой другой проблемой, я не могу сделать это сравнение со значениями меньше 0,01.

Некоторые карты имеют плитки, которые имеют аз диапазон индекса 0.00001f-1.0f, и было бы полезно, чтобы иметь возможность сделать некоторые огни между некоторыми плитки, такие как

  • плитка 1 - 0.00020f
  • свет - 0.00021f
  • плитка 2 - 0.00022f

мне удалось добиться гораздо большей точности с некоторыми RGBA-флоат кодирования/декодирования, но это супер тяжелый и в более низких настройках он просто портит everyt hing up и lights не рисуют в правильном порядке.

Вот код сцены ничья:

RenderTarget2D colorRT; 
RenderTarget2D depthRT; 
RenderTarget2D shadowRT; 
RenderTarget2D finalRT; 

protected void LoadContent() 
{ 
    colorRT = new RenderTarget2D(GraphicsDevice, width, height); 
    finalRT = new RenderTarget2D(GraphicsDevice, width, height); 
    depthRT = new RenderTarget2D(GraphicsDevice, width, height, false, SurfaceFormat.Single, DepthFormat.Depth24); 
    shadowRT = new RenderTarget2D(GraphicsDevice, width, height, false, SurfaceFormat.Single, DepthFormat.None); 
} 

protected override void Draw() 
{ 
    DrawColorMap(); 
    DrawDepthMap(); 
    GenerateShadowMap(); 
    DrawCombinedMaps(); 

    spriteBatch.GraphicsDevice.SetRenderTarget(null); 
    spriteBatch.GraphicsDevice.Clear(Color.Transparent); 

    //draw finalRT 
} 

private void DrawColorMap() 
{ 
    spriteBatch.GraphicsDevice.SetRenderTarget(colorRT); 
    spriteBatch.GraphicsDevice.Clear(Color.Transparent); 

    spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullCounterClockwise, null, map.camera.GetTransformation() * Resolution.getTransformationMatrix()); 

    map.Draw(spriteBatch, mapsTex, scroll, map.camera.GetTransformation()); 

    spriteBatch.End(); 
} 

private void DrawDepthMap() 
{ 
    GraphicsDevice.SetRenderTarget(depthRT); 
    GraphicsDevice.Clear(Color.White); 

    greyEffect.Parameters["World"].SetValue(map.camera.GetTransformation() * Resolution.getTransformationMatrix()); 

    spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.Opaque, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullCounterClockwise, greyEffect); 

    map.Draw(spriteBatch, mapsTex, scroll, map.camera.GetTransformation()); 

    spriteBatch.End(); 
} 

private Texture2D GenerateShadowMap() 
{ 
    GraphicsDevice.SetRenderTarget(shadowRT); 
    GraphicsDevice.Clear(new Color(0, 0, 0, 0)); 
    GraphicsDevice.BlendState = BlendState.AlphaBlend; 
    GraphicsDevice.DepthStencilState = DepthStencilState.None; 

    foreach (var light in map.lights) 
    { 
     if (light != null) 
     { 
      if (!light.IsEnabled) continue; 

      Vertices[0].Position = new Vector3(map.camera.WorldToScreen2(new Vector2(light.Location.X, light.Location.Y) - new Vector2(scroll.X, scroll.Y), map.camera.GetTransformation()), light.Location.Z); 
      Vertices[1].Position = new Vector3(map.camera.WorldToScreen2(new Vector2(light.Location.X + light.LightDecay, light.Location.Y) - new Vector2(scroll.X, scroll.Y), map.camera.GetTransformation()), light.Location.Z); 
      Vertices[2].Position = new Vector3(map.camera.WorldToScreen2(new Vector2(light.Location.X, light.Location.Y + light.LightDecay) - new Vector2(scroll.X, scroll.Y), map.camera.GetTransformation()), light.Location.Z); 
      Vertices[3].Position = new Vector3(map.camera.WorldToScreen2(new Vector2(light.Location.X + light.LightDecay, light.Location.Y + light.LightDecay) - new Vector2(scroll.X, scroll.Y), map.camera.GetTransformation()), light.Location.Z); 

      VertexBuffer.SetData(Vertices); 
      spriteBatch.GraphicsDevice.SetVertexBuffer(VertexBuffer); 

      _lightEffect.CurrentTechnique = _lightEffectTechniquePointLight; 
      _lightEffect.Parameters["DepthMap"].SetValue(depthRT); 
      _lightEffect.CurrentTechnique.Passes[0].Apply(); 

      // Add Belding (Black background) 
      spriteBatch.GraphicsDevice.BlendState = BlendBlack; 

      spriteBatch.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, Vertices, 0, 2); 
     } 

    return shadowRT; 
} 

private void DrawCombinedMaps() 
{ 
    GraphicsDevice.SetRenderTarget(finalRT); 
    GraphicsDevice.Clear(Color.LightSkyBlue); 

    _lightCombinedEffectParamColorMap.SetValue(colorRT); 
    _lightCombinedEffectParamShadowMap.SetValue(shadowRT); 

    spriteBatch.Begin(0, BlendState.Opaque, null, DepthStencilState.None, RasterizerState.CullCounterClockwise, _lightCombinedEffect); 

    spriteBatch.Draw(colorRT, new Rectangle(0, 0, viewPortStored.Width, viewPortStored.Height), Color.White); 

    spriteBatch.End(); 
} 

Глубина буфера шейдер & освещения шейдер: Код

/////////////////////////////////////////DEPTH BUFFER SHADER///////////////////////////////////////// 
float2 offset; 
float scale; 
float2 screenSize; 
float4x4 World; 

struct VertexShaderInput 
{ 
    float4 Position : POSITION0; 
    float2 TexCoord : TEXCOORD0; 
}; 

struct VertexShaderOutput 
{ 
    float4 Position : POSITION0; 
    float2 TexCoord : TEXCOORD0; 
    float2 Depth : TEXCOORD1; 
}; 

struct PixelShaderOutput 
{ 
    half4 Depth : COLOR0; 
}; 

VertexShaderOutput MyVertexShader(VertexShaderInput input) 
{ 
    VertexShaderOutput output; 

    // Half pixel offset for correct texel centering. 
    input.Position.xy -= 0.5; 

    // Viewport adjustment. 
    input.Position.xy = input.Position.xy/screenSize; 
    input.Position.xy -= offset; 

    input.Position.xy *= float2(2, -2); 
    input.Position.xy -= float2(1, -1); 

    output.Position = input.Position; 
    output.TexCoord = input.TexCoord; 
    output.Depth.x = output.Position.z; 
    output.Depth.y = output.Position.w; 

    return output; 
} 

PixelShaderOutput PointLightShader(VertexShaderOutput input) 
{ 
    PixelShaderOutput output; 

    output.Depth = input.Depth.x/input.Depth.y; 

    return output; 
} 
/////////////////////////////////////////END DEPTH BUFFER SHADER///////////////////////////////////////// 

/////////////////////////////////////////LIGHTING SHADER///////////////////////////////////////// 
float screenWidth; 
float screenHeight; 
float4 ambientColor; 

float lightStrength; 
float lightDecay; 
float3 lightPosition; 
float4 lightColor; 
float lightRadius; 
float lightCenterStrenght; 

float3 coneDirection; 
float coneAngle; 
float coneDecay; 

float scale; 
float2 offset; 

Texture DepthMap; 
sampler DepthMapSampler = sampler_state { 
    texture = <DepthMap>; 

    AddressU = CLAMP; 
    AddressV = CLAMP; 
}; 

struct VertexShaderInput 
{ 
    float4 Position : POSITION0; 
    float4 TexCoord : TEXCOORD0; 
}; 

struct VertexShaderOutput 
{ 
    float4 Position : POSITION0; 
    float4 TexCoord : TEXCOORD0; 
    float4 ScreenPosition : TEXCOORD1; 
}; 

VertexShaderOutput MyVertexShader(VertexShaderInput input) 
{ 
    VertexShaderOutput output; 

    // Half pixel offset for correct texel centering. 
    input.Position.xy -= 0.5; 

    // Viewport adjustment. 
    input.Position.xy = input.Position.xy/float2(screenWidth, screenHeight); 
    input.Position.xy -= offset; 

    output.ScreenPosition = input.Position; 

    input.Position.xy *= float2(2, -2); 
    input.Position.xy -= float2(1, -1); 

    output.Position = input.Position; 
    output.TexCoord = (lightDecay * 2 * input.TexCoord)/scale; 
    //Output.Color = color; 

    return output; 
} 

float4 PointLightShader(VertexShaderOutput input) : COLOR0 
{ 
    //float2 texCoord = 0.5f * (float2(input.ScreenPosition.x,-input.ScreenPosition.y) + 1); 
    //texCoord *= lightDecay * 2; 
    ////allign texels to pixels 
    //texCoord -= 0.5; 

    //input.ScreenPosition.xy /= input.ScreenPosition.w; 

    float depth = tex2D(DepthMapSampler, input.ScreenPosition.xy).r; 
    clip(abs(depth - lightPosition.z) < 0.05 ? 1 : -1); 
    //float4 position; 
    //position.xy = input.ScreenPosition.xy; 
    //position.z = depth; 
    //position.w = 1; 
    //position /= position.w; 

    float coneAttenuation; 

    float4 shading; 
    float2 pixelPosition = input.TexCoord.xy; 
    float2 lightPos = float2(lightDecay, lightDecay) * scale; 
    float2 lightDirection = (pixelPosition - lightPos)/scale; 

    //THIS ADDS THE CIRCLE IN THE CENTER OF THE LIGHT. 
    float distance; 
    if (lightCenterStrenght > 0) 
     distance = (1/length(pixelPosition - lightPos)) * lightStrength; 
    else 
     distance = lightStrength; 

    coneAttenuation = saturate(1.0f - length(lightDirection)/lightDecay); 

    shading = distance * coneAttenuation * lightColor; 

    //if (depth >= 1 * 256) 
     //return float4(255, 255, 255, coneAttenuation * lightStrength); 
    /* 
     if (realDepth >= 1) 
      return float4(255, 255, 255, coneAttenuation * lightStrength);*/ 

    return float4(shading.rgb, coneAttenuation * lightStrength); 
} 

float4 SpotLightShader2(float2 TexCoord : TEXCOORD0, float2 screenPos : TEXCOORD1) : COLOR0 
{ 
    float4 depth = tex2D(DepthMapSampler, screenPos); 
    float realDepth = 0; 

    float3 shading = float3(0, 0, 0); 
    float coneAttenuation; 

    float lightDepth = lightPosition.z; 

    if (realDepth < lightDepth) 
    { 
     float2 pixelPosition = TexCoord; 

     float2 lightVector = normalize((pixelPosition - float2(lightDecay, lightDecay) * scale)/scale); 
     // cosine of the angle between spotdirection and lightvector 
     float SdL = dot(coneDirection, -lightVector); 

     if (SdL > coneAngle) 
     { 
      float3 lightPos = float3(lightPosition.x, lightPosition.y, lightPosition.z); 
      float2 lightVector = (pixelPosition - float2(lightDecay, lightDecay) * scale)/scale; 
      lightVector = normalize(lightVector); 

      float3 coneDirectionTemp = coneDirection; 
      //coneDirectionTemp.z = 50.0f; 
      float spotIntensity = pow(abs(SdL), coneDecay); 

      float2 lightDirection = (pixelPosition - float2(lightDecay, lightDecay) * scale)/scale; 
      float3 halfVec = float3(0, 0, 1); 

      float amount = max(dot(1, lightVector), 0); 

      coneAttenuation = saturate(1.0f - length(lightDirection)/lightDecay); 

      float2 reflect = normalize(2 * amount * 1 - lightVector); 

      float2 r = normalize(2 * dot(lightVector, 1) * 1 - lightVector); 
      float2 v = normalize(mul(normalize(coneDirectionTemp), 1)); 
      float dotProduct = dot(r, v); 

      //float4 specular = light.specPower * light.specularColor * max(pow(dotProduct, 10), 0) * length(inColor); 
      float specular = min(pow(saturate(dot(reflect, halfVec)), 10), 1); 

      shading = lightColor * lightStrength; 
      shading += specular * 1; 
      shading += amount * 0.5; 
      shading *= coneAttenuation * spotIntensity; 
     } 
    } 
    else 
     shading = 0; 

    return float4(shading.r, shading.g, shading.b, coneAttenuation * lightStrength); 
} 
/////////////////////////////////////////END LIGHTING SHADER///////////////////////////////////////// 

Камера:

public class Camera 
{ 
    public Matrix Transform; 
    public Matrix PositionTransform = Matrix.CreateTranslation(new Vector3(0, 0, 0f)); 
    public Matrix GetTransformation() 
    { 
     Transform = Matrix.CreateScale(new Vector3(zoom, zoom, 1)) * PositionTransform; 

     return Transform; 
    } 

     public Vector2 WorldToScreen(Vector2 worldPosition, Matrix m) 
    { 
     return Vector2.Transform(worldPosition, Resolution.getTransformationMatrix()); 
    } 

    public Vector2 WorldToScreen2(Vector2 worldPosition, Matrix m) 
    { 
     return Vector2.Transform(worldPosition, m * Resolution.getTransformationMatrix()); 
    } 
} 

Смещение просто Vector2 действует как свиток ,

here's a screenshot with the problem, the light should be between the lamp and the banner (banner in front covering the light)

Если есть какая-то важная часть кода не хватает, чтобы помочь мне, дайте мне знать.

Подводя итог, я хотел бы иметь такую ​​же точность, как 8 десятичных точек при сравнении значений глубины карты с значением глубины света, если это возможно. Если нет, то какая была бы лучшая практика, чтобы иметь огни между любыми двумя плитами, которые я хочу?

Спасибо заранее

+0

Пожалуйста, добавьте соответствующую часть кода к этому вопросу. Не ссылайтесь на внешний контент. Кажется, это просто вопрос неприемлемых плоскостей отсечения z (btw, вы используете перспективную или орфографическую проекцию). Я уверен, что вы можете настроить их более разумно. –

+0

Извините, подумал, что было бы лучше поставить код на pastebin, так как он много, изменит его сразу. –

+0

Просто подумайте: почему вы используете 'half4 Depth: COLOR0;' и не плаваете для результирующей глубины? У половины есть очень плохая точность. – Gnietschow

ответ

0

Edit: На самом деле я думаю, что я вижу проблему. У вашего клипа есть допуск, который больше, чем разница в глубине, которую вы разместили для объектов в вашей сцене.

Если вы думаете, «мой z-буфер составляет всего 24 бита, и я не вижу его в z-fight, поэтому почему мой 32-битный буфер глубины?»

Ответ, вероятно, будет, поскольку z-буферы не линейны, они имеют более высокую точность, чем далеко.

Имейте в виду, что SurfaceFormat.Single на самом деле не поддерживается на всех платформах, поэтому вы можете избежать написания отложенного движка, который опирается на него.

Две общие решения либо ..

  1. Снизить дальнего расстояния.
  2. Кодировать нелинейную глубину (найдите, как нормальные z-буферы хранят глубину, но в основном это log/1-exp).
+0

Извините за поздний ответ, стек не уведомил меня по электронной почте. Как уменьшить дальнее расстояние или допуск операции клипа? Я смотрю учебники, статьи и что нет, но я не уверен, как реализовать или изменить его с помощью кода камеры. Cheers –

+0

"clip (abs (depth - lightPosition.z) <0.05? 1: -1);" 0.05 - это огромное значение, если вы сравниваете глубину пикселей в нормализованном диапазоне 0.0-1.0. – James

+0

Это не имеет никакого значения, к сожалению, я пробовал с большими значениями и меньше и даже удалял весь loc. –