using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace Kav { public class Renderer { private GraphicsDevice GraphicsDevice { get; } private int RenderDimensionsX { get; } private int RenderDimensionsY { get; } private RenderTargetCube DirectionalLightDepthTarget { get; } private SimpleDepthEffect SimpleDepthEffect { get; } private RenderTarget2D gPosition { get; } private RenderTarget2D gNormal { get; } private RenderTarget2D gAlbedo { get; } private RenderTarget2D gMetallicRoughness { get; } private RenderTarget2D deferredRenderTarget { get; } private RenderTargetBinding[] GBuffer { get; } private DeferredPBREffect DeferredPBREffect { get; } private SpriteBatch SpriteBatch { get; } public Renderer(GraphicsDevice graphicsDevice, int renderDimensionsX, int renderDimensionsY) { GraphicsDevice = graphicsDevice; RenderDimensionsX = renderDimensionsX; RenderDimensionsY = renderDimensionsY; DirectionalLightDepthTarget = new RenderTargetCube( GraphicsDevice, 1024, false, SurfaceFormat.Single, DepthFormat.Depth24 ); gPosition = new RenderTarget2D( GraphicsDevice, renderDimensionsX, renderDimensionsY, false, SurfaceFormat.Vector4, DepthFormat.Depth24 ); gNormal = new RenderTarget2D( GraphicsDevice, renderDimensionsX, renderDimensionsY, false, SurfaceFormat.Vector4, DepthFormat.None ); gAlbedo = new RenderTarget2D( GraphicsDevice, renderDimensionsX, renderDimensionsY, false, SurfaceFormat.Color, DepthFormat.None ); gMetallicRoughness = new RenderTarget2D( GraphicsDevice, renderDimensionsX, renderDimensionsY, false, SurfaceFormat.HalfVector2, DepthFormat.None ); GBuffer = new RenderTargetBinding[4] { new RenderTargetBinding(gPosition), new RenderTargetBinding(gNormal), new RenderTargetBinding(gAlbedo), new RenderTargetBinding(gMetallicRoughness) }; deferredRenderTarget = new RenderTarget2D( GraphicsDevice, renderDimensionsX, renderDimensionsY ); SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice); DeferredPBREffect = new DeferredPBREffect(GraphicsDevice); SpriteBatch = new SpriteBatch(GraphicsDevice); GraphicsDevice.SetRenderTarget(deferredRenderTarget); graphicsDevice.Clear(Color.White); for (int i = 0; i < 6; i++) { GraphicsDevice.SetRenderTarget(DirectionalLightDepthTarget, (CubeMapFace) i); GraphicsDevice.Clear(Color.White); } GraphicsDevice.SetRenderTarget(null); } public void DeferredRender( Camera camera, IEnumerable<(Model, Matrix)> modelTransforms, IEnumerable pointLights, IEnumerable directionalLights ) { var directionalLightIndex = 0; foreach (var directionalLight in directionalLights) { if (directionalLightIndex > 5) { break; } ShadowMapRender(modelTransforms, directionalLight, (CubeMapFace) directionalLightIndex); directionalLightIndex += 1; } GraphicsDevice.SetRenderTargets(GBuffer); GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); GraphicsDevice.DepthStencilState = DepthStencilState.Default; GraphicsDevice.BlendState = BlendState.Opaque; foreach (var (model, transform) in modelTransforms) { foreach (var modelMesh in model.Meshes) { foreach (var meshPart in modelMesh.MeshParts) { GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); GraphicsDevice.Indices = meshPart.IndexBuffer; if (meshPart.Effect is TransformEffect transformEffect) { transformEffect.World = transform; transformEffect.View = camera.View; transformEffect.Projection = camera.Projection; } foreach (var pass in meshPart.Effect.CurrentTechnique.Passes) { pass.Apply(); GraphicsDevice.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, meshPart.VertexBuffer.VertexCount, 0, meshPart.Triangles.Length ); } } } } GraphicsDevice.SetRenderTarget(null); GraphicsDevice.Clear(Color.CornflowerBlue); DeferredPBREffect.GPosition = gPosition; DeferredPBREffect.GAlbedo = gAlbedo; DeferredPBREffect.GNormal = gNormal; DeferredPBREffect.GMetallicRoughness = gMetallicRoughness; DeferredPBREffect.DirectionalShadowMap = DirectionalLightDepthTarget; DeferredPBREffect.EyePosition = Matrix.Invert(camera.View).Translation; int i = 0; foreach (var pointLight in pointLights) { if (i > DeferredPBREffect.MaxPointLights) { break; } DeferredPBREffect.PointLights[i] = pointLight; i++; } i = 0; foreach (var directionalLight in directionalLights) { if (i > DeferredPBREffect.MaxDirectionalLights) { break; } DeferredPBREffect.DirectionalLights[i] = directionalLight; i++; } SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, null, null, DeferredPBREffect); SpriteBatch.Draw(deferredRenderTarget, Vector2.Zero, Color.White); SpriteBatch.End(); } // for shadow mapping public void ShadowMapRender(IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight, CubeMapFace face) { GraphicsDevice.SetRenderTarget(DirectionalLightDepthTarget, face); GraphicsDevice.Clear(Color.White); GraphicsDevice.DepthStencilState = DepthStencilState.Default; GraphicsDevice.BlendState = BlendState.Opaque; SimpleDepthEffect.View = directionalLight.View; SimpleDepthEffect.Projection = directionalLight.Projection; SimpleDepthEffect.Near = 0.1f; // FIXME: this is a kludge SimpleDepthEffect.Far = 200f; foreach (var (model, transform) in modelTransforms) { foreach (var modelMesh in model.Meshes) { foreach (var meshPart in modelMesh.MeshParts) { GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); GraphicsDevice.Indices = meshPart.IndexBuffer; SimpleDepthEffect.Model = transform; foreach (var pass in SimpleDepthEffect.CurrentTechnique.Passes) { pass.Apply(); GraphicsDevice.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, meshPart.VertexBuffer.VertexCount, 0, meshPart.Triangles.Length ); } } } } } public void Render( Camera camera, IEnumerable<(Model, Matrix)> modelTransforms, IEnumerable pointLights, IEnumerable directionalLights ) { Render(camera.View, camera.Projection, modelTransforms, pointLights, directionalLights); } private void Render( Matrix view, Matrix projection, IEnumerable<(Model, Matrix)> modelTransforms, IEnumerable pointLights, IEnumerable directionalLights ) { foreach (var (model, transform) in modelTransforms) { foreach (var modelMesh in model.Meshes) { foreach (var meshPart in modelMesh.MeshParts) { GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); GraphicsDevice.Indices = meshPart.IndexBuffer; if (meshPart.Effect is TransformEffect transformEffect) { transformEffect.World = transform; transformEffect.View = view; transformEffect.Projection = projection; } if (meshPart.Effect is PointLightEffect pointLightEffect) { int i = 0; foreach (var pointLight in pointLights) { if (i > pointLightEffect.MaxPointLights) { break; } pointLightEffect.PointLights[i] = pointLight; i++; } } if (meshPart.Effect is DirectionalLightEffect directionalLightEffect) { int i = 0; foreach (var directionalLight in directionalLights) { if (i > directionalLightEffect.MaxDirectionalLights) { break; } directionalLightEffect.DirectionalLights[i] = directionalLight; i++; } } foreach (var pass in meshPart.Effect.CurrentTechnique.Passes) { pass.Apply(); GraphicsDevice.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, meshPart.VertexBuffer.VertexCount, 0, meshPart.Triangles.Length ); } } } } } } }