26 Sep 2021

General:
+ Added Summaries

Renderer
-------
SM.Base:
+ SM.Base.Controls.Mouse now has a feature to disable tracking.
+ Replaced Bloom Effect with the similar system how blender use it.
+ You can now disable ANY post processing effects.
+ Interpolation for CVectors.
+ MathUtils
+ RenderPipelines now have a central list for post processing effects.

~ Log-System is now ignored if a debugger is attached.
~ Post Processing Shader does now send the texel size as the "renderedTextureTexelSize"-uniform.
~ Improved Text Rendering

SM.OGL:
+ ColorAttachments now contain a reference to the framebuffer its connected.
+ ColorAttachments can now have a own size.
+ Framebuffer.Append(string key, Vector2 size, int pos)
+Framebuffers now have a method to completely reset itself.
+ Framebuffers now have a Blit-method called "CopyTo".

~ Framebuffer.GetCurrentlyActive() will now return an actual SM.OGL.Framebuffer-object.
~ Renderbuffers now are a class and contain the ID by itself.
~ Renamed Uniform-function to its class-name: f.E. SetBool, SetFloat instead of SetUniform1

Optionals:
Controls:
+ Framecache for the GameController.GetState()
This commit is contained in:
Nineto Nine 2021-09-26 21:27:14 +02:00
parent dffa581596
commit 9b52d401e7
61 changed files with 1529 additions and 818 deletions

View file

