Merge branch 'grid-update' of https://github.com/Swiggies/FlaxEngine into Swiggies-grid-update

#2663
This commit is contained in:
Olly Rybak
2024-06-10 23:17:39 +02:00
committed by Wojtek Figat
parent 892ab47b7a
commit 2a4a5d2519
4 changed files with 255 additions and 58 deletions

BIN
Content/Shaders/Editor/Grid.flax (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -2,6 +2,7 @@
using System;
using FlaxEngine;
using System.Runtime.InteropServices;
namespace FlaxEditor.Gizmo
{
@@ -15,91 +16,119 @@ namespace FlaxEditor.Gizmo
[HideInEditor]
private sealed class Renderer : PostProcessEffect
{
private IntPtr _debugDrawContext;
[StructLayout(LayoutKind.Sequential)]
private struct Data
{
public Matrix WorldMatrix;
public Matrix ViewProjectionMatrix;
public Float4 GridColor;
public Float3 ViewPos;
public float Far;
public Float3 Padding;
public float GridSize;
}
private static readonly uint[] _triangles =
{
0, 2, 1, // Face front
1, 3, 0,
};
private GPUBuffer[] _vbs = new GPUBuffer[1];
private GPUBuffer _vertexBuffer;
private GPUBuffer _indexBuffer;
private GPUPipelineState _psGrid;
private Shader _shader;
public Renderer()
{
Order = -100;
UseSingleTarget = true;
Location = PostProcessEffectLocation.BeforeForwardPass;
Location = PostProcessEffectLocation.Default;
_shader = FlaxEngine.Content.LoadAsyncInternal<Shader>("Shaders/Editor/Grid");
}
~Renderer()
{
if (_debugDrawContext != IntPtr.Zero)
{
DebugDraw.FreeContext(_debugDrawContext);
_debugDrawContext = IntPtr.Zero;
}
Destroy(ref _psGrid);
Destroy(ref _vertexBuffer);
Destroy(ref _indexBuffer);
_shader = null;
}
public override void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output)
public override unsafe void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output)
{
if (_shader == null)
return;
Profiler.BeginEventGPU("Editor Grid");
if (_debugDrawContext == IntPtr.Zero)
_debugDrawContext = DebugDraw.AllocateContext();
DebugDraw.SetContext(_debugDrawContext);
DebugDraw.UpdateContext(_debugDrawContext, 1.0f / Mathf.Max(Engine.FramesPerSecond, 1));
var viewPos = (Vector3)renderContext.View.Position;
var plane = new Plane(Vector3.Zero, Vector3.UnitY);
var dst = CollisionsHelper.DistancePlanePoint(ref plane, ref viewPos);
var options = Editor.Instance.Options.Options;
float space = options.Viewport.ViewportGridScale, size;
if (dst <= 500.0f)
Vector3 camPos = renderContext.View.WorldPosition;
float gridSize = renderContext.View.Far + 20000;
// Lazy-init resources
if (_vertexBuffer == null)
{
size = 8000;
_vertexBuffer = new GPUBuffer();
var desc = GPUBufferDescription.Vertex(sizeof(Float3), 4);
_vertexBuffer.Init(ref desc);
}
else if (dst <= 2000.0f)
if (_indexBuffer == null)
{
space *= 2;
size = 8000;
_indexBuffer = new GPUBuffer();
fixed (uint* ptr = _triangles)
{
var desc = GPUBufferDescription.Index(sizeof(uint), _triangles.Length, new IntPtr(ptr));
_indexBuffer.Init(ref desc);
}
}
else
if (_psGrid == null)
{
space *= 20;
size = 100000;
_psGrid = new GPUPipelineState();
var desc = GPUPipelineState.Description.Default;
desc.BlendMode = BlendingMode.AlphaBlend;
desc.CullMode = CullMode.TwoSided;
desc.VS = _shader.GPU.GetVS("VS_Grid");
desc.PS = _shader.GPU.GetPS("PS_Grid");
_psGrid.Init(ref desc);
}
float bigLineIntensity = 0.8f;
Color bigColor = Color.Gray * bigLineIntensity;
Color color = bigColor * 0.8f;
int count = (int)(size / space);
int midLine = count / 2;
int bigLinesMod = count / 8;
Vector3 start = new Vector3(0, 0, size * -0.5f);
Vector3 end = new Vector3(0, 0, size * 0.5f);
for (int i = 0; i <= count; i++)
// Update vertices of the plane
// TODO: perf this operation in a Vertex Shader
var vertices = new Float3[]
{
start.X = end.X = i * space + start.Z;
Color lineColor = color;
if (i == midLine)
lineColor = Color.Blue * bigLineIntensity;
else if (i % bigLinesMod == 0)
lineColor = bigColor;
DebugDraw.DrawLine(start, end, lineColor);
new Float3(-gridSize + camPos.X, 0, -gridSize + camPos.Z),
new Float3(gridSize + camPos.X, 0, gridSize + camPos.Z),
new Float3(-gridSize + camPos.X, 0, gridSize + camPos.Z),
new Float3(gridSize + camPos.X, 0, -gridSize + camPos.Z),
};
fixed (Float3* ptr = vertices)
{
context.UpdateBuffer(_vertexBuffer, new IntPtr(ptr), (uint)(sizeof(Float3) * vertices.Length));
}
start = new Vector3(size * -0.5f, 0, 0);
end = new Vector3(size * 0.5f, 0, 0);
for (int i = 0; i <= count; i++)
// Update constant buffer data
var cb = _shader.GPU.GetCB(0);
if (cb != IntPtr.Zero)
{
start.Z = end.Z = i * space + start.X;
Color lineColor = color;
if (i == midLine)
lineColor = Color.Red * bigLineIntensity;
else if (i % bigLinesMod == 0)
lineColor = bigColor;
DebugDraw.DrawLine(start, end, lineColor);
var data = new Data();
Matrix.Multiply(ref renderContext.View.View, ref renderContext.View.Projection, out var viewProjection);
data.WorldMatrix = Matrix.Identity;
Matrix.Transpose(ref viewProjection, out data.ViewProjectionMatrix);
data.ViewPos = renderContext.View.WorldPosition;
data.GridColor = options.Viewport.ViewportGridColor;
data.Far = renderContext.View.Far;
data.GridSize = options.Viewport.ViewportGridViewDistance;
context.UpdateCB(cb, new IntPtr(&data));
}
DebugDraw.Draw(ref renderContext, input.View(), null, true);
DebugDraw.SetContext(IntPtr.Zero);
// Draw geometry using custom Pixel Shader and Vertex Shader
context.BindCB(0, cb);
context.BindIB(_indexBuffer);
_vbs[0] = _vertexBuffer;
context.BindVB(_vbs);
context.SetState(_psGrid);
context.SetRenderTarget(renderContext.Buffers.DepthBuffer.View(), input.View());
context.DrawIndexed((uint)_triangles.Length);
Profiler.EndEventGPU();
}

View File

@@ -129,5 +129,19 @@ namespace FlaxEditor.Options
[DefaultValue(50.0f), Limit(25.0f, 500.0f, 5.0f)]
[EditorDisplay("Defaults"), EditorOrder(220), Tooltip("The default editor viewport grid scale.")]
public float ViewportGridScale { get; set; } = 50.0f;
/// <summary>
/// Gets or sets the view distance you can see the grid.
/// </summary>
[DefaultValue(2500.0f)]
[EditorDisplay("Grid"), EditorOrder(300), Tooltip("The maximum distance you will be able to see the grid.")]
public float ViewportGridViewDistance { get; set; } = 2500.0f;
/// <summary>
/// Gets or sets the grid color.
/// </summary>
[DefaultValue(typeof(Color), "0.5,0.5,0.5,1.0")]
[EditorDisplay("Grid"), EditorOrder(310), Tooltip("The color for the viewport grid.")]
public Color ViewportGridColor { get; set; } = new Color(0.5f, 0.5f, 0.5f, 1.0f);
}
}

