Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation to shaders #121

Merged
merged 2 commits into from
Dec 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
add_subdirectory(kcm)

set(effect_SRCS
ShapeCornersEffect.h
ShapeCornersEffect.cpp
ShapeCornersShader.h
ShapeCornersShader.cpp
plugin.cpp
)
Expand Down
90 changes: 88 additions & 2 deletions src/ShapeCornersShader.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,113 @@ namespace KWin {

class ShapeCornersShader {
public:
ShapeCornersShader();
/**
* \brief Loads the correct shader based on the GL platform in KWin.
* Then it assigns the location references for all its uniform variables so they could be written in.
*/
explicit ShapeCornersShader();

/**
* \return True if the GL platform version used in KWin is older than 1.40.
*/
static bool IsLegacy();
bool IsValid() const;

/**
* \return True if the shader is valid and loaded
*/
[[nodiscard]] bool IsValid() const;

/**
* \brief This function assigns the required variables to the shader.
* Then it pushes the shader to the stack of rendering.
* This needs to be called before each window is rendered.
* \param w The window that the effect will be rendering on
* \return A reference to the unique pointer of the loaded shader.
*/
const std::unique_ptr<KWin::GLShader>& Bind(KWin::EffectWindow *w) const;
const std::unique_ptr<KWin::GLShader>& Bind(QMatrix4x4 mvp, KWin::EffectWindow *w) const;

/**
* \brief Pop the shader from the stack of rendering.
* This needs to be called after each window is rendered.
*/
void Unbind() const;

/**
* \return A reference to the unique pointer of the loaded shader.
*/
std::unique_ptr<KWin::GLShader>& GetShader() { return m_shader; }

private:
/**
* \brief A pointer to the loaded shader used to assign the value of uniform variables.
*/
std::unique_ptr<KWin::GLShader> m_shader;

/**
* \brief An instance of `ShaderManager` used to load shader from file and push/pop the shader for each render.
*/
KWin::ShaderManager* m_manager;

/**
* \brief Used only for its `palette()` function which holds the currently active highlight colors.
*/
QWidget m_widget;

/**
* \brief Reference to `uniform vec2 windowSize;`
* Containing `window->frameGeometry().size()`
*/
int m_shader_windowSize = 0;

/**
* \brief Reference to `uniform vec2 windowExpandedSize;`
* Containing `window->expandedGeometry().size()`
* \note When `expandedGeometry = frameGeometry`, it means there is no shadow.
*/
int m_shader_windowExpandedSize = 0;

/**
* \brief Reference to `uniform vec2 windowTopLeft;`
* Containing the distance between the top-left of `expandedGeometry` and the top-left of `frameGeometry`.
* \note When `windowTopLeft = {0,0}`, it means `expandedGeometry = frameGeometry` and there is no shadow.
*/
int m_shader_windowTopLeft = 0;

/**
* \brief Reference to `uniform vec4 shadowColor;`
* Containing the RGBA of the shadow color specified in settings.
*/
int m_shader_shadowColor = 0;

/**
* \brief Reference to `uniform float shadowSize;`
* Containing the shadow size specified in settings.
*/
int m_shader_shadowSize = 0;

/**
* \brief Reference to `uniform float radius;`
* Containing the corner radius in pixels specified in settings.
*/
int m_shader_radius = 0;

/**
* \brief Reference to `uniform vec4 outlineColor;`
* Containing the RGBA of the outline color specified in settings.
*/
int m_shader_outlineColor = 0;

/**
* \brief Reference to `uniform float outlineThickness;`
* Containing the thickness of the outline in pixels specified in settings.
*/
int m_shader_outlineThickness = 0;

/**
* \brief Reference to `uniform sampler2D front;`
* Containing the active texture. When assigned to zero, it will contain the painted contents of the window.
*/
int m_shader_front = 0;
};

Expand Down
1 change: 1 addition & 0 deletions src/kcm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
set(kcm_SRCS
ShapeCornersKCM.h
ShapeCornersKCM.cpp
plugin.cpp
)
Expand Down
3 changes: 2 additions & 1 deletion src/kcm/ShapeCornersKCM.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

#pragma once

#include <kcmodule.h>
#include "ui_ShapeCornersKCM.h"
#include "ShapeCornersConfig.h"
Expand Down
89 changes: 65 additions & 24 deletions src/shaders_110/shapecorners.frag
Original file line number Diff line number Diff line change
@@ -1,87 +1,128 @@
#version 110

uniform sampler2D front;
uniform float radius;
uniform vec2 windowSize;
uniform vec2 windowExpandedSize;
uniform vec2 windowTopLeft;
uniform vec4 shadowColor;
uniform float shadowSize;
uniform vec4 outlineColor;
uniform float outlineThickness;
uniform vec4 modulation;
uniform float saturation;

varying vec2 texcoord0;
uniform sampler2D front; // The painted contents of the window.
uniform float radius; // The thickness of the outline in pixels specified in settings.
uniform vec2 windowSize; // Containing `window->frameGeometry().size()`
uniform vec2 windowExpandedSize; // Containing `window->expandedGeometry().size()`

uniform vec2 windowTopLeft; /* Containing the distance between the top-left of `expandedGeometry` and
* the top-left of `frameGeometry`. When `windowTopLeft = {0,0}`, it means
* `expandedGeometry = frameGeometry` and there is no shadow. */

uniform vec4 shadowColor; // The RGBA of the shadow color specified in settings.
uniform float shadowSize; // The shadow size specified in settings.
uniform vec4 outlineColor; // The RGBA of the outline color specified in settings.
uniform float outlineThickness; // The thickness of the outline in pixels specified in settings.

uniform vec4 modulation; // This variable is assigned and used by KWinEffects used for proper fading.
uniform float saturation; // This variable is assigned and used by KWinEffects used for proper fading.

varying vec2 texcoord0; // The XY location of the rendering pixel. Starting from {0.0, 0.0} to {1.0, 1.0}

// Note: This version of GLSL uses the built-in variable `gl_FragColor` instead of `out vec4 fragColor;`

bool isDrawingShadows() { return windowSize != windowExpandedSize && shadowColor.a > 0.0; }
bool isDrawingOutline() { return outlineColor.a > 0.0 && outlineThickness > 0.0; }

vec4 shadowCorner(float distance_from_center) {
/*
* \brief This function generates the shadow color based on the distance_from_center
* \param distance_from_center: Distance of the rendering point and the reference point that is being used for rounding corners.
* \return The RGBA color to be used for shadow.
*/
vec4 getShadowColor(float distance_from_center) {
if(!isDrawingShadows())
return vec4(0.0, 0.0, 0.0, 0.0);
float percent = -distance_from_center/shadowSize + 1.0;
if (percent < 0.0)
return vec4(0.0, 0.0, 0.0, 0.0);
else
return vec4(shadowColor.rgb * shadowColor.a * percent, shadowColor.a * percent);
}

/*
* \brief This function is used to choose the pixel color based on its distance to the center input.
* \param coord0: The XY point
* \param tex: The RGBA color of the pixel in XY
* \param center: The origin XY point that is being used as a reference for rounding corners.
* \return The RGBA color to be used instead of tex input.
*/
vec4 shapeCorner(vec2 coord0, vec4 tex, vec2 center) {
float distance_from_center = distance(coord0, center);
vec4 c = isDrawingShadows() ? shadowCorner(distance_from_center) : vec4(0.0,0.0,0.0,0.0);
vec4 c = getShadowColor(distance_from_center);

if(isDrawingOutline()) {
vec4 outlineOverlay = vec4(mix(tex.rgb, outlineColor.rgb, outlineColor.a), 1.0);

if (distance_from_center < radius - outlineThickness/2.0) {
// from the window to the outline
float antialiasing = clamp(radius-outlineThickness+0.5-distance_from_center, 0.0, 1.0);
return mix(outlineOverlay, tex, antialiasing);
}
else {
// from the outline to the shadow
float antialiasing = clamp(distance_from_center-radius+0.5, 0.0, 1.0);
return mix(outlineOverlay, c, antialiasing);
}
}
else {
// from the window to the shadow
float antialiasing = clamp(radius-distance_from_center, 0.0, 1.0);
return mix(c, tex, antialiasing);
}
}

void main(void)
{
vec4 tex = texture2D(front, texcoord0);
vec4 tex = texture2D(front, texcoord0); // The RGBA of the XY pixel for the painted window

/* Since `texcoord0` is ranging from {0.0, 0.0} to {1.0, 1.0} is not pixel intuitive,
* I am changing the range to become from {0.0, 0.0} to {width, height}
* in a way that {0,0} is the top-left of the window and not its shadow.
* This means areas with negative numbers and areas beyond windowSize is considered part of the shadow. */
vec2 coord0 = vec2(texcoord0.x * windowExpandedSize.x - windowTopLeft.x,
(1.0-texcoord0.y)* windowExpandedSize.y - windowTopLeft.y);

/*
Split the window into these sections below. They will have a different center of circle for rounding.

TL T T TR
L x x R
L x x R
BL B B BR
*/
if (coord0.y < radius) {
if (coord0.x < radius)
tex = shapeCorner(coord0, tex, vec2(radius, radius));
tex = shapeCorner(coord0, tex, vec2(radius, radius)); // Section TL
else if (coord0.x > windowSize.x - radius)
tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, radius));
tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, radius)); // Section TR
else if (coord0.y < outlineThickness)
tex = shapeCorner(coord0, tex, vec2(coord0.x, radius));
tex = shapeCorner(coord0, tex, vec2(coord0.x, radius)); // Section T
}
else if (coord0.y > windowSize.y - radius) {
if (coord0.x < radius)
tex = shapeCorner(coord0, tex, vec2(radius, windowSize.y - radius));
tex = shapeCorner(coord0, tex, vec2(radius, windowSize.y - radius)); // Section BL
else if (coord0.x > windowSize.x - radius)
tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, windowSize.y - radius));
tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, windowSize.y - radius)); // Section BR
else if (coord0.y > windowSize.y - outlineThickness)
tex = shapeCorner(coord0, tex, vec2(coord0.x, windowSize.y - radius));
tex = shapeCorner(coord0, tex, vec2(coord0.x, windowSize.y - radius)); // Section B
}
else {
if (coord0.x < radius)
tex = shapeCorner(coord0, tex, vec2(radius, coord0.y));
tex = shapeCorner(coord0, tex, vec2(radius, coord0.y)); // Section L
else if (coord0.x > windowSize.x - radius)
tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, coord0.y));
tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, coord0.y)); // Section R

// For section x, the tex is not changing
}

// Apply the saturation and modulation. This is essential for proper fades in other KWin effects.
if (saturation != 1.0) {
vec3 desaturated = tex.rgb * vec3( 0.30, 0.59, 0.11 );
desaturated = vec3( dot( desaturated, tex.rgb ));
tex.rgb = tex.rgb * vec3( saturation ) + desaturated * vec3( 1.0 - saturation );
}
tex *= modulation;

// Send to output
gl_FragColor = tex;
}
Loading