@ -1,212 +1,200 @@
#region usings
using System.Drawing;
using OpenTK;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL4;
using SM.Base.Drawing;
using SM.Base.PostProcess;
using SM.Base.Utility;
using SM.Base.Window;
using SM.OGL.Framebuffer;
using SM.OGL.Shaders;
using SM.OGL.Texture;
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SM.Base.PostEffects
{
/// <summary>
/// A bloom post process effect.
/// The recommended bloom effect, that looks way better than the old one.
/// <para>Based on Blender's implermentation, which is based on COD: Infinite Warfare.</para>
/// </summary>
public class BloomEffect : PostProcessEffect
public class BloomEffect : PostProcess.PostProcessEffect
{
private static BezierCurve _defaultCurve = new BezierCurve(Vector2.UnitY, Vector2.Zero, new Vector2(0.4f, 0), new Vector2(.5f,0));
private static readonly PostProcessShader _mergeShader = new PostProcessShader(
AssemblyUtility.ReadAssemblyFile(SMRenderer.PostProcessPath + ".bloom_merge_vert.glsl"),
AssemblyUtility.ReadAssemblyFile(SMRenderer.PostProcessPath + ".bloom_merge.glsl"));
private static readonly ShaderFile samplingFile = new ShaderFile(AssemblyUtility.ReadAssemblyFile(SMRenderer.PostProcessPath + ".bloom.sampling.frag"));
private static readonly PostProcessShader _shader =
new PostProcessShader(AssemblyUtility.ReadAssemblyFile(SMRenderer.PostProcessPath + ".bloom_blur.glsl"));
private const float _defaultTextureScale = .75f;
private static readonly PostProcessShader _filterShader = new PostProcessShader(
AssemblyUtility.ReadAssemblyFile(SMRenderer.PostProcessPath + ".bloom.filter.frag")
);
private static readonly PostProcessShader _downsampleShader = new PostProcessShader(
AssemblyUtility.ReadAssemblyFile(SMRenderer.PostProcessPath + ".bloom.downsample.frag")
);
private static readonly PostProcessShader _upsampleShader = new PostProcessShader(
AssemblyUtility.ReadAssemblyFile(SMRenderer.PostProcessPath + ".bloom.upsample.frag")
);
private static readonly PostProcessShader _combineShader = new PostProcessShader(
AssemblyUtility.ReadAssemblyFile(SMRenderer.PostProcessPath + ".bloom.combine.frag")
);
private Framebuffer _source;
static BloomEffect()
{
_upsampleShader.ShaderFiles.Fragment[0].GLSLExtensions.Add(samplingFile);
_combineShader.ShaderFiles.Fragment[0].GLSLExtensions.Add(samplingFile);
}
private Framebuffer _bloomBuffer1;
private Framebuffer _bloomBuffer2;
const int MAXBLOOMSTEPS = 8;
const float INTENSITY = .1f;
private readonly bool _hdr;
private readonly float _textureScale = .75f;
private List<Framebuffer> _downsampler;
private List<Framebuffer> _upsample;
private BezierCurve _weightCurve;
private float[] _weights;
private ColorAttachment _xBuffer;
private ColorAttachment _yBuffer;
private int _iterations;
private float _sampleSize;
private Vector4 _thresholdCurve;
private Color4 _bloomColor;
/// <summary>
/// A texture where you can define the amount of the bloom effect at each pixel.
/// </summary>
public TextureBase AmountMap;
/// <summary>
/// The transformation for the amount map.
/// </summary>
public TextureTransformation AmountTransform = new TextureTransformation();
/// <summary>
/// The maximal amount the amount map is clamped to.
/// <para>Default: 1</para>
/// </summary>
public float MaxAmount = 1;
/// <summary>
/// The minimal amount the amount map is clamped to.
/// <para>Default: 0</para>
/// </summary>
public float MinAmount = 0;
/// <summary>
/// The defines how often the x-y-flipflop happens.
/// <para>Default: 8</para>
/// </summary>
public int Iterations = 8;
/// <summary>
/// The Threshold for the bloom effect.
/// <para>Default: .8f</para>
/// The threshold, where the effect decided what is bright.
/// </summary>
public float Threshold = .8f;
/// <summary>
/// Increases the brightness of the resulting effect.
/// <para>Default: 1</para>
/// The radius of the effect.
/// </summary>
public float Power = 1;
public float Radius = 6.5f;
/// <summary>
/// Makes transition between under/over-threshold gradual.
/// </summary>
public float Knee = .5f;
/// <summary>
/// The intensity of the effect.
/// </summary>
public float Intensity = .5f;
/// <summary>
/// The tint of the effect.
/// </summary>
public Color4 Color = Color4.White;
/// <summary>
/// Radius of the effect
/// <para>Default: 2</para>
/// This creates a more prettier bloom effect.
/// </summary>
public float Radius = 1;
/// <summary>
/// This can disable the bloom calculation.
/// <para>Default: true</para>
/// </summary>
public bool Enable = true;
/// <summary>
/// This defines the weight curve.
/// </summary>
public BezierCurve WeightCurve
{
get => _weightCurve;
set
{
_weightCurve = value;
UpdateWeights();
}
}
/// <summary>
/// This defines how many picks the effect should pick from the weight curve.
/// </summary>
public int WeightCurvePickAmount = 4;
/// <summary>
/// This creates a bloom effect.
/// </summary>
/// <param name="source">This can specify a own source framebuffer. If not set, it will take the Pipeline MainFramebuffer.</param>
/// <param name="hdr">This allows to enable hdr returns.</param>
/// <param name="textureScale">This allows for a increase in performance, by lowering the calculating texture scale.</param>
public BloomEffect(Framebuffer source = null, bool hdr = false, float? textureScale = null)
public BloomEffect(bool hdr = false)
{
_source = source;
_hdr = hdr;
_textureScale = textureScale.GetValueOrDefault(_defaultTextureScale);
WeightCurve = _defaultCurve;
}
/// <inheritdoc/>
protected override void InitProcess() => CreateFramebuffers();
private void UpdateWeights()
private void CreateFramebuffers()
{
_weights = new float[WeightCurvePickAmount];
if (_downsampler != null) _downsampler.ForEach(a => a.Reset());
if (_upsample != null) _upsample.ForEach(a => a.Reset());
for (int i = 0; i < WeightCurvePickAmount; i++)
_weights[i] = _weightCurve.CalculatePoint((float) (i + 1) / (WeightCurvePickAmount + 1)).Y;
_downsampler = new List<Framebuffer>();
_upsample = new List<Framebuffer>();
Vector2 windowSize = Pipeline.ConnectedWindow.WindowSize;
float minDim = (float)Math.Min(windowSize.X, windowSize.Y);
float maxIter = (Radius - 8.0f) + (float)(Math.Log(minDim) / Math.Log(2));
int maxIterInt = (int)maxIter;
_iterations = Math.Max(Math.Min(MAXBLOOMSTEPS, maxIterInt), 1);
_sampleSize = .5f + maxIter - maxIterInt;
_thresholdCurve = new Vector4(
Threshold - Knee,
Knee * 2,
0.25f / Math.Max(1e-5f, Knee),
Threshold);
float intens = (Intensity * INTENSITY);
_bloomColor = new Color4(Color.R * intens, Color.G * intens, Color.B * intens, 1f);
PixelInformation pixel = new PixelInformation(PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb, PixelType.Float);
Vector2 texSize = windowSize;
Framebuffer f = new Framebuffer(texSize);
f.Append("0", new ColorAttachment(0, pixel));
f.Append("1", new ColorAttachment(1, pixel));
_downsampler.Add(f);
for (int i = 0; i < _iterations; i++)
{
texSize /= 2;
f = new Framebuffer(texSize);
f.Append("0", new ColorAttachment(0, pixel));
_downsampler.Add(f);
if (i == _iterations - 1) break;
f = new Framebuffer(texSize);
f.Append("0", new ColorAttachment(0, pixel));
_upsample.Add(f);
}
}
/// <inheritdoc/>
protected override void InitProcess()
public override void ScreenSizeChanged(IGenericWindow window)
{
_source ??= Pipeline.MainFramebuffer;
_source.ColorAttachments["color"].PixelInformation = PixelInformation.RGBA_HDR;
_bloomBuffer1 = new Framebuffer(Pipeline.ConnectedWindow, _textureScale)
{
Name = "BloomX"
};
_bloomBuffer1.Append("xBuffer", _xBuffer = new ColorAttachment(0, PixelInformation.RGBA_HDR));
_bloomBuffer1.Compile();
_bloomBuffer2 = new Framebuffer(Pipeline.ConnectedWindow, _textureScale)
{
Name = "BloomY"
};
_bloomBuffer2.Append("yBuffer", _yBuffer = new ColorAttachment(0, PixelInformation.RGBA_HDR));
_bloomBuffer2.Compile();
Pipeline.Framebuffers.Add(_bloomBuffer1);
Pipeline.Framebuffers.Add(_bloomBuffer2);
CreateFramebuffers();
}
/// <inheritdoc/>
public override void Draw(ColorAttachment source, DrawContext context)
protected override void Drawing(ColorAttachment source, DrawContext context)
{
if (Enable)
{
GL.Viewport(0, 0, (int) (Pipeline.ConnectedWindow.Width * _textureScale),
(int) (Pipeline.ConnectedWindow.Height * _textureScale));
Framebuffer target = Framebuffer.GetCurrentlyActive();
Framebuffer target = Framebuffer.GetCurrentlyActive();
bool first = true, hoz = true;
int iter = Iterations * 2;
for (int i = 0; i < iter; i++)
// Filtering
_downsampler[0].Activate(true);
_filterShader.Draw(source, col =>
{
col["ThresholdCurve"].SetVector4(_thresholdCurve);
});
// Downsampling
ColorAttachment last = _downsampler[0]["0"];
for(int i = 1; i < _iterations; i++)
{
ColorAttachment downsampleSource = last;
Framebuffer downsampleTarget = _downsampler[i];
downsampleTarget.Activate(true);
_downsampleShader.Draw(downsampleSource);
last = downsampleTarget["0"];
}
// Upsampling
for (int i = _iterations - 2; i >= 0; i--)
{
ColorAttachment downsampleSource = _downsampler[i]["0"];
Framebuffer upsampleTarget = _upsample[i];
upsampleTarget.Activate(true);
_upsampleShader.Draw(last, (a) =>
{
(hoz ? _bloomBuffer1 : _bloomBuffer2).Activate(false);
if (last != null) a["baseBuffer"].SetTexture(downsampleSource);
a["sampleSize"].SetFloat(_sampleSize);
});
_shader.Draw(collection =>
{
collection["renderedTexture"].SetTexture(first ? source : (hoz ? _yBuffer : _xBuffer));
collection["First"].SetUniform1(first);
collection["Threshold"].SetUniform1(Threshold);
collection["Horizontal"].SetUniform1(hoz);
collection["Weights"].SetUniform1(_weights);
collection["WeightCount"].SetUniform1(WeightCurvePickAmount);
collection["Power"].SetUniform1(Power);
collection["Radius"].SetUniform1(_textureScale * Radius);
});
hoz = !hoz;
if (first) first = false;
}
GL.Viewport(Pipeline.ConnectedWindow.ClientRectangle);
target.Activate();
last = upsampleTarget["0"];
}
_mergeShader.Draw(collection =>
// combine
target.Activate(true);
_combineShader.Draw(last, (a) =>
{
collection["Scene"].SetTexture(source);
collection["Bloom"].SetTexture(_yBuffer);
a["sampleSize"].SetFloat(_sampleSize);
collection["MinAmount"].SetUniform1(MinAmount);
collection["MaxAmount"].SetUniform1(MaxAmount);
collection["AmountMap"].SetTexture(AmountMap, collection["HasAmountMap"]);
collection["TextureTransform"].SetMatrix3(AmountTransform.GetMatrix());
a["scene"].SetTexture(_downsampler[0]["1"]);
a["bloomColor"].SetColor(_bloomColor);
collection["Exposure"].SetUniform1(context.UseCamera.Exposure);
collection["HDR"].SetUniform1(_hdr);
a["HDR"].SetBool(_hdr);
});
}
}
}
}

