307 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			307 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
| 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<PointLight> pointLights,
 | |
|             IEnumerable<DirectionalLight> 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<PointLight> pointLights,
 | |
|             IEnumerable<DirectionalLight> directionalLights
 | |
|         ) {
 | |
|             Render(camera.View, camera.Projection, modelTransforms, pointLights, directionalLights);
 | |
|         }
 | |
| 
 | |
|         private void Render(
 | |
|             Matrix view,
 | |
|             Matrix projection,
 | |
|             IEnumerable<(Model, Matrix)> modelTransforms,
 | |
|             IEnumerable<PointLight> pointLights,
 | |
|             IEnumerable<DirectionalLight> 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
 | |
|                             );
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |