Batched Billboard Implementation #5

Merged
cosmonaut merged 3 commits from sprites into main 2020-12-04 23:50:42 +00:00
2 changed files with 112 additions and 18 deletions

39
Geometry/Sprite.cs Normal file
View File

@ -0,0 +1,39 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public struct Sprite
{
public Texture2D Texture { get; }
public Vector3 Position { get; }
public Vector2 Origin { get; }
public float Rotation { get; }
public Vector2 Scale { get; }
public Sprite(
Texture2D texture,
Vector3 position,
Vector2 origin,
float rotation,
Vector2 scale
) {
Texture = texture;
Position = position;
Origin = origin;
Rotation = rotation;
Scale = scale;
}
public Sprite(
Texture2D texture,
Vector3 position
) {
Texture = texture;
Position = position;
Origin = Vector2.Zero;
Rotation = 0f;
Scale = Vector2.One;
}
}
}

View File

@ -33,6 +33,7 @@ namespace Kav
private LinearDepthEffect LinearDepthEffect { get; }
private Effect ToneMapEffect { get; }
private SkyboxEffect SkyboxEffect { get; }
private BasicEffect BasicEffect { get; }
private RenderTarget2D gPosition { get; }
private RenderTarget2D gNormal { get; }
@ -159,6 +160,7 @@ namespace Kav
ToneMapEffect = new Effect(graphicsDevice, Resources.ToneMapEffect);
Deferred_ToonEffect = new Deferred_ToonEffect(GraphicsDevice);
SkyboxEffect = new SkyboxEffect(GraphicsDevice);
BasicEffect = new BasicEffect(GraphicsDevice);
FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly);
FullscreenTriangle.SetData(new VertexPositionTexture[3] {
@ -176,6 +178,7 @@ namespace Kav
}
public void DeferredRender(
RenderTarget2D renderTarget,
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
AmbientLight ambientLight,
@ -198,21 +201,21 @@ namespace Kav
DirectionalLightRender(camera, modelTransforms, directionalLight);
GraphicsDevice.SetRenderTarget(null);
GraphicsDevice.Clear(Color.Black);
GraphicsDevice.SetRenderTarget(renderTarget);
SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, ToneMapEffect);
SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White);
SpriteBatch.End();
}
public void DeferredToonRender(
RenderTarget2D renderTarget,
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
AmbientLight ambientLight,
IEnumerable<PointLight> pointLights,
DirectionalLight directionalLight,
TextureCube skybox
) {
) {
GBufferRender(camera, modelTransforms);
GraphicsDevice.SetRenderTarget(ColorRenderTarget);
@ -230,12 +233,64 @@ namespace Kav
DirectionalLightToonRender(camera, modelTransforms, directionalLight);
SkyboxRender(camera, skybox);
GraphicsDevice.SetRenderTarget(null);
GraphicsDevice.SetRenderTarget(renderTarget);
SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, null);
SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White);
SpriteBatch.End();
}
// billboards sprites into the scene
// FIXME: we can frustum cull the sprites probably
public void BillboardSpriteRender(
RenderTarget2D renderTarget,
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
IEnumerable<Sprite> sprites
) {
GraphicsDevice.SetRenderTarget(ColorRenderTarget);
GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
DepthRender(camera, modelTransforms);
GraphicsDevice.Clear(ClearOptions.Target, new Color(0, 0, 0, 0), 1f, 0);
Matrix invertY = Matrix.CreateScale(1, -1, 1);
BasicEffect.World = invertY;
BasicEffect.View = Matrix.Identity;
BasicEffect.Projection = camera.Projection;
BasicEffect.TextureEnabled = true;
BasicEffect.VertexColorEnabled = true;
SpriteBatch.Begin(0, null, null, DepthStencilState.DepthRead, RasterizerState.CullNone, BasicEffect);
foreach (var sprite in sprites)
{
// transform view space on CPU so we don't have to break the batch
Vector3 viewSpacePosition = Vector3.Transform(sprite.Position, camera.View * invertY);
SpriteBatch.Draw(
sprite.Texture,
new Vector2(viewSpacePosition.X, viewSpacePosition.Y),
null,
Color.White,
0,
sprite.Origin,
sprite.Scale / new Vector2(sprite.Texture.Width, sprite.Texture.Height),
0,
viewSpacePosition.Z
);
}
SpriteBatch.End();
GraphicsDevice.SetRenderTarget(renderTarget);
GraphicsDevice.Clear(new Color(0, 0, 0, 0));
SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null);
SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White);
SpriteBatch.End();
}
private void DepthRender(
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms
@ -386,11 +441,11 @@ namespace Kav
DeferredPointLightEffect.GPosition = gPosition;
DeferredPointLightEffect.GAlbedo = gAlbedo;
DeferredPointLightEffect.GNormal = gNormal;
DeferredPointLightEffect.GMetallicRoughness = gMetallicRoughness;
DeferredPointLightEffect.GMetallicRoughness = gMetallicRoughness;
DeferredPointLightEffect.ShadowMap = PointShadowCubeMap;
DeferredPointLightEffect.PointLightPosition = pointLight.Position;
DeferredPointLightEffect.PointLightColor =
DeferredPointLightEffect.PointLightColor =
pointLight.Color.ToVector3() * pointLight.Intensity;
DeferredPointLightEffect.FarPlane = 25f; // FIXME: magic value
@ -428,11 +483,11 @@ namespace Kav
{
DeferredDirectionalLightEffect.ShadowMapFour = ShadowRenderTargets[3];
}
DeferredDirectionalLightEffect.DirectionalLightDirection = directionalLight.Direction;
DeferredDirectionalLightEffect.DirectionalLightColor =
DeferredDirectionalLightEffect.DirectionalLightColor =
directionalLight.Color.ToVector3() * directionalLight.Intensity;
DeferredDirectionalLightEffect.ViewMatrix = camera.View;
DeferredDirectionalLightEffect.EyePosition = camera.Position;
@ -467,7 +522,7 @@ namespace Kav
Deferred_ToonEffect.EyePosition = camera.Position;
Deferred_ToonEffect.DirectionalLightDirection = directionalLight.Direction;
Deferred_ToonEffect.DirectionalLightColor =
Deferred_ToonEffect.DirectionalLightColor =
directionalLight.Color.ToVector3() * directionalLight.Intensity;
Deferred_ToonEffect.ShadowMapOne = ShadowRenderTargets[0];
@ -483,7 +538,7 @@ namespace Kav
{
Deferred_ToonEffect.ShadowMapFour = ShadowRenderTargets[3];
}
Deferred_ToonEffect.ViewMatrix = camera.View;
foreach (EffectPass pass in Deferred_ToonEffect.CurrentTechnique.Passes)
@ -506,7 +561,7 @@ namespace Kav
{
var farPlane = camera.FarPlane / (MathHelper.Max((NumShadowCascades - i - 1) * 2f, 1f));
// divide the view frustum
// divide the view frustum
var shadowCamera = new PerspectiveCamera(
camera.Position,
camera.Forward,
@ -516,7 +571,7 @@ namespace Kav
previousFarPlane,
farPlane
);
// TODO: This is tightly coupled to the effect and it sucks
RenderDirectionalShadowMap(shadowCamera, modelTransforms, directionalLight, effect, i);
@ -526,8 +581,8 @@ namespace Kav
}
private void RenderDirectionalShadowMap(
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight,
ShadowCascadeEffect effect,
int shadowCascadeIndex
@ -536,7 +591,7 @@ namespace Kav
GraphicsDevice.Clear(Color.White);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.BlendState = BlendState.Opaque;
var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
Vector3[] frustumCorners = cameraBoundingFrustum.GetCorners();
@ -555,7 +610,7 @@ namespace Kav
}
BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners);
SimpleDepthEffect.View = lightView;
SimpleDepthEffect.Projection = Matrix.CreateOrthographicOffCenter(
lightBox.Min.X,
@ -586,7 +641,7 @@ namespace Kav
}
var boundingFrustum = new BoundingFrustum(lightSpaceMatrix);
foreach (var (model, transform) in FrustumCull(boundingFrustum, modelTransforms))
{
foreach (var modelMesh in model.Meshes)