View file

@ -52,8 +52,8 @@ namespace SM.Base.PostEffects
{
_hdrExposureShader.Draw(u =>
{
u["Gamma"].SetUniform1(Gamma);
u["Exposure"].SetUniform1(exposure);
u["Gamma"].SetFloat(Gamma);
u["Exposure"].SetFloat(exposure);
u["Scene"].SetTexture(attachment);
});
}
@ -66,7 +66,7 @@ namespace SM.Base.PostEffects
{
_gammaShader.Draw(u =>
{
u["Gamma"].SetUniform1(Gamma);
u["Gamma"].SetFloat(Gamma);
u["Scene"].SetTexture(attachment);
});
}

View file

@ -0,0 +1,32 @@
#version 330 core
in vec2 vTexture;
uniform sampler2D scene;
uniform vec4 bloomColor;
uniform bool HDR;
vec3 safe_color(vec3 c) {
return clamp(c, vec3(0.0), vec3(1e20));
}
vec3 upsample_filter_high();
layout(location = 0) out vec4 color;
vec3 reinhardTone(vec3 col) {
return col / (col + vec3(1.0));
}
void main() {
vec3 scene = safe_color(texture2D(scene, vTexture).rgb);
vec3 blur = upsample_filter_high() * bloomColor.rgb;
if (HDR) {
color = vec4(scene + blur, 1);
return;
}
color = vec4(scene + reinhardTone(blur), 1);
}

