Как подразумевается, я теряю точность или недостаточно, когда я использую буфер глубины для других целей рендеринга.Точность потери буфера глубины 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 действует как свиток ,
Если есть какая-то важная часть кода не хватает, чтобы помочь мне, дайте мне знать.
Подводя итог, я хотел бы иметь такую же точность, как 8 десятичных точек при сравнении значений глубины карты с значением глубины света, если это возможно. Если нет, то какая была бы лучшая практика, чтобы иметь огни между любыми двумя плитами, которые я хочу?
Спасибо заранее
Пожалуйста, добавьте соответствующую часть кода к этому вопросу. Не ссылайтесь на внешний контент. Кажется, это просто вопрос неприемлемых плоскостей отсечения z (btw, вы используете перспективную или орфографическую проекцию). Я уверен, что вы можете настроить их более разумно. –
Извините, подумал, что было бы лучше поставить код на pastebin, так как он много, изменит его сразу. –
Просто подумайте: почему вы используете 'half4 Depth: COLOR0;' и не плаваете для результирующей глубины? У половины есть очень плохая точность. – Gnietschow