View File

@@ -0,0 +1,151 @@
// Implementation based on:
// "The Best Darn Grid Shader (Yet)", Medium, Oct 2023
// Ben Golus
// https://bgolus.medium.com/the-best-darn-grid-shader-yet-727f9278b9d8#3e73
#define USE_FORWARD true;
#include "./Flax/Common.hlsl"
META_CB_BEGIN(0, Data)
float4x4 WorldMatrix;
float4x4 ViewProjectionMatrix;
float4 GridColor;
float3 ViewPos;
float Far;
float3 Padding;
float GridSize;
META_CB_END
// Geometry data passed to the vertex shader
struct ModelInput
{
float3 Position : POSITION;
};
// Interpolants passed from the vertex shader
struct VertexOutput
{
float4 Position : SV_Position;
float2 TexCoord : TEXCOORD0;
float3 WorldPosition : TEXCOORD1;
};
// Interpolants passed to the pixel shader
struct PixelInput
{
float4 Position : SV_Position;
noperspective float2 TexCoord : TEXCOORD0;
float3 WorldPosition : TEXCOORD1;
};
// Vertex shader function for grid rendering
META_VS(true, FEATURE_LEVEL_ES2)
META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT, 1, ALIGN, PER_VERTEX, 0, true)
VertexOutput VS_Grid(ModelInput input)
{
VertexOutput output;
output.WorldPosition = mul(float4(input.Position.xyz, 1), WorldMatrix).xyz;
output.Position = mul(float4(input.Position.xyz, 1), ViewProjectionMatrix);
return output;
}
float invLerp(float from, float to, float value)
{
return (value - from) / (to - from);
}
float remap(float origFrom, float origTo, float targetFrom, float targetTo, float value)
{
float rel = invLerp(origFrom, origTo, value);
return lerp(targetFrom, targetTo, rel);
}
float ddLength(float a)
{
return length(float2(ddx(a), ddy(a)));
}
float GetLine(float pos, float scale, float thickness)
{
float lineWidth = thickness;
float coord = (pos * 0.01) * scale;
float2 uvDDXY = float2(ddx(coord), ddy(coord));
float deriv = float(length(uvDDXY.xy));
float drawWidth = clamp(lineWidth, deriv, 0.5);
float lineAA = deriv * 1.5;
float gridUV = abs(coord);
float grid2 = smoothstep(drawWidth + lineAA, drawWidth - lineAA, gridUV);
grid2 *= saturate(lineWidth / drawWidth);
grid2 = lerp(grid2, lineWidth, saturate(deriv * 2.0 - 1.0));
float grid = lerp(grid2, 1.0, grid2);
return grid;
}
float GetGrid(float3 pos, float scale, float thickness)
{
float lineWidth = thickness;
float2 coord = (pos.xz * 0.01) * scale;
float4 uvDDXY = float4(ddx(coord), ddy(coord));
float2 deriv = float2(length(uvDDXY.xz), length(uvDDXY.yw));
float2 drawWidth = clamp(lineWidth, deriv, 0.5);
float2 lineAA = deriv * 1.5;
float2 gridUV = 1.0 - abs(frac(coord) * 2.0 - 1.0);
float2 grid2 = smoothstep(drawWidth + lineAA, drawWidth - lineAA, gridUV);
grid2 *= saturate(lineWidth / drawWidth);
grid2 = lerp(grid2, lineWidth, saturate(deriv * 2.0 - 1.0));
float grid = lerp(grid2.x, 1.0, grid2.y);
return grid;
}
float4 GetColor(float3 pos, float scale)
{
float dist = 1 - saturate(distance(float3(ViewPos.x, 0, ViewPos.z), pos) / GridSize);
// Line width
float g1LW = 0.01;
// Major line Z
float l1 = GetLine(pos.x, 1, g1LW * 2);
// Major line X
float l2 = GetLine(pos.z, 1, g1LW);
// Main grid
float g1 = GetGrid(pos, 1, g1LW * 0.8);
float g2 = GetGrid(pos, 2, g1LW * 0.4);
float g3 = GetGrid(pos, 0.1, g1LW * 2);
float camFadeLarge = clamp(invLerp(2500, 4000, abs(ViewPos.y)),0, g3);
g3 *= camFadeLarge;
float g4 = GetGrid(pos, 10, g1LW);
float camFadeTiny = clamp(invLerp(150, 100, abs(ViewPos.y)), 0, g4);
g4 *= camFadeTiny;
float grid = 0;
grid = max(l1, l2);
grid = max(grid, g1);
grid = max(grid, g2);
grid = max(grid, g3);
grid = max(grid, g4);
float4 color = grid * GridColor;
color = lerp(color, float4(1,0,0,1), l2);
color = lerp(color, float4(0,0,1,1), l1);
color *= dist;
return color;
}
// Pixel shader function for grid rendering
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_Grid(PixelInput input) : SV_Target
{
float4 color = GetColor(input.WorldPosition, 1);
return color;
}