mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
565 lines
17 KiB
Beef
565 lines
17 KiB
Beef
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<Vector4>(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<Matrix>(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<Matrix>(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<SubmittedMesh> _queue = new .(10000) ~ DeleteContainerAndDisposeItems!(_);
|
|
private static List<SubmittedLight> _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);
|
|
}
|
|
}
|
|
}
|