Set warp texel offset to 0 by default, as 0.5 introduces a drift.

Since this might be vendor-specific behavior, a new API function to configure the X/Y texel offsets has been added.

Signed-off-by: Kai Blaschke <kai.blaschke@kb-dev.net>
This commit is contained in:
yoyofr
2025-11-19 11:00:54 +01:00
committed by Kai Blaschke
parent d3734dc4c3
commit 15a083751d
6 changed files with 68 additions and 2 deletions

View File

@ -227,6 +227,34 @@ PROJECTM_EXPORT void projectm_set_mesh_size(projectm_handle instance, size_t wid
*/
PROJECTM_EXPORT void projectm_get_mesh_size(projectm_handle instance, size_t* width, size_t* height);
/**
* @brief Applies a sub-texel offset for main texture lookups in the warp shader.
*
* Original Milkdrop uses 0.5 here, but it doesn't seem to be required in OpenGL as this value
* introduces a slight warp drift to the top left. As this may be vendor-specific, this value can
* be configured externally to fix any possible drift.
*
* @param instance The projectM instance handle.
* @param offset_X The offset in texels in the horizontal direction. Milkdrop uses 0.5, default in projectM is 0.0.
* @param offset_y The offset in texels in the vertical direction. Milkdrop uses 0.5, default in projectM is 0.0.
* @since 4.2.0
*/
PROJECTM_EXPORT void projectm_set_texel_offset(projectm_handle instance, float offset_X, float offset_y);
/**
* @brief Retrieves the current sub-texel offsets for main texture lookups in the warp shader.
*
* Original Milkdrop uses 0.5 here, but it doesn't seem to be required in OpenGL as this value
* introduces a slight warp drift to the top left. As this may be vendor-specific, this value can
* be configured externally to fix any possible drift.
*
* @param instance The projectM instance handle.
* @param offset_X A valid pointer to a float variable that will receive the currently set horizontal texel offset.
* @param offset_y A valid pointer to a float variable that will receive the currently set vertical texel offset.
* @since 4.2.0
*/
PROJECTM_EXPORT void projectm_get_texel_offset(projectm_handle instance, float* offset_X, float* offset_y);
/**
* @brief Sets the current/average frames per second.
*

View File

@ -308,8 +308,8 @@ void PerPixelMesh::WarpedBlit(const PresetState& presetState,
};
// Texel alignment
glm::vec2 const texelOffsets{0.5f / static_cast<float>(presetState.renderContext.viewportSizeX),
0.5f / static_cast<float>(presetState.renderContext.viewportSizeY)};
glm::vec2 const texelOffsets{presetState.renderContext.texelOffsetX / static_cast<float>(presetState.renderContext.viewportSizeX),
presetState.renderContext.texelOffsetY / static_cast<float>(presetState.renderContext.viewportSizeY)};
// Decay
float decay = std::min(static_cast<float>(*perFrameContext.decay), 1.0f);

View File

@ -455,6 +455,18 @@ void ProjectM::SetMeshSize(uint32_t meshResolutionX, uint32_t meshResolutionY)
m_meshY = std::max(8u, std::min(400u, m_meshY));
}
void ProjectM::TexelOffsets(float& texelOffsetX, float& texelOffsetY) const
{
texelOffsetX = m_texelOffsetX;
texelOffsetY = m_texelOffsetY;
}
void ProjectM::SetTexelOffsets(float texelOffsetX, float texelOffsetY)
{
m_texelOffsetX = texelOffsetX;
m_texelOffsetY = texelOffsetY;
}
auto ProjectM::PCM() -> libprojectM::Audio::PCM&
{
return m_audioStorage;
@ -493,8 +505,13 @@ auto ProjectM::GetRenderContext() -> Renderer::RenderContext
ctx.aspectY = (m_windowWidth > m_windowHeight) ? static_cast<float>(m_windowHeight) / static_cast<float>(m_windowWidth) : 1.0f;
ctx.invAspectX = 1.0f / ctx.aspectX;
ctx.invAspectY = 1.0f / ctx.aspectY;
ctx.perPixelMeshX = static_cast<int>(m_meshX);
ctx.perPixelMeshY = static_cast<int>(m_meshY);
ctx.texelOffsetX = m_texelOffsetX;
ctx.texelOffsetY = m_texelOffsetY;
ctx.textureManager = m_textureManager.get();
ctx.shaderCache = m_shaderCache.get();

View File

@ -177,6 +177,10 @@ public:
void SetMeshSize(uint32_t meshResolutionX, uint32_t meshResolutionY);
void TexelOffsets(float& texelOffsetX, float& texelOffsetY) const;
void SetTexelOffsets(float texelOffsetX, float texelOffsetY);
void Touch(float touchX, float touchY, int pressure, int touchType);
void TouchDrag(float touchX, float touchY, int pressure);
@ -265,6 +269,8 @@ private:
bool m_aspectCorrection{true}; //!< If true, corrects aspect ratio for non-rectangular windows.
float m_easterEgg{1.0}; //!< Random preset duration modifier. See TimeKeeper class.
float m_previousFrameVolume{}; //!< Volume in previous frame, used for hard cuts.
float m_texelOffsetX{0.0}; //!< Horizontal warp shader texel offset
float m_texelOffsetY{0.0}; //!< Vertical warp shader texel offset
std::vector<std::string> m_textureSearchPaths; ///!< List of paths to search for texture files

View File

@ -273,6 +273,18 @@ void projectm_get_mesh_size(projectm_handle instance, size_t* width, size_t* hei
*height = static_cast<size_t>(h);
}
void projectm_set_texel_offset(projectm_handle instance, float offset_X, float offset_y)
{
auto projectMInstance = handle_to_instance(instance);
projectMInstance->SetTexelOffsets(offset_X, offset_y);
}
void projectm_get_texel_offset(projectm_handle instance, float* offset_X, float* offset_y)
{
auto projectMInstance = handle_to_instance(instance);
projectMInstance->TexelOffsets(*offset_X, *offset_y);
}
void projectm_set_mesh_size(projectm_handle instance, size_t width, size_t height)
{
auto projectMInstance = handle_to_instance(instance);

View File

@ -31,6 +31,9 @@ public:
int perPixelMeshX{64}; //!< Per-pixel/per-vertex mesh X resolution.
int perPixelMeshY{48}; //!< Per-pixel/per-vertex mesh Y resolution.
float texelOffsetX{0.0f}; //!< Horizontal texel offset in the warp shader.
float texelOffsetY{0.0f}; //!< Vertical texel offset in the warp shader.
TextureManager* textureManager{nullptr}; //!< Holds all loaded textures for shader access.
ShaderCache* shaderCache{nullptr}; //!< The shader chace of this projectM instance.
};