mirror of
https://github.com/projectM-visualizer/projectm.git
synced 2026-02-08 18:05:38 +00:00
Add PresetTransition class to render the transition quad/shader.
Also added the required shader files. Still need to pack them as resources into the library.
This commit is contained in:
@ -6,6 +6,8 @@ add_library(Renderer OBJECT
|
||||
IdleTextures.hpp
|
||||
MilkdropNoise.cpp
|
||||
MilkdropNoise.hpp
|
||||
PresetTransition.cpp
|
||||
PresetTransition.hpp
|
||||
RenderContext.hpp
|
||||
RenderItem.cpp
|
||||
RenderItem.hpp
|
||||
|
||||
125
src/libprojectM/Renderer/PresetTransition.cpp
Normal file
125
src/libprojectM/Renderer/PresetTransition.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
#include "PresetTransition.hpp"
|
||||
|
||||
#include "TextureManager.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
|
||||
PresetTransition::PresetTransition(const std::shared_ptr<Shader>& transitionShader, double durationSeconds)
|
||||
: m_transitionShader(transitionShader)
|
||||
, m_durationSeconds(durationSeconds)
|
||||
{
|
||||
std::mt19937 rand32(m_randomDevice());
|
||||
m_staticRandomValues = {rand32(), rand32(), rand32(), rand32()};
|
||||
}
|
||||
|
||||
void PresetTransition::InitVertexAttrib()
|
||||
{
|
||||
static const std::array<RenderItem::Point, 4> points{{{-1.0f, 1.0f},
|
||||
{1.0f, 1.0f},
|
||||
{-1.0f, -1.0f},
|
||||
{1.0f, -1.0f}}};
|
||||
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Point), reinterpret_cast<void*>(offsetof(Point, x))); // Position
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points.data(), GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
auto PresetTransition::IsDone() const -> bool
|
||||
{
|
||||
const auto secondsSinceStart = std::chrono::duration<double>(std::chrono::system_clock::now() - m_transitionStartTime).count();
|
||||
return m_durationSeconds <= 0.0 || secondsSinceStart >= m_durationSeconds;
|
||||
}
|
||||
|
||||
void PresetTransition::Draw(const Preset& oldPreset,
|
||||
const Preset& newPreset,
|
||||
const RenderContext& context,
|
||||
const libprojectM::Audio::FrameAudioData& audioData)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
if (m_transitionShader == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::mt19937 rand32(m_randomDevice());
|
||||
|
||||
// Calculate progress values
|
||||
const auto secondsSinceStart = std::chrono::duration<double>(std::chrono::system_clock::now() - m_transitionStartTime).count();
|
||||
|
||||
// If duration is zero,
|
||||
double linearProgress{1.0};
|
||||
double cosineProgress{1.0};
|
||||
double bicubicProgress{1.0};
|
||||
|
||||
if (m_durationSeconds > 0.0)
|
||||
{
|
||||
linearProgress = secondsSinceStart / m_durationSeconds;
|
||||
cosineProgress = (-std::cos(linearProgress * M_PI) + 1.0) * 0.5;
|
||||
bicubicProgress = linearProgress < 0.5 ? 4.0 * linearProgress * linearProgress * linearProgress : 1.0 - pow(-2.0 * linearProgress + 2.0, 3.0) / 2.0;
|
||||
}
|
||||
|
||||
m_transitionShader->Bind();
|
||||
|
||||
// Numerical parameters
|
||||
m_transitionShader->SetUniformFloat3("iResolution", {static_cast<float>(context.viewportSizeX),
|
||||
static_cast<float>(context.viewportSizeY),
|
||||
0.0f});
|
||||
|
||||
m_transitionShader->SetUniformFloat4("durationParams", {linearProgress,
|
||||
cosineProgress,
|
||||
bicubicProgress,
|
||||
m_durationSeconds});
|
||||
|
||||
m_transitionShader->SetUniformFloat2("timeParams", {secondsSinceStart,
|
||||
std::chrono::duration<float>(std::chrono::system_clock::now() - m_lastFrameTime).count()});
|
||||
|
||||
m_transitionShader->SetUniformInt4("iRandStatic", m_staticRandomValues);
|
||||
|
||||
m_transitionShader->SetUniformInt4("iRandFrame", {rand32(),
|
||||
rand32(),
|
||||
rand32(),
|
||||
rand32()});
|
||||
|
||||
m_transitionShader->SetUniformFloat3("iBeatValues", {audioData.bass,
|
||||
audioData.mid,
|
||||
audioData.treb});
|
||||
|
||||
m_transitionShader->SetUniformFloat3("iBeatAttValues", {audioData.bassAtt,
|
||||
audioData.midAtt,
|
||||
audioData.trebAtt});
|
||||
|
||||
// Texture samplers
|
||||
oldPreset.OutputTexture()->Bind(0, m_presetSampler);
|
||||
newPreset.OutputTexture()->Bind(1, m_presetSampler);
|
||||
|
||||
int textureUnit = 2;
|
||||
std::vector<TextureSamplerDescriptor> noiseDescriptors(m_noiseTextureNames.size());
|
||||
for (const auto& noiseTextureName : m_noiseTextureNames)
|
||||
{
|
||||
noiseDescriptors[textureUnit - 2] = context.textureManager->GetTexture(noiseTextureName);
|
||||
noiseDescriptors[textureUnit - 2].Bind(textureUnit, *m_transitionShader);
|
||||
textureUnit++;
|
||||
}
|
||||
|
||||
// Render the transition quad
|
||||
glBindVertexArray(m_vaoID);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glBindVertexArray(0);
|
||||
|
||||
// Clean up
|
||||
oldPreset.OutputTexture()->Unbind(0);
|
||||
newPreset.OutputTexture()->Unbind(1);
|
||||
|
||||
for (int i = 2; i < textureUnit; i++)
|
||||
{
|
||||
noiseDescriptors[textureUnit - 2].Unbind(textureUnit);
|
||||
}
|
||||
|
||||
Shader::Unbind();
|
||||
|
||||
// Update last frame time.
|
||||
m_lastFrameTime = std::chrono::system_clock::now();
|
||||
}
|
||||
66
src/libprojectM/Renderer/PresetTransition.hpp
Normal file
66
src/libprojectM/Renderer/PresetTransition.hpp
Normal file
@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include "RenderItem.hpp"
|
||||
#include "Shader.hpp"
|
||||
#include "TextureSamplerDescriptor.hpp"
|
||||
|
||||
#include <Preset.hpp>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
|
||||
/**
|
||||
* @brief Implements the shader and rendering logic to blend two presets into each other.
|
||||
*/
|
||||
class PresetTransition : public RenderItem
|
||||
{
|
||||
public:
|
||||
PresetTransition() = delete;
|
||||
|
||||
explicit PresetTransition(const std::shared_ptr<Shader>& transitionShader, double durationSeconds);
|
||||
|
||||
void InitVertexAttrib() override;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the transition is done.
|
||||
* @return false if the transition is still in progress, true if it's done.
|
||||
*/
|
||||
auto IsDone() const -> bool;
|
||||
|
||||
/**
|
||||
* @brief Updates the transition variables and renders the shader quad to the current FBO.
|
||||
* @param oldPreset A reference to the old (fading out) preset.
|
||||
* @param newPreset A reference to the new (fading in) preset.
|
||||
* @param context The rendering context used to render the presets.
|
||||
* @param audioData Current audio data and beat detection values.
|
||||
*/
|
||||
void Draw(const Preset& oldPreset,
|
||||
const Preset& newPreset,
|
||||
const RenderContext& context,
|
||||
const libprojectM::Audio::FrameAudioData& audioData);
|
||||
|
||||
private:
|
||||
std::vector<std::string> m_noiseTextureNames{"noise_lq",
|
||||
"pw_noise_lq",
|
||||
"noise_mq",
|
||||
"pw_noise_mq",
|
||||
"noise_hq",
|
||||
"pw_noise_hq",
|
||||
"noisevol_lq",
|
||||
"pw_noisevol_lq",
|
||||
"noisevol_hq",
|
||||
"pw_noisevol_hq"}; //!< Names of noise textures to retrieve from TextureManager.
|
||||
|
||||
std::shared_ptr<Shader> m_transitionShader; //!< The compiled shader used for this transition.
|
||||
std::shared_ptr<Sampler> m_presetSampler{std::make_shared<Sampler>(GL_CLAMP_TO_EDGE, GL_LINEAR)}; //!< Sampler for preset textures. Uses bilinear interpolation and no repeat.
|
||||
|
||||
double m_durationSeconds{3.0}; //!< Transition duration in seconds.
|
||||
std::chrono::time_point<std::chrono::system_clock> m_transitionStartTime{std::chrono::system_clock::now()}; //!< Start time of this transition. Duration is measured from this point.
|
||||
std::chrono::time_point<std::chrono::system_clock> m_lastFrameTime{std::chrono::system_clock::now()}; //!< Time when the previous frame was rendered.
|
||||
|
||||
glm::ivec4 m_staticRandomValues{}; //!< Four random integers, remaining static during the whole transition.
|
||||
|
||||
std::random_device m_randomDevice; //!< Seed for the random number generator
|
||||
};
|
||||
@ -0,0 +1,13 @@
|
||||
|
||||
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
||||
{
|
||||
vec2 uv = fragCoord / iResolution.xy;
|
||||
vec3 imgOld = texture(iChannel0, uv).xyz;
|
||||
vec3 imgNew = texture(iChannel1, uv).xyz;
|
||||
|
||||
// Blending
|
||||
vec3 col = vec3((1.0 - iProgressCosine) * imgOld + iProgressCosine * imgNew);
|
||||
|
||||
// Output to screen
|
||||
fragColor = vec4(col, 1.0);
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
// Uniforms
|
||||
uniform vec3 iResolution;
|
||||
uniform vec4 durationParams;
|
||||
uniform vec2 timeParams;
|
||||
uniform float iFrameRate;
|
||||
uniform int iFrame;
|
||||
uniform ivec4 iRandStatic;
|
||||
uniform ivec4 iRandFrame;
|
||||
uniform vec3 iBeatValues;
|
||||
uniform vec3 iBeatAttValues;
|
||||
|
||||
#define iProgressLinear durationParams.x
|
||||
#define iProgressCosine durationParams.y
|
||||
#define iProgressBicubic durationParams.z
|
||||
#define iTransitionDuration durationParams.w
|
||||
|
||||
#define iTime timeParams.x
|
||||
#define iTimeDelta timeParams.y
|
||||
|
||||
#define iBass iBeatValues.x;
|
||||
#define iMid iBeatValues.y;
|
||||
#define iTreb iBeatValues.z;
|
||||
|
||||
#define iBassAtt iBeatAttValues.x;
|
||||
#define iMidAtt iBeatAttValues.y;
|
||||
#define iTrebAtt iBeatAttValues.z;
|
||||
|
||||
// Samplers
|
||||
uniform sampler2D iChannel0;
|
||||
uniform sampler2D iChannel1;
|
||||
|
||||
// These are named as in Milkdrop shaders so we can reuse the code.
|
||||
uniform sampler2D sampler_noise_lq;
|
||||
uniform sampler2D sampler_pw_noise_lq;
|
||||
uniform sampler2D sampler_noise_mq;
|
||||
uniform sampler2D sampler_pw_noise_mq;
|
||||
uniform sampler2D sampler_noise_hq;
|
||||
uniform sampler2D sampler_pw_noise_hq;
|
||||
uniform sampler3D sampler_noisevol_lq;
|
||||
uniform sampler3D sampler_pw_noisevol_lq;
|
||||
uniform sampler3D sampler_noisevol_hq;
|
||||
uniform sampler3D sampler_pw_noisevol_hq;
|
||||
|
||||
#define iNoiseLQ sampler_noise_lq;
|
||||
#define iNoiseLQNearest sampler_pw_noise_lq;
|
||||
#define iNoiseMQ sampler_noise_mq;
|
||||
#define iNoiseMQNearest sampler_pw_noise_mq;
|
||||
#define iNoiseHQ sampler_noise_hq;
|
||||
#define iNoiseHQNearest sampler_pw_noise_hq;
|
||||
#define iNoiseVolLQ sampler_noisevol_lq;
|
||||
#define iNoiseVolLQNearest sampler_pw_noisevol_lq;
|
||||
#define iNoiseVolHQ sampler_noisevol_hq;
|
||||
#define iNoiseVolHQNearest sampler_pw_noisevol_hq;
|
||||
|
||||
// Shader output
|
||||
out vec4 _prjm_transition_out;
|
||||
@ -0,0 +1,10 @@
|
||||
|
||||
void main() {
|
||||
_prjm_transition_out = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
vec4 _user_out_color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
mainImage(_user_out_color, gl_FragCoord.xy);
|
||||
|
||||
_prjm_transition_out = vec4(_user_out_color.xyz, 1.0);
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
precision mediump float;
|
||||
|
||||
layout(location = 0) in vec2 iPosition;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(iPosition, 0.0, 1.0);
|
||||
}
|
||||
Reference in New Issue
Block a user