View file

@ -0,0 +1,41 @@
#version 330 core
uniform vec2 renderedTextureTexelSize;
vec4 GetRenderColor();
vec4 GetRenderColorOffset(vec2);
float getBrightness(vec3 col) {
return max(col.r, max(col.g, col.b));
return (col.r + col.r + col.b + col.g + col.g + col.g) / 6.0;
}
layout(location = 0) out vec4 color;
vec3 downsample_high() {
vec4 d = renderedTextureTexelSize.xyxy * vec4(-1,-1, +1, +1);
vec3 s1 = GetRenderColorOffset(d.xy).rgb; // - -
// X -
vec3 s2 = GetRenderColorOffset(d.zy).rgb; // - -
// - X
vec3 s3 = GetRenderColorOffset(d.xw).rgb; // X -
// - -
vec3 s4 = GetRenderColorOffset(d.zw).rgb; // X -
// - -
float s1w = 1.0 / (getBrightness(s1) + 1.0);
float s2w = 1.0 / (getBrightness(s2) + 1.0);
float s3w = 1.0 / (getBrightness(s3) + 1.0);
float s4w = 1.0 / (getBrightness(s4) + 1.0);
float one_div = 1.0 / (s1w + s2w + s3w + s4w);
return (s1 * s1w + s2 * s2w + s3 * s3w + s4 * s4w) * one_div;
}
void main() {
color = vec4(downsample_high(),1);
}

