【发布时间】:2017-02-15 03:31:58
【问题描述】:
正如主题所暗示的,当我在其他渲染目标上使用深度缓冲区时,我正在失去精度或没有足够的精度。
目前,我的渲染是这样的:
- 画出一切
- 再次绘制到深度缓冲区。 (这是一个使用 z 索引作为红色通道的渲染目标)
- 绘制灯光(这里我传递了上面创建的深度缓冲区,因此灯光被绘制在对象的前面或后面,在这里我没有得到足够的精度)
- 结合两个渲染目标。
我不确定,或者更确切地说,我知道我做错了什么,但不知道在哪里或如何做。
我希望能够在灯光着色器中执行此操作:
- 如果(像素深度 > 光深度) 丢弃(不要对其施加光)
但是由于精度损失或其他问题,我无法与小于 0.01 的值进行比较。
有些地图的图块的 z 索引范围为 0.00001f-1.0f,如果能够在某些图块之间绘制一些灯光,例如
- 平铺 1 - 0.00020f
- 光 - 0.00021f
- 平铺 2 - 0.00022f
我能够通过一些 rgba-float 编码/解码实现更高的精度,但这非常繁重,在较低的设置中它只会把所有东西都弄乱,而且灯光不会以正确的顺序绘制。
这是场景绘制代码:
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());
}
}
Offset 只是一个 Vector2 用作滚动。
如果代码中缺少某些重要部分来帮助我,请告诉我。
总而言之,如果可能的话,我希望在将深度图值与光的深度值进行比较时,能够达到小数点后 8 位的精度。 如果没有,在我想要的任何两个瓷砖之间放置灯光的最佳做法是什么?
提前谢谢你
【问题讨论】:
-
请将代码的相关部分添加到此问题中。不要链接到外部内容。似乎这只是不适当的 z 剪裁平面的问题(顺便说一句,您使用的是透视投影还是正交投影)。我相信您可以更合理地设置它们。
-
对不起,我觉得把代码放在pastebin上会更好,因为它很多,马上改。
-
想一想:为什么你使用
half4 Depth : COLOR0;而不是浮动的结果深度?一半的精度很差。 -
我已经尝试了所有可能的组合,half4 是最后一个,希望能出现某种奇迹。我从浮动开始。
-
过去一周我一直在努力完成这项工作,但没有任何结果。尝试更改渲染目标 Surface 格式、混合状态、精灵排序模式,启用和禁用 DepthStencilState,但结果是相同的。我能想到的最后一件事是近/远平面,但我不确定如何使用我当前的代码进行设置,以及 spritebatch 是否允许我想做的事情。