using GlitchyEngine.Math; using GlitchyEngine.World; using System.Collections; using System; namespace GlitchyEngine.Renderer { public class Renderer { struct SceneConstants { public Matrix ViewProjection; public Vector3 CameraPosition; public RenderTargetGroup CameraTarget; public RenderTargetGroup CompositionTarget; } struct ObjectConstants { public Matrix Transform; } class GBuffer { private uint32 _width; private uint32 _height; public uint32 Width => _width; public uint32 Height => _height; public Int2 Size => .(_width, _height); public RenderTargetGroup Target ~ _?.ReleaseRef(); public void EnsureSize(uint32 width, uint32 height) { if (width <= _width && height <= _height) return; if (_width == 0 || _height == 0) { SamplerStateDescription desc = .(); desc.MinFilter = .Point; desc.MagFilter = .Point; RenderTargetGroupDescription targetDesc = .(width, height, TargetDescription[]( .(RenderTargetFormat.R8G8B8A8_UNorm){SamplerDescription = desc}, .(RenderTargetFormat.R16G16B16A16_SNorm){SamplerDescription = desc}, .(RenderTargetFormat.R16G16B16A16_SNorm){SamplerDescription = desc}, .(RenderTargetFormat.R32G32B32A32_Float){SamplerDescription = desc}, .(RenderTargetFormat.R8G8B8A8_UNorm){SamplerDescription = desc}, .(RenderTargetFormat.R32_UInt){SamplerDescription = desc, ClearColor = .UInt(uint32.MaxValue)}, ), TargetDescription(.D24_UNorm_S8_UInt){ SamplerDescription = desc, ClearColor = .DepthStencil(1.0f, 0) }); Target = new RenderTargetGroup(targetDesc); } _width = width; _height = height; Target.Resize(_width, _height); } public void Bind() { RenderCommand.SetRenderTargetGroup(Target); RenderCommand.BindRenderTargets(); } public void Clear() { RenderCommand.Clear(Target, .ColorDepth); } } static internal GraphicsContext _context; static SceneConstants _sceneConstants; static Effect LineEffect; static VertexBuffer LineVertices; static GeometryBinding LineGeometry; static GBuffer _gBuffer; static Effect TestFullscreenEffect; static Effect s_tonemappingEffect; static BlendState _gBufferBlend; static BlendState _lightBlend; static DepthStencilState _fullscreenDepthState; static Buffer _sceneBuffer; static Buffer _objectBuffer; [Ordered, CRepr] struct ObjectConstantsBuffer { public Matrix Transform; public Matrix4x3 Transform_InvT; public uint32 EntityId; private Vector3 _padding; } public static void Init(GraphicsContext context, EffectLibrary effectLibrary) { Debug.Profiler.ProfileFunction!(); _context = context..AddRef(); RenderCommand.Init(); Renderer2D.Init(); FullscreenQuad.Init(); InitLineRenderer(effectLibrary); InitDeferredRenderer(effectLibrary); } public static void Deinit() { Debug.Profiler.ProfileFunction!(); DeinitDeferredRenderer(); DeinitLineRenderer(); FullscreenQuad.Deinit(); Renderer2D.Deinit(); _context.ReleaseRef(); } static void InitLineRenderer(EffectLibrary effectLibrary) { Debug.Profiler.ProfileFunction!(); LineEffect = effectLibrary.Load("content\\Shaders\\lineShader.hlsl"); LineGeometry = new GeometryBinding(); LineGeometry.SetPrimitiveTopology(.LineList); Vector4[2] vertices = .(.Zero, .Zero); LineVertices = new VertexBuffer(sizeof(Vector4), 2, .Dynamic, .Write); LineVertices.SetData(vertices, 0, .WriteDiscard); LineGeometry.SetVertexBufferSlot(LineVertices, 0); uint16[2] indices = .(0, 1); IndexBuffer indexBuffer = new IndexBuffer(2, .Immutable); indexBuffer.SetData(indices); LineGeometry.SetIndexBuffer(indexBuffer); indexBuffer.ReleaseRef(); VertexElement[] vertexElements = new VertexElement[1]; vertexElements[0] = .(.R32G32B32_Float, "POSITION"); VertexLayout layout = new VertexLayout(vertexElements, true); LineGeometry.SetVertexLayout(layout..ReleaseRefNoDelete()); } static void DeinitLineRenderer() { LineVertices.ReleaseRef(); LineGeometry.ReleaseRef(); LineEffect.ReleaseRef(); } static void InitDeferredRenderer(EffectLibrary effectLibrary) { TestFullscreenEffect = effectLibrary.Load("content\\Shaders\\simpleLight.hlsl"); s_tonemappingEffect = effectLibrary.Load("content\\Shaders\\SimpleTonemapping.hlsl"); _gBuffer = new GBuffer(); BlendStateDescription gBufferBlendDesc = .Default; _gBufferBlend = new BlendState(gBufferBlendDesc); BlendStateDescription lightBlendDesc = .Default; lightBlendDesc.RenderTarget[0] = .(){ BlendEnable = true, SourceBlend = .One, DestinationBlend = .One, BlendOperation = .Add, SourceBlendAlpha = .One, DestinationBlendAlpha = .Zero, BlendOperationAlpha = .Add, RenderTargetWriteMask = .All }; _lightBlend = new BlendState(lightBlendDesc); DepthStencilStateDescription dsDesc = .Default; dsDesc.DepthEnabled = false; _fullscreenDepthState = new DepthStencilState(dsDesc); BufferDescription sceneBufferDesc = .(sizeof(Matrix), .Constant, .Dynamic, .Write); _sceneBuffer = new Buffer(sceneBufferDesc); BufferDescription objectBufferDesc = .(sizeof(ObjectConstantsBuffer), .Constant, .Dynamic, .Write); _objectBuffer = new Buffer(objectBufferDesc); } static void DeinitDeferredRenderer() { _objectBuffer.ReleaseRef(); _sceneBuffer.ReleaseRef(); _fullscreenDepthState.ReleaseRef(); _lightBlend.ReleaseRef(); _gBufferBlend.ReleaseRef(); delete _gBuffer; s_tonemappingEffect.ReleaseRef(); TestFullscreenEffect.ReleaseRef(); } // [Obsolete("", false)] public static void BeginScene(OldCamera camera) { Debug.Profiler.ProfileRendererFunction!(); _sceneConstants.ViewProjection = camera.ViewProjection; } public static void BeginScene(Camera camera, Matrix transform, RenderTargetGroup renderTarget, RenderTargetGroup finalTarget) { Debug.Profiler.ProfileRendererFunction!(); Matrix viewProjection = camera.Projection * Matrix.Invert(transform); _sceneConstants.ViewProjection = viewProjection; _sceneConstants.CameraPosition = transform.Translation; _sceneConstants.CameraTarget = renderTarget; _sceneConstants.CompositionTarget = finalTarget; _sceneBuffer.SetData(viewProjection , 0, .WriteDiscard); } public static void BeginScene(EditorCamera camera, RenderTargetGroup finalTarget) { Debug.Profiler.ProfileRendererFunction!(); Matrix viewProjection = camera.Projection * camera.View; _sceneConstants.ViewProjection = viewProjection; _sceneConstants.CameraPosition = camera.Position; _sceneConstants.CameraTarget = camera.RenderTarget; _sceneConstants.CompositionTarget = finalTarget; _sceneBuffer.SetData(viewProjection, 0, .WriteDiscard); } public static int SortMeshes(SubmittedMesh left, SubmittedMesh right) { // TODO: Once Material "inheritance" is ready we could perhaps check how similar materials are (e.g. shared textures/variables/etc...) // Similar thing could be done for Meshes. Both would probably require a different way to sort however?... int cmp = (int)Internal.UnsafeCastToPtr(left.Material) <=> (int)Internal.UnsafeCastToPtr(right.Material); // Material equal: Sort by Mesh if (cmp == 0) { cmp = (int)Internal.UnsafeCastToPtr(left.Mesh) <=> (int)Internal.UnsafeCastToPtr(right.Mesh); // Material and Mesh equal: sort by distance if (cmp == 0) { float distLeftSq = Vector3.DistanceSquared(_sceneConstants.CameraPosition, left.Transform.Translation); float distRightSq = Vector3.DistanceSquared(_sceneConstants.CameraPosition, right.Transform.Translation); // Whether or not the values are squared doesn't affect the order (because square(root) is a monotonic function) cmp = distLeftSq <=> distRightSq; } } return 0; } public static void EndScene() { Debug.Profiler.ProfileRendererFunction!(); { Debug.Profiler.ProfileRendererScope!("Sort Meshes"); _queue.Sort(scope => SortMeshes); } // Deferred renderer: // TODO: foreach light: draw shadow map // foreach camera: // { { Debug.Profiler.ProfileRendererScope!("Draw GBuffer"); _gBuffer.EnsureSize(_sceneConstants.CameraTarget.Width, _sceneConstants.CameraTarget.Height); _gBuffer.Clear(); RenderCommand.UnbindRenderTargets(); _gBuffer.Bind(); RenderCommand.SetViewport(0, 0, _sceneConstants.CameraTarget.Width, _sceneConstants.CameraTarget.Height); RenderCommand.SetBlendState(_gBufferBlend); for (SubmittedMesh entry in _queue) { Debug.Profiler.ProfileRendererScope!("Draw Mesh"); { Debug.Profiler.ProfileRendererScope!("Update object buffer"); ObjectConstantsBuffer objectData = ?; objectData.Transform = entry.Transform; Matrix4x3 mat = Matrix4x3((Matrix3x3)(entry.Transform).Invert().Transpose()); objectData.Transform_InvT = mat; objectData.EntityId = entry.EntityId; _objectBuffer.SetData(objectData, 0, .WriteDiscard); } entry.Material.Bind(_context); RenderCommand.BindConstantBuffer(_sceneBuffer, 0, .All); RenderCommand.BindConstantBuffer(_objectBuffer, 1, .All); entry.Mesh.Bind(); RenderCommand.DrawIndexed(entry.Mesh); } } { Debug.Profiler.ProfileRendererScope!("Draw Lights"); RenderCommand.UnbindRenderTargets(); RenderCommand.SetRenderTargetGroup(_sceneConstants.CameraTarget); RenderCommand.BindRenderTargets(); RenderCommand.Clear(_sceneConstants.CameraTarget, .ColorDepth); RenderCommand.SetBlendState(_lightBlend); RenderCommand.SetDepthStencilState(_fullscreenDepthState); // Scaling to make sure that only the part of the gbuffer that we actually used gets rendered into the viewport. Vector2 scaling = Vector2(_sceneConstants.CameraTarget.Width, _sceneConstants.CameraTarget.Height) / (Vector2)_gBuffer.Size; for (SubmittedLight light in _lights) { Debug.Profiler.ProfileRendererScope!("Draw Light"); Vector3 lightDir = -light.Transform.Forward; TestFullscreenEffect.SetTexture("GBuffer_Albedo", _gBuffer.Target, 0); TestFullscreenEffect.SetTexture("GBuffer_Normal", _gBuffer.Target, 1); TestFullscreenEffect.SetTexture("GBuffer_Tangent", _gBuffer.Target, 2); TestFullscreenEffect.SetTexture("GBuffer_Position", _gBuffer.Target, 3); TestFullscreenEffect.SetTexture("GBuffer_Material", _gBuffer.Target, 4); TestFullscreenEffect.Variables["LightColor"].SetData(light.Light.Color); TestFullscreenEffect.Variables["Illuminance"].SetData(light.Light.Illuminance); TestFullscreenEffect.Variables["LightDir"].SetData(lightDir); TestFullscreenEffect.Variables["CameraPos"].SetData(_sceneConstants.CameraPosition); TestFullscreenEffect.Variables["Scaling"].SetData(scaling); TestFullscreenEffect.Bind(_context); FullscreenQuad.Draw(); } _lights.Clear(); // Copy EntityIDs to compositionTarget _gBuffer.Target.CopyTo(_sceneConstants.CompositionTarget, 1, Int2.Zero, Int2(_sceneConstants.CameraTarget.Width, _sceneConstants.CameraTarget.Height), Int2.Zero, 5); RenderCommand.SetBlendState(_gBufferBlend); RenderCommand.UnbindRenderTargets(); RenderCommand.SetRenderTargetGroup(_sceneConstants.CompositionTarget, true); RenderCommand.BindRenderTargets(); // TODO: Postprocessing effects s_tonemappingEffect.SetTexture("CameraTarget", _sceneConstants.CameraTarget, 0); s_tonemappingEffect.Bind(_context); FullscreenQuad.Draw(); RenderCommand.UnbindTextures(); } // TODO: Draw lights to camera target // } // Queue entries increase the reference counter of the mesh/material thus we have to dispose of them. ClearAndDisposeItems!(_queue); } public static void Submit(GeometryBinding geometry, Effect effect, Matrix transform = .Identity) { Debug.Profiler.ProfileRendererFunction!(); //effect.PixelShader?.Buffers.TryReplaceBuffer("SceneConstants", _sceneConstants); //effect.VertexShader?.Buffers.TryReplaceBuffer("SceneConstants", _sceneConstants); //_objectConstants.Data.Transform = transform; //_objectConstants.Update(); //effect.PixelShader?.Buffers.TryReplaceBuffer("ObjectConstants", _objectConstants); //effect.VertexShader?.Buffers.TryReplaceBuffer("ObjectConstants", _objectConstants); effect.Variables["ViewProjection"].SetData(_sceneConstants.ViewProjection); effect.Variables["Transform"].SetData(transform); effect.Bind(_context); geometry.Bind(); RenderCommand.DrawIndexed(geometry); } struct SubmittedMesh : IDisposable { public GeometryBinding Mesh; public Material Material; public Matrix Transform; public uint32 EntityId; public this(GeometryBinding mesh, Material material, Matrix transform, uint32 id) { Mesh = mesh..AddRef(); Material = material..AddRef(); Transform = transform; EntityId = id; } public void Dispose() { Mesh.ReleaseRef(); Material.ReleaseRef(); } } struct SubmittedLight { public SceneLight Light; public Matrix Transform; public this(SceneLight light, Matrix transform) { Light = light; Transform = transform; } } private static List _queue = new .(10000) ~ DeleteContainerAndDisposeItems!(_); private static List _lights = new .(100) ~ delete _; public static void Submit(GeometryBinding geometry, Material material, Matrix transform = .Identity) { Debug.Profiler.ProfileRendererFunction!(); _queue.Add(SubmittedMesh(geometry, material, transform, uint32.MaxValue)); } public static void Submit(GeometryBinding geometry, Material material, EcsEntity entity, Matrix transform = .Identity) { Debug.Profiler.ProfileRendererFunction!(); _queue.Add(SubmittedMesh(geometry, material, transform, entity.[Friend]Index)); } public static void Submit(SceneLight light, Matrix transform = .Identity) { Debug.Profiler.ProfileRendererFunction!(); _lights.Add(SubmittedLight(light, transform)); } /** @brief Draws a line. * @param start The start point of the line. * @param end The end point of the line. * @param color The color of the line. */ public static void DrawLine(Vector3 start, Vector3 end, Color color) { DrawLine(Vector4(start, 1.0f), Vector4(end, 1.0f), (ColorRGBA)color, .Identity); } /** @brief Draws a line. * @param start The start point of the line. * @param end The end point of the line. * @param color The color of the line. * @param transform A transform matrix transforming the line. */ public static void DrawLine(Vector3 start, Vector3 end, ColorRGBA color, Matrix transform) { DrawLine(Vector4(start, 1.0f), Vector4(end, 1.0f), color, transform); } /** @brief Draws a ray. * @param start The start point of the ray. * @param direction The direction of the ray. * @param color The color of the ray. */ public static void DrawRay(Vector3 start, Vector3 direction, ColorRGBA color) { DrawLine(Vector4(start, 1.0f), Vector4(direction, 0.0f), color, .Identity); } /** @brief Draws a ray. * @param start The start point of the ray. * @param direction The direction of the ray. * @param color The color of the ray. * @param transform A transform matrix transforming the ray. */ public static void DrawRay(Vector3 start, Vector3 direction, ColorRGBA color, Matrix transform) { DrawLine(Vector4(start, 1.0f), Vector4(direction, 0.0f), color, transform); } /** @brief Draws a line. * @param start The start point of the line. * @param end The end point of the line. * @param color The color of the line. * @param transform A transform matrix transforming the line. */ public static void DrawLine(Vector4 start, Vector4 end, ColorRGBA color, Matrix transform) { Debug.Profiler.ProfileRendererFunction!(); LineVertices.SetData(Vector4[2](start, end), 0, .WriteDiscard); LineEffect.Variables["ViewProjection"].SetData(_sceneConstants.ViewProjection * transform); LineEffect.Variables["Color"].SetData(color); LineEffect.Bind(_context); LineGeometry.Bind(); RenderCommand.DrawIndexed(LineGeometry); } /** @brief Draws a line. * @param start The start point of the line. * @param end The end point of the line. * @param color The color of the line. * @param transform A transform matrix transforming the line. * @param viewProjection The observing cameras viewprojection */ public static void DrawLine(Vector4 start, Vector4 end, ColorRGBA color, Matrix transform, Matrix viewProjection) { Debug.Profiler.ProfileRendererFunction!(); LineVertices.SetData(Vector4[2](start, end), 0, .WriteDiscard); LineEffect.Variables["ViewProjection"].SetData(viewProjection * transform); LineEffect.Variables["Color"].SetData(color); LineEffect.Bind(_context); LineGeometry.Bind(); RenderCommand.DrawIndexed(LineGeometry); } } }