View file

@ -0,0 +1,48 @@
#version 330 core
uniform vec4 ThresholdCurve;
uniform vec2 renderedTextureTexelSize;
vec4 GetRenderColorOffset(vec2);
layout(location = 0) out vec4 color;
layout(location = 1) out vec4 scene;
vec3 safe_color(vec3 c) {
return clamp(c, vec3(0.0), vec3(1e20));
}
vec3 median(vec3 a, vec3 b, vec3 c)
{
return a + b + c - min(min(a, b), c) - max(max(a, b), c);
}
float getBrightness(vec3 col) {
return max(col.r, max(col.g, col.b));
return (col.r + col.r + col.b + col.g + col.g + col.g) / 6.0;
}
void main() {
scene = vec4(safe_color(GetRenderColorOffset(vec2(0)).rgb), 1);
vec3 d = renderedTextureTexelSize.xyx * vec3(1,1,0);
vec3 s0 = scene.rgb + vec3(.1);
vec3 s1 = safe_color(GetRenderColorOffset(-d.xz).rgb) + vec3(.1);
vec3 s2 = safe_color(GetRenderColorOffset(+d.xz).rgb) + vec3(.1);
vec3 s3 = safe_color(GetRenderColorOffset(-d.zy).rgb) + vec3(.1);
vec3 s4 = safe_color(GetRenderColorOffset(+d.zy).rgb) + vec3(.1);
vec3 col = median(median(s0, s1, s2), s3, s4);
float br = getBrightness(col);
/*vec3 col = safe_color(GetRenderColor().rgb);
float br = getBrightness(col);*/
float rq = clamp(br - ThresholdCurve.x, 0, ThresholdCurve.y);
rq = ThresholdCurve.z * rq * rq;
float resultBr = max(rq, br - ThresholdCurve.w) / max(1e-5, br);
col *= resultBr;
color = vec4(col, 1);
}

View file

@ -0,0 +1,30 @@
#version 330 core
uniform vec2 renderedTextureTexelSize;
uniform float sampleSize;
vec4 GetRenderColorOffset(vec2);
vec3 upsample_filter_high() {
vec4 d = renderedTextureTexelSize.xyxy * vec4(1, 1,-1,0);
vec3 s;
// Line + 1
s = GetRenderColorOffset(d.zy).rgb; // x - -
s += GetRenderColorOffset(d.wy).rgb * 2; // - X -
s += GetRenderColorOffset(d.xy).rgb; // - - X
// Line 0
s += GetRenderColorOffset(d.zw).rgb * 2; // X - -
s += GetRenderColorOffset(vec2(0)).rgb * 4; // - X -
s += GetRenderColorOffset(d.xw).rgb * 2; // - - X
// Line - 1
s += GetRenderColorOffset(d.zz).rgb; // X - -
s += GetRenderColorOffset(d.wz).rgb * 2; // - X -
s += GetRenderColorOffset(d.xz).rgb; // - - X
return s * 0.0625; // 1 / 16 = 0.0625
}

