Support for 3d images in the renderer.
This commit is contained in:
parent
4d5e0dd8f2
commit
5aa9a2f4e6
@ -11,12 +11,14 @@ namespace Quik.Media.Defaults
|
||||
{
|
||||
private readonly StbImage image;
|
||||
private QImageBuffer buffer;
|
||||
private bool isSdf = false;
|
||||
|
||||
public override int Width => image.Width;
|
||||
|
||||
public override int Height => image.Height;
|
||||
|
||||
public override int Depth => 1;
|
||||
public override bool IsSdf => isSdf;
|
||||
public override QImageFormat InternalFormat => Stb2QImageFormat(image.Format);
|
||||
|
||||
public QImageStbi(Stream source)
|
||||
@ -79,6 +81,11 @@ namespace Quik.Media.Defaults
|
||||
buffer.UnlockBits();
|
||||
}
|
||||
|
||||
public void SdfHint(bool value = true)
|
||||
{
|
||||
isSdf = value;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
@ -10,6 +10,7 @@ namespace Quik.Media
|
||||
public abstract QImageFormat InternalFormat { get; }
|
||||
public virtual int MipMapLevels => 0;
|
||||
public virtual bool Premultiplied => false;
|
||||
public virtual bool IsSdf => false;
|
||||
|
||||
public abstract void LockBits2d(out QImageLock imageLock, QImageLockOptions options);
|
||||
public abstract void LockBits3d(out QImageLock imageLock, QImageLockOptions options);
|
||||
|
@ -3,6 +3,8 @@ using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using Quik.VertexGenerator;
|
||||
using static Quik.OpenGL.GLEnum;
|
||||
using Quik.Media;
|
||||
using System.Linq;
|
||||
|
||||
namespace Quik.OpenGL
|
||||
{
|
||||
@ -10,18 +12,23 @@ namespace Quik.OpenGL
|
||||
{
|
||||
private int program;
|
||||
private int v2Position;
|
||||
private int iZIndex;
|
||||
private int fZIndex;
|
||||
private int v2TexPos;
|
||||
private int fTexLayer;
|
||||
private int v4Color;
|
||||
private int m4Transforms;
|
||||
private int iMaxZ;
|
||||
private int fMaxZ;
|
||||
private int iEnableSdf;
|
||||
private int iEnableTexture;
|
||||
private int iAlphaDiscard;
|
||||
private int fSdfThreshold;
|
||||
private int txTexture;
|
||||
private int tx2d;
|
||||
private int tx2darray;
|
||||
|
||||
private bool isDiposed;
|
||||
private readonly Dictionary<DrawQueue, DrawData> data = new Dictionary<DrawQueue, DrawData>();
|
||||
private readonly TextureManager textures = new TextureManager();
|
||||
|
||||
public bool IsInit { get; private set; } = false;
|
||||
public event Action<GL21Driver> OnGCDispose;
|
||||
|
||||
@ -57,16 +64,19 @@ namespace Quik.OpenGL
|
||||
GL.DeleteShader(fs);
|
||||
|
||||
v2Position = GL.GetAttribLocation(program, nameof(v2Position));
|
||||
iZIndex = GL.GetAttribLocation(program, nameof(iZIndex));
|
||||
fZIndex = GL.GetAttribLocation(program, nameof(fZIndex));
|
||||
v2TexPos = GL.GetAttribLocation(program, nameof(v2TexPos));
|
||||
fTexLayer = GL.GetAttribLocation(program, nameof(fTexLayer));
|
||||
v4Color = GL.GetAttribLocation(program, nameof(v4Color));
|
||||
|
||||
m4Transforms = GL.GetUniformLocation(program, nameof(m4Transforms));
|
||||
iMaxZ = GL.GetUniformLocation(program, nameof(iMaxZ));
|
||||
fMaxZ = GL.GetUniformLocation(program, nameof(fMaxZ));
|
||||
fSdfThreshold = GL.GetUniformLocation(program, nameof(fSdfThreshold));
|
||||
iEnableSdf = GL.GetUniformLocation(program, nameof(iEnableSdf));
|
||||
iEnableTexture = GL.GetUniformLocation(program, nameof(iEnableSdf));
|
||||
iEnableTexture = GL.GetUniformLocation(program, nameof(iEnableTexture));
|
||||
iAlphaDiscard = GL.GetUniformLocation(program, nameof(iAlphaDiscard));
|
||||
txTexture = GL.GetUniformLocation(program, nameof(txTexture));
|
||||
tx2d = GL.GetUniformLocation(program, nameof(tx2d));
|
||||
tx2darray = GL.GetUniformLocation(program, nameof(tx2darray));
|
||||
|
||||
IsInit = true;
|
||||
}
|
||||
@ -93,23 +103,39 @@ namespace Quik.OpenGL
|
||||
QMat4.Orthographic(out QMat4 matrix, view);
|
||||
|
||||
GL.UseProgram(program);
|
||||
GL.Uniform1(iMaxZ, queue.ZDepth+1);
|
||||
GL.Uniform1(fMaxZ, (float)(queue.ZDepth+1));
|
||||
GL.UniformMatrix4(m4Transforms, false, in matrix);
|
||||
GL.Uniform1(fSdfThreshold, 0.0f);
|
||||
GL.Uniform1(txTexture, 0);
|
||||
GL.Uniform1(tx2d, 0);
|
||||
GL.Enable(GL_BLEND);
|
||||
GL.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
foreach (DrawCall call in queue)
|
||||
{
|
||||
GL.Viewport(
|
||||
(int)call.Bounds.Left,
|
||||
(int)(view.Bottom - call.Bounds.Bottom),
|
||||
(int)call.Bounds.Right,
|
||||
(int)(view.Bottom - call.Bounds.Top));
|
||||
GL.ActiveTexture(GL_TEXTURE0);
|
||||
GL.BindTexture(GL_TEXTURE_2D, 0);
|
||||
if (call.Texture != null)
|
||||
{
|
||||
// TODO: actually use textures?
|
||||
GL.Uniform1(iEnableTexture, 1);
|
||||
GL.Uniform1(iEnableSdf, call.Texture.SignedDistanceField ? 1 : 0);
|
||||
GL.Uniform1(iEnableSdf, call.Texture.IsSdf ? 1 : 0);
|
||||
GL.Uniform1(iAlphaDiscard, 1);
|
||||
|
||||
if (call.Texture.Depth > 1)
|
||||
{
|
||||
GL.Uniform1(iEnableTexture, 3);
|
||||
GL.ActiveTexture(GL_TEXTURE1);
|
||||
GL.BindTexture(GL_TEXTURE_2D_ARRAY, textures.GetTexture(call.Texture));
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.Uniform1(iEnableTexture, 2);
|
||||
GL.ActiveTexture(GL_TEXTURE0);
|
||||
GL.BindTexture(GL_TEXTURE_2D, textures.GetTexture(call.Texture));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -254,13 +280,15 @@ namespace Quik.OpenGL
|
||||
GL.BufferData(GL_ARRAY_BUFFER, QuikVertex.Stride * Queue.VertexCount, Queue.VertexArray, GL_STREAM_DRAW);
|
||||
|
||||
GL.VertexAttribPointer(driver.v2Position, 2, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.PositionOffset);
|
||||
GL.VertexAttribIPointer(driver.iZIndex, 1, GL_UNSIGNED_INT, QuikVertex.Stride, QuikVertex.ZIndexOffset);
|
||||
GL.VertexAttribPointer(driver.fZIndex, 1, GL_UNSIGNED_INT, false, QuikVertex.Stride, QuikVertex.ZIndexOffset);
|
||||
GL.VertexAttribPointer(driver.v2TexPos, 2, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.TextureCoordinatesOffset);
|
||||
GL.VertexAttribPointer(driver.fTexLayer, 1, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.TextureLayerOffset);
|
||||
GL.VertexAttribPointer(driver.v4Color, 4, GL_UNSIGNED_BYTE, true, QuikVertex.Stride, QuikVertex.ColorOffset);
|
||||
GL.EnableVertexAttribArray(driver.v2Position);
|
||||
GL.EnableVertexAttribArray(driver.iZIndex);
|
||||
GL.EnableVertexAttribArray(driver.fZIndex);
|
||||
GL.EnableVertexAttribArray(driver.v2TexPos);
|
||||
GL.EnableVertexAttribArray(driver.v4Color);
|
||||
GL.EnableVertexAttribArray(driver.fTexLayer);
|
||||
|
||||
GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, Queue.ElementCount * sizeof(int), Queue.ElementArray, GL_STREAM_DRAW);
|
||||
@ -287,4 +315,115 @@ namespace Quik.OpenGL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class TextureManager : IDisposable
|
||||
{
|
||||
private readonly Dictionary<QImage, int> textures = new Dictionary<QImage, int>();
|
||||
private readonly HashSet<QImage> imagesNotUsed = new HashSet<QImage>();
|
||||
private bool isDisposed = false;
|
||||
|
||||
public void BeginFrame()
|
||||
{
|
||||
if (imagesNotUsed.Count > 0)
|
||||
{
|
||||
foreach (QImage image in imagesNotUsed)
|
||||
{
|
||||
GL.DeleteTexture(textures[image]);
|
||||
}
|
||||
|
||||
imagesNotUsed.Clear();
|
||||
}
|
||||
|
||||
foreach (QImage image in textures.Keys)
|
||||
{
|
||||
imagesNotUsed.Add(image);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetTexture(QImage image)
|
||||
{
|
||||
if (textures.TryGetValue(image, out int texture))
|
||||
{
|
||||
return texture;
|
||||
}
|
||||
|
||||
if (image.Depth > 1)
|
||||
{
|
||||
texture = UploadTexture3d(image);
|
||||
}
|
||||
else
|
||||
{
|
||||
texture = UploadTexture2d(image);
|
||||
}
|
||||
|
||||
return textures[image] = texture;
|
||||
}
|
||||
|
||||
public int UploadTexture3d(QImage image3d)
|
||||
{
|
||||
int texture = GL.GenTexture();
|
||||
|
||||
GL.BindTexture(GL_TEXTURE_2D_ARRAY, texture);
|
||||
|
||||
image3d.LockBits3d(out QImageLock lck, QImageLockOptions.Default);
|
||||
GL.TexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, lck.Width, lck.Height, lck.Depth, 0, s_InternalFormat[lck.Format], s_PixelType[lck.Format], lck.ImagePtr);
|
||||
image3d.UnlockBits();
|
||||
|
||||
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
public int UploadTexture2d(QImage image2d)
|
||||
{
|
||||
int texture = GL.GenTexture();
|
||||
|
||||
GL.BindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
image2d.LockBits2d(out QImageLock lck, QImageLockOptions.Default);
|
||||
GL.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, lck.Width, lck.Height, 0, s_InternalFormat[lck.Format], s_PixelType[lck.Format], lck.ImagePtr);
|
||||
image2d.UnlockBits();
|
||||
|
||||
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (isDisposed)
|
||||
return;
|
||||
|
||||
isDisposed = true;
|
||||
|
||||
int[] ids = textures.Values.ToArray();
|
||||
GL.DeleteTextures(ids);
|
||||
}
|
||||
|
||||
private static readonly Dictionary<QImageFormat, GLEnum> s_InternalFormat = new Dictionary<QImageFormat, GLEnum>()
|
||||
{
|
||||
[QImageFormat.AlphaF] = GL_ALPHA,
|
||||
[QImageFormat.AlphaU8] = GL_ALPHA,
|
||||
[QImageFormat.RedF] = GL_RED,
|
||||
[QImageFormat.RedU8] = GL_RED,
|
||||
[QImageFormat.RgbF] = GL_RGB,
|
||||
[QImageFormat.RgbU8] = GL_RGB,
|
||||
[QImageFormat.RgbaU8] = GL_RGBA,
|
||||
[QImageFormat.RgbaF] = GL_RGBA,
|
||||
};
|
||||
|
||||
private static readonly Dictionary<QImageFormat, GLEnum> s_PixelType = new Dictionary<QImageFormat, GLEnum>()
|
||||
{
|
||||
[QImageFormat.AlphaF] = GL_FLOAT,
|
||||
[QImageFormat.RedF] = GL_FLOAT,
|
||||
[QImageFormat.RgbF] = GL_FLOAT,
|
||||
[QImageFormat.RgbaF] = GL_FLOAT,
|
||||
[QImageFormat.AlphaU8] = GL_UNSIGNED_BYTE,
|
||||
[QImageFormat.RedU8] = GL_UNSIGNED_BYTE,
|
||||
[QImageFormat.RgbU8] = GL_UNSIGNED_BYTE,
|
||||
[QImageFormat.RgbaU8] = GL_UNSIGNED_BYTE,
|
||||
};
|
||||
}
|
||||
}
|
@ -2,16 +2,34 @@
|
||||
* QUIK: User Interface Kit
|
||||
* Copyright (C) 2023 Halit Utku Maden, et al.
|
||||
*/
|
||||
#version 120
|
||||
#version 130
|
||||
|
||||
varying vec2 fv2TexPos;
|
||||
varying vec4 fv4Color;
|
||||
in vec2 fv2TexPos;
|
||||
in vec4 fv4Color;
|
||||
in float ffTexLayer;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform int iEnableSdf;
|
||||
uniform int iEnableTexture;
|
||||
uniform int iAlphaDiscard;
|
||||
uniform float fSdfThreshold;
|
||||
uniform sampler2D txTexture;
|
||||
uniform sampler2D tx2d;
|
||||
uniform sampler2DArray tx2dArray;
|
||||
|
||||
const float fAlphaThreshold = 0.01;
|
||||
|
||||
vec4 getTexture()
|
||||
{
|
||||
if (iEnableTexture == 3)
|
||||
{
|
||||
return texture(tx2dArray, vec3(fv2TexPos, ffTexLayer));
|
||||
}
|
||||
else
|
||||
{
|
||||
return texture(tx2d, fv2TexPos);
|
||||
}
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
@ -19,16 +37,19 @@ void main(void)
|
||||
|
||||
if (iEnableTexture != 0)
|
||||
{
|
||||
vec4 value = texture2D(txTexture, fv2TexPos);
|
||||
vec4 value = getTexture();
|
||||
|
||||
if (iEnableSdf != 0)
|
||||
{
|
||||
float a = max(value.r, value.a);
|
||||
|
||||
value = (a >= fSdfThreshold) ? vec4(1,1,1,1) : vec4(0,0,0,0);
|
||||
value =
|
||||
(a >= fSdfThreshold) ?
|
||||
vec4(1.0,1.0,1.0,1.0) :
|
||||
vec4(0.0,0.0,0.0,0.0);
|
||||
}
|
||||
|
||||
if (iAlphaDiscard != 0 && value.a == 0.0)
|
||||
if (iAlphaDiscard != 0 && value.a <= fAlphaThreshold)
|
||||
{
|
||||
discard;
|
||||
}
|
||||
@ -36,5 +57,5 @@ void main(void)
|
||||
albedo = albedo * value;
|
||||
}
|
||||
|
||||
gl_FragColor = albedo;
|
||||
fragColor = albedo;
|
||||
}
|
@ -2,24 +2,34 @@
|
||||
* QUIK: User Interface Kit
|
||||
* Copyright (C) 2023 Halit Utku Maden, et al.
|
||||
*/
|
||||
#version 120
|
||||
#version 130
|
||||
|
||||
attribute vec2 v2Position; /**< The vertex position.*/
|
||||
attribute int iZIndex; /**< The z index. */
|
||||
attribute vec2 v2TexPos; /**< The texture coorindates. */
|
||||
attribute vec4 v4Color; /**< The vertex color. */
|
||||
in vec2 v2Position; /**< The vertex position.*/
|
||||
in float fZIndex; /**< The z index. */
|
||||
in vec2 v2TexPos; /**< The texture coorindates. */
|
||||
in float fTexLayer; /**< The texture layer for 3D textures. */
|
||||
in vec4 v4Color; /**< The vertex color. */
|
||||
|
||||
varying vec2 fv2TexPos;
|
||||
varying vec4 fv4Color;
|
||||
out vec2 fv2TexPos;
|
||||
out float ffTexLayer;
|
||||
out vec4 fv4Color;
|
||||
|
||||
uniform mat4 m4Transforms; /**< The view matrix. */
|
||||
uniform int iMaxZ; /**< Highest Z coordinate. */
|
||||
uniform float fMaxZ; /**< Highest Z coordinate. */
|
||||
|
||||
const mat4 m4BaseTransforms = mat4(
|
||||
vec4( 2.0, 0.0, 0.0, 0.0),
|
||||
vec4( 0.0, -2.0, 0.0, 0.0),
|
||||
vec4( 0.0, 0.0, 1.0, 0.0),
|
||||
vec4(-1.0, 1.0, 0.0, 1.0)
|
||||
);
|
||||
|
||||
void main(void)
|
||||
{
|
||||
vec4 v = vec4(v2Position, float(iZIndex)/float(iMaxZ), 1);
|
||||
gl_Position = v * m4Transforms;
|
||||
vec4 v = vec4(v2Position, fZIndex/fMaxZ, 1);
|
||||
gl_Position = m4Transforms * v;
|
||||
|
||||
fv2TexPos = v2TexPos;
|
||||
fv4Color = v4Color;
|
||||
ffTexLayer = fTexLayer;
|
||||
}
|
Loading…
Reference in New Issue
Block a user