mirror of
https://github.com/projectM-visualizer/projectm.git
synced 2026-02-05 01:25:28 +00:00
Implemented motion vector grid.
Have to do the motion reverse-propagation on the GPU, as the warp mesh is also calculated there. This is done storing the U/V warp coordinates in a separate texture attachment and then read the (already interpolated) coordinate in the next frame. Added a second color buffer output to the HLSL shader to accomplish it with both the default warp shader and preset-supplied ones. Luckily, the HLSL transpiler knows what to do. Since all framebuffer textures must match in size, this adds a bit more VRAM usage. We also need to mask all other draw calls from writing to the second texture expect in the warp shader.
This commit is contained in:
@ -74,25 +74,36 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
|
||||
m_state.renderContext = renderContext;
|
||||
|
||||
// Update framebuffer size if needed
|
||||
m_framebuffer.SetSize(renderContext.viewportSizeX, renderContext.viewportSizeY);
|
||||
if (m_framebuffer.SetSize(renderContext.viewportSizeX, renderContext.viewportSizeY))
|
||||
{
|
||||
m_isFirstFrame = true;
|
||||
}
|
||||
m_state.mainTexture = m_framebuffer.GetColorAttachmentTexture(m_previousFrameBuffer, 0);
|
||||
|
||||
// First evaluate per-frame code
|
||||
PerFrameUpdate();
|
||||
|
||||
// Motion vector field. Drawn to the previous frame texture before warping it.
|
||||
m_framebuffer.Bind(m_previousFrameBuffer);
|
||||
m_motionVectors.Draw(m_perFrameContext);
|
||||
// Only do it after drawing one frame after init or resize.
|
||||
if (!m_isFirstFrame)
|
||||
{
|
||||
m_framebuffer.Bind(m_previousFrameBuffer);
|
||||
m_motionVectors.Draw(m_perFrameContext, m_framebuffer.GetColorAttachmentTexture(m_previousFrameBuffer, 1));
|
||||
}
|
||||
|
||||
// We now draw to the first framebuffer, but read from the second one for warping and textured shapes.
|
||||
m_framebuffer.BindRead(m_previousFrameBuffer);
|
||||
m_framebuffer.BindDraw(m_currentFrameBuffer);
|
||||
|
||||
// Unmask the motion vector u/v texture for the warp mesh draw and clean both buffers.
|
||||
m_framebuffer.MaskDrawBuffer(1, false);
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// Draw previous frame image warped via per-pixel mesh and warp shader
|
||||
m_perPixelMesh.Draw(m_state, m_perFrameContext, m_perPixelContext);
|
||||
m_framebuffer.MaskDrawBuffer(1, true);
|
||||
|
||||
// Update blur textures
|
||||
m_state.blurTexture.Update(m_state, m_perFrameContext);
|
||||
@ -135,6 +146,8 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
|
||||
|
||||
// Swap framebuffers for the next frame.
|
||||
std::swap(m_currentFrameBuffer, m_previousFrameBuffer);
|
||||
|
||||
m_isFirstFrame = false;
|
||||
}
|
||||
|
||||
|
||||
@ -189,8 +202,14 @@ void MilkdropPreset::Load(std::istream& stream)
|
||||
void MilkdropPreset::InitializePreset(PresetFileParser& parsedFile)
|
||||
{
|
||||
// Create the offscreen rendering surfaces.
|
||||
m_framebuffer.CreateColorAttachment(0, 0);
|
||||
m_framebuffer.CreateColorAttachment(1, 0);
|
||||
m_framebuffer.CreateColorAttachment(0, 0); // Main image
|
||||
m_framebuffer.CreateColorAttachment(0, 1, GL_RG32F, GL_RG, GL_FLOAT); // Motion vector u/v
|
||||
m_framebuffer.CreateColorAttachment(1, 0); // Main image
|
||||
m_framebuffer.CreateColorAttachment(1, 1, GL_RG32F, GL_RG, GL_FLOAT); // Motion vector u/v
|
||||
|
||||
// Mask the motion vector buffer by default.
|
||||
m_framebuffer.MaskDrawBuffer(1, true);
|
||||
|
||||
Framebuffer::Unbind();
|
||||
|
||||
// Load global init variables into the state
|
||||
|
||||
@ -122,4 +122,6 @@ private:
|
||||
Border m_border; //!< Inner/outer borders.
|
||||
|
||||
FinalComposite m_finalComposite; //!< Final composite shader or filters.
|
||||
|
||||
bool m_isFirstFrame{true}; //!< Controls drawing the motion vectors starting with the second frame.
|
||||
};
|
||||
|
||||
@ -327,7 +327,7 @@ void MilkdropShader::PreprocessPresetShader(std::string& program)
|
||||
|
||||
if (m_type == ShaderType::WarpShader)
|
||||
{
|
||||
program.replace(int(found), 11, "void PS(float4 _vDiffuse : COLOR, float4 _uv : TEXCOORD0, float2 _rad_ang : TEXCOORD1, out float4 _return_value : COLOR)\n");
|
||||
program.replace(int(found), 11, "void PS(float4 _vDiffuse : COLOR, float4 _uv : TEXCOORD0, float2 _rad_ang : TEXCOORD1, out float4 _return_value : COLOR0, out float4 _mv_tex_coords : COLOR1)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -346,9 +346,11 @@ void MilkdropShader::PreprocessPresetShader(std::string& program)
|
||||
#ifdef MILKDROP_PRESET_DEBUG
|
||||
std::cerr << "[MilkdropShader] First '{' found at: " << int(found) << std::endl;
|
||||
#endif
|
||||
const char* progMain =
|
||||
"{\n"
|
||||
"float3 ret = 0;\n";
|
||||
std::string progMain = "{\nfloat3 ret = 0;\n";
|
||||
if (m_type == ShaderType::WarpShader)
|
||||
{
|
||||
progMain.append("_mv_tex_coords.xy = _uv.xy;\n");
|
||||
}
|
||||
program.replace(int(found), 1, progMain);
|
||||
}
|
||||
else
|
||||
|
||||
@ -1,23 +1,36 @@
|
||||
#include "MotionVectors.hpp"
|
||||
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <Renderer/StaticGlShaders.hpp>
|
||||
#include <Renderer/TextureManager.hpp>
|
||||
|
||||
MotionVectors::MotionVectors(PresetState& presetState)
|
||||
: RenderItem()
|
||||
, m_presetState(presetState)
|
||||
{
|
||||
auto staticShaders = StaticGlShaders::Get();
|
||||
m_motionVectorShader.CompileProgram(staticShaders->GetPresetMotionVectorsVertexShader(),
|
||||
staticShaders->GetV2fC4fFragmentShader());
|
||||
RenderItem::Init();
|
||||
}
|
||||
|
||||
void MotionVectors::InitVertexAttrib()
|
||||
{
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
|
||||
glDisableVertexAttribArray(1);
|
||||
glEnableVertexAttribArray(2);
|
||||
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(MotionVectorVertex), reinterpret_cast<void*>(offsetof(MotionVectorVertex, x)));
|
||||
glVertexAttribIPointer(2, 1, GL_INT, sizeof(MotionVectorVertex), reinterpret_cast<void*>(offsetof(MotionVectorVertex, index)));
|
||||
}
|
||||
|
||||
void MotionVectors::Draw(const PerFrameContext& presetPerFrameContext)
|
||||
void MotionVectors::Draw(const PerFrameContext& presetPerFrameContext, std::shared_ptr<class Texture> motionTexture)
|
||||
{
|
||||
// Don't draw if invisible.
|
||||
if (*presetPerFrameContext.mv_a < 0.0001f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int countX = static_cast<int>(*presetPerFrameContext.mv_x);
|
||||
int countY = static_cast<int>(*presetPerFrameContext.mv_y);
|
||||
|
||||
@ -40,10 +53,8 @@ void MotionVectors::Draw(const PerFrameContext& presetPerFrameContext)
|
||||
divertY = 0.0f;
|
||||
}
|
||||
|
||||
auto divertX2 = static_cast<float>(*presetPerFrameContext.mv_dx);
|
||||
auto divertY2 = static_cast<float>(*presetPerFrameContext.mv_dy);
|
||||
|
||||
auto lengthMultiplier = static_cast<float>(*presetPerFrameContext.mv_l);
|
||||
auto const divertX2 = static_cast<float>(*presetPerFrameContext.mv_dx);
|
||||
auto const divertY2 = static_cast<float>(*presetPerFrameContext.mv_dy);
|
||||
|
||||
// Clamp X/Y diversions to 0..1
|
||||
if (divertX < 0.0f)
|
||||
@ -63,16 +74,25 @@ void MotionVectors::Draw(const PerFrameContext& presetPerFrameContext)
|
||||
divertY = 1.0f;
|
||||
}
|
||||
|
||||
float const inverseWidth = 1.0f / static_cast<float>(m_presetState.renderContext.viewportSizeX);
|
||||
float const minimalLength = 1.0f * inverseWidth;
|
||||
// Tweaked this a bit to ensure lines are always at least a bit more than 1px long.
|
||||
// Line smoothing makes some of them disappear otherwise.
|
||||
float const inverseWidth = 1.25f / static_cast<float>(m_presetState.renderContext.viewportSizeX);
|
||||
float const inverseHeight = 1.25f / static_cast<float>(m_presetState.renderContext.viewportSizeY);
|
||||
float const minimumLength = sqrtf(inverseWidth * inverseWidth + inverseHeight * inverseHeight);
|
||||
|
||||
std::vector<Point> lineVertices(static_cast<std::size_t>(countX + 1) * 2); // countX + 1 lines for each grid row, 2 vertices each.
|
||||
std::vector<MotionVectorVertex> lineVertices(static_cast<std::size_t>(countX + 1) * 2); // countX + 1 lines for each grid row, 2 vertices each.
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
m_presetState.untexturedShader.Bind();
|
||||
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
|
||||
m_motionVectorShader.Bind();
|
||||
m_motionVectorShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
|
||||
m_motionVectorShader.SetUniformFloat("length_multiplier", static_cast<float>(*presetPerFrameContext.mv_l));
|
||||
m_motionVectorShader.SetUniformFloat("minimum_length", minimumLength);
|
||||
|
||||
m_motionVectorShader.SetUniformInt("warp_coordinates", 0);
|
||||
|
||||
motionTexture->Bind(0, m_sampler);
|
||||
|
||||
glVertexAttrib4f(1,
|
||||
static_cast<float>(*presetPerFrameContext.mv_r),
|
||||
@ -83,6 +103,10 @@ void MotionVectors::Draw(const PerFrameContext& presetPerFrameContext)
|
||||
glBindVertexArray(m_vaoID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_vboID);
|
||||
|
||||
glLineWidth(1);
|
||||
#if USE_GLES == 0
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
#endif
|
||||
|
||||
for (int y = 0; y < countY; y++)
|
||||
{
|
||||
@ -97,64 +121,31 @@ void MotionVectors::Draw(const PerFrameContext& presetPerFrameContext)
|
||||
|
||||
if (posX > 0.0001f && posX < 0.9999f)
|
||||
{
|
||||
float posX2{};
|
||||
float posY2{};
|
||||
lineVertices[vertex].x = posX;
|
||||
lineVertices[vertex].y = posY;
|
||||
lineVertices[vertex].index = vertex;
|
||||
|
||||
// Uses the warp mesh texture transformation to get the motion direction of this point.
|
||||
ReversePropagatePoint(posX, posY, posX2, posY2);
|
||||
|
||||
// Enforce minimum trail length
|
||||
{
|
||||
float distX = posX2 - posX;
|
||||
float distY = posY2 - posY;
|
||||
|
||||
distX *= lengthMultiplier;
|
||||
distY *= lengthMultiplier;
|
||||
|
||||
float length = sqrtf(distX * distX + distY * distY);
|
||||
if (length > minimalLength)
|
||||
{
|
||||
}
|
||||
else if (length > 0.00000001f)
|
||||
{
|
||||
length = minimalLength / length;
|
||||
distX *= length;
|
||||
distY *= length;
|
||||
}
|
||||
else
|
||||
{
|
||||
distX = minimalLength;
|
||||
distY = minimalLength;
|
||||
}
|
||||
|
||||
posX2 = posX + distX;
|
||||
posY2 = posY + distY;
|
||||
}
|
||||
|
||||
// Assign line vertices
|
||||
lineVertices.at(vertex).x = posX * 2.0f - 1.0f;
|
||||
lineVertices.at(vertex).y = posY * 2.0f - 1.0f;
|
||||
lineVertices.at(vertex + 1).x = posX2 * 2.0f - 1.0f;
|
||||
lineVertices.at(vertex + 1).y = posY2 * 2.0f - 1.0f;
|
||||
lineVertices[vertex + 1] = lineVertices[vertex];
|
||||
lineVertices[vertex + 1].index++;
|
||||
|
||||
vertex += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw a row of lines.
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(Point) * lineVertices.size(), lineVertices.data(), GL_DYNAMIC_DRAW);
|
||||
glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(lineVertices.size()));
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(MotionVectorVertex) * vertex, lineVertices.data(), GL_DYNAMIC_DRAW);
|
||||
glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(vertex));
|
||||
}
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
#if USE_GLES == 0
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
#endif
|
||||
|
||||
Shader::Unbind();
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void MotionVectors::ReversePropagatePoint(float posX1, float posY1, float& posX2, float& posY2)
|
||||
{
|
||||
}
|
||||
|
||||
@ -26,11 +26,20 @@ public:
|
||||
/**
|
||||
* Redners the motion vectors.
|
||||
* @param presetPerFrameContext The per-frame context variables.
|
||||
* @param motionTexture The u/v "motion" texture written by the warp shader.
|
||||
*/
|
||||
void Draw(const PerFrameContext& presetPerFrameContext);
|
||||
void Draw(const PerFrameContext& presetPerFrameContext, std::shared_ptr<class Texture> motionTexture);
|
||||
|
||||
private:
|
||||
void ReversePropagatePoint(float posX1, float posY1, float& posX2, float&posY2);
|
||||
struct MotionVectorVertex
|
||||
{
|
||||
float x{};
|
||||
float y{};
|
||||
int32_t index{};
|
||||
};
|
||||
|
||||
PresetState& m_presetState; //!< The global preset state.
|
||||
|
||||
Shader m_motionVectorShader; //!< The motion vector shader, calculates the trace positions in the GPU.
|
||||
std::shared_ptr<Sampler> m_sampler{std::make_shared<Sampler>(GL_CLAMP_TO_EDGE, GL_LINEAR)}; //!< The texture sampler.
|
||||
};
|
||||
|
||||
@ -53,7 +53,34 @@ public:
|
||||
const PerFrameContext& perFrameContext,
|
||||
PerPixelContext& perPixelContext);
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
* Warp mesh vertex with all required attributes.
|
||||
*/
|
||||
struct MeshVertex {
|
||||
float x{};
|
||||
float y{};
|
||||
float radius{};
|
||||
float angle{};
|
||||
|
||||
float zoom{};
|
||||
float zoomExp{};
|
||||
float rot{};
|
||||
float warp{};
|
||||
|
||||
float centerX{};
|
||||
float centerY{};
|
||||
|
||||
float distanceX{};
|
||||
float distanceY{};
|
||||
|
||||
float stretchX{};
|
||||
float stretchY{};
|
||||
};
|
||||
|
||||
using VertexList = std::vector<MeshVertex>;
|
||||
|
||||
/**
|
||||
* @brief Initializes the vertex array and fills in static data if needed.
|
||||
*
|
||||
@ -81,40 +108,16 @@ private:
|
||||
*/
|
||||
void WarpedBlit(const PresetState& presetState, const PerFrameContext& perFrameContext);
|
||||
|
||||
/**
|
||||
* Warp mesh vertex with all required attributes.
|
||||
*/
|
||||
struct MeshVertex {
|
||||
float x{};
|
||||
float y{};
|
||||
float radius{};
|
||||
float angle{};
|
||||
|
||||
float zoom{};
|
||||
float zoomExp{};
|
||||
float rot{};
|
||||
float warp{};
|
||||
|
||||
float centerX{};
|
||||
float centerY{};
|
||||
|
||||
float distanceX{};
|
||||
float distanceY{};
|
||||
|
||||
float stretchX{};
|
||||
float stretchY{};
|
||||
};
|
||||
|
||||
int m_gridSizeX{}; //!< Warp mesh X resolution.
|
||||
int m_gridSizeY{}; //!< Warp mesh Y resolution.
|
||||
|
||||
int m_viewportWidth{}; //!< Last known viewport width.
|
||||
int m_viewportHeight{}; //!< Last known viewport height.
|
||||
|
||||
std::vector<MeshVertex> m_vertices; //!< The calculated mesh vertices.
|
||||
VertexList m_vertices; //!< The calculated mesh vertices.
|
||||
|
||||
std::vector<int> m_listIndices; //!< List of vertex indices to render.
|
||||
std::vector<MeshVertex> m_drawVertices; //!< Temp data buffer for the vertices to be drawn.
|
||||
VertexList m_drawVertices; //!< Temp data buffer for the vertices to be drawn.
|
||||
|
||||
Shader m_perPixelMeshShader; //!< Special shader which calculates the per-pixel UV coordinates.
|
||||
std::unique_ptr<MilkdropShader> m_warpShader; //!< The warp shader. Either preset-defined or a default shader.
|
||||
|
||||
@ -71,12 +71,12 @@ void Framebuffer::Unbind()
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void Framebuffer::SetSize(int width, int height)
|
||||
bool Framebuffer::SetSize(int width, int height)
|
||||
{
|
||||
if (width == 0 || height == 0 ||
|
||||
(width == m_width && height == m_height))
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_width = width;
|
||||
@ -92,16 +92,23 @@ void Framebuffer::SetSize(int width, int height)
|
||||
}
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Framebuffer::CreateColorAttachment(int framebufferIndex, int attachmentIndex)
|
||||
{
|
||||
CreateColorAttachment(framebufferIndex, attachmentIndex, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
|
||||
}
|
||||
|
||||
void Framebuffer::CreateColorAttachment(int framebufferIndex, int attachmentIndex, GLint internalFormat, GLenum format, GLenum type)
|
||||
{
|
||||
if (framebufferIndex < 0 || framebufferIndex >= static_cast<int>(m_framebufferIds.size()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TextureAttachment textureAttachment{TextureAttachment::AttachmentType::Color, m_width, m_height};
|
||||
TextureAttachment textureAttachment{internalFormat, format, type, m_width, m_height };
|
||||
const auto texture = textureAttachment.Texture();
|
||||
m_attachments.at(framebufferIndex).insert({GL_COLOR_ATTACHMENT0 + attachmentIndex, std::move(textureAttachment)});
|
||||
|
||||
@ -190,6 +197,13 @@ void Framebuffer::CreateDepthStencilAttachment(int framebufferIndex)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void Framebuffer::MaskDrawBuffer(int bufferIndex, bool masked)
|
||||
{
|
||||
// Invert the flag, as "true" means the color channel *will* be written.
|
||||
auto glMasked = static_cast<GLboolean>(!masked);
|
||||
glColorMaski(bufferIndex, glMasked, glMasked, glMasked, glMasked);
|
||||
}
|
||||
|
||||
void Framebuffer::UpdateDrawBuffers(int framebufferIndex)
|
||||
{
|
||||
if (framebufferIndex < 0 || framebufferIndex >= static_cast<int>(m_framebufferIds.size()))
|
||||
|
||||
@ -89,8 +89,9 @@ public:
|
||||
* won't be changed.
|
||||
* @param width The width of the framebuffer.
|
||||
* @param height The height of the framebuffer.
|
||||
* @return true if the framebuffer was resized, false if it's contents remain unchanged.
|
||||
*/
|
||||
void SetSize(int width, int height);
|
||||
bool SetSize(int width, int height);
|
||||
|
||||
/**
|
||||
* @brief Adds a new color attachment to the framebuffer.
|
||||
@ -100,6 +101,17 @@ public:
|
||||
*/
|
||||
void CreateColorAttachment(int framebufferIndex, int attachmentIndex);
|
||||
|
||||
/**
|
||||
* @brief Adds a new color attachment to the framebuffer with the specified format.
|
||||
* @param framebufferIndex The framebuffer index.
|
||||
* @param index The index of the attachment, at least indices 0-7 are guaranteed to be available.
|
||||
* @param internalFormat OpenGL internal format, e.g. GL_RGBA8
|
||||
* @param format OpenGL color format, e.g. GL_RGBA
|
||||
* @param type OpenGL component storage type, e.g. GL_UNSIGNED _BYTE
|
||||
*/
|
||||
void CreateColorAttachment(int framebufferIndex, int attachmentIndex,
|
||||
GLint internalFormat, GLenum format, GLenum type);
|
||||
|
||||
/**
|
||||
* @brief Returns the texture ID of the given framebuffer and color attachment.
|
||||
* @param framebufferIndex The framebuffer index.
|
||||
@ -126,6 +138,14 @@ public:
|
||||
*/
|
||||
void CreateDepthStencilAttachment(int framebufferIndex);
|
||||
|
||||
/**
|
||||
* @brief Sets the masked flag for a specific draw buffer.
|
||||
* This can be used to enable or disable rendering to specific color attachments.
|
||||
* @param bufferIndex The index of the buffer to set the mask flag on.
|
||||
* @param masked true if the attachment should be masked, false if not.
|
||||
*/
|
||||
void MaskDrawBuffer(int bufferIndex, bool masked);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Updates the draw buffer list for the fragment shader outputs of the given framebuffer.
|
||||
|
||||
@ -8,6 +8,9 @@
|
||||
|
||||
namespace {
|
||||
// Variants of shaders for GLSL1.2
|
||||
const std::string kPresetMotionVectorsVertexShaderGlsl120 = R"(
|
||||
)";
|
||||
|
||||
const std::string kPresetWarpVertexShaderGlsl120 = R"(
|
||||
#define pos vertex_position.xy
|
||||
#define radius vertex_position.z
|
||||
@ -444,6 +447,63 @@ void main(){
|
||||
)";
|
||||
|
||||
// Variants of shaders for GLSL3.3
|
||||
const std::string kPresetMotionVectorsVertexShaderGlsl330 = R"(
|
||||
layout(location = 0) in vec2 vertex_position;
|
||||
layout(location = 1) in vec4 vertex_color;
|
||||
layout(location = 2) in int vertex_index;
|
||||
|
||||
uniform mat4 vertex_transformation;
|
||||
uniform float length_multiplier;
|
||||
uniform float minimum_length;
|
||||
|
||||
uniform sampler2D warp_coordinates;
|
||||
|
||||
out vec4 fragment_color;
|
||||
|
||||
void main() {
|
||||
// Input positions are given in texture coordinates (0...1), not the usual
|
||||
// screen coordinates.
|
||||
vec2 pos = vertex_position;
|
||||
|
||||
if (vertex_index % 2 == 1)
|
||||
{
|
||||
// Reverse propagation using the u/v texture written in the previous frame.
|
||||
// Milkdrop's original code did a simple bilinear interpolation, but here it was already
|
||||
// done by the fragment shader during the warp mesh drawing. We just need to look up the
|
||||
// motion vector coordinate.
|
||||
vec2 oldUV = texture(warp_coordinates, pos.xy).xy;
|
||||
|
||||
// Enforce minimum trail length
|
||||
vec2 dist = oldUV - pos;
|
||||
dist *= length_multiplier;
|
||||
float len = length(dist);
|
||||
if (len > minimum_length)
|
||||
{}
|
||||
else if (len > 0.00000001f)
|
||||
{
|
||||
len = minimum_length / len;
|
||||
dist *= len;
|
||||
}
|
||||
else
|
||||
{
|
||||
dist = vec2(minimum_length);
|
||||
}
|
||||
|
||||
pos += dist;
|
||||
}
|
||||
|
||||
// Transform positions from 0...1 to -1...1 in each direction.
|
||||
pos = pos * 2.0 - 1.0;
|
||||
|
||||
// Flip final Y, as we draw this top-down, which is bottom-up in OpenGL.
|
||||
pos.y = -pos.y;
|
||||
|
||||
// Now we've got the usual coordinates, apply our orthogonal transformation.
|
||||
gl_Position = vertex_transformation * vec4(pos, 0.0, 1.0);
|
||||
fragment_color = vertex_color;
|
||||
}
|
||||
)";
|
||||
|
||||
const std::string kPresetWarpVertexShaderGlsl330 = R"(
|
||||
#define pos vertex_position.xy
|
||||
#define radius vertex_position.z
|
||||
@ -529,10 +589,14 @@ in vec2 frag_TEXCOORD1;
|
||||
|
||||
uniform sampler2D texture_sampler;
|
||||
|
||||
out vec4 color;
|
||||
layout(location = 0) out vec4 color;
|
||||
layout(location = 1) out vec2 texCoords;
|
||||
|
||||
void main() {
|
||||
// Main image
|
||||
color = frag_COLOR * texture(texture_sampler, frag_TEXCOORD0.xy);
|
||||
// Motion vector grid u/v coords for the next frame
|
||||
texCoords = frag_TEXCOORD0.xy;
|
||||
}
|
||||
)";
|
||||
|
||||
@ -575,6 +639,7 @@ const std::string kV2fC4fFragmentShaderGlsl330 = R"(
|
||||
precision mediump float;
|
||||
|
||||
in vec4 fragment_color;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
void main(){
|
||||
@ -1024,6 +1089,7 @@ std::string StaticGlShaders::AddVersionHeader(std::string shader_text) {
|
||||
return k##name##Glsl330; \
|
||||
}
|
||||
|
||||
DECLARE_SHADER_ACCESSOR(PresetMotionVectorsVertexShader);
|
||||
DECLARE_SHADER_ACCESSOR(PresetWarpVertexShader);
|
||||
DECLARE_SHADER_ACCESSOR(PresetWarpFragmentShader);
|
||||
DECLARE_SHADER_ACCESSOR(PresetCompVertexShader);
|
||||
|
||||
@ -26,6 +26,7 @@ class StaticGlShaders {
|
||||
}
|
||||
|
||||
// Returns the named static GL shader resource.
|
||||
std::string GetPresetMotionVectorsVertexShader();
|
||||
std::string GetPresetWarpVertexShader();
|
||||
std::string GetPresetWarpFragmentShader();
|
||||
std::string GetPresetCompVertexShader();
|
||||
|
||||
@ -3,23 +3,36 @@
|
||||
#include <utility>
|
||||
|
||||
Texture::Texture(std::string name, const int width, const int height, const bool isUserTexture)
|
||||
: m_type(GL_TEXTURE_2D)
|
||||
: m_target(GL_TEXTURE_2D)
|
||||
, m_name(std::move(name))
|
||||
, m_width(width)
|
||||
, m_height(height)
|
||||
, m_isUserTexture(isUserTexture)
|
||||
, m_internalFormat(GL_RGB)
|
||||
, m_format(GL_RGB)
|
||||
, m_type(GL_UNSIGNED_BYTE)
|
||||
{
|
||||
glGenTextures(1, &m_textureId);
|
||||
glBindTexture(GL_TEXTURE_2D, m_textureId);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
CreateEmptyTexture();
|
||||
}
|
||||
|
||||
Texture::Texture(std::string name, const GLuint texID, const GLenum type,
|
||||
const int width, const int height,
|
||||
const bool isUserTexture)
|
||||
: m_textureId(texID)
|
||||
Texture::Texture(std::string name, int width, int height,
|
||||
GLint internalFormat, GLenum format, GLenum type, bool isUserTexture)
|
||||
: m_target(GL_TEXTURE_2D)
|
||||
, m_name(std::move(name))
|
||||
, m_width(width)
|
||||
, m_height(height)
|
||||
, m_isUserTexture(isUserTexture)
|
||||
, m_internalFormat(internalFormat)
|
||||
, m_format(format)
|
||||
, m_type(type)
|
||||
{
|
||||
CreateEmptyTexture();
|
||||
}
|
||||
|
||||
Texture::Texture(std::string name, const GLuint texID, const GLenum target,
|
||||
const int width, const int height, const bool isUserTexture)
|
||||
: m_textureId(texID)
|
||||
, m_target(target)
|
||||
, m_name(std::move(name))
|
||||
, m_width(width)
|
||||
, m_height(height)
|
||||
@ -39,7 +52,7 @@ Texture::~Texture()
|
||||
void Texture::Bind(GLint slot, const Sampler::Ptr& sampler) const
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + slot);
|
||||
glBindTexture(m_type, m_textureId);
|
||||
glBindTexture(m_target, m_textureId);
|
||||
|
||||
if (sampler)
|
||||
{
|
||||
@ -50,7 +63,7 @@ void Texture::Bind(GLint slot, const Sampler::Ptr& sampler) const
|
||||
void Texture::Unbind(GLint slot) const
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + slot);
|
||||
glBindTexture(m_type, 0);
|
||||
glBindTexture(m_target, 0);
|
||||
}
|
||||
|
||||
auto Texture::TextureID() const -> GLuint
|
||||
@ -65,7 +78,7 @@ auto Texture::Name() const -> const std::string&
|
||||
|
||||
auto Texture::Type() const -> GLenum
|
||||
{
|
||||
return m_type;
|
||||
return m_target;
|
||||
}
|
||||
|
||||
auto Texture::Width() const -> int
|
||||
@ -82,3 +95,11 @@ auto Texture::IsUserTexture() const -> bool
|
||||
{
|
||||
return m_isUserTexture;
|
||||
}
|
||||
|
||||
void Texture::CreateEmptyTexture()
|
||||
{
|
||||
glGenTextures(1, &m_textureId);
|
||||
glBindTexture(m_target, m_textureId);
|
||||
glTexImage2D(m_target, 0, m_internalFormat, m_width, m_height, 0, m_format, m_type, nullptr);
|
||||
glBindTexture(m_target, 0);
|
||||
}
|
||||
|
||||
@ -36,16 +36,26 @@ public:
|
||||
explicit Texture(std::string name, int width, int height, bool isUserTexture);
|
||||
|
||||
/**
|
||||
* @brief Constructor. Creates a new texture instance from an existing OpenGL texture.
|
||||
* The class will take ownership of the texture, e.g. freeing it when destroyed!
|
||||
* @brief Constructor. Allocates a new, empty texture with the given size and format.
|
||||
* @param name Optional name of the texture for referencing in Milkdrop shaders.
|
||||
* @param texID The OpenGL texture name (ID).
|
||||
* @param type The texture type, e.g. GL_TEXTURE_2D.
|
||||
* @param width Width in pixels.
|
||||
* @param height Height in pixels.
|
||||
* @param isUserTexture true if the texture is an externally-loaded image, false if it's an internal texture.
|
||||
*/
|
||||
explicit Texture(std::string name, GLuint texID, GLenum type,
|
||||
explicit Texture(std::string name, int width, int height,
|
||||
GLint internalFormat, GLenum format, GLenum type, bool isUserTexture);
|
||||
|
||||
/**
|
||||
* @brief Constructor. Creates a new texture instance from an existing OpenGL texture.
|
||||
* The class will take ownership of the texture, e.g. freeing it when destroyed!
|
||||
* @param name Optional name of the texture for referencing in Milkdrop shaders.
|
||||
* @param texID The OpenGL texture name (ID).
|
||||
* @param target The texture target type, e.g. GL_TEXTURE_2D.
|
||||
* @param width Width in pixels.
|
||||
* @param height Height in pixels.
|
||||
* @param isUserTexture true if the texture is an externally-loaded image, false if it's an internal texture.
|
||||
*/
|
||||
explicit Texture(std::string name, GLuint texID, GLenum target,
|
||||
int width, int height,
|
||||
bool isUserTexture);
|
||||
|
||||
@ -105,11 +115,17 @@ public:
|
||||
auto IsUserTexture() const -> bool;
|
||||
|
||||
private:
|
||||
GLuint m_textureId{0}; //!< The OpenGL texture name/ID.
|
||||
GLenum m_type{GL_NONE}; //!< The OpenGL texture type, e.g. GL_TEXTURE_2D.
|
||||
void CreateEmptyTexture();
|
||||
|
||||
GLuint m_textureId{0}; //!< The OpenGL texture name/ID.
|
||||
GLenum m_target{GL_NONE}; //!< The OpenGL texture target, e.g. GL_TEXTURE_2D.
|
||||
|
||||
std::string m_name; //!< The texture name for identifying it in shaders.
|
||||
int m_width{0}; //!< Texture width in pixels.
|
||||
int m_height{0}; //!< Texture height in pixels.
|
||||
bool m_isUserTexture{false}; //!< true if it's a user texture, false if an internal one.
|
||||
|
||||
GLint m_internalFormat{}; //!< OpenGL internal format, e.g. GL_RGBA8
|
||||
GLenum m_format{}; //!< OpenGL color format, e.g. GL_RGBA
|
||||
GLenum m_type{}; //!< OpenGL component storage type, e.g. GL_UNSIGNED _BYTE
|
||||
};
|
||||
|
||||
@ -9,6 +9,18 @@ TextureAttachment::TextureAttachment(AttachmentType attachment, int width, int h
|
||||
}
|
||||
}
|
||||
|
||||
TextureAttachment::TextureAttachment(GLint internalFormat, GLenum format, GLenum type, int width, int height)
|
||||
: m_attachmentType(AttachmentType::Color)
|
||||
, m_internalFormat(internalFormat)
|
||||
, m_format(format)
|
||||
, m_type(type)
|
||||
{
|
||||
if (width > 0 && height > 0)
|
||||
{
|
||||
ReplaceTexture(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
auto TextureAttachment::Type() const -> TextureAttachment::AttachmentType
|
||||
{
|
||||
return m_attachmentType;
|
||||
@ -40,9 +52,18 @@ void TextureAttachment::ReplaceTexture(int width, int height)
|
||||
switch(m_attachmentType)
|
||||
{
|
||||
case AttachmentType::Color:
|
||||
internalFormat = GL_RGBA;
|
||||
textureFormat = GL_RGBA;
|
||||
pixelFormat = GL_UNSIGNED_BYTE;
|
||||
if (m_internalFormat == 0)
|
||||
{
|
||||
internalFormat = GL_RGBA;
|
||||
textureFormat = GL_RGBA;
|
||||
pixelFormat = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
else
|
||||
{
|
||||
internalFormat = m_internalFormat;
|
||||
textureFormat = m_format;
|
||||
pixelFormat = m_type;
|
||||
}
|
||||
break;
|
||||
case AttachmentType::Depth:
|
||||
internalFormat = GL_DEPTH_COMPONENT16;
|
||||
@ -79,4 +100,4 @@ void TextureAttachment::ReplaceTexture(int width, int height)
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
m_texture = std::make_shared<class Texture>("", textureId, GL_TEXTURE_2D, width, height, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,6 +39,17 @@ public:
|
||||
*/
|
||||
explicit TextureAttachment(AttachmentType attachmentType, int width, int height);
|
||||
|
||||
/**
|
||||
* @brief Creates a new 2D color texture attachment with the given format and size.
|
||||
* Must have the same size as the framebuffer.
|
||||
* @param internalFormat OpenGL internal format, e.g. GL_RGBA8
|
||||
* @param format OpenGL color format, e.g. GL_RGBA
|
||||
* @param type OpenGL component storage type, e.g. GL_UNSIGNED _BYTE
|
||||
* @param width The width of the texture in pixels.
|
||||
* @param height The height of the texture in pixels.
|
||||
*/
|
||||
explicit TextureAttachment(GLint internalFormat, GLenum format, GLenum type, int width, int height);
|
||||
|
||||
TextureAttachment(TextureAttachment&& other) = default;
|
||||
auto operator=(TextureAttachment&& other) -> TextureAttachment& = default;
|
||||
|
||||
@ -74,4 +85,8 @@ private:
|
||||
|
||||
AttachmentType m_attachmentType{AttachmentType::Color}; //!< Attachment type of this texture.
|
||||
std::shared_ptr<class Texture> m_texture{std::make_shared<class Texture>()}; //!< The texture.
|
||||
|
||||
GLint m_internalFormat{}; //!< OpenGL internal format, e.g. GL_RGBA8
|
||||
GLenum m_format{}; //!< OpenGL color format, e.g. GL_RGBA
|
||||
GLenum m_type{}; //!< OpenGL component storage type, e.g. GL_UNSIGNED _BYTE
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user