View file

@ -0,0 +1,13 @@
#version 330 core
in vec2 vTexture;
uniform sampler2D baseBuffer;
layout(location = 0) out vec4 color;
vec3 upsample_filter_high();
void main() {
color = vec4(texture2D(baseBuffer, vTexture).rgb + upsample_filter_high(),1);
}

View file

@ -1,48 +0,0 @@
#version 330
#define PI 3.14159265359
uniform sampler2D renderedTexture;
uniform float RenderScale;
uniform bool First;
uniform float Threshold;
uniform bool Horizontal;
uniform float[32] Weights;
uniform int WeightCount;
uniform float Power;
uniform float Radius;
layout(location = 0) out vec4 color;
vec4 GetRenderColorOffset(vec2 offset);
float brightness(vec3 c)
{
return max(max(c.r, c.g), c.b);
}
float bright;
float GetWeight(int dif) {
return Weights[dif];
}
void main() {
vec3 thres = vec3(First ? Threshold : 0);
vec2 tex_offset = 1.0 / textureSize(renderedTexture, 0) * vec2(Horizontal ? 1 : 0, Horizontal ? 0 : 1);
vec3 result = max(GetRenderColorOffset(vec2(0)).rgb - thres, 0) * (First ? Power : 1) * GetWeight(0);
float radi = Radius + (length(result));
for(int i = 1; i < WeightCount; i++) {
result += max(GetRenderColorOffset(tex_offset * i * radi).rgb - thres, 0) * (First ? Power : 1) * GetWeight(i);
result += max(GetRenderColorOffset(-tex_offset * i * radi).rgb - thres, 0) * (First ? Power : 1) * GetWeight(i);
}
color = vec4(result, 1);
}

View file

@ -1,30 +0,0 @@
#version 330
in vec2 vTexture;
in vec2 TransformedTexture;
uniform sampler2D Scene;
uniform sampler2D Bloom;
uniform float MinAmount;
uniform float MaxAmount;
uniform sampler2D AmountMap;
uniform bool HasAmountMap;
uniform float Exposure;
uniform bool HDR;
layout(location = 0) out vec4 color;
void main() {
vec3 result = texture(Bloom, vTexture).rgb;
if (HasAmountMap) result *= clamp(length(texture(AmountMap, TransformedTexture).rgb) * (MaxAmount - MinAmount) + MinAmount, 0, 1);
if (!HDR) {
result = vec3(1.0) - exp(-result * Exposure);
}
result = texture(Scene, vTexture).rgb + result;
color = vec4(result, 1);
}

View file

@ -1,11 +0,0 @@
#version 330
layout(location = 1) in vec2 aTex;
uniform mat3 TextureTransform;
out vec2 TransformedTexture;
void vertex() {
TransformedTexture = vec2(TextureTransform * vec3(aTex, 1));
}

View file

@ -8,8 +8,28 @@ uniform float Gamma;
layout(location = 0) out vec4 color;
vec3 ACES(vec3 x) {
const float a = 2.51;
const float b = 0.03;
const float c = 2.43;
const float d = 0.59;
const float e = 0.14;
return clamp((x * (a * x + b)) / (x * (c * x + d) + e), 0,1.0);
}
vec3 reinhardTone(vec3 col) {
return col / (col + vec3(1.0));
}
vec3 exposure(vec3 scene) {
return vec3(1) - exp(-texture(Scene, vTexture).rgb * Exposure);
}
void main() {
vec3 result = vec3(1) - exp(-texture(Scene, vTexture).rgb * Exposure);
vec3 scene = texture2D(Scene, vTexture).rgb;
vec3 result = reinhardTone(scene);
color = vec4(pow(result, vec3(1 / Gamma)), 1);
}